Merge branch 'master' into lua-scripting
[screen-lua.git] / src / screen.c
blob80abdd0564ff82c8806a8c78da325589837ec444
1 /* Copyright (c) 1993-2002
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Copyright (c) 1987 Oliver Laumann
5 #ifdef HAVE_BRAILLE
6 * Modified by:
7 * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu
8 * Bill Barry barryb@dots.physics.orst.edu
9 * Randy Lundquist randyl@dots.physics.orst.edu
11 * Modifications Copyright (c) 1995 by
12 * Science Access Project, Oregon State University.
13 #endif
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2, or (at your option)
18 * any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program (see the file COPYING); if not, write to the
27 * Free Software Foundation, Inc.,
28 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
30 ****************************************************************
33 #include <sys/types.h>
34 #include <ctype.h>
36 #include <fcntl.h>
38 #ifdef sgi
39 # include <sys/sysmacros.h>
40 #endif
42 #include <sys/stat.h>
43 #ifndef sun
44 # include <sys/ioctl.h>
45 #endif
47 #ifndef SIGINT
48 # include <signal.h>
49 #endif
51 #include "config.h"
53 #ifdef HAVE_STROPTS_H
54 # include <sys/stropts.h>
55 #endif
57 #if defined(SYSV) && !defined(ISC)
58 # include <sys/utsname.h>
59 #endif
61 #if defined(sequent) || defined(SVR4)
62 # include <sys/resource.h>
63 #endif /* sequent || SVR4 */
65 #ifdef ISC
66 # include <sys/tty.h>
67 # include <sys/sioctl.h>
68 # include <sys/pty.h>
69 #endif /* ISC */
71 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
72 # include <compat.h>
73 #endif
74 #if defined(USE_LOCALE) || defined(ENCODINGS)
75 # include <locale.h>
76 #endif
77 #if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
78 # include <langinfo.h>
79 #endif
81 #include "screen.h"
82 #ifdef HAVE_BRAILLE
83 # include "braille.h"
84 #endif
86 #include "patchlevel.h"
89 * At the moment we only need the real password if the
90 * builtin lock is used. Therefore disable SHADOWPW if
91 * we do not really need it (kind of security thing).
93 #ifndef LOCK
94 # undef SHADOWPW
95 #endif
97 #include <pwd.h>
98 #ifdef SHADOWPW
99 # include <shadow.h>
100 #endif /* SHADOWPW */
102 #include "logfile.h" /* islogfile, logfflush */
104 #ifdef DEBUG
105 FILE *dfp;
106 #endif
109 extern char Term[], screenterm[], **environ, Termcap[];
110 int force_vt = 1;
111 int VBellWait, MsgWait, MsgMinWait, SilenceWait;
113 extern struct acluser *users;
114 extern struct display *displays, *display;
117 extern int visual_bell;
118 #ifdef COPY_PASTE
119 extern unsigned char mark_key_tab[];
120 #endif
121 extern char version[];
122 extern char DefaultShell[];
123 #ifdef ZMODEM
124 extern char *zmodem_sendcmd;
125 extern char *zmodem_recvcmd;
126 #endif
127 extern struct layout *layout_last;
130 char *ShellProg;
131 char *ShellArgs[2];
133 extern struct NewWindow nwin_undef, nwin_default, nwin_options;
134 struct backtick;
136 static struct passwd *getpwbyname __P((char *, struct passwd *));
137 static void SigChldHandler __P((void));
138 static sigret_t SigChld __P(SIGPROTOARG);
139 static sigret_t SigInt __P(SIGPROTOARG);
140 static sigret_t CoreDump __P(SIGPROTOARG);
141 static sigret_t FinitHandler __P(SIGPROTOARG);
142 static void DoWait __P((void));
143 static void serv_read_fn __P((struct event *, char *));
144 static void serv_select_fn __P((struct event *, char *));
145 static void logflush_fn __P((struct event *, char *));
146 static void backtick_filter __P((struct backtick *));
147 static void backtick_fn __P((struct event *, char *));
148 static char *runbacktick __P((struct backtick *, int *, time_t));
149 static int IsSymbol __P((char *, char *));
150 static char *ParseChar __P((char *, char *));
151 static int ParseEscape __P((char *));
152 static char *pad_expand __P((char *, char *, int, int));
153 #ifdef DEBUG
154 static void fds __P((void));
155 #endif
157 int nversion; /* numerical version, used for secondary DA */
159 /* the attacher */
160 struct passwd *ppp;
161 char *attach_tty;
162 int attach_fd = -1;
163 char *attach_term;
164 char *LoginName;
165 struct mode attach_Mode;
167 char SockPath[MAXPATHLEN + 2 * MAXSTR];
168 char *SockName; /* SockName is pointer in SockPath */
169 char *SockMatch = NULL; /* session id command line argument */
170 int ServerSocket = -1;
171 struct event serv_read;
172 struct event serv_select;
173 struct event logflushev;
175 char **NewEnv = NULL;
177 char *RcFileName = NULL;
178 char *home;
180 char *screenlogfile; /* filename layout */
181 int log_flush = 10; /* flush interval in seconds */
182 int logtstamp_on = 0; /* tstamp disabled */
183 char *logtstamp_string; /* stamp layout */
184 int logtstamp_after = 120; /* first tstamp after 120s */
185 char *hardcopydir = NULL;
186 char *BellString;
187 char *VisualBellString;
188 char *ActivityString;
189 #ifdef COPY_PASTE
190 char *BufferFile;
191 #endif
192 #ifdef POW_DETACH
193 char *PowDetachString;
194 #endif
195 char *hstatusstring;
196 char *captionstring;
197 char *timestring;
198 char *wliststr;
199 char *wlisttit;
200 int auto_detach = 1;
201 int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
202 int cmdflag;
203 int adaptflag;
205 #ifdef MULTIUSER
206 char *multi;
207 char *multi_home;
208 int multi_uid;
209 int own_uid;
210 int multiattach;
211 int tty_mode;
212 int tty_oldmode = -1;
213 #endif
215 char HostName[MAXSTR];
216 int MasterPid, PanicPid;
217 int real_uid, real_gid, eff_uid, eff_gid;
218 int default_startup;
219 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
220 char *preselect = NULL; /* only used in Attach() */
222 #ifdef UTF8
223 char *screenencodings;
224 #endif
226 #ifdef DW_CHARS
227 int cjkwidth;
228 #endif
230 #ifdef NETHACK
231 int nethackflag = 0;
232 #endif
233 int maxwin = MAXWIN;
236 struct layer *flayer;
237 struct win *fore;
238 struct win *windows;
239 struct win *console_window;
244 * Do this last
246 #include "extern.h"
248 char strnomem[] = "Out of memory.";
251 static int InterruptPlease;
252 static int GotSigChld;
254 static int
255 lf_secreopen(name, wantfd, l)
256 char *name;
257 int wantfd;
258 struct logfile *l;
260 int got_fd;
262 close(wantfd);
263 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
264 lf_move_fd(got_fd, wantfd) < 0)
266 logfclose(l);
267 debug1("lf_secreopen: failed for %s\n", name);
268 return -1;
270 l->st->st_ino = l->st->st_dev = 0;
271 debug2("lf_secreopen: %d = %s\n", wantfd, name);
272 return 0;
275 /********************************************************************/
276 /********************************************************************/
277 /********************************************************************/
280 static struct passwd *
281 getpwbyname(name, ppp)
282 char *name;
283 struct passwd *ppp;
285 int n;
286 #ifdef SHADOWPW
287 struct spwd *sss = NULL;
288 static char *spw = NULL;
289 #endif
291 if (!ppp && !(ppp = getpwnam(name)))
292 return NULL;
294 /* Do password sanity check..., allow ##user for SUN_C2 security */
295 #ifdef SHADOWPW
296 pw_try_again:
297 #endif
298 n = 0;
299 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
300 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
301 n = 13;
302 for (; n < 13; n++)
304 char c = ppp->pw_passwd[n];
305 if (!(c == '.' || c == '/' || c == '$' ||
306 (c >= '0' && c <= '9') ||
307 (c >= 'a' && c <= 'z') ||
308 (c >= 'A' && c <= 'Z')))
309 break;
312 #ifdef SHADOWPW
313 /* try to determine real password */
314 if (n < 13 && sss == 0)
316 sss = getspnam(ppp->pw_name);
317 if (sss)
319 if (spw)
320 free(spw);
321 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
322 endspent(); /* this should delete all buffers ... */
323 goto pw_try_again;
325 endspent(); /* this should delete all buffers ... */
327 #endif
328 if (n < 13)
329 ppp->pw_passwd = 0;
330 #ifdef linux
331 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
332 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
333 #endif
335 return ppp;
340 main(ac, av)
341 int ac;
342 char **av;
344 register int n;
345 char *ap;
346 char *av0;
347 char socknamebuf[2 * MAXSTR];
348 int mflag = 0;
349 char *myname = (ac == 0) ? "screen" : av[0];
350 char *SockDir;
351 struct stat st;
352 #ifdef _MODE_T /* (jw) */
353 mode_t oumask;
354 #else
355 int oumask;
356 #endif
357 #if defined(SYSV) && !defined(ISC)
358 struct utsname utsnam;
359 #endif
360 struct NewWindow nwin;
361 int detached = 0; /* start up detached */
362 #ifdef MULTIUSER
363 char *sockp;
364 #endif
365 char *script_file = 0;
366 char *sty = 0;
368 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
369 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
370 #endif
371 #if defined(sun) && defined(SVR4)
373 /* Solaris' login blocks SIGHUP! This is _very bad_ */
374 sigset_t sset;
375 sigemptyset(&sset);
376 sigprocmask(SIG_SETMASK, &sset, 0);
378 #endif
381 * First, close all unused descriptors
382 * (otherwise, we might have problems with the select() call)
384 closeallfiles(0);
385 #ifdef DEBUG
386 opendebug(1, 0);
387 #endif
388 sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
389 PATCHLEVEL, STATE, ORIGIN, DATE);
390 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
391 debug2("-- screen debug started %s (%s)\n", *av, version);
392 #ifdef POSIX
393 debug("POSIX\n");
394 #endif
395 #ifdef TERMIO
396 debug("TERMIO\n");
397 #endif
398 #ifdef SYSV
399 debug("SYSV\n");
400 #endif
401 #ifdef SYSVSIGS
402 debug("SYSVSIGS\n");
403 #endif
404 #ifdef NAMEDPIPE
405 debug("NAMEDPIPE\n");
406 #endif
407 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
408 debug("Window size changing enabled\n");
409 #endif
410 #ifdef HAVE_SETREUID
411 debug("SETREUID\n");
412 #endif
413 #ifdef HAVE_SETEUID
414 debug("SETEUID\n");
415 #endif
416 #ifdef hpux
417 debug("hpux\n");
418 #endif
419 #ifdef USEBCOPY
420 debug("USEBCOPY\n");
421 #endif
422 #ifdef UTMPOK
423 debug("UTMPOK\n");
424 #endif
425 #ifdef LOADAV
426 debug("LOADAV\n");
427 #endif
428 #ifdef NETHACK
429 debug("NETHACK\n");
430 #endif
431 #ifdef TERMINFO
432 debug("TERMINFO\n");
433 #endif
434 #ifdef SHADOWPW
435 debug("SHADOWPW\n");
436 #endif
437 #ifdef NAME_MAX
438 debug1("NAME_MAX = %d\n", NAME_MAX);
439 #endif
441 BellString = SaveStr("Bell in window %n");
442 VisualBellString = SaveStr(" Wuff, Wuff!! ");
443 ActivityString = SaveStr("Activity in window %n");
444 screenlogfile = SaveStr("screenlog.%n");
445 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
446 hstatusstring = SaveStr("%h");
447 captionstring = SaveStr("%3n %t");
448 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
449 wlisttit = SaveStr("Num Name%=Flags");
450 wliststr = SaveStr("%3n %t%=%f");
451 #ifdef COPY_PASTE
452 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
453 #endif
454 ShellProg = NULL;
455 #ifdef POW_DETACH
456 PowDetachString = 0;
457 #endif
458 default_startup = (ac > 1) ? 0 : 1;
459 adaptflag = 0;
460 VBellWait = VBELLWAIT * 1000;
461 MsgWait = MSGWAIT * 1000;
462 MsgMinWait = MSGMINWAIT * 1000;
463 SilenceWait = SILENCEWAIT;
464 #ifdef HAVE_BRAILLE
465 InitBraille();
466 #endif
467 #ifdef ZMODEM
468 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
469 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
470 #endif
472 #ifdef COPY_PASTE
473 CompileKeys((char *)0, 0, mark_key_tab);
474 #endif
475 #ifdef UTF8
476 InitBuiltinTabs();
477 screenencodings = SaveStr(SCREENENCODINGS);
478 #endif
479 #ifdef DW_CHARS
480 cjkwidth = 0;
481 #endif
482 nwin = nwin_undef;
483 nwin_options = nwin_undef;
484 strcpy(screenterm, "screen");
486 logreopen_register(lf_secreopen);
488 av0 = *av;
489 /* if this is a login screen, assume -RR */
490 if (*av0 == '-')
492 rflag = 4;
493 #ifdef MULTI
494 xflag = 1;
495 #else
496 dflag = 1;
497 #endif
498 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
500 while (ac > 0)
502 ap = *++av;
503 if (--ac > 0 && *ap == '-')
505 if (ap[1] == '-' && ap[2] == 0)
507 av++;
508 ac--;
509 break;
511 if (ap[1] == '-' && !strcmp(ap, "--version"))
512 Panic(0, "Screen version %s", version);
513 if (ap[1] == '-' && !strcmp(ap, "--help"))
514 exit_with_usage(myname, NULL, NULL);
515 while (ap && *ap && *++ap)
517 switch (*ap)
519 case 'a':
520 nwin_options.aflag = 1;
521 break;
522 case 'A':
523 adaptflag = 1;
524 break;
525 case 'p': /* preselect */
526 if (*++ap)
527 preselect = ap;
528 else
530 if (!--ac)
531 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
532 preselect = *++av;
534 ap = NULL;
535 break;
536 #ifdef HAVE_BRAILLE
537 case 'B':
538 bd.bd_start_braille = 1;
539 break;
540 #endif
541 case 'c':
542 if (*++ap)
543 RcFileName = ap;
544 else
546 if (--ac == 0)
547 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
548 RcFileName = *++av;
550 ap = NULL;
551 break;
552 case 'e':
553 if (!*++ap)
555 if (--ac == 0)
556 exit_with_usage(myname, "Specify command characters with -e", NULL);
557 ap = *++av;
559 if (ParseEscape(ap))
560 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
561 ap = NULL;
562 break;
563 case 'f':
564 ap++;
565 switch (*ap++)
567 case 'n':
568 case '0':
569 nwin_options.flowflag = FLOW_NOW * 0;
570 break;
571 case '\0':
572 ap--;
573 /* FALLTHROUGH */
574 case 'y':
575 case '1':
576 nwin_options.flowflag = FLOW_NOW * 1;
577 break;
578 case 'a':
579 nwin_options.flowflag = FLOW_AUTOFLAG;
580 break;
581 default:
582 exit_with_usage(myname, "Unknown flow option -%s", --ap);
584 break;
585 case 'h':
586 if (--ac == 0)
587 exit_with_usage(myname, NULL, NULL);
588 nwin_options.histheight = atoi(*++av);
589 if (nwin_options.histheight < 0)
590 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
591 break;
592 case 'i':
593 iflag = 1;
594 break;
595 case 't': /* title, the former AkA == -k */
596 if (--ac == 0)
597 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
598 nwin_options.aka = *++av;
599 break;
600 case 'l':
601 ap++;
602 switch (*ap++)
604 case 'n':
605 case '0':
606 nwin_options.lflag = 0;
607 break;
608 case '\0':
609 ap--;
610 /* FALLTHROUGH */
611 case 'y':
612 case '1':
613 nwin_options.lflag = 1;
614 break;
615 case 'a':
616 nwin_options.lflag = 3;
617 break;
618 case 's': /* -ls */
619 case 'i': /* -list */
620 lsflag = 1;
621 if (ac > 1 && !SockMatch)
623 SockMatch = *++av;
624 ac--;
626 ap = NULL;
627 break;
628 default:
629 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
631 break;
632 case 'w':
633 lsflag = 1;
634 wipeflag = 1;
635 if (ac > 1 && !SockMatch)
637 SockMatch = *++av;
638 ac--;
640 break;
641 case 'L':
642 nwin_options.Lflag = 1;
643 break;
644 case 'm':
645 mflag = 1;
646 break;
647 case 'O': /* to be (or not to be?) deleted. jw. */
648 force_vt = 0;
649 break;
650 case 'T':
651 if (--ac == 0)
652 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
653 if (strlen(*++av) < 20)
654 strcpy(screenterm, *av);
655 else
656 Panic(0, "-T: terminal name too long. (max. 20 char)");
657 nwin_options.term = screenterm;
658 break;
659 case 'q':
660 quietflag = 1;
661 break;
662 case 'r':
663 case 'R':
664 #ifdef MULTI
665 case 'x':
666 #endif
667 if (ac > 1 && *av[1] != '-' && !SockMatch)
669 SockMatch = *++av;
670 ac--;
671 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
673 #ifdef MULTI
674 if (*ap == 'x')
675 xflag = 1;
676 #endif
677 if (rflag)
678 rflag = 2;
679 rflag += (*ap == 'R') ? 2 : 1;
680 break;
681 #ifdef REMOTE_DETACH
682 case 'd':
683 dflag = 1;
684 /* FALLTHROUGH */
685 case 'D':
686 if (!dflag)
687 dflag = 2;
688 if (ac == 2)
690 if (*av[1] != '-' && !SockMatch)
692 SockMatch = *++av;
693 ac--;
694 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
697 break;
698 #endif
699 case 's':
700 if (--ac == 0)
701 exit_with_usage(myname, "Specify shell with -s", NULL);
702 if (ShellProg)
703 free(ShellProg);
704 ShellProg = SaveStr(*++av);
705 debug1("ShellProg: '%s'\n", ShellProg);
706 break;
707 case 'S':
708 if (!SockMatch)
710 if (--ac == 0)
711 exit_with_usage(myname, "Specify session-name with -S", NULL);
712 SockMatch = *++av;
714 if (!*SockMatch)
715 exit_with_usage(myname, "Empty session-name?", NULL);
716 break;
717 case 'X':
718 cmdflag = 1;
719 break;
720 case 'v':
721 Panic(0, "Screen version %s", version);
722 /* NOTREACHED */
723 #ifdef UTF8
724 case 'U':
725 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
726 break;
727 #endif
728 case 'u':
729 if (--ac == 0)
730 exit_with_usage(myname, "Specify lua script file with -u", NULL);
731 if (script_file)
732 free(script_file);
733 script_file = SaveStr(*++av);
734 break;
735 default:
736 exit_with_usage(myname, "Unknown option %s", --ap);
740 else
741 break;
744 real_uid = getuid();
745 real_gid = getgid();
746 eff_uid = geteuid();
747 eff_gid = getegid();
748 if (eff_uid != real_uid)
750 /* if running with s-bit, we must install a special signal
751 * handler routine that resets the s-bit, so that we get a
752 * core file anyway.
754 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
755 signal(SIGBUS, CoreDump);
756 #endif /* SIGBUS */
757 signal(SIGSEGV, CoreDump);
760 #ifdef USE_LOCALE
761 setlocale(LC_ALL, "");
762 #endif
763 #ifdef ENCODINGS
764 if (nwin_options.encoding == -1)
766 /* ask locale if we should start in UTF-8 mode */
767 # ifdef HAVE_NL_LANGINFO
768 # ifndef USE_LOCALE
769 setlocale(LC_CTYPE, "");
770 # endif
771 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
772 debug1("locale says encoding = %d\n", nwin_options.encoding);
773 # else
774 # ifdef UTF8
775 char *s;
776 if (((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
777 (s = getenv("LANG"))) && InStr(s, "UTF-8"))
778 nwin_options.encoding = UTF8;
779 # endif
780 debug1("environment says encoding=%d\n", nwin_options.encoding);
781 #endif
783 # ifdef DW_CHARS
785 char *s;
786 if((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
787 (s = getenv("LANG")))
789 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
791 cjkwidth = 1;
795 #endif
796 #endif
797 if (SockMatch && strlen(SockMatch) >= MAXSTR)
798 Panic(0, "Ridiculously long socketname - try again.");
799 if (cmdflag && !rflag && !dflag && !xflag)
800 xflag = 1;
801 if (!cmdflag && dflag && mflag && !(rflag || xflag))
802 detached = 1;
803 nwin = nwin_options;
804 #ifdef ENCODINGS
805 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
806 #endif
807 if (ac)
808 nwin.args = av;
810 /* make the write() calls return -1 on all errors */
811 #ifdef SIGXFSZ
813 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
814 * It appears that in System V Release 4, UNIX, if you are writing
815 * an output file and you exceed the currently set file size limit,
816 * you _don't_ just get the call to `write' returning with a
817 * failure code. Rather, you get a signal called `SIGXFSZ' which,
818 * if neither handled nor ignored, will cause your program to crash
819 * with a core dump.
821 signal(SIGXFSZ, SIG_IGN);
822 #endif /* SIGXFSZ */
824 #ifdef SIGPIPE
825 signal(SIGPIPE, SIG_IGN);
826 #endif
828 if (!ShellProg)
830 register char *sh;
832 sh = getenv("SHELL");
833 ShellProg = SaveStr(sh ? sh : DefaultShell);
835 ShellArgs[0] = ShellProg;
836 home = getenv("HOME");
837 if (!mflag && !SockMatch)
839 sty = getenv("STY");
840 if (sty && *sty == 0)
841 sty = 0;
844 #ifdef NETHACK
845 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
847 char nethackrc[MAXPATHLEN];
849 if (home && (strlen(home) < (MAXPATHLEN - 20)))
851 sprintf(nethackrc,"%s/.nethackrc", home);
852 nethackflag = !access(nethackrc, F_OK);
855 #endif
857 #ifdef MULTIUSER
858 own_uid = multi_uid = real_uid;
859 if (SockMatch && (sockp = index(SockMatch, '/')))
861 if (eff_uid)
862 Panic(0, "Must run suid root for multiuser support.");
863 *sockp = 0;
864 multi = SockMatch;
865 SockMatch = sockp + 1;
866 if (*multi)
868 struct passwd *mppp;
869 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
870 Panic(0, "Cannot identify account '%s'.", multi);
871 multi_uid = mppp->pw_uid;
872 multi_home = SaveStr(mppp->pw_dir);
873 if (strlen(multi_home) > MAXPATHLEN - 10)
874 Panic(0, "home directory path too long");
875 # ifdef MULTI
876 /* always fake multi attach mode */
877 if (rflag || lsflag)
878 xflag = 1;
879 # endif /* MULTI */
880 detached = 0;
881 multiattach = 1;
884 if (SockMatch && *SockMatch == 0)
885 SockMatch = 0;
886 #endif /* MULTIUSER */
888 if ((LoginName = getlogin()) && LoginName[0] != '\0')
890 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
891 if ((int)ppp->pw_uid != real_uid)
892 ppp = (struct passwd *) 0;
894 if (ppp == 0)
896 if ((ppp = getpwuid(real_uid)) == 0)
898 Panic(0, "getpwuid() can't identify your account!");
899 exit(1);
901 LoginName = ppp->pw_name;
903 LoginName = SaveStr(LoginName);
905 ppp = getpwbyname(LoginName, ppp);
907 #if !defined(SOCKDIR) && defined(MULTIUSER)
908 if (multi && !multiattach)
910 if (home && strcmp(home, ppp->pw_dir))
911 Panic(0, "$HOME must match passwd entry for multiuser screens.");
913 #endif
915 #define SET_GUID() do \
917 setgid(real_gid); \
918 setuid(real_uid); \
919 eff_uid = real_uid; \
920 eff_gid = real_gid; \
921 } while (0)
923 #define SET_TTYNAME(fatal) do \
925 if (!(attach_tty = ttyname(0))) \
927 if (fatal) \
928 Panic(0, "Must be connected to a terminal."); \
929 else \
930 attach_tty = ""; \
932 else if (stat(attach_tty, &st)) \
933 Panic(errno, "Cannot access '%s'", attach_tty); \
934 if (strlen(attach_tty) >= MAXPATHLEN) \
935 Panic(0, "TtyName too long - sorry."); \
936 } while (0)
938 if (home == 0 || *home == '\0')
939 home = ppp->pw_dir;
940 if (strlen(LoginName) > 20)
941 Panic(0, "LoginName too long - sorry.");
942 #ifdef MULTIUSER
943 if (multi && strlen(multi) > 20)
944 Panic(0, "Screen owner name too long - sorry.");
945 #endif
946 if (strlen(home) > MAXPATHLEN - 25)
947 Panic(0, "$HOME too long - sorry.");
949 attach_tty = "";
950 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty))
952 #ifndef NAMEDPIPE
953 int fl;
954 #endif
956 /* ttyname implies isatty */
957 SET_TTYNAME(1);
958 #ifdef MULTIUSER
959 tty_mode = (int)st.st_mode & 0777;
960 #endif
962 #ifndef NAMEDPIPE
963 fl = fcntl(0, F_GETFL, 0);
964 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
965 attach_fd = 0;
966 #endif
967 if (attach_fd == -1)
969 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
970 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
971 close(n);
973 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
975 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
976 Panic(0, "Please set a terminal type.");
977 if (strlen(attach_term) > sizeof(D_termname) - 1)
978 Panic(0, "$TERM too long - sorry.");
979 GetTTY(0, &attach_Mode);
980 #ifdef DEBUGGGGGGGGGGGGGGG
981 DebugTTY(&attach_Mode);
982 #endif /* DEBUG */
985 #ifdef _MODE_T
986 oumask = umask(0); /* well, unsigned never fails? jw. */
987 #else
988 if ((oumask = (int)umask(0)) == -1)
989 Panic(errno, "Cannot change umask to zero");
990 #endif
991 SockDir = getenv("SCREENDIR");
992 if (SockDir)
994 if (strlen(SockDir) >= MAXPATHLEN - 1)
995 Panic(0, "Ridiculously long $SCREENDIR - try again.");
996 #ifdef MULTIUSER
997 if (multi)
998 Panic(0, "No $SCREENDIR with multi screens, please.");
999 #endif
1001 #ifdef MULTIUSER
1002 if (multiattach)
1004 # ifndef SOCKDIR
1005 sprintf(SockPath, "%s/.screen", multi_home);
1006 SockDir = SockPath;
1007 # else
1008 SockDir = SOCKDIR;
1009 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1010 # endif
1012 else
1013 #endif
1015 #ifndef SOCKDIR
1016 if (SockDir == 0)
1018 sprintf(SockPath, "%s/.screen", home);
1019 SockDir = SockPath;
1021 #endif
1022 if (SockDir)
1024 if (access(SockDir, F_OK))
1026 debug1("SockDir '%s' missing ...\n", SockDir);
1027 if (UserContext() > 0)
1029 if (mkdir(SockDir, 0700))
1030 UserReturn(0);
1031 UserReturn(1);
1033 if (UserStatus() <= 0)
1034 Panic(0, "Cannot make directory '%s'.", SockDir);
1036 if (SockDir != SockPath)
1037 strcpy(SockPath, SockDir);
1039 #ifdef SOCKDIR
1040 else
1042 SockDir = SOCKDIR;
1043 if (lstat(SockDir, &st))
1045 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1046 (eff_gid != real_gid) ? 0775 :
1047 #ifdef S_ISVTX
1048 0777|S_ISVTX;
1049 #else
1050 0777;
1051 #endif
1052 if (mkdir(SockDir, n) == -1)
1053 Panic(errno, "Cannot make directory '%s'", SockDir);
1055 else
1057 if (!S_ISDIR(st.st_mode))
1058 Panic(0, "'%s' must be a directory.", SockDir);
1059 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1060 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1061 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1062 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1063 0777;
1064 if (((int)st.st_mode & 0777) != n)
1065 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1067 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1068 if (access(SockPath, F_OK))
1070 if (mkdir(SockPath, 0700) == -1)
1071 Panic(errno, "Cannot make directory '%s'", SockPath);
1072 (void) chown(SockPath, real_uid, real_gid);
1075 #endif
1078 if (stat(SockPath, &st) == -1)
1079 Panic(errno, "Cannot access %s", SockPath);
1080 else
1081 if (!S_ISDIR(st.st_mode))
1082 Panic(0, "%s is not a directory.", SockPath);
1083 #ifdef MULTIUSER
1084 if (multi)
1086 if ((int)st.st_uid != multi_uid)
1087 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1089 else
1090 #endif
1092 if ((int)st.st_uid != real_uid)
1093 Panic(0, "You are not the owner of %s.", SockPath);
1095 if ((st.st_mode & 0777) != 0700)
1096 Panic(0, "Directory %s must have mode 700.", SockPath);
1097 if (SockMatch && index(SockMatch, '/'))
1098 Panic(0, "Bad session name '%s'", SockMatch);
1099 SockName = SockPath + strlen(SockPath) + 1;
1100 *SockName = 0;
1101 (void) umask(oumask);
1102 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1104 #if defined(SYSV) && !defined(ISC)
1105 if (uname(&utsnam) == -1)
1106 Panic(errno, "uname");
1107 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1108 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1109 #else
1110 (void) gethostname(HostName, MAXSTR);
1111 HostName[MAXSTR - 1] = '\0';
1112 #endif
1113 if ((ap = index(HostName, '.')) != NULL)
1114 *ap = '\0';
1116 if (lsflag)
1118 int i, fo, oth;
1120 #ifdef MULTIUSER
1121 if (multi)
1122 real_uid = multi_uid;
1123 #endif
1124 SET_GUID();
1125 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1126 if (quietflag)
1127 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1128 if (fo == 0)
1129 Panic(0, "No Sockets found in %s.\n", SockPath);
1130 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1131 /* NOTREACHED */
1133 signal(SIG_BYE, AttacherFinit); /* prevent races */
1134 if (cmdflag)
1136 /* attach_tty is not mandatory */
1137 SET_TTYNAME(0);
1138 if (!*av)
1139 Panic(0, "Please specify a command.");
1140 SET_GUID();
1141 SendCmdMessage(sty, SockMatch, av);
1142 exit(0);
1144 else if (rflag || xflag)
1146 debug("screen -r: - is there anybody out there?\n");
1147 if (Attach(MSG_ATTACH))
1149 Attacher();
1150 /* NOTREACHED */
1152 #ifdef MULTIUSER
1153 if (multiattach)
1154 Panic(0, "Can't create sessions of other users.");
1155 #endif
1156 debug("screen -r: backend not responding -- still crying\n");
1158 else if (dflag && !mflag)
1160 (void) Attach(MSG_DETACH);
1161 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1162 eexit(0);
1163 /* NOTREACHED */
1165 if (!SockMatch && !mflag && sty)
1167 /* attach_tty is not mandatory */
1168 SET_TTYNAME(0);
1169 SET_GUID();
1170 nwin_options.args = av;
1171 SendCreateMsg(sty, &nwin);
1172 exit(0);
1173 /* NOTREACHED */
1175 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1177 if (!detached || dflag != 2)
1178 MasterPid = fork();
1179 else
1180 MasterPid = 0;
1182 switch (MasterPid)
1184 case -1:
1185 Panic(errno, "fork");
1186 /* NOTREACHED */
1187 case 0:
1188 break;
1189 default:
1190 if (detached)
1191 exit(0);
1192 if (SockMatch)
1193 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1194 else
1195 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1196 for (ap = socknamebuf; *ap; ap++)
1197 if (*ap == '/')
1198 *ap = '-';
1199 #ifdef NAME_MAX
1200 if (strlen(socknamebuf) > NAME_MAX)
1201 socknamebuf[NAME_MAX] = 0;
1202 #endif
1203 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1204 SET_GUID();
1205 Attacher();
1206 /* NOTREACHED */
1209 if (!detached)
1210 PanicPid = getppid();
1212 if (DefaultEsc == -1)
1213 DefaultEsc = Ctrl('a');
1214 if (DefaultMetaEsc == -1)
1215 DefaultMetaEsc = 'a';
1217 ap = av0 + strlen(av0) - 1;
1218 while (ap >= av0)
1220 if (!strncmp("screen", ap, 6))
1222 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1223 break;
1225 ap--;
1227 if (ap < av0)
1228 *av0 = 'S';
1230 #ifdef DEBUG
1232 char buf[256];
1234 if (dfp && dfp != stderr)
1235 fclose(dfp);
1236 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1237 if ((dfp = fopen(buf, "w")) == NULL)
1238 dfp = stderr;
1239 else
1240 (void) chmod(buf, 0666);
1242 #endif
1243 if (!detached)
1245 if (attach_fd == -1)
1247 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1248 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1250 else
1251 n = dup(attach_fd);
1253 else
1254 n = -1;
1255 freopen("/dev/null", "r", stdin);
1256 freopen("/dev/null", "w", stdout);
1258 #ifdef DEBUG
1259 if (dfp != stderr)
1260 #endif
1261 freopen("/dev/null", "w", stderr);
1262 debug("-- screen.back debug started\n");
1265 * This guarantees that the session owner is listed, even when we
1266 * start detached. From now on we should not refer to 'LoginName'
1267 * any more, use users->u_name instead.
1269 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1270 Panic(0, "Could not create user info");
1271 if (!detached)
1273 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1274 Panic(0, "Could not alloc display");
1275 PanicPid = 0;
1276 #ifdef ENCODINGS
1277 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1278 debug1("D_encoding = %d\n", D_encoding);
1279 #endif
1282 if (SockMatch)
1284 /* user started us with -S option */
1285 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1287 else
1289 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1290 HostName);
1292 for (ap = socknamebuf; *ap; ap++)
1293 if (*ap == '/')
1294 *ap = '-';
1295 #ifdef NAME_MAX
1296 if (strlen(socknamebuf) > NAME_MAX)
1298 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1299 socknamebuf[NAME_MAX] = 0;
1301 #endif
1302 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1304 ServerSocket = MakeServerSocket();
1305 InitKeytab();
1307 LoadScripts();
1308 ScriptInit();
1309 if (script_file)
1311 ScriptSource(script_file);
1312 free(script_file);
1313 script_file = 0;
1316 #ifdef ETCSCREENRC
1317 # ifdef ALLOW_SYSSCREENRC
1318 if ((ap = getenv("SYSSCREENRC")))
1319 (void)StartRc(ap, 0);
1320 else
1321 # endif
1322 (void)StartRc(ETCSCREENRC, 0);
1323 #endif
1324 (void)StartRc(RcFileName, 0);
1325 # ifdef UTMPOK
1326 # ifndef UTNOKEEP
1327 InitUtmp();
1328 # endif /* UTNOKEEP */
1329 # endif /* UTMPOK */
1330 if (display)
1332 if (InitTermcap(0, 0))
1334 debug("Could not init termcap - exiting\n");
1335 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1336 freetty();
1337 if (D_userpid)
1338 Kill(D_userpid, SIG_BYE);
1339 eexit(1);
1341 MakeDefaultCanvas();
1342 InitTerm(0);
1343 #ifdef UTMPOK
1344 RemoveLoginSlot();
1345 #endif
1347 else
1348 MakeTermcap(1);
1349 #ifdef LOADAV
1350 InitLoadav();
1351 #endif /* LOADAV */
1352 MakeNewEnv();
1353 signal(SIGHUP, SigHup);
1354 signal(SIGINT, FinitHandler);
1355 signal(SIGQUIT, FinitHandler);
1356 signal(SIGTERM, FinitHandler);
1357 #ifdef BSDJOBS
1358 signal(SIGTTIN, SIG_IGN);
1359 signal(SIGTTOU, SIG_IGN);
1360 #endif
1362 if (display)
1364 brktty(D_userfd);
1365 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1366 /* Note: SetMode must be called _before_ FinishRc. */
1367 SetTTY(D_userfd, &D_NewMode);
1368 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1369 Msg(errno, "Warning: NBLOCK fcntl failed");
1371 else
1372 brktty(-1); /* just try */
1373 signal(SIGCHLD, SigChld);
1374 #ifdef ETCSCREENRC
1375 # ifdef ALLOW_SYSSCREENRC
1376 if ((ap = getenv("SYSSCREENRC")))
1377 FinishRc(ap);
1378 else
1379 # endif
1380 FinishRc(ETCSCREENRC);
1381 #endif
1382 FinishRc(RcFileName);
1384 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1385 if (windows == NULL)
1387 debug("We open one default window, as screenrc did not specify one.\n");
1388 if (MakeWindow(&nwin) == -1)
1390 Msg(0, "Sorry, could not find a PTY.");
1391 sleep(5);
1392 Finit(0);
1393 /* NOTREACHED */
1397 #ifdef HAVE_BRAILLE
1398 StartBraille();
1399 #endif
1401 if (display && default_startup)
1402 display_copyright();
1403 signal(SIGINT, SigInt);
1404 if (rflag && (rflag & 1) == 0 && !quietflag)
1406 Msg(0, "New screen...");
1407 rflag = 0;
1410 serv_read.type = EV_READ;
1411 serv_read.fd = ServerSocket;
1412 serv_read.handler = serv_read_fn;
1413 evenq(&serv_read);
1415 serv_select.pri = -10;
1416 serv_select.type = EV_ALWAYS;
1417 serv_select.handler = serv_select_fn;
1418 evenq(&serv_select);
1420 logflushev.type = EV_TIMEOUT;
1421 logflushev.handler = logflush_fn;
1423 sched();
1424 /* NOTREACHED */
1425 return 0;
1428 void
1429 WindowDied(p, wstat, wstat_valid)
1430 struct win *p;
1431 int wstat;
1432 int wstat_valid;
1434 int killit = 0;
1436 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1437 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1438 killit = 1;
1440 if (ZombieKey_destroy && !killit)
1442 char buf[100], *s, reason[100];
1443 time_t now;
1445 if (wstat_valid) {
1446 if (WIFEXITED(wstat))
1447 if (WEXITSTATUS(wstat))
1448 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1449 else
1450 sprintf(reason, "terminated normally");
1451 else if (WIFSIGNALED(wstat))
1452 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1453 #ifdef WCOREDUMP
1454 WCOREDUMP(wstat) ? " (core file generated)" : "");
1455 #else
1456 "");
1457 #endif
1458 } else
1459 sprintf(reason, "detached from window");
1461 (void) time(&now);
1462 s = ctime(&now);
1463 if (s && *s)
1464 s[strlen(s) - 1] = '\0';
1465 debug3("window %d (%s) going into zombie state fd %d",
1466 p->w_number, p->w_title, p->w_ptyfd);
1467 #ifdef UTMPOK
1468 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1470 RemoveUtmp(p);
1471 p->w_slot = 0; /* "detached" */
1473 #endif
1474 CloseDevice(p);
1476 p->w_deadpid = p->w_pid;
1477 p->w_pid = 0;
1478 ResetWindow(p);
1479 /* p->w_y = p->w_bot; */
1480 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1481 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1482 WriteString(p, buf, strlen(buf));
1483 WindowChanged(p, 'f');
1485 else
1486 KillWindow(p);
1487 #ifdef UTMPOK
1488 CarefulUtmp();
1489 #endif
1492 static void
1493 SigChldHandler()
1495 struct stat st;
1496 #ifdef DEBUG
1497 fds();
1498 #endif
1499 while (GotSigChld)
1501 GotSigChld = 0;
1502 DoWait();
1503 #ifdef SYSVSIGS
1504 signal(SIGCHLD, SigChld);
1505 #endif
1507 if (stat(SockPath, &st) == -1)
1509 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1510 if (!RecoverSocket())
1512 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1513 Finit(1);
1515 else
1516 debug1("'%s' reconstructed\n", SockPath);
1518 else
1519 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1522 static sigret_t
1523 SigChld SIGDEFARG
1525 debug("SigChld()\n");
1526 GotSigChld = 1;
1527 SIGRETURN;
1530 sigret_t
1531 SigHup SIGDEFARG
1533 /* Hangup all displays */
1534 while ((display = displays) != 0)
1535 Hangup();
1536 SIGRETURN;
1540 * the backend's Interrupt handler
1541 * we cannot insert the intrc directly, as we never know
1542 * if fore is valid.
1544 static sigret_t
1545 SigInt SIGDEFARG
1547 #if HAZARDOUS
1548 char ibuf;
1550 debug("SigInt()\n");
1551 if (fore && displays)
1553 # if defined(TERMIO) || defined(POSIX)
1554 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1555 # else
1556 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1557 # endif
1558 fore->w_inlen = 0;
1559 write(fore->w_ptyfd, &ibuf, 1);
1561 #else
1562 signal(SIGINT, SigInt);
1563 debug("SigInt() careful\n");
1564 InterruptPlease = 1;
1565 #endif
1566 SIGRETURN;
1569 static sigret_t
1570 CoreDump SIGDEFARG
1572 struct display *disp;
1573 char buf[80];
1575 #if defined(SYSVSIGS) && defined(SIGHASARG)
1576 signal(sigsig, SIG_IGN);
1577 #endif
1578 setgid(getgid());
1579 setuid(getuid());
1580 unlink("core");
1581 #ifdef SIGHASARG
1582 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1583 #else
1584 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1585 #endif
1586 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1588 #else /* SHADOWPW && !DEBUG */
1589 " (core dumped)"
1590 #endif /* SHADOWPW && !DEBUG */
1592 for (disp = displays; disp; disp = disp->d_next)
1594 fcntl(disp->d_userfd, F_SETFL, 0);
1595 SetTTY(disp->d_userfd, &D_OldMode);
1596 write(disp->d_userfd, buf, strlen(buf));
1597 Kill(disp->d_userpid, SIG_BYE);
1599 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1600 Kill(getpid(), SIGKILL);
1601 eexit(11);
1602 #else /* SHADOWPW && !DEBUG */
1603 abort();
1604 #endif /* SHADOWPW && !DEBUG */
1605 SIGRETURN;
1608 static void
1609 DoWait()
1611 register int pid;
1612 struct win *p, *next;
1613 #ifdef BSDWAIT
1614 union wait wstat;
1615 #else
1616 int wstat;
1617 #endif
1619 #ifdef BSDJOBS
1620 # ifndef BSDWAIT
1621 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1622 # else
1623 # ifdef USE_WAIT2
1625 * From: rouilj@sni-usa.com (John Rouillard)
1626 * note that WUNTRACED is not documented to work, but it is defined in
1627 * /usr/include/sys/wait.h, so it may work
1629 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1630 # else /* USE_WAIT2 */
1631 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1632 # endif /* USE_WAIT2 */
1633 # endif
1634 #else /* BSDJOBS */
1635 while ((pid = wait(&wstat)) < 0)
1636 if (errno != EINTR)
1637 break;
1638 if (pid > 0)
1639 #endif /* BSDJOBS */
1641 for (p = windows; p; p = next)
1643 next = p->w_next;
1644 if ( (p->w_pid && pid == p->w_pid) ||
1645 (p->w_deadpid && pid == p->w_deadpid) )
1647 /* child has ceased to exist */
1648 p->w_pid = 0;
1650 #ifdef BSDJOBS
1651 if (WIFSTOPPED(wstat))
1653 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1654 #ifdef SIGTTIN
1655 if (WSTOPSIG(wstat) == SIGTTIN)
1657 Msg(0, "Suspended (tty input)");
1658 continue;
1660 #endif
1661 #ifdef SIGTTOU
1662 if (WSTOPSIG(wstat) == SIGTTOU)
1664 Msg(0, "Suspended (tty output)");
1665 continue;
1667 #endif
1668 /* Try to restart process */
1669 Msg(0, "Child has been stopped, restarting.");
1670 if (killpg(pid, SIGCONT))
1671 kill(pid, SIGCONT);
1673 else
1674 #endif
1676 WindowDied(p, wstat, 1);
1678 break;
1680 #ifdef PSEUDOS
1681 if (p->w_pwin && pid == p->w_pwin->p_pid)
1683 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1684 FreePseudowin(p);
1685 break;
1687 #endif
1689 if (p == 0)
1691 debug1("pid %d not found - hope that's ok\n", pid);
1697 static sigret_t
1698 FinitHandler SIGDEFARG
1700 #ifdef SIGHASARG
1701 debug1("FinitHandler called, sig %d.\n", sigsig);
1702 #else
1703 debug("FinitHandler called.\n");
1704 #endif
1705 Finit(1);
1706 SIGRETURN;
1709 void
1710 Finit(i)
1711 int i;
1713 signal(SIGCHLD, SIG_DFL);
1714 signal(SIGHUP, SIG_IGN);
1715 debug1("Finit(%d);\n", i);
1717 ScriptFinit();
1719 while (windows)
1721 struct win *p = windows;
1722 windows = windows->w_next;
1723 FreeWindow(p);
1725 if (ServerSocket != -1)
1727 debug1("we unlink(%s)\n", SockPath);
1728 #ifdef USE_SETEUID
1729 xseteuid(real_uid);
1730 xsetegid(real_gid);
1731 #endif
1732 (void) unlink(SockPath);
1733 #ifdef USE_SETEUID
1734 xseteuid(eff_uid);
1735 xsetegid(eff_gid);
1736 #endif
1738 for (display = displays; display; display = display->d_next)
1740 if (D_status)
1741 RemoveStatus();
1742 FinitTerm();
1743 #ifdef UTMPOK
1744 RestoreLoginSlot();
1745 #endif
1746 AddStr("[screen is terminating]\r\n");
1747 Flush();
1748 SetTTY(D_userfd, &D_OldMode);
1749 fcntl(D_userfd, F_SETFL, 0);
1750 freetty();
1751 Kill(D_userpid, SIG_BYE);
1754 * we _cannot_ call eexit(i) here,
1755 * instead of playing with the Socket above. Sigh.
1757 exit(i);
1760 void
1761 eexit(e)
1762 int e;
1764 debug("eexit\n");
1765 if (ServerSocket != -1)
1767 debug1("we unlink(%s)\n", SockPath);
1768 setgid(real_gid);
1769 setuid(real_uid);
1770 (void) unlink(SockPath);
1772 exit(e);
1775 void
1776 Hangup()
1778 if (display == 0)
1779 return;
1780 debug1("Hangup %x\n", display);
1781 if (D_userfd >= 0)
1783 close(D_userfd);
1784 D_userfd = -1;
1786 if (auto_detach || displays->d_next)
1787 Detach(D_HANGUP);
1788 else
1789 Finit(0);
1793 * Detach now has the following modes:
1794 *D_DETACH SIG_BYE detach backend and exit attacher
1795 *D_HANGUP SIG_BYE detach backend and exit attacher
1796 *D_STOP SIG_STOP stop attacher (and detach backend)
1797 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1798 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1799 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1800 *D_LOCK SIG_LOCK lock the attacher
1801 * (jw)
1802 * we always remove our utmp slots. (even when "lock" or "stop")
1803 * Note: Take extra care here, we may be called by interrupt!
1805 void
1806 Detach(mode)
1807 int mode;
1809 int sign = 0, pid;
1810 struct canvas *cv;
1811 struct win *p;
1813 if (display == 0)
1814 return;
1816 signal(SIGHUP, SIG_IGN);
1817 debug1("Detach(%d)\n", mode);
1818 if (D_status)
1819 RemoveStatus();
1820 FinitTerm();
1821 if (!display)
1822 return;
1823 switch (mode)
1825 case D_HANGUP:
1826 sign = SIG_BYE;
1827 break;
1828 case D_DETACH:
1829 AddStr("[detached]\r\n");
1830 sign = SIG_BYE;
1831 break;
1832 #ifdef BSDJOBS
1833 case D_STOP:
1834 sign = SIG_STOP;
1835 break;
1836 #endif
1837 #ifdef REMOTE_DETACH
1838 case D_REMOTE:
1839 AddStr("[remote detached]\r\n");
1840 sign = SIG_BYE;
1841 break;
1842 #endif
1843 #ifdef POW_DETACH
1844 case D_POWER:
1845 AddStr("[power detached]\r\n");
1846 if (PowDetachString)
1848 AddStr(PowDetachString);
1849 AddStr("\r\n");
1851 sign = SIG_POWER_BYE;
1852 break;
1853 #ifdef REMOTE_DETACH
1854 case D_REMOTE_POWER:
1855 AddStr("[remote power detached]\r\n");
1856 if (PowDetachString)
1858 AddStr(PowDetachString);
1859 AddStr("\r\n");
1861 sign = SIG_POWER_BYE;
1862 break;
1863 #endif
1864 #endif
1865 case D_LOCK:
1866 ClearAll();
1867 sign = SIG_LOCK;
1868 /* tell attacher to lock terminal with a lockprg. */
1869 break;
1871 #ifdef UTMPOK
1872 if (displays->d_next == 0)
1874 for (p = windows; p; p = p->w_next)
1876 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1878 RemoveUtmp(p);
1880 * Set the slot to 0 to get the window
1881 * logged in again.
1883 p->w_slot = (slot_t) 0;
1887 if (mode != D_HANGUP)
1888 RestoreLoginSlot();
1889 #endif
1890 if (displays->d_next == 0 && console_window)
1892 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1894 debug("could not release console - killing window\n");
1895 KillWindow(console_window);
1896 display = displays; /* restore display */
1899 if (D_fore)
1901 #ifdef MULTIUSER
1902 ReleaseAutoWritelock(display, D_fore);
1903 #endif
1904 D_user->u_detachwin = D_fore->w_number;
1905 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1907 AutosaveLayout(D_layout);
1908 layout_last = D_layout;
1909 for (cv = D_cvlist; cv; cv = cv->c_next)
1911 p = Layer2Window(cv->c_layer);
1912 SetCanvasWindow(cv, 0);
1913 if (p)
1914 WindowChanged(p, 'u');
1917 pid = D_userpid;
1918 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1919 FreeDisplay();
1920 if (displays == 0)
1921 /* Flag detached-ness */
1922 (void) chsock();
1924 * tell father what to do. We do that after we
1925 * freed the tty, thus getty feels more comfortable on hpux
1926 * if it was a power detach.
1928 Kill(pid, sign);
1929 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1930 debug("Detach returns, we are successfully detached.\n");
1931 signal(SIGHUP, SigHup);
1934 static int
1935 IsSymbol(e, s)
1936 char *e, *s;
1938 register int l;
1940 l = strlen(s);
1941 return strncmp(e, s, l) == 0 && e[l] == '=';
1944 void
1945 MakeNewEnv()
1947 register char **op, **np;
1948 static char stybuf[MAXSTR];
1950 for (op = environ; *op; ++op)
1952 if (NewEnv)
1953 free((char *)NewEnv);
1954 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1955 if (!NewEnv)
1956 Panic(0, strnomem);
1957 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1958 *np++ = stybuf; /* NewEnv[0] */
1959 *np++ = Term; /* NewEnv[1] */
1960 np++; /* room for SHELL */
1961 #ifdef TIOCSWINSZ
1962 np += 2; /* room for TERMCAP and WINDOW */
1963 #else
1964 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
1965 #endif
1967 for (op = environ; *op; ++op)
1969 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
1970 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
1971 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
1972 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
1974 *np++ = *op;
1976 *np = 0;
1979 void
1980 /*VARARGS2*/
1981 #if defined(USEVARARGS) && defined(__STDC__)
1982 Msg(int err, char *fmt, VA_DOTS)
1983 #else
1984 Msg(err, fmt, VA_DOTS)
1985 int err;
1986 char *fmt;
1987 VA_DECL
1988 #endif
1990 VA_LIST(ap)
1991 char buf[MAXPATHLEN*2];
1992 char *p = buf;
1994 VA_START(ap, fmt);
1995 fmt = DoNLS(fmt);
1996 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1997 VA_END(ap);
1998 if (err)
2000 p += strlen(p);
2001 *p++ = ':';
2002 *p++ = ' ';
2003 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2004 buf[sizeof(buf) - 1] = 0;
2006 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2008 if (display && displays)
2009 MakeStatus(buf);
2010 else if (displays)
2012 for (display = displays; display; display = display->d_next)
2013 MakeStatus(buf);
2015 else if (display)
2017 /* no displays but a display - must have forked.
2018 * send message to backend!
2020 char *tty = D_usertty;
2021 struct display *olddisplay = display;
2022 display = 0; /* only send once */
2023 SendErrorMsg(tty, buf);
2024 display = olddisplay;
2026 else
2027 printf("%s\r\n", buf);
2031 * Call FinitTerm for all displays, write a message to each and call eexit();
2033 void
2034 /*VARARGS2*/
2035 #if defined(USEVARARGS) && defined(__STDC__)
2036 Panic(int err, char *fmt, VA_DOTS)
2037 #else
2038 Panic(err, fmt, VA_DOTS)
2039 int err;
2040 char *fmt;
2041 VA_DECL
2042 #endif
2044 VA_LIST(ap)
2045 char buf[MAXPATHLEN*2];
2046 char *p = buf;
2048 VA_START(ap, fmt);
2049 fmt = DoNLS(fmt);
2050 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2051 VA_END(ap);
2052 if (err)
2054 p += strlen(p);
2055 *p++ = ':';
2056 *p++ = ' ';
2057 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2058 buf[sizeof(buf) - 1] = 0;
2060 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2061 if (displays == 0 && display == 0)
2063 printf("%s\r\n", buf);
2064 if (PanicPid)
2065 Kill(PanicPid, SIG_BYE);
2067 else if (displays == 0)
2069 /* no displays but a display - must have forked.
2070 * send message to backend!
2072 char *tty = D_usertty;
2073 display = 0;
2074 SendErrorMsg(tty, buf);
2075 sleep(2);
2076 _exit(1);
2078 else
2079 for (display = displays; display; display = display->d_next)
2081 if (D_status)
2082 RemoveStatus();
2083 FinitTerm();
2084 Flush();
2085 #ifdef UTMPOK
2086 RestoreLoginSlot();
2087 #endif
2088 SetTTY(D_userfd, &D_OldMode);
2089 fcntl(D_userfd, F_SETFL, 0);
2090 write(D_userfd, buf, strlen(buf));
2091 write(D_userfd, "\n", 1);
2092 freetty();
2093 if (D_userpid)
2094 Kill(D_userpid, SIG_BYE);
2096 #ifdef MULTIUSER
2097 if (tty_oldmode >= 0)
2099 # ifdef USE_SETEUID
2100 if (setuid(own_uid))
2101 xseteuid(own_uid); /* may be a loop. sigh. */
2102 # else
2103 setuid(own_uid);
2104 # endif
2105 debug1("Panic: changing back modes from %s\n", attach_tty);
2106 chmod(attach_tty, tty_oldmode);
2108 #endif
2109 eexit(1);
2114 * '^' is allowed as an escape mechanism for control characters. jw.
2116 * Added time insertion using ideas/code from /\ndy Jones
2117 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2121 #ifndef USE_LOCALE
2122 static const char days[] = "SunMonTueWedThuFriSat";
2123 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2124 #endif
2126 static char winmsg_buf[MAXSTR];
2127 #define MAX_WINMSG_REND 16 /* rendition changes */
2128 static int winmsg_rend[MAX_WINMSG_REND];
2129 static int winmsg_rendpos[MAX_WINMSG_REND];
2130 static int winmsg_numrend;
2132 static char *
2133 pad_expand(buf, p, numpad, padlen)
2134 char *buf;
2135 char *p;
2136 int numpad;
2137 int padlen;
2139 char *pn, *pn2;
2140 int i, r;
2142 padlen = padlen - (p - buf); /* space for rent */
2143 if (padlen < 0)
2144 padlen = 0;
2145 pn2 = pn = p + padlen;
2146 r = winmsg_numrend;
2147 while (p >= buf)
2149 if (r && p - buf == winmsg_rendpos[r - 1])
2151 winmsg_rendpos[--r] = pn - buf;
2152 continue;
2154 *pn-- = *p;
2155 if (*p-- == 127)
2157 pn[1] = ' ';
2158 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2159 padlen -= i;
2160 while (i-- > 0)
2161 *pn-- = ' ';
2162 numpad--;
2165 return pn2;
2168 struct backtick {
2169 struct backtick *next;
2170 int num;
2171 int tick;
2172 int lifespan;
2173 time_t bestbefore;
2174 char result[MAXSTR];
2175 char **cmdv;
2176 struct event ev;
2177 char *buf;
2178 int bufi;
2181 struct backtick *backticks;
2183 static void
2184 backtick_filter(bt)
2185 struct backtick *bt;
2187 char *p, *q;
2188 int c;
2190 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2192 if (c == '\t')
2193 c = ' ';
2194 if (c >= ' ' || c == '\005')
2195 *q++ = c;
2197 *q = 0;
2200 static void
2201 backtick_fn(ev, data)
2202 struct event *ev;
2203 char *data;
2205 struct backtick *bt;
2206 int i, j, k, l;
2208 bt = (struct backtick *)data;
2209 debug1("backtick_fn for #%d\n", bt->num);
2210 i = bt->bufi;
2211 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2212 if (l <= 0)
2214 debug1("EOF on backtick #%d\n", bt->num);
2215 evdeq(ev);
2216 close(ev->fd);
2217 ev->fd = -1;
2218 return;
2220 debug1("read %d bytes\n", l);
2221 i += l;
2222 for (j = 0; j < l; j++)
2223 if (bt->buf[i - j - 1] == '\n')
2224 break;
2225 if (j < l)
2227 for (k = i - j - 2; k >= 0; k--)
2228 if (bt->buf[k] == '\n')
2229 break;
2230 k++;
2231 bcopy(bt->buf + k, bt->result, i - j - k);
2232 bt->result[i - j - k - 1] = 0;
2233 backtick_filter(bt);
2234 WindowChanged(0, '`');
2236 if (j == l && i == MAXSTR)
2238 j = MAXSTR/2;
2239 l = j + 1;
2241 if (j < l)
2243 if (j)
2244 bcopy(bt->buf + i - j, bt->buf, j);
2245 i = j;
2247 bt->bufi = i;
2250 void
2251 setbacktick(num, lifespan, tick, cmdv)
2252 int num;
2253 int lifespan;
2254 int tick;
2255 char **cmdv;
2257 struct backtick **btp, *bt;
2258 char **v;
2260 debug1("setbacktick called for backtick #%d\n", num);
2261 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2262 if (bt->num == num)
2263 break;
2264 if (!bt && !cmdv)
2265 return;
2266 if (bt)
2268 for (v = bt->cmdv; *v; v++)
2269 free(*v);
2270 free(bt->cmdv);
2271 if (bt->buf)
2272 free(bt->buf);
2273 if (bt->ev.fd >= 0)
2274 close(bt->ev.fd);
2275 evdeq(&bt->ev);
2277 if (bt && !cmdv)
2279 *btp = bt->next;
2280 free(bt);
2281 return;
2283 if (!bt)
2285 bt = (struct backtick *)malloc(sizeof *bt);
2286 if (!bt)
2288 Msg(0, strnomem);
2289 return;
2291 bzero(bt, sizeof(*bt));
2292 bt->next = 0;
2293 *btp = bt;
2295 bt->num = num;
2296 bt->tick = tick;
2297 bt->lifespan = lifespan;
2298 bt->bestbefore = 0;
2299 bt->result[0] = 0;
2300 bt->buf = 0;
2301 bt->bufi = 0;
2302 bt->cmdv = cmdv;
2303 bt->ev.fd = -1;
2304 if (bt->tick == 0 && bt->lifespan == 0)
2306 debug("setbacktick: continuous mode\n");
2307 bt->buf = (char *)malloc(MAXSTR);
2308 if (bt->buf == 0)
2310 Msg(0, strnomem);
2311 setbacktick(num, 0, 0, (char **)0);
2312 return;
2314 bt->ev.type = EV_READ;
2315 bt->ev.fd = readpipe(bt->cmdv);
2316 bt->ev.handler = backtick_fn;
2317 bt->ev.data = (char *)bt;
2318 if (bt->ev.fd >= 0)
2319 evenq(&bt->ev);
2323 static char *
2324 runbacktick(bt, tickp, now)
2325 struct backtick *bt;
2326 int *tickp;
2327 time_t now;
2329 int f, i, l, j;
2330 time_t now2;
2332 debug1("runbacktick called for backtick #%d\n", bt->num);
2333 if (bt->tick && (!*tickp || bt->tick < *tickp))
2334 *tickp = bt->tick;
2335 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2337 debug1("returning old result (%d)\n", bt->lifespan);
2338 return bt->result;
2340 f = readpipe(bt->cmdv);
2341 if (f == -1)
2342 return bt->result;
2343 i = 0;
2344 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2346 debug1("runbacktick: read %d bytes\n", l);
2347 i += l;
2348 for (j = 1; j < l; j++)
2349 if (bt->result[i - j - 1] == '\n')
2350 break;
2351 if (j == l && i == sizeof(bt->result))
2353 j = sizeof(bt->result) / 2;
2354 l = j + 1;
2356 if (j < l)
2358 bcopy(bt->result + i - j, bt->result, j);
2359 i = j;
2362 close(f);
2363 bt->result[sizeof(bt->result) - 1] = '\n';
2364 if (i && bt->result[i - 1] == '\n')
2365 i--;
2366 debug1("runbacktick: finished, %d bytes\n", i);
2367 bt->result[i] = 0;
2368 backtick_filter(bt);
2369 (void)time(&now2);
2370 bt->bestbefore = now2 + bt->lifespan;
2371 return bt->result;
2374 void
2375 AppendWinMsgRend(str, color)
2376 char *str, *color;
2378 char *p;
2379 int r = -1;
2380 if (winmsg_numrend >= MAX_WINMSG_REND)
2381 return;
2382 p = winmsg_buf + strlen(winmsg_buf);
2383 if (color)
2385 if (*color != '-')
2387 r = ParseAttrColor(color, 0, 0);
2388 if (r == -1)
2389 return;
2391 winmsg_rend[winmsg_numrend] = r;
2392 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2393 winmsg_numrend++;
2395 strncpy(p, str, winmsg_buf + sizeof(winmsg_buf) - p);
2398 char *
2399 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2400 char *str;
2401 struct win *win;
2402 int esc;
2403 int padlen;
2404 struct event *ev;
2405 int rec;
2407 static int tick;
2408 char *s = str;
2409 register char *p = winmsg_buf;
2410 register int ctrl;
2411 struct timeval now;
2412 struct tm *tm;
2413 int l, i, r;
2414 int num;
2415 int zeroflg;
2416 int longflg;
2417 int minusflg;
2418 int plusflg;
2419 int qmflag = 0, omflag = 0, qmnumrend = 0;
2420 char *qmpos = 0;
2421 int numpad = 0;
2422 int lastpad = 0;
2423 int truncpos = -1;
2424 int truncper = 0;
2425 int trunclong = 0;
2426 struct backtick *bt;
2428 if (winmsg_numrend >= 0)
2429 winmsg_numrend = 0;
2430 else
2431 winmsg_numrend = -winmsg_numrend;
2433 *p = '\0';
2434 if (ScriptProcessCaption(str, win, padlen))
2435 return winmsg_buf;
2436 if (!display)
2437 return winmsg_buf;
2439 tick = 0;
2440 tm = 0;
2441 ctrl = 0;
2442 gettimeofday(&now, NULL);
2443 for (s = str; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2445 *p = *s;
2446 if (ctrl)
2448 ctrl = 0;
2449 if (*s != '^' && *s >= 64)
2450 *p &= 0x1f;
2451 continue;
2453 if (*s != esc)
2455 if (esc == '%')
2457 switch (*s)
2459 #if 0
2460 case '~':
2461 *p = BELL;
2462 break;
2463 #endif
2464 case '^':
2465 ctrl = 1;
2466 *p-- = '^';
2467 break;
2468 default:
2469 break;
2472 continue;
2474 if (*++s == esc) /* double escape ? */
2475 continue;
2476 if ((plusflg = *s == '+') != 0)
2477 s++;
2478 if ((minusflg = *s == '-') != 0)
2479 s++;
2480 if ((zeroflg = *s == '0') != 0)
2481 s++;
2482 num = 0;
2483 while(*s >= '0' && *s <= '9')
2484 num = num * 10 + (*s++ - '0');
2485 if ((longflg = *s == 'L') != 0)
2486 s++;
2487 switch (*s)
2489 case '?':
2490 p--;
2491 if (qmpos)
2493 if ((!qmflag && !omflag) || omflag == 1)
2495 p = qmpos;
2496 if (qmnumrend < winmsg_numrend)
2497 winmsg_numrend = qmnumrend;
2499 qmpos = 0;
2500 break;
2502 qmpos = p;
2503 qmnumrend = winmsg_numrend;
2504 qmflag = omflag = 0;
2505 break;
2506 case ':':
2507 p--;
2508 if (!qmpos)
2509 break;
2510 if (qmflag && omflag != 1)
2512 omflag = 1;
2513 qmpos = p;
2514 qmnumrend = winmsg_numrend;
2516 else
2518 p = qmpos;
2519 if (qmnumrend < winmsg_numrend)
2520 winmsg_numrend = qmnumrend;
2521 omflag = -1;
2523 break;
2524 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2525 case 'a': case 'A': case 's': case 'c': case 'C':
2526 if (l < 4)
2527 break;
2528 if (tm == 0)
2530 time_t nowsec = now.tv_sec;
2531 tm = localtime(&nowsec);
2533 qmflag = 1;
2534 if (!tick || tick > 3600)
2535 tick = 3600;
2536 switch (*s)
2538 case 'd':
2539 sprintf(p, "%02d", tm->tm_mday % 100);
2540 break;
2541 case 'D':
2542 #ifdef USE_LOCALE
2543 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2544 #else
2545 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2546 #endif
2547 break;
2548 case 'm':
2549 sprintf(p, "%02d", tm->tm_mon + 1);
2550 break;
2551 case 'M':
2552 #ifdef USE_LOCALE
2553 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2554 #else
2555 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2556 #endif
2557 break;
2558 case 'y':
2559 sprintf(p, "%02d", tm->tm_year % 100);
2560 break;
2561 case 'Y':
2562 sprintf(p, "%04d", tm->tm_year + 1900);
2563 break;
2564 case 'a':
2565 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2566 break;
2567 case 'A':
2568 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2569 break;
2570 case 's':
2571 sprintf(p, "%02d", tm->tm_sec);
2572 tick = 1;
2573 break;
2574 case 'c':
2575 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2576 if (!tick || tick > 60)
2577 tick = 60;
2578 break;
2579 case 'C':
2580 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2581 if (!tick || tick > 60)
2582 tick = 60;
2583 break;
2584 default:
2585 break;
2587 p += strlen(p) - 1;
2588 break;
2589 case 'l':
2590 #ifdef LOADAV
2591 *p = 0;
2592 if (l > 20)
2593 AddLoadav(p);
2594 if (*p)
2596 qmflag = 1;
2597 p += strlen(p) - 1;
2599 else
2600 *p = '?';
2601 if (!tick || tick > 60)
2602 tick = 60;
2603 #else
2604 *p = '?';
2605 #endif
2606 p += strlen(p) - 1;
2607 break;
2608 case '`':
2609 case 'h':
2610 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2612 p--;
2613 break;
2615 if (*s == '`')
2617 for (bt = backticks; bt; bt = bt->next)
2618 if (bt->num == num)
2619 break;
2620 if (bt == 0)
2622 p--;
2623 break;
2627 char savebuf[sizeof(winmsg_buf)];
2628 int oldtick = tick;
2629 int oldnumrend = winmsg_numrend;
2631 *p = 0;
2632 strcpy(savebuf, winmsg_buf);
2633 winmsg_numrend = -winmsg_numrend;
2634 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2635 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2636 if (!tick || oldtick < tick)
2637 tick = oldtick;
2638 if ((int)strlen(winmsg_buf) < l)
2639 strcat(savebuf, winmsg_buf);
2640 strcpy(winmsg_buf, savebuf);
2641 while (oldnumrend < winmsg_numrend)
2642 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2643 if (*p)
2644 qmflag = 1;
2645 p += strlen(p) - 1;
2647 break;
2648 case 'w':
2649 case 'W':
2651 struct win *oldfore = 0;
2652 char *ss;
2654 if (display)
2656 oldfore = D_fore;
2657 D_fore = win;
2659 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2660 if (minusflg)
2661 *ss = 0;
2662 if (display)
2663 D_fore = oldfore;
2665 if (*p)
2666 qmflag = 1;
2667 p += strlen(p) - 1;
2668 break;
2669 case 'u':
2670 *p = 0;
2671 if (win)
2672 AddOtherUsers(p, l - 1, win);
2673 if (*p)
2674 qmflag = 1;
2675 p += strlen(p) - 1;
2676 break;
2677 case 'f':
2678 *p = 0;
2679 if (win)
2680 AddWindowFlags(p, l - 1, win);
2681 if (*p)
2682 qmflag = 1;
2683 p += strlen(p) - 1;
2684 break;
2685 case 't':
2686 *p = 0;
2687 if (win && (int)strlen(win->w_title) < l)
2689 strcpy(p, win->w_title);
2690 if (*p)
2691 qmflag = 1;
2693 p += strlen(p) - 1;
2694 break;
2695 case '{':
2697 char rbuf[128];
2698 s++;
2699 for (i = 0; i < 127; i++)
2700 if (s[i] && s[i] != '}')
2701 rbuf[i] = s[i];
2702 else
2703 break;
2704 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2706 r = -1;
2707 rbuf[i] = 0;
2708 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2709 if (i != 1 || rbuf[0] != '-')
2710 r = ParseAttrColor(rbuf, (char *)0, 0);
2711 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2713 winmsg_rend[winmsg_numrend] = r;
2714 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2715 winmsg_numrend++;
2718 s += i;
2719 p--;
2721 break;
2722 case 'H':
2723 *p = 0;
2724 if ((int)strlen(HostName) < l)
2726 strcpy(p, HostName);
2727 if (*p)
2728 qmflag = 1;
2730 p += strlen(p) - 1;
2731 break;
2732 case 'F':
2733 p--;
2734 /* small hack */
2735 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2736 minusflg = !minusflg;
2737 if (minusflg)
2738 qmflag = 1;
2739 break;
2740 case '>':
2741 truncpos = p - winmsg_buf;
2742 truncper = num > 100 ? 100 : num;
2743 trunclong = longflg;
2744 p--;
2745 break;
2746 case '=':
2747 case '<':
2748 *p = ' ';
2749 if (num || zeroflg || plusflg || longflg || (*s != '='))
2751 /* expand all pads */
2752 if (minusflg)
2754 num = (plusflg ? lastpad : padlen) - num;
2755 if (!plusflg && padlen == 0)
2756 num = p - winmsg_buf;
2757 plusflg = 0;
2759 else if (!zeroflg)
2761 if (*s != '=' && num == 0 && !plusflg)
2762 num = 100;
2763 if (num > 100)
2764 num = 100;
2765 if (padlen == 0)
2766 num = p - winmsg_buf;
2767 else
2768 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2770 if (num < 0)
2771 num = 0;
2772 if (plusflg)
2773 num += lastpad;
2774 if (num > MAXSTR - 1)
2775 num = MAXSTR - 1;
2776 if (numpad)
2777 p = pad_expand(winmsg_buf, p, numpad, num);
2778 numpad = 0;
2779 if (p - winmsg_buf > num && !longflg)
2781 int left, trunc;
2783 if (truncpos == -1)
2785 truncpos = lastpad;
2786 truncper = 0;
2788 trunc = lastpad + truncper * (num - lastpad) / 100;
2789 if (trunc > num)
2790 trunc = num;
2791 if (trunc < lastpad)
2792 trunc = lastpad;
2793 left = truncpos - trunc;
2794 if (left > p - winmsg_buf - num)
2795 left = p - winmsg_buf - num;
2796 debug1("lastpad = %d, ", lastpad);
2797 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2798 if (left > 0)
2800 if (left + lastpad > p - winmsg_buf)
2801 left = p - winmsg_buf - lastpad;
2802 if (p - winmsg_buf - lastpad - left > 0)
2803 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2804 p -= left;
2805 r = winmsg_numrend;
2806 while (r && winmsg_rendpos[r - 1] > lastpad)
2808 r--;
2809 winmsg_rendpos[r] -= left;
2810 if (winmsg_rendpos[r] < lastpad)
2811 winmsg_rendpos[r] = lastpad;
2813 if (trunclong)
2815 if (p - winmsg_buf > lastpad)
2816 winmsg_buf[lastpad] = '.';
2817 if (p - winmsg_buf > lastpad + 1)
2818 winmsg_buf[lastpad + 1] = '.';
2819 if (p - winmsg_buf > lastpad + 2)
2820 winmsg_buf[lastpad + 2] = '.';
2823 if (p - winmsg_buf > num)
2825 p = winmsg_buf + num;
2826 if (trunclong)
2828 if (num - 1 >= lastpad)
2829 p[-1] = '.';
2830 if (num - 2 >= lastpad)
2831 p[-2] = '.';
2832 if (num - 3 >= lastpad)
2833 p[-3] = '.';
2835 r = winmsg_numrend;
2836 while (r && winmsg_rendpos[r - 1] > num)
2837 winmsg_rendpos[--r] = num;
2839 truncpos = -1;
2840 trunclong = 0;
2841 if (lastpad > p - winmsg_buf)
2842 lastpad = p - winmsg_buf;
2843 debug1("lastpad now %d\n", lastpad);
2845 if (*s == '=')
2847 while (p - winmsg_buf < num)
2848 *p++ = ' ';
2849 lastpad = p - winmsg_buf;
2850 truncpos = -1;
2851 trunclong = 0;
2852 debug1("lastpad2 now %d\n", lastpad);
2854 p--;
2856 else if (padlen)
2858 *p = 127; /* internal pad representation */
2859 numpad++;
2861 break;
2862 case 'n':
2863 s++;
2864 /* FALLTHROUGH */
2865 default:
2866 s--;
2867 if (l > 10 + num)
2869 if (num == 0)
2870 num = 1;
2871 if (!win)
2872 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2873 else
2874 sprintf(p, "%*d", num, win->w_number);
2875 qmflag = 1;
2876 p += strlen(p) - 1;
2878 break;
2881 if (qmpos && !qmflag)
2882 p = qmpos + 1;
2883 *p = '\0';
2884 if (numpad)
2886 if (padlen > MAXSTR - 1)
2887 padlen = MAXSTR - 1;
2888 p = pad_expand(winmsg_buf, p, numpad, padlen);
2890 if (ev)
2892 evdeq(ev); /* just in case */
2893 ev->timeout.tv_sec = 0;
2894 ev->timeout.tv_usec = 0;
2896 if (ev && tick)
2898 now.tv_usec = 100000;
2899 if (tick == 1)
2900 now.tv_sec++;
2901 else
2902 now.tv_sec += tick - (now.tv_sec % tick);
2903 ev->timeout = now;
2904 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2906 return winmsg_buf;
2909 char *
2910 MakeWinMsg(s, win, esc)
2911 char *s;
2912 struct win *win;
2913 int esc;
2915 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2918 void
2919 PutWinMsg(s, start, max)
2920 char *s;
2921 int start, max;
2923 int i, p, l, r, n;
2924 struct mchar rend;
2925 struct mchar rendstack[MAX_WINMSG_REND];
2926 int rendstackn = 0;
2928 if (s != winmsg_buf)
2930 /* sorry, no fancy coloring available */
2931 debug1("PutWinMsg %s plain\n", s);
2932 l = strlen(s);
2933 if (l > max)
2934 l = max;
2935 l -= start;
2936 s += start;
2937 while (l-- > 0)
2938 PUTCHARLP(*s++);
2939 return;
2941 rend = D_rend;
2942 p = 0;
2943 l = strlen(s);
2944 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
2945 for (i = 0; i < winmsg_numrend && max > 0; i++)
2947 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
2948 break;
2949 if (p < winmsg_rendpos[i])
2951 n = winmsg_rendpos[i] - p;
2952 if (n > max)
2953 n = max;
2954 max -= n;
2955 p += n;
2956 while(n-- > 0)
2958 if (start-- > 0)
2959 s++;
2960 else
2961 PUTCHARLP(*s++);
2964 r = winmsg_rend[i];
2965 if (r == -1)
2967 if (rendstackn > 0)
2968 rend = rendstack[--rendstackn];
2970 else
2972 rendstack[rendstackn++] = rend;
2973 ApplyAttrColor(r, &rend);
2975 SetRendition(&rend);
2977 if (p < l)
2979 n = l - p;
2980 if (n > max)
2981 n = max;
2982 while(n-- > 0)
2984 if (start-- > 0)
2985 s++;
2986 else
2987 PUTCHARLP(*s++);
2993 #ifdef DEBUG
2994 static void
2995 fds1(i, j)
2996 int i, j;
2998 while (i < j)
3000 debug1("%d ", i);
3001 i++;
3003 if ((j = open("/dev/null", 0)) >= 0)
3005 fds1(i + 1, j);
3006 close(j);
3008 else
3010 while (dup(++i) < 0 && errno != EBADF)
3011 debug1("%d ", i);
3012 debug1(" [%d]\n", i);
3016 static void
3017 fds()
3019 debug("fds: ");
3020 fds1(-1, -1);
3022 #endif
3024 static void
3025 serv_read_fn(ev, data)
3026 struct event *ev;
3027 char *data;
3029 debug("Knock - knock!\n");
3030 ReceiveMsg();
3033 static void
3034 serv_select_fn(ev, data)
3035 struct event *ev;
3036 char *data;
3038 struct win *p;
3040 debug("serv_select_fn called\n");
3041 /* XXX: messages?? */
3042 if (GotSigChld)
3044 SigChldHandler();
3046 if (InterruptPlease)
3048 debug("Backend received interrupt\n");
3049 /* This approach is rather questionable in a multi-display
3050 * environment */
3051 if (fore && displays)
3053 #if defined(TERMIO) || defined(POSIX)
3054 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3055 #else
3056 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3057 #endif
3058 #ifdef PSEUDOS
3059 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3060 &ibuf, 1);
3061 debug1("Backend wrote interrupt to %d", fore->w_number);
3062 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3063 #else
3064 write(fore->w_ptyfd, &ibuf, 1);
3065 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3066 #endif
3068 InterruptPlease = 0;
3071 for (p = windows; p; p = p->w_next)
3073 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3075 struct canvas *cv;
3076 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3077 p->w_bell = BELL_ON;
3078 for (display = displays; display; display = display->d_next)
3080 for (cv = D_cvlist; cv; cv = cv->c_next)
3081 if (cv->c_layer->l_bottom == &p->w_layer)
3082 break;
3083 if (cv == 0)
3085 p->w_bell = BELL_DONE;
3086 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3088 else if (visual && !D_VB && (!D_status || !D_status_bell))
3090 Msg(0, "%s", VisualBellString);
3091 if (D_status)
3093 D_status_bell = 1;
3094 debug1("using vbell timeout %d\n", VBellWait);
3095 SetTimeout(&D_statusev, VBellWait );
3099 /* don't annoy the user with two messages */
3100 if (p->w_monitor == MON_FOUND)
3101 p->w_monitor = MON_DONE;
3102 WindowChanged(p, 'f');
3104 if (p->w_monitor == MON_FOUND)
3106 struct canvas *cv;
3107 p->w_monitor = MON_ON;
3108 for (display = displays; display; display = display->d_next)
3110 for (cv = D_cvlist; cv; cv = cv->c_next)
3111 if (cv->c_layer->l_bottom == &p->w_layer)
3112 break;
3113 if (cv)
3114 continue; /* user already sees window */
3115 #ifdef MULTIUSER
3116 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3117 continue; /* user doesn't care */
3118 #endif
3119 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3120 p->w_monitor = MON_DONE;
3122 WindowChanged(p, 'f');
3126 for (display = displays; display; display = display->d_next)
3128 struct canvas *cv;
3129 if (D_status == STATUS_ON_WIN)
3130 continue;
3131 /* XXX: should use display functions! */
3132 for (cv = D_cvlist; cv; cv = cv->c_next)
3134 int lx, ly;
3136 /* normalize window, see resize.c */
3137 lx = cv->c_layer->l_x;
3138 ly = cv->c_layer->l_y;
3139 if (lx == cv->c_layer->l_width)
3140 lx--;
3141 if (ly + cv->c_yoff < cv->c_ys)
3143 int i, n = cv->c_ys - (ly + cv->c_yoff);
3144 cv->c_yoff = cv->c_ys - ly;
3145 RethinkViewportOffsets(cv);
3146 if (n > cv->c_layer->l_height)
3147 n = cv->c_layer->l_height;
3148 CV_CALL(cv,
3149 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3150 LayRedisplayLine(-1, -1, -1, 1);
3151 for (i = 0; i < n; i++)
3152 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3153 if (cv == cv->c_display->d_forecv)
3154 LaySetCursor();
3157 else if (ly + cv->c_yoff > cv->c_ye)
3159 int i, n = ly + cv->c_yoff - cv->c_ye;
3160 cv->c_yoff = cv->c_ye - ly;
3161 RethinkViewportOffsets(cv);
3162 if (n > cv->c_layer->l_height)
3163 n = cv->c_layer->l_height;
3164 CV_CALL(cv,
3165 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3166 LayRedisplayLine(-1, -1, -1, 1);
3167 for (i = 0; i < n; i++)
3168 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3169 if (cv == cv->c_display->d_forecv)
3170 LaySetCursor();
3173 if (lx + cv->c_xoff < cv->c_xs)
3175 int i, n = cv->c_xs - (lx + cv->c_xoff);
3176 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3177 n = (cv->c_xe - cv->c_xs + 1) / 2;
3178 if (cv->c_xoff + n > cv->c_xs)
3179 n = cv->c_xs - cv->c_xoff;
3180 cv->c_xoff += n;
3181 RethinkViewportOffsets(cv);
3182 if (n > cv->c_layer->l_width)
3183 n = cv->c_layer->l_width;
3184 CV_CALL(cv,
3185 LayRedisplayLine(-1, -1, -1, 1);
3186 for (i = 0; i < flayer->l_height; i++)
3188 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3189 LayRedisplayLine(i, 0, n - 1, 1);
3191 if (cv == cv->c_display->d_forecv)
3192 LaySetCursor();
3195 else if (lx + cv->c_xoff > cv->c_xe)
3197 int i, n = lx + cv->c_xoff - cv->c_xe;
3198 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3199 n = (cv->c_xe - cv->c_xs + 1) / 2;
3200 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3201 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3202 cv->c_xoff -= n;
3203 RethinkViewportOffsets(cv);
3204 if (n > cv->c_layer->l_width)
3205 n = cv->c_layer->l_width;
3206 CV_CALL(cv,
3207 LayRedisplayLine(-1, -1, -1, 1);
3208 for (i = 0; i < flayer->l_height; i++)
3210 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3211 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3213 if (cv == cv->c_display->d_forecv)
3214 LaySetCursor();
3220 for (display = displays; display; display = display->d_next)
3222 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3223 continue;
3224 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3225 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3229 static void
3230 logflush_fn(ev, data)
3231 struct event *ev;
3232 char *data;
3234 struct win *p;
3235 char *buf;
3236 int n;
3238 if (!islogfile(NULL))
3239 return; /* no more logfiles */
3240 logfflush(NULL);
3241 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3242 if (n)
3244 SetTimeout(ev, n * 1000);
3245 evenq(ev); /* re-enqueue ourself */
3247 if (!logtstamp_on)
3248 return;
3249 /* write fancy time-stamp */
3250 for (p = windows; p; p = p->w_next)
3252 if (!p->w_log)
3253 continue;
3254 p->w_logsilence += n;
3255 if (p->w_logsilence < logtstamp_after)
3256 continue;
3257 if (p->w_logsilence - n >= logtstamp_after)
3258 continue;
3259 buf = MakeWinMsg(logtstamp_string, p, '%');
3260 logfwrite(p->w_log, buf, strlen(buf));
3265 * Interprets ^?, ^@ and other ^-control-char notation.
3266 * Interprets \ddd octal notation
3268 * The result is placed in *cp, p is advanced behind the parsed expression and
3269 * returned.
3271 static char *
3272 ParseChar(p, cp)
3273 char *p, *cp;
3275 if (*p == 0)
3276 return 0;
3277 if (*p == '^' && p[1])
3279 if (*++p == '?')
3280 *cp = '\177';
3281 else if (*p >= '@')
3282 *cp = Ctrl(*p);
3283 else
3284 return 0;
3285 ++p;
3287 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3289 *cp = 0;
3291 *cp = *cp * 8 + *p - '0';
3292 while (*++p <= '7' && *p >= '0');
3294 else
3295 *cp = *p++;
3296 return p;
3299 static int
3300 ParseEscape(p)
3301 char *p;
3303 unsigned char buf[2];
3305 if (*p == 0)
3306 SetEscape((struct acluser *)0, -1, -1);
3307 else
3309 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3310 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3311 return -1;
3312 SetEscape((struct acluser *)0, buf[0], buf[1]);
3314 return 0;