Require password, when applicable, for remote detach
[screen-lua.git] / src / screen.c
blob20954ba6ab65fe2a088bf67bd305e79ca4fded97
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;
338 static char *
339 locale_name(void)
341 char *s;
343 s = getenv("LC_ALL");
344 if (s == NULL)
345 s = getenv("LC_CTYPE");
346 if (s == NULL)
347 s = getenv("LANG");
348 return s;
352 main(ac, av)
353 int ac;
354 char **av;
356 register int n;
357 char *ap;
358 char *av0;
359 char socknamebuf[2 * MAXSTR];
360 int mflag = 0;
361 char *myname = (ac == 0) ? "screen" : av[0];
362 char *SockDir;
363 struct stat st;
364 #ifdef _MODE_T /* (jw) */
365 mode_t oumask;
366 #else
367 int oumask;
368 #endif
369 #if defined(SYSV) && !defined(ISC)
370 struct utsname utsnam;
371 #endif
372 struct NewWindow nwin;
373 int detached = 0; /* start up detached */
374 #ifdef MULTIUSER
375 char *sockp;
376 #endif
377 char *sty = 0;
379 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
380 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
381 #endif
382 #if defined(sun) && defined(SVR4)
384 /* Solaris' login blocks SIGHUP! This is _very bad_ */
385 sigset_t sset;
386 sigemptyset(&sset);
387 sigprocmask(SIG_SETMASK, &sset, 0);
389 #endif
392 * First, close all unused descriptors
393 * (otherwise, we might have problems with the select() call)
395 closeallfiles(0);
396 #ifdef DEBUG
397 opendebug(1, 0);
398 #endif
399 sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
400 PATCHLEVEL, STATE, ORIGIN, DATE);
401 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
402 debug2("-- screen debug started %s (%s)\n", *av, version);
403 #ifdef POSIX
404 debug("POSIX\n");
405 #endif
406 #ifdef TERMIO
407 debug("TERMIO\n");
408 #endif
409 #ifdef SYSV
410 debug("SYSV\n");
411 #endif
412 #ifdef SYSVSIGS
413 debug("SYSVSIGS\n");
414 #endif
415 #ifdef NAMEDPIPE
416 debug("NAMEDPIPE\n");
417 #endif
418 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
419 debug("Window size changing enabled\n");
420 #endif
421 #ifdef HAVE_SETREUID
422 debug("SETREUID\n");
423 #endif
424 #ifdef HAVE_SETEUID
425 debug("SETEUID\n");
426 #endif
427 #ifdef hpux
428 debug("hpux\n");
429 #endif
430 #ifdef USEBCOPY
431 debug("USEBCOPY\n");
432 #endif
433 #ifdef UTMPOK
434 debug("UTMPOK\n");
435 #endif
436 #ifdef LOADAV
437 debug("LOADAV\n");
438 #endif
439 #ifdef NETHACK
440 debug("NETHACK\n");
441 #endif
442 #ifdef TERMINFO
443 debug("TERMINFO\n");
444 #endif
445 #ifdef SHADOWPW
446 debug("SHADOWPW\n");
447 #endif
448 #ifdef NAME_MAX
449 debug1("NAME_MAX = %d\n", NAME_MAX);
450 #endif
452 BellString = SaveStr("Bell in window %n");
453 VisualBellString = SaveStr(" Wuff, Wuff!! ");
454 ActivityString = SaveStr("Activity in window %n");
455 screenlogfile = SaveStr("screenlog.%n");
456 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
457 hstatusstring = SaveStr("%h");
458 captionstring = SaveStr("%3n %t");
459 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
460 wlisttit = SaveStr("Num Name%=Flags");
461 wliststr = SaveStr("%3n %t%=%f");
462 #ifdef COPY_PASTE
463 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
464 #endif
465 ShellProg = NULL;
466 #ifdef POW_DETACH
467 PowDetachString = 0;
468 #endif
469 default_startup = (ac > 1) ? 0 : 1;
470 adaptflag = 0;
471 VBellWait = VBELLWAIT * 1000;
472 MsgWait = MSGWAIT * 1000;
473 MsgMinWait = MSGMINWAIT * 1000;
474 SilenceWait = SILENCEWAIT;
475 #ifdef HAVE_BRAILLE
476 InitBraille();
477 #endif
478 #ifdef ZMODEM
479 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
480 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
481 #endif
483 #ifdef COPY_PASTE
484 CompileKeys((char *)0, 0, mark_key_tab);
485 #endif
486 #ifdef UTF8
487 InitBuiltinTabs();
488 screenencodings = SaveStr(SCREENENCODINGS);
489 #endif
490 #ifdef DW_CHARS
491 cjkwidth = 0;
492 #endif
493 nwin = nwin_undef;
494 nwin_options = nwin_undef;
495 strcpy(screenterm, "screen");
497 logreopen_register(lf_secreopen);
499 av0 = *av;
500 /* if this is a login screen, assume -RR */
501 if (*av0 == '-')
503 rflag = 4;
504 #ifdef MULTI
505 xflag = 1;
506 #else
507 dflag = 1;
508 #endif
509 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
511 while (ac > 0)
513 ap = *++av;
514 if (--ac > 0 && *ap == '-')
516 if (ap[1] == '-' && ap[2] == 0)
518 av++;
519 ac--;
520 break;
522 if (ap[1] == '-' && !strcmp(ap, "--version"))
523 Panic(0, "Screen version %s", version);
524 if (ap[1] == '-' && !strcmp(ap, "--help"))
525 exit_with_usage(myname, NULL, NULL);
526 while (ap && *ap && *++ap)
528 switch (*ap)
530 case 'a':
531 nwin_options.aflag = 1;
532 break;
533 case 'A':
534 adaptflag = 1;
535 break;
536 case 'p': /* preselect */
537 if (*++ap)
538 preselect = ap;
539 else
541 if (!--ac)
542 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
543 preselect = *++av;
545 ap = NULL;
546 break;
547 #ifdef HAVE_BRAILLE
548 case 'B':
549 bd.bd_start_braille = 1;
550 break;
551 #endif
552 case 'c':
553 if (*++ap)
554 RcFileName = ap;
555 else
557 if (--ac == 0)
558 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
559 RcFileName = *++av;
561 ap = NULL;
562 break;
563 case 'e':
564 if (!*++ap)
566 if (--ac == 0)
567 exit_with_usage(myname, "Specify command characters with -e", NULL);
568 ap = *++av;
570 if (ParseEscape(ap))
571 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
572 ap = NULL;
573 break;
574 case 'f':
575 ap++;
576 switch (*ap++)
578 case 'n':
579 case '0':
580 nwin_options.flowflag = FLOW_NOW * 0;
581 break;
582 case '\0':
583 ap--;
584 /* FALLTHROUGH */
585 case 'y':
586 case '1':
587 nwin_options.flowflag = FLOW_NOW * 1;
588 break;
589 case 'a':
590 nwin_options.flowflag = FLOW_AUTOFLAG;
591 break;
592 default:
593 exit_with_usage(myname, "Unknown flow option -%s", --ap);
595 break;
596 case 'h':
597 if (--ac == 0)
598 exit_with_usage(myname, NULL, NULL);
599 nwin_options.histheight = atoi(*++av);
600 if (nwin_options.histheight < 0)
601 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
602 break;
603 case 'i':
604 iflag = 1;
605 break;
606 case 't': /* title, the former AkA == -k */
607 if (--ac == 0)
608 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
609 nwin_options.aka = *++av;
610 break;
611 case 'l':
612 ap++;
613 switch (*ap++)
615 case 'n':
616 case '0':
617 nwin_options.lflag = 0;
618 break;
619 case '\0':
620 ap--;
621 /* FALLTHROUGH */
622 case 'y':
623 case '1':
624 nwin_options.lflag = 1;
625 break;
626 case 'a':
627 nwin_options.lflag = 3;
628 break;
629 case 's': /* -ls */
630 case 'i': /* -list */
631 lsflag = 1;
632 if (ac > 1 && !SockMatch)
634 SockMatch = *++av;
635 ac--;
637 ap = NULL;
638 break;
639 default:
640 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
642 break;
643 case 'w':
644 lsflag = 1;
645 wipeflag = 1;
646 if (ac > 1 && !SockMatch)
648 SockMatch = *++av;
649 ac--;
651 break;
652 case 'L':
653 nwin_options.Lflag = 1;
654 break;
655 case 'm':
656 mflag = 1;
657 break;
658 case 'O': /* to be (or not to be?) deleted. jw. */
659 force_vt = 0;
660 break;
661 case 'T':
662 if (--ac == 0)
663 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
664 if (strlen(*++av) < 20)
665 strcpy(screenterm, *av);
666 else
667 Panic(0, "-T: terminal name too long. (max. 20 char)");
668 nwin_options.term = screenterm;
669 break;
670 case 'q':
671 quietflag = 1;
672 break;
673 case 'r':
674 case 'R':
675 #ifdef MULTI
676 case 'x':
677 #endif
678 if (ac > 1 && *av[1] != '-' && !SockMatch)
680 SockMatch = *++av;
681 ac--;
682 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
684 #ifdef MULTI
685 if (*ap == 'x')
686 xflag = 1;
687 #endif
688 if (rflag)
689 rflag = 2;
690 rflag += (*ap == 'R') ? 2 : 1;
691 break;
692 #ifdef REMOTE_DETACH
693 case 'd':
694 dflag = 1;
695 /* FALLTHROUGH */
696 case 'D':
697 if (!dflag)
698 dflag = 2;
699 if (ac == 2)
701 if (*av[1] != '-' && !SockMatch)
703 SockMatch = *++av;
704 ac--;
705 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
708 break;
709 #endif
710 case 's':
711 if (--ac == 0)
712 exit_with_usage(myname, "Specify shell with -s", NULL);
713 if (ShellProg)
714 free(ShellProg);
715 ShellProg = SaveStr(*++av);
716 debug1("ShellProg: '%s'\n", ShellProg);
717 break;
718 case 'S':
719 if (!SockMatch)
721 if (--ac == 0)
722 exit_with_usage(myname, "Specify session-name with -S", NULL);
723 SockMatch = *++av;
725 if (!*SockMatch)
726 exit_with_usage(myname, "Empty session-name?", NULL);
727 break;
728 case 'X':
729 cmdflag = 1;
730 break;
731 case 'v':
732 Panic(0, "Screen version %s", version);
733 /* NOTREACHED */
734 #ifdef UTF8
735 case 'U':
736 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
737 break;
738 #endif
739 default:
740 exit_with_usage(myname, "Unknown option %s", --ap);
744 else
745 break;
748 real_uid = getuid();
749 real_gid = getgid();
750 eff_uid = geteuid();
751 eff_gid = getegid();
752 if (eff_uid != real_uid)
754 /* if running with s-bit, we must install a special signal
755 * handler routine that resets the s-bit, so that we get a
756 * core file anyway.
758 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
759 signal(SIGBUS, CoreDump);
760 #endif /* SIGBUS */
761 signal(SIGSEGV, CoreDump);
764 #ifdef USE_LOCALE
765 setlocale(LC_ALL, "");
766 #endif
767 #ifdef ENCODINGS
768 if (nwin_options.encoding == -1)
770 /* ask locale if we should start in UTF-8 mode */
771 # ifdef HAVE_NL_LANGINFO
772 # ifndef USE_LOCALE
773 setlocale(LC_CTYPE, "");
774 # endif
775 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
776 debug1("locale says encoding = %d\n", nwin_options.encoding);
777 # else
778 # ifdef UTF8
779 char *s;
780 if ((s = locale_name()) && InStr(s, "UTF-8"))
781 nwin_options.encoding = UTF8;
782 # endif
783 debug1("environment says encoding=%d\n", nwin_options.encoding);
784 #endif
786 # ifdef DW_CHARS
788 char *s;
789 if ((s = locale_name()))
791 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
793 cjkwidth = 1;
797 #endif
798 #endif
799 if (SockMatch && strlen(SockMatch) >= MAXSTR)
800 Panic(0, "Ridiculously long socketname - try again.");
801 if (cmdflag && !rflag && !dflag && !xflag)
802 xflag = 1;
803 if (!cmdflag && dflag && mflag && !(rflag || xflag))
804 detached = 1;
805 nwin = nwin_options;
806 #ifdef ENCODINGS
807 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
808 #endif
809 if (ac)
810 nwin.args = av;
812 /* make the write() calls return -1 on all errors */
813 #ifdef SIGXFSZ
815 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
816 * It appears that in System V Release 4, UNIX, if you are writing
817 * an output file and you exceed the currently set file size limit,
818 * you _don't_ just get the call to `write' returning with a
819 * failure code. Rather, you get a signal called `SIGXFSZ' which,
820 * if neither handled nor ignored, will cause your program to crash
821 * with a core dump.
823 signal(SIGXFSZ, SIG_IGN);
824 #endif /* SIGXFSZ */
826 #ifdef SIGPIPE
827 signal(SIGPIPE, SIG_IGN);
828 #endif
830 if (!ShellProg)
832 register char *sh;
834 sh = getenv("SHELL");
835 ShellProg = SaveStr(sh ? sh : DefaultShell);
837 ShellArgs[0] = ShellProg;
838 home = getenv("HOME");
839 if (!mflag && !SockMatch)
841 sty = getenv("STY");
842 if (sty && *sty == 0)
843 sty = 0;
846 #ifdef NETHACK
847 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
849 char nethackrc[MAXPATHLEN];
851 if (home && (strlen(home) < (MAXPATHLEN - 20)))
853 sprintf(nethackrc,"%s/.nethackrc", home);
854 nethackflag = !access(nethackrc, F_OK);
857 #endif
859 #ifdef MULTIUSER
860 own_uid = multi_uid = real_uid;
861 if (SockMatch && (sockp = index(SockMatch, '/')))
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;
883 /* Special case: effective user is multiuser. */
884 if (eff_uid && (multi_uid != eff_uid))
885 Panic(0, "Must run suid root for multiuser support.");
887 if (SockMatch && *SockMatch == 0)
888 SockMatch = 0;
889 #endif /* MULTIUSER */
891 if ((LoginName = getlogin()) && LoginName[0] != '\0')
893 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
894 if ((int)ppp->pw_uid != real_uid)
895 ppp = (struct passwd *) 0;
897 if (ppp == 0)
899 if ((ppp = getpwuid(real_uid)) == 0)
901 Panic(0, "getpwuid() can't identify your account!");
902 exit(1);
904 LoginName = ppp->pw_name;
906 LoginName = SaveStr(LoginName);
908 ppp = getpwbyname(LoginName, ppp);
910 #if !defined(SOCKDIR) && defined(MULTIUSER)
911 if (multi && !multiattach)
913 if (home && strcmp(home, ppp->pw_dir))
914 Panic(0, "$HOME must match passwd entry for multiuser screens.");
916 #endif
918 #define SET_GUID() do \
920 setgid(real_gid); \
921 setuid(real_uid); \
922 eff_uid = real_uid; \
923 eff_gid = real_gid; \
924 } while (0)
926 #define SET_TTYNAME(fatal) do \
928 if (!(attach_tty = ttyname(0))) \
930 if (fatal) \
931 Panic(0, "Must be connected to a terminal."); \
932 else \
933 attach_tty = ""; \
935 else if (stat(attach_tty, &st)) \
936 Panic(errno, "Cannot access '%s'", attach_tty); \
937 if (strlen(attach_tty) >= MAXPATHLEN) \
938 Panic(0, "TtyName too long - sorry."); \
939 } while (0)
941 if (home == 0 || *home == '\0')
942 home = ppp->pw_dir;
943 if (strlen(LoginName) > 20)
944 Panic(0, "LoginName too long - sorry.");
945 #ifdef MULTIUSER
946 if (multi && strlen(multi) > 20)
947 Panic(0, "Screen owner name too long - sorry.");
948 #endif
949 if (strlen(home) > MAXPATHLEN - 25)
950 Panic(0, "$HOME too long - sorry.");
952 attach_tty = "";
953 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty))
955 #ifndef NAMEDPIPE
956 int fl;
957 #endif
959 /* ttyname implies isatty */
960 SET_TTYNAME(1);
961 #ifdef MULTIUSER
962 tty_mode = (int)st.st_mode & 0777;
963 #endif
965 #ifndef NAMEDPIPE
966 fl = fcntl(0, F_GETFL, 0);
967 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
968 attach_fd = 0;
969 #endif
970 if (attach_fd == -1)
972 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
973 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
974 close(n);
976 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
978 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
979 Panic(0, "Please set a terminal type.");
980 if (strlen(attach_term) > sizeof(D_termname) - 1)
981 Panic(0, "$TERM too long - sorry.");
982 GetTTY(0, &attach_Mode);
983 #ifdef DEBUGGGGGGGGGGGGGGG
984 DebugTTY(&attach_Mode);
985 #endif /* DEBUG */
988 #ifdef _MODE_T
989 oumask = umask(0); /* well, unsigned never fails? jw. */
990 #else
991 if ((oumask = (int)umask(0)) == -1)
992 Panic(errno, "Cannot change umask to zero");
993 #endif
994 SockDir = getenv("SCREENDIR");
995 if (SockDir)
997 if (strlen(SockDir) >= MAXPATHLEN - 1)
998 Panic(0, "Ridiculously long $SCREENDIR - try again.");
999 #ifdef MULTIUSER
1000 if (multi)
1001 Panic(0, "No $SCREENDIR with multi screens, please.");
1002 #endif
1004 #ifdef MULTIUSER
1005 if (multiattach)
1007 # ifndef SOCKDIR
1008 sprintf(SockPath, "%s/.screen", multi_home);
1009 SockDir = SockPath;
1010 # else
1011 SockDir = SOCKDIR;
1012 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1013 # endif
1015 else
1016 #endif
1018 #ifndef SOCKDIR
1019 if (SockDir == 0)
1021 sprintf(SockPath, "%s/.screen", home);
1022 SockDir = SockPath;
1024 #endif
1025 if (SockDir)
1027 if (access(SockDir, F_OK))
1029 debug1("SockDir '%s' missing ...\n", SockDir);
1030 if (UserContext() > 0)
1032 if (mkdir(SockDir, 0700))
1033 UserReturn(0);
1034 UserReturn(1);
1036 if (UserStatus() <= 0)
1037 Panic(0, "Cannot make directory '%s'.", SockDir);
1039 if (SockDir != SockPath)
1040 strcpy(SockPath, SockDir);
1042 #ifdef SOCKDIR
1043 else
1045 SockDir = SOCKDIR;
1046 if (lstat(SockDir, &st))
1048 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1049 (eff_gid != real_gid) ? 0775 :
1050 #ifdef S_ISVTX
1051 0777|S_ISVTX;
1052 #else
1053 0777;
1054 #endif
1055 if (mkdir(SockDir, n) == -1)
1056 Panic(errno, "Cannot make directory '%s'", SockDir);
1058 else
1060 if (!S_ISDIR(st.st_mode))
1061 Panic(0, "'%s' must be a directory.", SockDir);
1062 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1063 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1064 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1065 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1066 0777;
1067 if (((int)st.st_mode & 0777) != n)
1068 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1070 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1071 if (access(SockPath, F_OK))
1073 if (mkdir(SockPath, 0700) == -1)
1074 Panic(errno, "Cannot make directory '%s'", SockPath);
1075 (void) chown(SockPath, real_uid, real_gid);
1078 #endif
1081 if (stat(SockPath, &st) == -1)
1082 Panic(errno, "Cannot access %s", SockPath);
1083 else
1084 if (!S_ISDIR(st.st_mode))
1085 Panic(0, "%s is not a directory.", SockPath);
1086 #ifdef MULTIUSER
1087 if (multi)
1089 if ((int)st.st_uid != multi_uid)
1090 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1092 else
1093 #endif
1095 if ((int)st.st_uid != real_uid)
1096 Panic(0, "You are not the owner of %s.", SockPath);
1098 if ((st.st_mode & 0777) != 0700)
1099 Panic(0, "Directory %s must have mode 700.", SockPath);
1100 if (SockMatch && index(SockMatch, '/'))
1101 Panic(0, "Bad session name '%s'", SockMatch);
1102 SockName = SockPath + strlen(SockPath) + 1;
1103 *SockName = 0;
1104 (void) umask(oumask);
1105 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1107 #if defined(SYSV) && !defined(ISC)
1108 if (uname(&utsnam) == -1)
1109 Panic(errno, "uname");
1110 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1111 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1112 #else
1113 (void) gethostname(HostName, MAXSTR);
1114 HostName[MAXSTR - 1] = '\0';
1115 #endif
1116 if ((ap = index(HostName, '.')) != NULL)
1117 *ap = '\0';
1119 if (lsflag)
1121 int i, fo, oth;
1123 #ifdef MULTIUSER
1124 if (multi)
1125 real_uid = multi_uid;
1126 #endif
1127 SET_GUID();
1128 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1129 if (quietflag)
1130 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1131 if (fo == 0)
1132 Panic(0, "No Sockets found in %s.\n", SockPath);
1133 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1134 /* NOTREACHED */
1136 signal(SIG_BYE, AttacherFinit); /* prevent races */
1137 if (cmdflag)
1139 /* attach_tty is not mandatory */
1140 SET_TTYNAME(0);
1141 if (!*av)
1142 Panic(0, "Please specify a command.");
1143 SET_GUID();
1144 SendCmdMessage(sty, SockMatch, av);
1145 exit(0);
1147 else if (rflag || xflag)
1149 debug("screen -r: - is there anybody out there?\n");
1150 if (Attach(MSG_ATTACH))
1152 Attacher();
1153 /* NOTREACHED */
1155 #ifdef MULTIUSER
1156 if (multiattach)
1157 Panic(0, "Can't create sessions of other users.");
1158 #endif
1159 debug("screen -r: backend not responding -- still crying\n");
1161 else if (dflag && !mflag)
1163 SET_TTYNAME(0);
1164 Attach(MSG_DETACH);
1165 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1166 eexit(0);
1167 /* NOTREACHED */
1169 if (!SockMatch && !mflag && sty)
1171 /* attach_tty is not mandatory */
1172 SET_TTYNAME(0);
1173 SET_GUID();
1174 nwin_options.args = av;
1175 SendCreateMsg(sty, &nwin);
1176 exit(0);
1177 /* NOTREACHED */
1179 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1181 if (!detached || dflag != 2)
1182 MasterPid = fork();
1183 else
1184 MasterPid = 0;
1186 switch (MasterPid)
1188 case -1:
1189 Panic(errno, "fork");
1190 /* NOTREACHED */
1191 case 0:
1192 break;
1193 default:
1194 if (detached)
1195 exit(0);
1196 if (SockMatch)
1197 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1198 else
1199 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1200 for (ap = socknamebuf; *ap; ap++)
1201 if (*ap == '/')
1202 *ap = '-';
1203 #ifdef NAME_MAX
1204 if (strlen(socknamebuf) > NAME_MAX)
1205 socknamebuf[NAME_MAX] = 0;
1206 #endif
1207 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1208 SET_GUID();
1209 Attacher();
1210 /* NOTREACHED */
1213 if (!detached)
1214 PanicPid = getppid();
1216 if (DefaultEsc == -1)
1217 DefaultEsc = Ctrl('a');
1218 if (DefaultMetaEsc == -1)
1219 DefaultMetaEsc = 'a';
1221 ap = av0 + strlen(av0) - 1;
1222 while (ap >= av0)
1224 if (!strncmp("screen", ap, 6))
1226 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1227 break;
1229 ap--;
1231 if (ap < av0)
1232 *av0 = 'S';
1234 #ifdef DEBUG
1236 char buf[256];
1238 if (dfp && dfp != stderr)
1239 fclose(dfp);
1240 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1241 if ((dfp = fopen(buf, "w")) == NULL)
1242 dfp = stderr;
1243 else
1244 (void) chmod(buf, 0666);
1246 #endif
1247 if (!detached)
1249 if (attach_fd == -1)
1251 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1252 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1254 else
1255 n = dup(attach_fd);
1257 else
1258 n = -1;
1259 freopen("/dev/null", "r", stdin);
1260 freopen("/dev/null", "w", stdout);
1262 #ifdef DEBUG
1263 if (dfp != stderr)
1264 #endif
1265 freopen("/dev/null", "w", stderr);
1266 debug("-- screen.back debug started\n");
1269 * This guarantees that the session owner is listed, even when we
1270 * start detached. From now on we should not refer to 'LoginName'
1271 * any more, use users->u_name instead.
1273 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1274 Panic(0, "Could not create user info");
1275 if (!detached)
1277 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1278 Panic(0, "Could not alloc display");
1279 PanicPid = 0;
1280 #ifdef ENCODINGS
1281 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1282 debug1("D_encoding = %d\n", D_encoding);
1283 #endif
1286 if (SockMatch)
1288 /* user started us with -S option */
1289 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1291 else
1293 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1294 HostName);
1296 for (ap = socknamebuf; *ap; ap++)
1297 if (*ap == '/')
1298 *ap = '-';
1299 #ifdef NAME_MAX
1300 if (strlen(socknamebuf) > NAME_MAX)
1302 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1303 socknamebuf[NAME_MAX] = 0;
1305 #endif
1306 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1308 ServerSocket = MakeServerSocket();
1309 InitKeytab();
1310 #ifdef ETCSCREENRC
1311 # ifdef ALLOW_SYSSCREENRC
1312 if ((ap = getenv("SYSSCREENRC")))
1313 (void)StartRc(ap, 0);
1314 else
1315 # endif
1316 (void)StartRc(ETCSCREENRC, 0);
1317 #endif
1318 (void)StartRc(RcFileName, 0);
1319 # ifdef UTMPOK
1320 # ifndef UTNOKEEP
1321 InitUtmp();
1322 # endif /* UTNOKEEP */
1323 # endif /* UTMPOK */
1324 if (display)
1326 if (InitTermcap(0, 0))
1328 debug("Could not init termcap - exiting\n");
1329 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1330 freetty();
1331 if (D_userpid)
1332 Kill(D_userpid, SIG_BYE);
1333 eexit(1);
1335 MakeDefaultCanvas();
1336 InitTerm(0);
1337 #ifdef UTMPOK
1338 RemoveLoginSlot();
1339 #endif
1341 else
1342 MakeTermcap(1);
1343 #ifdef LOADAV
1344 InitLoadav();
1345 #endif /* LOADAV */
1346 MakeNewEnv();
1347 signal(SIGHUP, SigHup);
1348 signal(SIGINT, FinitHandler);
1349 signal(SIGQUIT, FinitHandler);
1350 signal(SIGTERM, FinitHandler);
1351 #ifdef BSDJOBS
1352 signal(SIGTTIN, SIG_IGN);
1353 signal(SIGTTOU, SIG_IGN);
1354 #endif
1356 if (display)
1358 brktty(D_userfd);
1359 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1360 /* Note: SetMode must be called _before_ FinishRc. */
1361 SetTTY(D_userfd, &D_NewMode);
1362 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1363 Msg(errno, "Warning: NBLOCK fcntl failed");
1365 else
1366 brktty(-1); /* just try */
1367 signal(SIGCHLD, SigChld);
1368 #ifdef ETCSCREENRC
1369 # ifdef ALLOW_SYSSCREENRC
1370 if ((ap = getenv("SYSSCREENRC")))
1371 FinishRc(ap);
1372 else
1373 # endif
1374 FinishRc(ETCSCREENRC);
1375 #endif
1376 FinishRc(RcFileName);
1378 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1379 if (windows == NULL)
1381 debug("We open one default window, as screenrc did not specify one.\n");
1382 if (MakeWindow(&nwin) == -1)
1384 Msg(0, "Sorry, could not find a PTY.");
1385 sleep(5);
1386 Finit(0);
1387 /* NOTREACHED */
1391 #ifdef HAVE_BRAILLE
1392 StartBraille();
1393 #endif
1395 if (display && default_startup)
1396 display_copyright();
1397 signal(SIGINT, SigInt);
1398 if (rflag && (rflag & 1) == 0 && !quietflag)
1400 Msg(0, "New screen...");
1401 rflag = 0;
1404 serv_read.type = EV_READ;
1405 serv_read.fd = ServerSocket;
1406 serv_read.handler = serv_read_fn;
1407 evenq(&serv_read);
1409 serv_select.pri = -10;
1410 serv_select.type = EV_ALWAYS;
1411 serv_select.handler = serv_select_fn;
1412 evenq(&serv_select);
1414 logflushev.type = EV_TIMEOUT;
1415 logflushev.handler = logflush_fn;
1417 sched();
1418 /* NOTREACHED */
1419 return 0;
1422 void
1423 WindowDied(p, wstat, wstat_valid)
1424 struct win *p;
1425 int wstat;
1426 int wstat_valid;
1428 int killit = 0;
1430 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1431 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1432 killit = 1;
1434 if (ZombieKey_destroy && !killit)
1436 char buf[100], *s, reason[100];
1437 time_t now;
1439 if (wstat_valid) {
1440 if (WIFEXITED(wstat))
1441 if (WEXITSTATUS(wstat))
1442 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1443 else
1444 sprintf(reason, "terminated normally");
1445 else if (WIFSIGNALED(wstat))
1446 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1447 #ifdef WCOREDUMP
1448 WCOREDUMP(wstat) ? " (core file generated)" : "");
1449 #else
1450 "");
1451 #endif
1452 } else
1453 sprintf(reason, "detached from window");
1455 (void) time(&now);
1456 s = ctime(&now);
1457 if (s && *s)
1458 s[strlen(s) - 1] = '\0';
1459 debug3("window %d (%s) going into zombie state fd %d",
1460 p->w_number, p->w_title, p->w_ptyfd);
1461 #ifdef UTMPOK
1462 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1464 RemoveUtmp(p);
1465 p->w_slot = 0; /* "detached" */
1467 #endif
1468 CloseDevice(p);
1470 p->w_deadpid = p->w_pid;
1471 p->w_pid = 0;
1472 ResetWindow(p);
1473 /* p->w_y = p->w_bot; */
1474 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1475 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1476 WriteString(p, buf, strlen(buf));
1477 WindowChanged(p, 'f');
1479 else
1480 KillWindow(p);
1481 #ifdef UTMPOK
1482 CarefulUtmp();
1483 #endif
1486 static void
1487 SigChldHandler()
1489 struct stat st;
1490 #ifdef DEBUG
1491 fds();
1492 #endif
1493 while (GotSigChld)
1495 GotSigChld = 0;
1496 DoWait();
1497 #ifdef SYSVSIGS
1498 signal(SIGCHLD, SigChld);
1499 #endif
1501 if (stat(SockPath, &st) == -1)
1503 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1504 if (!RecoverSocket())
1506 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1507 Finit(1);
1509 else
1510 debug1("'%s' reconstructed\n", SockPath);
1512 else
1513 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1516 static sigret_t
1517 SigChld SIGDEFARG
1519 debug("SigChld()\n");
1520 GotSigChld = 1;
1521 SIGRETURN;
1524 sigret_t
1525 SigHup SIGDEFARG
1527 /* Hangup all displays */
1528 while ((display = displays) != 0)
1529 Hangup();
1530 SIGRETURN;
1534 * the backend's Interrupt handler
1535 * we cannot insert the intrc directly, as we never know
1536 * if fore is valid.
1538 static sigret_t
1539 SigInt SIGDEFARG
1541 #if HAZARDOUS
1542 char ibuf;
1544 debug("SigInt()\n");
1545 if (fore && displays)
1547 # if defined(TERMIO) || defined(POSIX)
1548 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1549 # else
1550 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1551 # endif
1552 fore->w_inlen = 0;
1553 write(fore->w_ptyfd, &ibuf, 1);
1555 #else
1556 signal(SIGINT, SigInt);
1557 debug("SigInt() careful\n");
1558 InterruptPlease = 1;
1559 #endif
1560 SIGRETURN;
1563 static sigret_t
1564 CoreDump SIGDEFARG
1566 struct display *disp;
1567 char buf[80];
1569 #if defined(SYSVSIGS) && defined(SIGHASARG)
1570 signal(sigsig, SIG_IGN);
1571 #endif
1572 setgid(getgid());
1573 setuid(getuid());
1574 unlink("core");
1575 #ifdef SIGHASARG
1576 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1577 #else
1578 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1579 #endif
1580 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1582 #else /* SHADOWPW && !DEBUG */
1583 " (core dumped)"
1584 #endif /* SHADOWPW && !DEBUG */
1586 for (disp = displays; disp; disp = disp->d_next)
1588 fcntl(disp->d_userfd, F_SETFL, 0);
1589 SetTTY(disp->d_userfd, &D_OldMode);
1590 write(disp->d_userfd, buf, strlen(buf));
1591 Kill(disp->d_userpid, SIG_BYE);
1593 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1594 Kill(getpid(), SIGKILL);
1595 eexit(11);
1596 #else /* SHADOWPW && !DEBUG */
1597 abort();
1598 #endif /* SHADOWPW && !DEBUG */
1599 SIGRETURN;
1602 static void
1603 DoWait()
1605 register int pid;
1606 struct win *p, *next;
1607 #ifdef BSDWAIT
1608 union wait wstat;
1609 #else
1610 int wstat;
1611 #endif
1613 #ifdef BSDJOBS
1614 # ifndef BSDWAIT
1615 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1616 # else
1617 # ifdef USE_WAIT2
1619 * From: rouilj@sni-usa.com (John Rouillard)
1620 * note that WUNTRACED is not documented to work, but it is defined in
1621 * /usr/include/sys/wait.h, so it may work
1623 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1624 # else /* USE_WAIT2 */
1625 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1626 # endif /* USE_WAIT2 */
1627 # endif
1628 #else /* BSDJOBS */
1629 while ((pid = wait(&wstat)) < 0)
1630 if (errno != EINTR)
1631 break;
1632 if (pid > 0)
1633 #endif /* BSDJOBS */
1635 for (p = windows; p; p = next)
1637 next = p->w_next;
1638 if ( (p->w_pid && pid == p->w_pid) ||
1639 (p->w_deadpid && pid == p->w_deadpid) )
1641 /* child has ceased to exist */
1642 p->w_pid = 0;
1644 #ifdef BSDJOBS
1645 if (WIFSTOPPED(wstat))
1647 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1648 #ifdef SIGTTIN
1649 if (WSTOPSIG(wstat) == SIGTTIN)
1651 Msg(0, "Suspended (tty input)");
1652 continue;
1654 #endif
1655 #ifdef SIGTTOU
1656 if (WSTOPSIG(wstat) == SIGTTOU)
1658 Msg(0, "Suspended (tty output)");
1659 continue;
1661 #endif
1662 /* Try to restart process */
1663 Msg(0, "Child has been stopped, restarting.");
1664 if (killpg(pid, SIGCONT))
1665 kill(pid, SIGCONT);
1667 else
1668 #endif
1670 WindowDied(p, wstat, 1);
1672 break;
1674 #ifdef PSEUDOS
1675 if (p->w_pwin && pid == p->w_pwin->p_pid)
1677 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1678 FreePseudowin(p);
1679 break;
1681 #endif
1683 if (p == 0)
1685 debug1("pid %d not found - hope that's ok\n", pid);
1691 static sigret_t
1692 FinitHandler SIGDEFARG
1694 #ifdef SIGHASARG
1695 debug1("FinitHandler called, sig %d.\n", sigsig);
1696 #else
1697 debug("FinitHandler called.\n");
1698 #endif
1699 Finit(1);
1700 SIGRETURN;
1703 void
1704 Finit(i)
1705 int i;
1707 signal(SIGCHLD, SIG_DFL);
1708 signal(SIGHUP, SIG_IGN);
1709 debug1("Finit(%d);\n", i);
1710 while (windows)
1712 struct win *p = windows;
1713 windows = windows->w_next;
1714 FreeWindow(p);
1716 if (ServerSocket != -1)
1718 debug1("we unlink(%s)\n", SockPath);
1719 #ifdef USE_SETEUID
1720 xseteuid(real_uid);
1721 xsetegid(real_gid);
1722 #endif
1723 (void) unlink(SockPath);
1724 #ifdef USE_SETEUID
1725 xseteuid(eff_uid);
1726 xsetegid(eff_gid);
1727 #endif
1729 for (display = displays; display; display = display->d_next)
1731 if (D_status)
1732 RemoveStatus();
1733 FinitTerm();
1734 #ifdef UTMPOK
1735 RestoreLoginSlot();
1736 #endif
1737 AddStr("[screen is terminating]\r\n");
1738 Flush();
1739 SetTTY(D_userfd, &D_OldMode);
1740 fcntl(D_userfd, F_SETFL, 0);
1741 freetty();
1742 Kill(D_userpid, SIG_BYE);
1745 * we _cannot_ call eexit(i) here,
1746 * instead of playing with the Socket above. Sigh.
1748 exit(i);
1751 void
1752 eexit(e)
1753 int e;
1755 debug("eexit\n");
1756 if (ServerSocket != -1)
1758 debug1("we unlink(%s)\n", SockPath);
1759 setgid(real_gid);
1760 setuid(real_uid);
1761 (void) unlink(SockPath);
1763 exit(e);
1766 void
1767 Hangup()
1769 if (display == 0)
1770 return;
1771 debug1("Hangup %x\n", display);
1772 if (D_userfd >= 0)
1774 close(D_userfd);
1775 D_userfd = -1;
1777 if (auto_detach || displays->d_next)
1778 Detach(D_HANGUP);
1779 else
1780 Finit(0);
1784 * Detach now has the following modes:
1785 *D_DETACH SIG_BYE detach backend and exit attacher
1786 *D_HANGUP SIG_BYE detach backend and exit attacher
1787 *D_STOP SIG_STOP stop attacher (and detach backend)
1788 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1789 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1790 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1791 *D_LOCK SIG_LOCK lock the attacher
1792 * (jw)
1793 * we always remove our utmp slots. (even when "lock" or "stop")
1794 * Note: Take extra care here, we may be called by interrupt!
1796 void
1797 Detach(mode)
1798 int mode;
1800 int sign = 0, pid;
1801 struct canvas *cv;
1802 struct win *p;
1804 if (display == 0)
1805 return;
1807 signal(SIGHUP, SIG_IGN);
1808 debug1("Detach(%d)\n", mode);
1809 if (D_status)
1810 RemoveStatus();
1811 FinitTerm();
1812 if (!display)
1813 return;
1814 switch (mode)
1816 case D_HANGUP:
1817 sign = SIG_BYE;
1818 break;
1819 case D_DETACH:
1820 AddStr("[detached]\r\n");
1821 sign = SIG_BYE;
1822 break;
1823 #ifdef BSDJOBS
1824 case D_STOP:
1825 sign = SIG_STOP;
1826 break;
1827 #endif
1828 #ifdef REMOTE_DETACH
1829 case D_REMOTE:
1830 AddStr("[remote detached]\r\n");
1831 sign = SIG_BYE;
1832 break;
1833 #endif
1834 #ifdef POW_DETACH
1835 case D_POWER:
1836 AddStr("[power detached]\r\n");
1837 if (PowDetachString)
1839 AddStr(PowDetachString);
1840 AddStr("\r\n");
1842 sign = SIG_POWER_BYE;
1843 break;
1844 #ifdef REMOTE_DETACH
1845 case D_REMOTE_POWER:
1846 AddStr("[remote power detached]\r\n");
1847 if (PowDetachString)
1849 AddStr(PowDetachString);
1850 AddStr("\r\n");
1852 sign = SIG_POWER_BYE;
1853 break;
1854 #endif
1855 #endif
1856 case D_LOCK:
1857 ClearAll();
1858 sign = SIG_LOCK;
1859 /* tell attacher to lock terminal with a lockprg. */
1860 break;
1862 #ifdef UTMPOK
1863 if (displays->d_next == 0)
1865 for (p = windows; p; p = p->w_next)
1867 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1869 RemoveUtmp(p);
1871 * Set the slot to 0 to get the window
1872 * logged in again.
1874 p->w_slot = (slot_t) 0;
1878 if (mode != D_HANGUP)
1879 RestoreLoginSlot();
1880 #endif
1881 if (displays->d_next == 0 && console_window)
1883 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1885 debug("could not release console - killing window\n");
1886 KillWindow(console_window);
1887 display = displays; /* restore display */
1890 if (D_fore)
1892 #ifdef MULTIUSER
1893 ReleaseAutoWritelock(display, D_fore);
1894 #endif
1895 D_user->u_detachwin = D_fore->w_number;
1896 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1898 AutosaveLayout(D_layout);
1899 layout_last = D_layout;
1900 for (cv = D_cvlist; cv; cv = cv->c_next)
1902 p = Layer2Window(cv->c_layer);
1903 SetCanvasWindow(cv, 0);
1904 if (p)
1905 WindowChanged(p, 'u');
1908 pid = D_userpid;
1909 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1910 FreeDisplay();
1911 if (displays == 0)
1912 /* Flag detached-ness */
1913 (void) chsock();
1915 * tell father what to do. We do that after we
1916 * freed the tty, thus getty feels more comfortable on hpux
1917 * if it was a power detach.
1919 Kill(pid, sign);
1920 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1921 debug("Detach returns, we are successfully detached.\n");
1922 signal(SIGHUP, SigHup);
1925 static int
1926 IsSymbol(e, s)
1927 char *e, *s;
1929 register int l;
1931 l = strlen(s);
1932 return strncmp(e, s, l) == 0 && e[l] == '=';
1935 void
1936 MakeNewEnv()
1938 register char **op, **np;
1939 static char stybuf[MAXSTR];
1941 for (op = environ; *op; ++op)
1943 if (NewEnv)
1944 free((char *)NewEnv);
1945 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1946 if (!NewEnv)
1947 Panic(0, strnomem);
1948 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1949 *np++ = stybuf; /* NewEnv[0] */
1950 *np++ = Term; /* NewEnv[1] */
1951 np++; /* room for SHELL */
1952 #ifdef TIOCSWINSZ
1953 np += 2; /* room for TERMCAP and WINDOW */
1954 #else
1955 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
1956 #endif
1958 for (op = environ; *op; ++op)
1960 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
1961 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
1962 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
1963 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
1965 *np++ = *op;
1967 *np = 0;
1970 void
1971 /*VARARGS2*/
1972 #if defined(USEVARARGS) && defined(__STDC__)
1973 Msg(int err, char *fmt, VA_DOTS)
1974 #else
1975 Msg(err, fmt, VA_DOTS)
1976 int err;
1977 char *fmt;
1978 VA_DECL
1979 #endif
1981 VA_LIST(ap)
1982 char buf[MAXPATHLEN*2];
1983 char *p = buf;
1985 VA_START(ap, fmt);
1986 fmt = DoNLS(fmt);
1987 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1988 VA_END(ap);
1989 if (err)
1991 p += strlen(p);
1992 *p++ = ':';
1993 *p++ = ' ';
1994 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
1995 buf[sizeof(buf) - 1] = 0;
1997 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
1999 if (display && displays)
2000 MakeStatus(buf);
2001 else if (displays)
2003 for (display = displays; display; display = display->d_next)
2004 MakeStatus(buf);
2006 else if (display)
2008 /* no displays but a display - must have forked.
2009 * send message to backend!
2011 char *tty = D_usertty;
2012 struct display *olddisplay = display;
2013 display = 0; /* only send once */
2014 SendErrorMsg(tty, buf);
2015 display = olddisplay;
2017 else
2018 printf("%s\r\n", buf);
2022 * Call FinitTerm for all displays, write a message to each and call eexit();
2024 void
2025 /*VARARGS2*/
2026 #if defined(USEVARARGS) && defined(__STDC__)
2027 Panic(int err, char *fmt, VA_DOTS)
2028 #else
2029 Panic(err, fmt, VA_DOTS)
2030 int err;
2031 char *fmt;
2032 VA_DECL
2033 #endif
2035 VA_LIST(ap)
2036 char buf[MAXPATHLEN*2];
2037 char *p = buf;
2039 VA_START(ap, fmt);
2040 fmt = DoNLS(fmt);
2041 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2042 VA_END(ap);
2043 if (err)
2045 p += strlen(p);
2046 *p++ = ':';
2047 *p++ = ' ';
2048 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2049 buf[sizeof(buf) - 1] = 0;
2051 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2052 if (displays == 0 && display == 0)
2054 printf("%s\r\n", buf);
2055 if (PanicPid)
2056 Kill(PanicPid, SIG_BYE);
2058 else if (displays == 0)
2060 /* no displays but a display - must have forked.
2061 * send message to backend!
2063 char *tty = D_usertty;
2064 display = 0;
2065 SendErrorMsg(tty, buf);
2066 sleep(2);
2067 _exit(1);
2069 else
2070 for (display = displays; display; display = display->d_next)
2072 if (D_status)
2073 RemoveStatus();
2074 FinitTerm();
2075 Flush();
2076 #ifdef UTMPOK
2077 RestoreLoginSlot();
2078 #endif
2079 SetTTY(D_userfd, &D_OldMode);
2080 fcntl(D_userfd, F_SETFL, 0);
2081 write(D_userfd, buf, strlen(buf));
2082 write(D_userfd, "\n", 1);
2083 freetty();
2084 if (D_userpid)
2085 Kill(D_userpid, SIG_BYE);
2087 #ifdef MULTIUSER
2088 if (tty_oldmode >= 0)
2090 # ifdef USE_SETEUID
2091 if (setuid(own_uid))
2092 xseteuid(own_uid); /* may be a loop. sigh. */
2093 # else
2094 setuid(own_uid);
2095 # endif
2096 debug1("Panic: changing back modes from %s\n", attach_tty);
2097 chmod(attach_tty, tty_oldmode);
2099 #endif
2100 eexit(1);
2105 * '^' is allowed as an escape mechanism for control characters. jw.
2107 * Added time insertion using ideas/code from /\ndy Jones
2108 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2112 #ifndef USE_LOCALE
2113 static const char days[] = "SunMonTueWedThuFriSat";
2114 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2115 #endif
2117 static char winmsg_buf[MAXSTR];
2118 #define MAX_WINMSG_REND 16 /* rendition changes */
2119 static int winmsg_rend[MAX_WINMSG_REND];
2120 static int winmsg_rendpos[MAX_WINMSG_REND];
2121 static int winmsg_numrend;
2123 static char *
2124 pad_expand(buf, p, numpad, padlen)
2125 char *buf;
2126 char *p;
2127 int numpad;
2128 int padlen;
2130 char *pn, *pn2;
2131 int i, r;
2133 padlen = padlen - (p - buf); /* space for rent */
2134 if (padlen < 0)
2135 padlen = 0;
2136 pn2 = pn = p + padlen;
2137 r = winmsg_numrend;
2138 while (p >= buf)
2140 if (r && p - buf == winmsg_rendpos[r - 1])
2142 winmsg_rendpos[--r] = pn - buf;
2143 continue;
2145 *pn-- = *p;
2146 if (*p-- == 127)
2148 pn[1] = ' ';
2149 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2150 padlen -= i;
2151 while (i-- > 0)
2152 *pn-- = ' ';
2153 numpad--;
2156 return pn2;
2159 struct backtick {
2160 struct backtick *next;
2161 int num;
2162 int tick;
2163 int lifespan;
2164 time_t bestbefore;
2165 char result[MAXSTR];
2166 char **cmdv;
2167 struct event ev;
2168 char *buf;
2169 int bufi;
2172 struct backtick *backticks;
2174 static void
2175 backtick_filter(bt)
2176 struct backtick *bt;
2178 char *p, *q;
2179 int c;
2181 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2183 if (c == '\t')
2184 c = ' ';
2185 if (c >= ' ' || c == '\005')
2186 *q++ = c;
2188 *q = 0;
2191 static void
2192 backtick_fn(ev, data)
2193 struct event *ev;
2194 char *data;
2196 struct backtick *bt;
2197 int i, j, k, l;
2199 bt = (struct backtick *)data;
2200 debug1("backtick_fn for #%d\n", bt->num);
2201 i = bt->bufi;
2202 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2203 if (l <= 0)
2205 debug1("EOF on backtick #%d\n", bt->num);
2206 evdeq(ev);
2207 close(ev->fd);
2208 ev->fd = -1;
2209 return;
2211 debug1("read %d bytes\n", l);
2212 i += l;
2213 for (j = 0; j < l; j++)
2214 if (bt->buf[i - j - 1] == '\n')
2215 break;
2216 if (j < l)
2218 for (k = i - j - 2; k >= 0; k--)
2219 if (bt->buf[k] == '\n')
2220 break;
2221 k++;
2222 bcopy(bt->buf + k, bt->result, i - j - k);
2223 bt->result[i - j - k - 1] = 0;
2224 backtick_filter(bt);
2225 WindowChanged(0, '`');
2227 if (j == l && i == MAXSTR)
2229 j = MAXSTR/2;
2230 l = j + 1;
2232 if (j < l)
2234 if (j)
2235 bcopy(bt->buf + i - j, bt->buf, j);
2236 i = j;
2238 bt->bufi = i;
2241 void
2242 setbacktick(num, lifespan, tick, cmdv)
2243 int num;
2244 int lifespan;
2245 int tick;
2246 char **cmdv;
2248 struct backtick **btp, *bt;
2249 char **v;
2251 debug1("setbacktick called for backtick #%d\n", num);
2252 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2253 if (bt->num == num)
2254 break;
2255 if (!bt && !cmdv)
2256 return;
2257 if (bt)
2259 for (v = bt->cmdv; *v; v++)
2260 free(*v);
2261 free(bt->cmdv);
2262 if (bt->buf)
2263 free(bt->buf);
2264 if (bt->ev.fd >= 0)
2265 close(bt->ev.fd);
2266 evdeq(&bt->ev);
2268 if (bt && !cmdv)
2270 *btp = bt->next;
2271 free(bt);
2272 return;
2274 if (!bt)
2276 bt = (struct backtick *)malloc(sizeof *bt);
2277 if (!bt)
2279 Msg(0, strnomem);
2280 return;
2282 bzero(bt, sizeof(*bt));
2283 bt->next = 0;
2284 *btp = bt;
2286 bt->num = num;
2287 bt->tick = tick;
2288 bt->lifespan = lifespan;
2289 bt->bestbefore = 0;
2290 bt->result[0] = 0;
2291 bt->buf = 0;
2292 bt->bufi = 0;
2293 bt->cmdv = cmdv;
2294 bt->ev.fd = -1;
2295 if (bt->tick == 0 && bt->lifespan == 0)
2297 debug("setbacktick: continuous mode\n");
2298 bt->buf = (char *)malloc(MAXSTR);
2299 if (bt->buf == 0)
2301 Msg(0, strnomem);
2302 setbacktick(num, 0, 0, (char **)0);
2303 return;
2305 bt->ev.type = EV_READ;
2306 bt->ev.fd = readpipe(bt->cmdv);
2307 bt->ev.handler = backtick_fn;
2308 bt->ev.data = (char *)bt;
2309 if (bt->ev.fd >= 0)
2310 evenq(&bt->ev);
2314 static char *
2315 runbacktick(bt, tickp, now)
2316 struct backtick *bt;
2317 int *tickp;
2318 time_t now;
2320 int f, i, l, j;
2321 time_t now2;
2323 debug1("runbacktick called for backtick #%d\n", bt->num);
2324 if (bt->tick && (!*tickp || bt->tick < *tickp))
2325 *tickp = bt->tick;
2326 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2328 debug1("returning old result (%d)\n", bt->lifespan);
2329 return bt->result;
2331 f = readpipe(bt->cmdv);
2332 if (f == -1)
2333 return bt->result;
2334 i = 0;
2335 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2337 debug1("runbacktick: read %d bytes\n", l);
2338 i += l;
2339 for (j = 1; j < l; j++)
2340 if (bt->result[i - j - 1] == '\n')
2341 break;
2342 if (j == l && i == sizeof(bt->result))
2344 j = sizeof(bt->result) / 2;
2345 l = j + 1;
2347 if (j < l)
2349 bcopy(bt->result + i - j, bt->result, j);
2350 i = j;
2353 close(f);
2354 bt->result[sizeof(bt->result) - 1] = '\n';
2355 if (i && bt->result[i - 1] == '\n')
2356 i--;
2357 debug1("runbacktick: finished, %d bytes\n", i);
2358 bt->result[i] = 0;
2359 backtick_filter(bt);
2360 (void)time(&now2);
2361 bt->bestbefore = now2 + bt->lifespan;
2362 return bt->result;
2365 char *
2366 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2367 char *str;
2368 struct win *win;
2369 int esc;
2370 int padlen;
2371 struct event *ev;
2372 int rec;
2374 static int tick;
2375 char *s = str;
2376 register char *p = winmsg_buf;
2377 register int ctrl;
2378 struct timeval now;
2379 struct tm *tm;
2380 int l, i, r;
2381 int num;
2382 int zeroflg;
2383 int longflg;
2384 int minusflg;
2385 int plusflg;
2386 int qmflag = 0, omflag = 0, qmnumrend = 0;
2387 char *qmpos = 0;
2388 int numpad = 0;
2389 int lastpad = 0;
2390 int truncpos = -1;
2391 int truncper = 0;
2392 int trunclong = 0;
2393 struct backtick *bt;
2395 if (winmsg_numrend >= 0)
2396 winmsg_numrend = 0;
2397 else
2398 winmsg_numrend = -winmsg_numrend;
2400 tick = 0;
2401 tm = 0;
2402 ctrl = 0;
2403 gettimeofday(&now, NULL);
2404 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2406 *p = *s;
2407 if (ctrl)
2409 ctrl = 0;
2410 if (*s != '^' && *s >= 64)
2411 *p &= 0x1f;
2412 continue;
2414 if (*s != esc)
2416 if (esc == '%')
2418 switch (*s)
2420 #if 0
2421 case '~':
2422 *p = BELL;
2423 break;
2424 #endif
2425 case '^':
2426 ctrl = 1;
2427 *p-- = '^';
2428 break;
2429 default:
2430 break;
2433 continue;
2435 if (*++s == esc) /* double escape ? */
2436 continue;
2437 if ((plusflg = *s == '+') != 0)
2438 s++;
2439 if ((minusflg = *s == '-') != 0)
2440 s++;
2441 if ((zeroflg = *s == '0') != 0)
2442 s++;
2443 num = 0;
2444 while(*s >= '0' && *s <= '9')
2445 num = num * 10 + (*s++ - '0');
2446 if ((longflg = *s == 'L') != 0)
2447 s++;
2448 switch (*s)
2450 case '?':
2451 p--;
2452 if (qmpos)
2454 if ((!qmflag && !omflag) || omflag == 1)
2456 p = qmpos;
2457 if (qmnumrend < winmsg_numrend)
2458 winmsg_numrend = qmnumrend;
2460 qmpos = 0;
2461 break;
2463 qmpos = p;
2464 qmnumrend = winmsg_numrend;
2465 qmflag = omflag = 0;
2466 break;
2467 case ':':
2468 p--;
2469 if (!qmpos)
2470 break;
2471 if (qmflag && omflag != 1)
2473 omflag = 1;
2474 qmpos = p;
2475 qmnumrend = winmsg_numrend;
2477 else
2479 p = qmpos;
2480 if (qmnumrend < winmsg_numrend)
2481 winmsg_numrend = qmnumrend;
2482 omflag = -1;
2484 break;
2485 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2486 case 'a': case 'A': case 's': case 'c': case 'C':
2487 if (l < 4)
2488 break;
2489 if (tm == 0)
2491 time_t nowsec = now.tv_sec;
2492 tm = localtime(&nowsec);
2494 qmflag = 1;
2495 if (!tick || tick > 3600)
2496 tick = 3600;
2497 switch (*s)
2499 case 'd':
2500 sprintf(p, "%02d", tm->tm_mday % 100);
2501 break;
2502 case 'D':
2503 #ifdef USE_LOCALE
2504 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2505 #else
2506 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2507 #endif
2508 break;
2509 case 'm':
2510 sprintf(p, "%02d", tm->tm_mon + 1);
2511 break;
2512 case 'M':
2513 #ifdef USE_LOCALE
2514 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2515 #else
2516 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2517 #endif
2518 break;
2519 case 'y':
2520 sprintf(p, "%02d", tm->tm_year % 100);
2521 break;
2522 case 'Y':
2523 sprintf(p, "%04d", tm->tm_year + 1900);
2524 break;
2525 case 'a':
2526 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2527 break;
2528 case 'A':
2529 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2530 break;
2531 case 's':
2532 sprintf(p, "%02d", tm->tm_sec);
2533 tick = 1;
2534 break;
2535 case 'c':
2536 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2537 if (!tick || tick > 60)
2538 tick = 60;
2539 break;
2540 case 'C':
2541 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2542 if (!tick || tick > 60)
2543 tick = 60;
2544 break;
2545 default:
2546 break;
2548 p += strlen(p) - 1;
2549 break;
2550 case 'l':
2551 #ifdef LOADAV
2552 *p = 0;
2553 if (l > 20)
2554 AddLoadav(p);
2555 if (*p)
2557 qmflag = 1;
2558 p += strlen(p) - 1;
2560 else
2561 *p = '?';
2562 if (!tick || tick > 60)
2563 tick = 60;
2564 #else
2565 *p = '?';
2566 #endif
2567 p += strlen(p) - 1;
2568 break;
2569 case '`':
2570 case 'h':
2571 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2573 p--;
2574 break;
2576 if (*s == '`')
2578 for (bt = backticks; bt; bt = bt->next)
2579 if (bt->num == num)
2580 break;
2581 if (bt == 0)
2583 p--;
2584 break;
2588 char savebuf[sizeof(winmsg_buf)];
2589 int oldtick = tick;
2590 int oldnumrend = winmsg_numrend;
2592 *p = 0;
2593 strcpy(savebuf, winmsg_buf);
2594 winmsg_numrend = -winmsg_numrend;
2595 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2596 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2597 if (!tick || oldtick < tick)
2598 tick = oldtick;
2599 if ((int)strlen(winmsg_buf) < l)
2600 strcat(savebuf, winmsg_buf);
2601 strcpy(winmsg_buf, savebuf);
2602 while (oldnumrend < winmsg_numrend)
2603 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2604 if (*p)
2605 qmflag = 1;
2606 p += strlen(p) - 1;
2608 break;
2609 case 'w':
2610 case 'W':
2612 struct win *oldfore = 0;
2613 char *ss;
2615 if (display)
2617 oldfore = D_fore;
2618 D_fore = win;
2620 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2621 if (minusflg)
2622 *ss = 0;
2623 if (display)
2624 D_fore = oldfore;
2626 if (*p)
2627 qmflag = 1;
2628 p += strlen(p) - 1;
2629 break;
2630 case 'u':
2631 *p = 0;
2632 if (win)
2633 AddOtherUsers(p, l - 1, win);
2634 if (*p)
2635 qmflag = 1;
2636 p += strlen(p) - 1;
2637 break;
2638 case 'f':
2639 *p = 0;
2640 if (win)
2641 AddWindowFlags(p, l - 1, win);
2642 if (*p)
2643 qmflag = 1;
2644 p += strlen(p) - 1;
2645 break;
2646 case 't':
2647 *p = 0;
2648 if (win && (int)strlen(win->w_title) < l)
2650 strcpy(p, win->w_title);
2651 if (*p)
2652 qmflag = 1;
2654 p += strlen(p) - 1;
2655 break;
2656 case '{':
2658 char rbuf[128];
2659 s++;
2660 for (i = 0; i < 127; i++)
2661 if (s[i] && s[i] != '}')
2662 rbuf[i] = s[i];
2663 else
2664 break;
2665 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2667 r = -1;
2668 rbuf[i] = 0;
2669 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2670 if (i != 1 || rbuf[0] != '-')
2671 r = ParseAttrColor(rbuf, (char *)0, 0);
2672 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2674 winmsg_rend[winmsg_numrend] = r;
2675 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2676 winmsg_numrend++;
2679 s += i;
2680 p--;
2682 break;
2683 case 'H':
2684 *p = 0;
2685 if ((int)strlen(HostName) < l)
2687 strcpy(p, HostName);
2688 if (*p)
2689 qmflag = 1;
2691 p += strlen(p) - 1;
2692 break;
2693 case 'S':
2695 char *session_name;
2696 *p = 0;
2697 session_name = strchr(SockName, '.') + 1;
2698 if ((int)strlen(session_name) < l)
2700 strcpy(p, session_name);
2701 if (*p)
2702 qmflag = 1;
2704 p += strlen(p) - 1;
2706 break;
2707 case 'F':
2708 p--;
2709 /* small hack */
2710 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2711 minusflg = !minusflg;
2712 if (minusflg)
2713 qmflag = 1;
2714 break;
2715 case '>':
2716 truncpos = p - winmsg_buf;
2717 truncper = num > 100 ? 100 : num;
2718 trunclong = longflg;
2719 p--;
2720 break;
2721 case '=':
2722 case '<':
2723 *p = ' ';
2724 if (num || zeroflg || plusflg || longflg || (*s != '='))
2726 /* expand all pads */
2727 if (minusflg)
2729 num = (plusflg ? lastpad : padlen) - num;
2730 if (!plusflg && padlen == 0)
2731 num = p - winmsg_buf;
2732 plusflg = 0;
2734 else if (!zeroflg)
2736 if (*s != '=' && num == 0 && !plusflg)
2737 num = 100;
2738 if (num > 100)
2739 num = 100;
2740 if (padlen == 0)
2741 num = p - winmsg_buf;
2742 else
2743 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2745 if (num < 0)
2746 num = 0;
2747 if (plusflg)
2748 num += lastpad;
2749 if (num > MAXSTR - 1)
2750 num = MAXSTR - 1;
2751 if (numpad)
2752 p = pad_expand(winmsg_buf, p, numpad, num);
2753 numpad = 0;
2754 if (p - winmsg_buf > num && !longflg)
2756 int left, trunc;
2758 if (truncpos == -1)
2760 truncpos = lastpad;
2761 truncper = 0;
2763 trunc = lastpad + truncper * (num - lastpad) / 100;
2764 if (trunc > num)
2765 trunc = num;
2766 if (trunc < lastpad)
2767 trunc = lastpad;
2768 left = truncpos - trunc;
2769 if (left > p - winmsg_buf - num)
2770 left = p - winmsg_buf - num;
2771 debug1("lastpad = %d, ", lastpad);
2772 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2773 if (left > 0)
2775 if (left + lastpad > p - winmsg_buf)
2776 left = p - winmsg_buf - lastpad;
2777 if (p - winmsg_buf - lastpad - left > 0)
2778 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2779 p -= left;
2780 r = winmsg_numrend;
2781 while (r && winmsg_rendpos[r - 1] > lastpad)
2783 r--;
2784 winmsg_rendpos[r] -= left;
2785 if (winmsg_rendpos[r] < lastpad)
2786 winmsg_rendpos[r] = lastpad;
2788 if (trunclong)
2790 if (p - winmsg_buf > lastpad)
2791 winmsg_buf[lastpad] = '.';
2792 if (p - winmsg_buf > lastpad + 1)
2793 winmsg_buf[lastpad + 1] = '.';
2794 if (p - winmsg_buf > lastpad + 2)
2795 winmsg_buf[lastpad + 2] = '.';
2798 if (p - winmsg_buf > num)
2800 p = winmsg_buf + num;
2801 if (trunclong)
2803 if (num - 1 >= lastpad)
2804 p[-1] = '.';
2805 if (num - 2 >= lastpad)
2806 p[-2] = '.';
2807 if (num - 3 >= lastpad)
2808 p[-3] = '.';
2810 r = winmsg_numrend;
2811 while (r && winmsg_rendpos[r - 1] > num)
2812 winmsg_rendpos[--r] = num;
2814 truncpos = -1;
2815 trunclong = 0;
2816 if (lastpad > p - winmsg_buf)
2817 lastpad = p - winmsg_buf;
2818 debug1("lastpad now %d\n", lastpad);
2820 if (*s == '=')
2822 while (p - winmsg_buf < num)
2823 *p++ = ' ';
2824 lastpad = p - winmsg_buf;
2825 truncpos = -1;
2826 trunclong = 0;
2827 debug1("lastpad2 now %d\n", lastpad);
2829 p--;
2831 else if (padlen)
2833 *p = 127; /* internal pad representation */
2834 numpad++;
2836 break;
2837 case 'n':
2838 s++;
2839 /* FALLTHROUGH */
2840 default:
2841 s--;
2842 if (l > 10 + num)
2844 if (num == 0)
2845 num = 1;
2846 if (!win)
2847 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2848 else
2849 sprintf(p, "%*d", num, win->w_number);
2850 qmflag = 1;
2851 p += strlen(p) - 1;
2853 break;
2856 if (qmpos && !qmflag)
2857 p = qmpos + 1;
2858 *p = '\0';
2859 if (numpad)
2861 if (padlen > MAXSTR - 1)
2862 padlen = MAXSTR - 1;
2863 p = pad_expand(winmsg_buf, p, numpad, padlen);
2865 if (ev)
2867 evdeq(ev); /* just in case */
2868 ev->timeout.tv_sec = 0;
2869 ev->timeout.tv_usec = 0;
2871 if (ev && tick)
2873 now.tv_usec = 100000;
2874 if (tick == 1)
2875 now.tv_sec++;
2876 else
2877 now.tv_sec += tick - (now.tv_sec % tick);
2878 ev->timeout = now;
2879 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2881 return winmsg_buf;
2884 char *
2885 MakeWinMsg(s, win, esc)
2886 char *s;
2887 struct win *win;
2888 int esc;
2890 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2893 void
2894 PutWinMsg(s, start, max)
2895 char *s;
2896 int start, max;
2898 int i, p, l, r, n;
2899 struct mchar rend;
2900 struct mchar rendstack[MAX_WINMSG_REND];
2901 int rendstackn = 0;
2903 if (s != winmsg_buf)
2905 /* sorry, no fancy coloring available */
2906 debug1("PutWinMsg %s plain\n", s);
2907 l = strlen(s);
2908 if (l > max)
2909 l = max;
2910 l -= start;
2911 s += start;
2912 while (l-- > 0)
2913 PUTCHARLP(*s++);
2914 return;
2916 rend = D_rend;
2917 p = 0;
2918 l = strlen(s);
2919 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
2920 for (i = 0; i < winmsg_numrend && max > 0; i++)
2922 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
2923 break;
2924 if (p < winmsg_rendpos[i])
2926 n = winmsg_rendpos[i] - p;
2927 if (n > max)
2928 n = max;
2929 max -= n;
2930 p += n;
2931 while(n-- > 0)
2933 if (start-- > 0)
2934 s++;
2935 else
2936 PUTCHARLP(*s++);
2939 r = winmsg_rend[i];
2940 if (r == -1)
2942 if (rendstackn > 0)
2943 rend = rendstack[--rendstackn];
2945 else
2947 rendstack[rendstackn++] = rend;
2948 ApplyAttrColor(r, &rend);
2950 SetRendition(&rend);
2952 if (p < l)
2954 n = l - p;
2955 if (n > max)
2956 n = max;
2957 while(n-- > 0)
2959 if (start-- > 0)
2960 s++;
2961 else
2962 PUTCHARLP(*s++);
2968 #ifdef DEBUG
2969 static void
2970 fds1(i, j)
2971 int i, j;
2973 while (i < j)
2975 debug1("%d ", i);
2976 i++;
2978 if ((j = open("/dev/null", 0)) >= 0)
2980 fds1(i + 1, j);
2981 close(j);
2983 else
2985 while (dup(++i) < 0 && errno != EBADF)
2986 debug1("%d ", i);
2987 debug1(" [%d]\n", i);
2991 static void
2992 fds()
2994 debug("fds: ");
2995 fds1(-1, -1);
2997 #endif
2999 static void
3000 serv_read_fn(ev, data)
3001 struct event *ev;
3002 char *data;
3004 debug("Knock - knock!\n");
3005 ReceiveMsg();
3008 static void
3009 serv_select_fn(ev, data)
3010 struct event *ev;
3011 char *data;
3013 struct win *p;
3015 debug("serv_select_fn called\n");
3016 /* XXX: messages?? */
3017 if (GotSigChld)
3019 SigChldHandler();
3021 if (InterruptPlease)
3023 debug("Backend received interrupt\n");
3024 /* This approach is rather questionable in a multi-display
3025 * environment */
3026 if (fore && displays)
3028 #if defined(TERMIO) || defined(POSIX)
3029 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3030 #else
3031 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3032 #endif
3033 #ifdef PSEUDOS
3034 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3035 &ibuf, 1);
3036 debug1("Backend wrote interrupt to %d", fore->w_number);
3037 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3038 #else
3039 write(fore->w_ptyfd, &ibuf, 1);
3040 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3041 #endif
3043 InterruptPlease = 0;
3046 for (p = windows; p; p = p->w_next)
3048 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3050 struct canvas *cv;
3051 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3052 p->w_bell = BELL_ON;
3053 for (display = displays; display; display = display->d_next)
3055 for (cv = D_cvlist; cv; cv = cv->c_next)
3056 if (cv->c_layer->l_bottom == &p->w_layer)
3057 break;
3058 if (cv == 0)
3060 p->w_bell = BELL_DONE;
3061 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3063 else if (visual && !D_VB && (!D_status || !D_status_bell))
3065 Msg(0, "%s", VisualBellString);
3066 if (D_status)
3068 D_status_bell = 1;
3069 debug1("using vbell timeout %d\n", VBellWait);
3070 SetTimeout(&D_statusev, VBellWait );
3074 /* don't annoy the user with two messages */
3075 if (p->w_monitor == MON_FOUND)
3076 p->w_monitor = MON_DONE;
3077 WindowChanged(p, 'f');
3079 if (p->w_monitor == MON_FOUND)
3081 struct canvas *cv;
3082 p->w_monitor = MON_ON;
3083 for (display = displays; display; display = display->d_next)
3085 for (cv = D_cvlist; cv; cv = cv->c_next)
3086 if (cv->c_layer->l_bottom == &p->w_layer)
3087 break;
3088 if (cv)
3089 continue; /* user already sees window */
3090 #ifdef MULTIUSER
3091 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3092 continue; /* user doesn't care */
3093 #endif
3094 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3095 p->w_monitor = MON_DONE;
3097 WindowChanged(p, 'f');
3101 for (display = displays; display; display = display->d_next)
3103 struct canvas *cv;
3104 if (D_status == STATUS_ON_WIN)
3105 continue;
3106 /* XXX: should use display functions! */
3107 for (cv = D_cvlist; cv; cv = cv->c_next)
3109 int lx, ly;
3111 /* normalize window, see resize.c */
3112 lx = cv->c_layer->l_x;
3113 ly = cv->c_layer->l_y;
3114 if (lx == cv->c_layer->l_width)
3115 lx--;
3116 if (ly + cv->c_yoff < cv->c_ys)
3118 int i, n = cv->c_ys - (ly + cv->c_yoff);
3119 cv->c_yoff = cv->c_ys - ly;
3120 RethinkViewportOffsets(cv);
3121 if (n > cv->c_layer->l_height)
3122 n = cv->c_layer->l_height;
3123 CV_CALL(cv,
3124 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3125 LayRedisplayLine(-1, -1, -1, 1);
3126 for (i = 0; i < n; i++)
3127 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3128 if (cv == cv->c_display->d_forecv)
3129 LaySetCursor();
3132 else if (ly + cv->c_yoff > cv->c_ye)
3134 int i, n = ly + cv->c_yoff - cv->c_ye;
3135 cv->c_yoff = cv->c_ye - ly;
3136 RethinkViewportOffsets(cv);
3137 if (n > cv->c_layer->l_height)
3138 n = cv->c_layer->l_height;
3139 CV_CALL(cv,
3140 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3141 LayRedisplayLine(-1, -1, -1, 1);
3142 for (i = 0; i < n; i++)
3143 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3144 if (cv == cv->c_display->d_forecv)
3145 LaySetCursor();
3148 if (lx + cv->c_xoff < cv->c_xs)
3150 int i, n = cv->c_xs - (lx + cv->c_xoff);
3151 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3152 n = (cv->c_xe - cv->c_xs + 1) / 2;
3153 if (cv->c_xoff + n > cv->c_xs)
3154 n = cv->c_xs - cv->c_xoff;
3155 cv->c_xoff += n;
3156 RethinkViewportOffsets(cv);
3157 if (n > cv->c_layer->l_width)
3158 n = cv->c_layer->l_width;
3159 CV_CALL(cv,
3160 LayRedisplayLine(-1, -1, -1, 1);
3161 for (i = 0; i < flayer->l_height; i++)
3163 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3164 LayRedisplayLine(i, 0, n - 1, 1);
3166 if (cv == cv->c_display->d_forecv)
3167 LaySetCursor();
3170 else if (lx + cv->c_xoff > cv->c_xe)
3172 int i, n = lx + cv->c_xoff - cv->c_xe;
3173 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3174 n = (cv->c_xe - cv->c_xs + 1) / 2;
3175 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3176 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3177 cv->c_xoff -= n;
3178 RethinkViewportOffsets(cv);
3179 if (n > cv->c_layer->l_width)
3180 n = cv->c_layer->l_width;
3181 CV_CALL(cv,
3182 LayRedisplayLine(-1, -1, -1, 1);
3183 for (i = 0; i < flayer->l_height; i++)
3185 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3186 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3188 if (cv == cv->c_display->d_forecv)
3189 LaySetCursor();
3195 for (display = displays; display; display = display->d_next)
3197 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3198 continue;
3199 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3200 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3204 static void
3205 logflush_fn(ev, data)
3206 struct event *ev;
3207 char *data;
3209 struct win *p;
3210 char *buf;
3211 int n;
3213 if (!islogfile(NULL))
3214 return; /* no more logfiles */
3215 logfflush(NULL);
3216 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3217 if (n)
3219 SetTimeout(ev, n * 1000);
3220 evenq(ev); /* re-enqueue ourself */
3222 if (!logtstamp_on)
3223 return;
3224 /* write fancy time-stamp */
3225 for (p = windows; p; p = p->w_next)
3227 if (!p->w_log)
3228 continue;
3229 p->w_logsilence += n;
3230 if (p->w_logsilence < logtstamp_after)
3231 continue;
3232 if (p->w_logsilence - n >= logtstamp_after)
3233 continue;
3234 buf = MakeWinMsg(logtstamp_string, p, '%');
3235 logfwrite(p->w_log, buf, strlen(buf));
3240 * Interprets ^?, ^@ and other ^-control-char notation.
3241 * Interprets \ddd octal notation
3243 * The result is placed in *cp, p is advanced behind the parsed expression and
3244 * returned.
3246 static char *
3247 ParseChar(p, cp)
3248 char *p, *cp;
3250 if (*p == 0)
3251 return 0;
3252 if (*p == '^' && p[1])
3254 if (*++p == '?')
3255 *cp = '\177';
3256 else if (*p >= '@')
3257 *cp = Ctrl(*p);
3258 else
3259 return 0;
3260 ++p;
3262 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3264 *cp = 0;
3266 *cp = *cp * 8 + *p - '0';
3267 while (*++p <= '7' && *p >= '0');
3269 else
3270 *cp = *p++;
3271 return p;
3274 static int
3275 ParseEscape(p)
3276 char *p;
3278 unsigned char buf[2];
3280 if (*p == 0)
3281 SetEscape((struct acluser *)0, -1, -1);
3282 else
3284 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3285 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3286 return -1;
3287 SetEscape((struct acluser *)0, buf[0], buf[1]);
3289 return 0;