Merge branch 'master' into lua-scripting
[screen-lua.git] / src / screen.c
blob3df7e9b2384add12d60c240a7c10e46ee9721a4d
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 SVR4
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 NETHACK
227 int nethackflag = 0;
228 #endif
229 int maxwin = MAXWIN;
232 struct layer *flayer;
233 struct win *fore;
234 struct win *windows;
235 struct win *console_window;
240 * Do this last
242 #include "extern.h"
244 char strnomem[] = "Out of memory.";
247 static int InterruptPlease;
248 static int GotSigChld;
250 static int
251 lf_secreopen(name, wantfd, l)
252 char *name;
253 int wantfd;
254 struct logfile *l;
256 int got_fd;
258 close(wantfd);
259 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
260 lf_move_fd(got_fd, wantfd) < 0)
262 logfclose(l);
263 debug1("lf_secreopen: failed for %s\n", name);
264 return -1;
266 l->st->st_ino = l->st->st_dev = 0;
267 debug2("lf_secreopen: %d = %s\n", wantfd, name);
268 return 0;
271 /********************************************************************/
272 /********************************************************************/
273 /********************************************************************/
276 static struct passwd *
277 getpwbyname(name, ppp)
278 char *name;
279 struct passwd *ppp;
281 int n;
282 #ifdef SHADOWPW
283 struct spwd *sss = NULL;
284 static char *spw = NULL;
285 #endif
287 if (!ppp && !(ppp = getpwnam(name)))
288 return NULL;
290 /* Do password sanity check..., allow ##user for SUN_C2 security */
291 #ifdef SHADOWPW
292 pw_try_again:
293 #endif
294 n = 0;
295 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
296 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
297 n = 13;
298 for (; n < 13; n++)
300 char c = ppp->pw_passwd[n];
301 if (!(c == '.' || c == '/' || c == '$' ||
302 (c >= '0' && c <= '9') ||
303 (c >= 'a' && c <= 'z') ||
304 (c >= 'A' && c <= 'Z')))
305 break;
308 #ifdef SHADOWPW
309 /* try to determine real password */
310 if (n < 13 && sss == 0)
312 sss = getspnam(ppp->pw_name);
313 if (sss)
315 if (spw)
316 free(spw);
317 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
318 endspent(); /* this should delete all buffers ... */
319 goto pw_try_again;
321 endspent(); /* this should delete all buffers ... */
323 #endif
324 if (n < 13)
325 ppp->pw_passwd = 0;
326 #ifdef linux
327 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
328 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
329 #endif
331 return ppp;
336 main(ac, av)
337 int ac;
338 char **av;
340 register int n;
341 char *ap;
342 char *av0;
343 char socknamebuf[2 * MAXSTR];
344 int mflag = 0;
345 char *myname = (ac == 0) ? "screen" : av[0];
346 char *SockDir;
347 struct stat st;
348 #ifdef _MODE_T /* (jw) */
349 mode_t oumask;
350 #else
351 int oumask;
352 #endif
353 #if defined(SYSV) && !defined(ISC)
354 struct utsname utsnam;
355 #endif
356 struct NewWindow nwin;
357 int detached = 0; /* start up detached */
358 #ifdef MULTIUSER
359 char *sockp;
360 #endif
361 char *script_file = 0;
363 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
364 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
365 #endif
366 #if defined(sun) && defined(SVR4)
368 /* Solaris' login blocks SIGHUP! This is _very bad_ */
369 sigset_t sset;
370 sigemptyset(&sset);
371 sigprocmask(SIG_SETMASK, &sset, 0);
373 #endif
376 * First, close all unused descriptors
377 * (otherwise, we might have problems with the select() call)
379 closeallfiles(0);
380 #ifdef DEBUG
381 opendebug(1, 0);
382 #endif
383 sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
384 PATCHLEVEL, STATE, ORIGIN, DATE);
385 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
386 debug2("-- screen debug started %s (%s)\n", *av, version);
387 #ifdef POSIX
388 debug("POSIX\n");
389 #endif
390 #ifdef TERMIO
391 debug("TERMIO\n");
392 #endif
393 #ifdef SYSV
394 debug("SYSV\n");
395 #endif
396 #ifdef SYSVSIGS
397 debug("SYSVSIGS\n");
398 #endif
399 #ifdef NAMEDPIPE
400 debug("NAMEDPIPE\n");
401 #endif
402 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
403 debug("Window size changing enabled\n");
404 #endif
405 #ifdef HAVE_SETREUID
406 debug("SETREUID\n");
407 #endif
408 #ifdef HAVE_SETEUID
409 debug("SETEUID\n");
410 #endif
411 #ifdef hpux
412 debug("hpux\n");
413 #endif
414 #ifdef USEBCOPY
415 debug("USEBCOPY\n");
416 #endif
417 #ifdef UTMPOK
418 debug("UTMPOK\n");
419 #endif
420 #ifdef LOADAV
421 debug("LOADAV\n");
422 #endif
423 #ifdef NETHACK
424 debug("NETHACK\n");
425 #endif
426 #ifdef TERMINFO
427 debug("TERMINFO\n");
428 #endif
429 #ifdef SHADOWPW
430 debug("SHADOWPW\n");
431 #endif
432 #ifdef NAME_MAX
433 debug1("NAME_MAX = %d\n", NAME_MAX);
434 #endif
436 BellString = SaveStr("Bell in window %n");
437 VisualBellString = SaveStr(" Wuff, Wuff!! ");
438 ActivityString = SaveStr("Activity in window %n");
439 screenlogfile = SaveStr("screenlog.%n");
440 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
441 hstatusstring = SaveStr("%h");
442 captionstring = SaveStr("%3n %t");
443 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
444 wlisttit = SaveStr("Num Name%=Flags");
445 wliststr = SaveStr("%3n %t%=%f");
446 #ifdef COPY_PASTE
447 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
448 #endif
449 ShellProg = NULL;
450 #ifdef POW_DETACH
451 PowDetachString = 0;
452 #endif
453 default_startup = (ac > 1) ? 0 : 1;
454 adaptflag = 0;
455 VBellWait = VBELLWAIT * 1000;
456 MsgWait = MSGWAIT * 1000;
457 MsgMinWait = MSGMINWAIT * 1000;
458 SilenceWait = SILENCEWAIT;
459 #ifdef HAVE_BRAILLE
460 InitBraille();
461 #endif
462 #ifdef ZMODEM
463 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
464 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
465 #endif
467 #ifdef COPY_PASTE
468 CompileKeys((char *)0, 0, mark_key_tab);
469 #endif
470 #ifdef UTF8
471 InitBuiltinTabs();
472 screenencodings = SaveStr(SCREENENCODINGS);
473 #endif
474 nwin = nwin_undef;
475 nwin_options = nwin_undef;
476 strcpy(screenterm, "screen");
478 logreopen_register(lf_secreopen);
480 av0 = *av;
481 /* if this is a login screen, assume -RR */
482 if (*av0 == '-')
484 rflag = 4;
485 #ifdef MULTI
486 xflag = 1;
487 #else
488 dflag = 1;
489 #endif
490 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
492 while (ac > 0)
494 ap = *++av;
495 if (--ac > 0 && *ap == '-')
497 if (ap[1] == '-' && ap[2] == 0)
499 av++;
500 ac--;
501 break;
503 if (ap[1] == '-' && !strcmp(ap, "--version"))
504 Panic(0, "Screen version %s", version);
505 if (ap[1] == '-' && !strcmp(ap, "--help"))
506 exit_with_usage(myname, NULL, NULL);
507 while (ap && *ap && *++ap)
509 switch (*ap)
511 case 'a':
512 nwin_options.aflag = 1;
513 break;
514 case 'A':
515 adaptflag = 1;
516 break;
517 case 'p': /* preselect */
518 if (*++ap)
519 preselect = ap;
520 else
522 if (!--ac)
523 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
524 preselect = *++av;
526 ap = NULL;
527 break;
528 #ifdef HAVE_BRAILLE
529 case 'B':
530 bd.bd_start_braille = 1;
531 break;
532 #endif
533 case 'c':
534 if (*++ap)
535 RcFileName = ap;
536 else
538 if (--ac == 0)
539 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
540 RcFileName = *++av;
542 ap = NULL;
543 break;
544 case 'e':
545 if (!*++ap)
547 if (--ac == 0)
548 exit_with_usage(myname, "Specify command characters with -e", NULL);
549 ap = *++av;
551 if (ParseEscape(ap))
552 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
553 ap = NULL;
554 break;
555 case 'f':
556 ap++;
557 switch (*ap++)
559 case 'n':
560 case '0':
561 nwin_options.flowflag = FLOW_NOW * 0;
562 break;
563 case '\0':
564 ap--;
565 /* FALLTHROUGH */
566 case 'y':
567 case '1':
568 nwin_options.flowflag = FLOW_NOW * 1;
569 break;
570 case 'a':
571 nwin_options.flowflag = FLOW_AUTOFLAG;
572 break;
573 default:
574 exit_with_usage(myname, "Unknown flow option -%s", --ap);
576 break;
577 case 'h':
578 if (--ac == 0)
579 exit_with_usage(myname, NULL, NULL);
580 nwin_options.histheight = atoi(*++av);
581 if (nwin_options.histheight < 0)
582 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
583 break;
584 case 'i':
585 iflag = 1;
586 break;
587 case 't': /* title, the former AkA == -k */
588 if (--ac == 0)
589 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
590 nwin_options.aka = *++av;
591 break;
592 case 'l':
593 ap++;
594 switch (*ap++)
596 case 'n':
597 case '0':
598 nwin_options.lflag = 0;
599 break;
600 case '\0':
601 ap--;
602 /* FALLTHROUGH */
603 case 'y':
604 case '1':
605 nwin_options.lflag = 1;
606 break;
607 case 'a':
608 nwin_options.lflag = 3;
609 break;
610 case 's': /* -ls */
611 case 'i': /* -list */
612 lsflag = 1;
613 if (ac > 1 && !SockMatch)
615 SockMatch = *++av;
616 ac--;
618 ap = NULL;
619 break;
620 default:
621 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
623 break;
624 case 'w':
625 lsflag = 1;
626 wipeflag = 1;
627 if (ac > 1 && !SockMatch)
629 SockMatch = *++av;
630 ac--;
632 break;
633 case 'L':
634 nwin_options.Lflag = 1;
635 break;
636 case 'm':
637 mflag = 1;
638 break;
639 case 'O': /* to be (or not to be?) deleted. jw. */
640 force_vt = 0;
641 break;
642 case 'T':
643 if (--ac == 0)
644 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
645 if (strlen(*++av) < 20)
646 strcpy(screenterm, *av);
647 else
648 Panic(0, "-T: terminal name too long. (max. 20 char)");
649 nwin_options.term = screenterm;
650 break;
651 case 'q':
652 quietflag = 1;
653 break;
654 case 'r':
655 case 'R':
656 #ifdef MULTI
657 case 'x':
658 #endif
659 if (ac > 1 && *av[1] != '-' && !SockMatch)
661 SockMatch = *++av;
662 ac--;
663 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
665 #ifdef MULTI
666 if (*ap == 'x')
667 xflag = 1;
668 #endif
669 if (rflag)
670 rflag = 2;
671 rflag += (*ap == 'R') ? 2 : 1;
672 break;
673 #ifdef REMOTE_DETACH
674 case 'd':
675 dflag = 1;
676 /* FALLTHROUGH */
677 case 'D':
678 if (!dflag)
679 dflag = 2;
680 if (ac == 2)
682 if (*av[1] != '-' && !SockMatch)
684 SockMatch = *++av;
685 ac--;
686 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
689 break;
690 #endif
691 case 's':
692 if (--ac == 0)
693 exit_with_usage(myname, "Specify shell with -s", NULL);
694 if (ShellProg)
695 free(ShellProg);
696 ShellProg = SaveStr(*++av);
697 debug1("ShellProg: '%s'\n", ShellProg);
698 break;
699 case 'S':
700 if (!SockMatch)
702 if (--ac == 0)
703 exit_with_usage(myname, "Specify session-name with -S", NULL);
704 SockMatch = *++av;
706 if (!*SockMatch)
707 exit_with_usage(myname, "Empty session-name?", NULL);
708 break;
709 case 'X':
710 cmdflag = 1;
711 break;
712 case 'v':
713 Panic(0, "Screen version %s", version);
714 /* NOTREACHED */
715 #ifdef UTF8
716 case 'U':
717 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
718 break;
719 #endif
720 case 'u':
721 if (--ac == 0)
722 exit_with_usage(myname, "Specify lua script file with -u", NULL);
723 if (script_file)
724 free(script_file);
725 script_file = SaveStr(*++av);
726 break;
727 default:
728 exit_with_usage(myname, "Unknown option %s", --ap);
732 else
733 break;
736 real_uid = getuid();
737 real_gid = getgid();
738 eff_uid = geteuid();
739 eff_gid = getegid();
740 if (eff_uid != real_uid)
742 /* if running with s-bit, we must install a special signal
743 * handler routine that resets the s-bit, so that we get a
744 * core file anyway.
746 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
747 signal(SIGBUS, CoreDump);
748 #endif /* SIGBUS */
749 signal(SIGSEGV, CoreDump);
752 #ifdef USE_LOCALE
753 setlocale(LC_ALL, "");
754 #endif
755 #ifdef ENCODINGS
756 if (nwin_options.encoding == -1)
758 /* ask locale if we should start in UTF-8 mode */
759 # ifdef HAVE_NL_LANGINFO
760 # ifndef USE_LOCALE
761 setlocale(LC_CTYPE, "");
762 # endif
763 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
764 debug1("locale says encoding = %d\n", nwin_options.encoding);
765 # else
766 # ifdef UTF8
767 char *s;
768 if (((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
769 (s = getenv("LANG"))) && InStr(s, "UTF-8"))
770 nwin_options.encoding = UTF8;
771 # endif
772 debug1("environment says encoding=%d\n", nwin_options.encoding);
773 #endif
775 #endif
776 if (SockMatch && strlen(SockMatch) >= MAXSTR)
777 Panic(0, "Ridiculously long socketname - try again.");
778 if (cmdflag && !rflag && !dflag && !xflag)
779 xflag = 1;
780 if (!cmdflag && dflag && mflag && !(rflag || xflag))
781 detached = 1;
782 nwin = nwin_options;
783 #ifdef ENCODINGS
784 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
785 #endif
786 if (ac)
787 nwin.args = av;
789 /* make the write() calls return -1 on all errors */
790 #ifdef SIGXFSZ
792 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
793 * It appears that in System V Release 4, UNIX, if you are writing
794 * an output file and you exceed the currently set file size limit,
795 * you _don't_ just get the call to `write' returning with a
796 * failure code. Rather, you get a signal called `SIGXFSZ' which,
797 * if neither handled nor ignored, will cause your program to crash
798 * with a core dump.
800 signal(SIGXFSZ, SIG_IGN);
801 #endif /* SIGXFSZ */
803 #ifdef SIGPIPE
804 signal(SIGPIPE, SIG_IGN);
805 #endif
807 if (!ShellProg)
809 register char *sh;
811 sh = getenv("SHELL");
812 ShellProg = SaveStr(sh ? sh : DefaultShell);
814 ShellArgs[0] = ShellProg;
815 home = getenv("HOME");
817 #ifdef NETHACK
818 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
820 char nethackrc[MAXPATHLEN];
822 if (home && (strlen(home) < (MAXPATHLEN - 20)))
824 sprintf(nethackrc,"%s/.nethackrc", home);
825 nethackflag = !access(nethackrc, F_OK);
828 #endif
830 #ifdef MULTIUSER
831 own_uid = multi_uid = real_uid;
832 if (SockMatch && (sockp = index(SockMatch, '/')))
834 if (eff_uid)
835 Panic(0, "Must run suid root for multiuser support.");
836 *sockp = 0;
837 multi = SockMatch;
838 SockMatch = sockp + 1;
839 if (*multi)
841 struct passwd *mppp;
842 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
843 Panic(0, "Cannot identify account '%s'.", multi);
844 multi_uid = mppp->pw_uid;
845 multi_home = SaveStr(mppp->pw_dir);
846 if (strlen(multi_home) > MAXPATHLEN - 10)
847 Panic(0, "home directory path too long");
848 # ifdef MULTI
849 /* always fake multi attach mode */
850 if (rflag || lsflag)
851 xflag = 1;
852 # endif /* MULTI */
853 detached = 0;
854 multiattach = 1;
857 if (SockMatch && *SockMatch == 0)
858 SockMatch = 0;
859 #endif /* MULTIUSER */
861 if ((LoginName = getlogin()) && LoginName[0] != '\0')
863 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
864 if ((int)ppp->pw_uid != real_uid)
865 ppp = (struct passwd *) 0;
867 if (ppp == 0)
869 if ((ppp = getpwuid(real_uid)) == 0)
871 Panic(0, "getpwuid() can't identify your account!");
872 exit(1);
874 LoginName = ppp->pw_name;
876 LoginName = SaveStr(LoginName);
878 ppp = getpwbyname(LoginName, ppp);
880 #if !defined(SOCKDIR) && defined(MULTIUSER)
881 if (multi && !multiattach)
883 if (home && strcmp(home, ppp->pw_dir))
884 Panic(0, "$HOME must match passwd entry for multiuser screens.");
886 #endif
888 if (home == 0 || *home == '\0')
889 home = ppp->pw_dir;
890 if (strlen(LoginName) > 20)
891 Panic(0, "LoginName too long - sorry.");
892 #ifdef MULTIUSER
893 if (multi && strlen(multi) > 20)
894 Panic(0, "Screen owner name too long - sorry.");
895 #endif
896 if (strlen(home) > MAXPATHLEN - 25)
897 Panic(0, "$HOME too long - sorry.");
899 attach_tty = "";
900 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag))
902 #ifndef NAMEDPIPE
903 int fl;
904 #endif
906 /* ttyname implies isatty */
907 if (!(attach_tty = ttyname(0)))
908 Panic(0, "Must be connected to a terminal.");
909 if (strlen(attach_tty) >= MAXPATHLEN)
910 Panic(0, "TtyName too long - sorry.");
911 if (stat(attach_tty, &st))
912 Panic(errno, "Cannot access '%s'", attach_tty);
913 #ifdef MULTIUSER
914 tty_mode = (int)st.st_mode & 0777;
915 #endif
917 #ifndef NAMEDPIPE
918 fl = fcntl(0, F_GETFL, 0);
919 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
920 attach_fd = 0;
921 #endif
922 if (attach_fd == -1)
924 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
925 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
926 close(n);
928 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
930 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
931 Panic(0, "Please set a terminal type.");
932 if (strlen(attach_term) > sizeof(D_termname) - 1)
933 Panic(0, "$TERM too long - sorry.");
934 GetTTY(0, &attach_Mode);
935 #ifdef DEBUGGGGGGGGGGGGGGG
936 DebugTTY(&attach_Mode);
937 #endif /* DEBUG */
940 #ifdef _MODE_T
941 oumask = umask(0); /* well, unsigned never fails? jw. */
942 #else
943 if ((oumask = (int)umask(0)) == -1)
944 Panic(errno, "Cannot change umask to zero");
945 #endif
946 SockDir = getenv("SCREENDIR");
947 if (SockDir)
949 if (strlen(SockDir) >= MAXPATHLEN - 1)
950 Panic(0, "Ridiculously long $SCREENDIR - try again.");
951 #ifdef MULTIUSER
952 if (multi)
953 Panic(0, "No $SCREENDIR with multi screens, please.");
954 #endif
956 #ifdef MULTIUSER
957 if (multiattach)
959 # ifndef SOCKDIR
960 sprintf(SockPath, "%s/.screen", multi_home);
961 SockDir = SockPath;
962 # else
963 SockDir = SOCKDIR;
964 sprintf(SockPath, "%s/S-%s", SockDir, multi);
965 # endif
967 else
968 #endif
970 #ifndef SOCKDIR
971 if (SockDir == 0)
973 sprintf(SockPath, "%s/.screen", home);
974 SockDir = SockPath;
976 #endif
977 if (SockDir)
979 if (access(SockDir, F_OK))
981 debug1("SockDir '%s' missing ...\n", SockDir);
982 if (UserContext() > 0)
984 if (mkdir(SockDir, 0700))
985 UserReturn(0);
986 UserReturn(1);
988 if (UserStatus() <= 0)
989 Panic(0, "Cannot make directory '%s'.", SockDir);
991 if (SockDir != SockPath)
992 strcpy(SockPath, SockDir);
994 #ifdef SOCKDIR
995 else
997 SockDir = SOCKDIR;
998 if (lstat(SockDir, &st))
1000 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1001 (eff_gid != real_gid) ? 0775 :
1002 #ifdef S_ISVTX
1003 0777|S_ISVTX;
1004 #else
1005 0777;
1006 #endif
1007 if (mkdir(SockDir, n) == -1)
1008 Panic(errno, "Cannot make directory '%s'", SockDir);
1010 else
1012 if (!S_ISDIR(st.st_mode))
1013 Panic(0, "'%s' must be a directory.", SockDir);
1014 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1015 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1016 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1017 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1018 0777;
1019 if (((int)st.st_mode & 0777) != n)
1020 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1022 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1023 if (access(SockPath, F_OK))
1025 if (mkdir(SockPath, 0700) == -1)
1026 Panic(errno, "Cannot make directory '%s'", SockPath);
1027 (void) chown(SockPath, real_uid, real_gid);
1030 #endif
1033 if (stat(SockPath, &st) == -1)
1034 Panic(errno, "Cannot access %s", SockPath);
1035 else
1036 if (!S_ISDIR(st.st_mode))
1037 Panic(0, "%s is not a directory.", SockPath);
1038 #ifdef MULTIUSER
1039 if (multi)
1041 if ((int)st.st_uid != multi_uid)
1042 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1044 else
1045 #endif
1047 if ((int)st.st_uid != real_uid)
1048 Panic(0, "You are not the owner of %s.", SockPath);
1050 if ((st.st_mode & 0777) != 0700)
1051 Panic(0, "Directory %s must have mode 700.", SockPath);
1052 if (SockMatch && index(SockMatch, '/'))
1053 Panic(0, "Bad session name '%s'", SockMatch);
1054 SockName = SockPath + strlen(SockPath) + 1;
1055 *SockName = 0;
1056 (void) umask(oumask);
1057 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1059 #if defined(SYSV) && !defined(ISC)
1060 if (uname(&utsnam) == -1)
1061 Panic(errno, "uname");
1062 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1063 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1064 #else
1065 (void) gethostname(HostName, MAXSTR);
1066 HostName[MAXSTR - 1] = '\0';
1067 #endif
1068 if ((ap = index(HostName, '.')) != NULL)
1069 *ap = '\0';
1071 if (lsflag)
1073 int i, fo, oth;
1075 #ifdef MULTIUSER
1076 if (multi)
1077 real_uid = multi_uid;
1078 #endif
1079 setgid(real_gid);
1080 setuid(real_uid);
1081 eff_uid = real_uid;
1082 eff_gid = real_gid;
1083 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1084 if (quietflag)
1085 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1086 if (fo == 0)
1087 Panic(0, "No Sockets found in %s.\n", SockPath);
1088 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1089 /* NOTREACHED */
1091 signal(SIG_BYE, AttacherFinit); /* prevent races */
1092 if (cmdflag)
1094 char *sty = 0;
1096 /* attach_tty is not mandatory */
1097 if ((attach_tty = ttyname(0)) == 0)
1098 attach_tty = "";
1099 if (strlen(attach_tty) >= MAXPATHLEN)
1100 Panic(0, "TtyName too long - sorry.");
1101 if (!*av)
1102 Panic(0, "Please specify a command.");
1103 setgid(real_gid);
1104 setuid(real_uid);
1105 eff_uid = real_uid;
1106 eff_gid = real_gid;
1107 if (!mflag && !SockMatch)
1109 sty = getenv("STY");
1110 if (sty && *sty == 0)
1111 sty = 0;
1113 SendCmdMessage(sty, SockMatch, av);
1114 exit(0);
1116 else if (rflag || xflag)
1118 debug("screen -r: - is there anybody out there?\n");
1119 if (Attach(MSG_ATTACH))
1121 Attacher();
1122 /* NOTREACHED */
1124 #ifdef MULTIUSER
1125 if (multiattach)
1126 Panic(0, "Can't create sessions of other users.");
1127 #endif
1128 debug("screen -r: backend not responding -- still crying\n");
1130 else if (dflag && !mflag)
1132 (void) Attach(MSG_DETACH);
1133 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1134 eexit(0);
1135 /* NOTREACHED */
1137 if (!SockMatch && !mflag)
1139 register char *sty;
1141 if ((sty = getenv("STY")) != 0 && *sty != '\0')
1143 setgid(real_gid);
1144 setuid(real_uid);
1145 eff_uid = real_uid;
1146 eff_gid = real_gid;
1147 nwin_options.args = av;
1148 SendCreateMsg(sty, &nwin);
1149 exit(0);
1150 /* NOTREACHED */
1153 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1155 if (!detached || dflag != 2)
1156 MasterPid = fork();
1157 else
1158 MasterPid = 0;
1160 switch (MasterPid)
1162 case -1:
1163 Panic(errno, "fork");
1164 /* NOTREACHED */
1165 case 0:
1166 break;
1167 default:
1168 if (detached)
1169 exit(0);
1170 if (SockMatch)
1171 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1172 else
1173 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1174 for (ap = socknamebuf; *ap; ap++)
1175 if (*ap == '/')
1176 *ap = '-';
1177 #ifdef NAME_MAX
1178 if (strlen(socknamebuf) > NAME_MAX)
1179 socknamebuf[NAME_MAX] = 0;
1180 #endif
1181 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1182 setgid(real_gid);
1183 setuid(real_uid);
1184 eff_uid = real_uid;
1185 eff_gid = real_gid;
1186 Attacher();
1187 /* NOTREACHED */
1190 if (!detached)
1191 PanicPid = getppid();
1193 if (DefaultEsc == -1)
1194 DefaultEsc = Ctrl('a');
1195 if (DefaultMetaEsc == -1)
1196 DefaultMetaEsc = 'a';
1198 ap = av0 + strlen(av0) - 1;
1199 while (ap >= av0)
1201 if (!strncmp("screen", ap, 6))
1203 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1204 break;
1206 ap--;
1208 if (ap < av0)
1209 *av0 = 'S';
1211 #ifdef DEBUG
1213 char buf[256];
1215 if (dfp && dfp != stderr)
1216 fclose(dfp);
1217 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1218 if ((dfp = fopen(buf, "w")) == NULL)
1219 dfp = stderr;
1220 else
1221 (void) chmod(buf, 0666);
1223 #endif
1224 if (!detached)
1226 if (attach_fd == -1)
1228 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1229 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1231 else
1232 n = dup(attach_fd);
1234 else
1235 n = -1;
1236 freopen("/dev/null", "r", stdin);
1237 freopen("/dev/null", "w", stdout);
1239 #ifdef DEBUG
1240 if (dfp != stderr)
1241 #endif
1242 freopen("/dev/null", "w", stderr);
1243 debug("-- screen.back debug started\n");
1246 * This guarantees that the session owner is listed, even when we
1247 * start detached. From now on we should not refer to 'LoginName'
1248 * any more, use users->u_name instead.
1250 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1251 Panic(0, "Could not create user info");
1252 if (!detached)
1254 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1255 Panic(0, "Could not alloc display");
1256 PanicPid = 0;
1257 #ifdef ENCODINGS
1258 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1259 debug1("D_encoding = %d\n", D_encoding);
1260 #endif
1263 if (SockMatch)
1265 /* user started us with -S option */
1266 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1268 else
1270 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1271 HostName);
1273 for (ap = socknamebuf; *ap; ap++)
1274 if (*ap == '/')
1275 *ap = '-';
1276 #ifdef NAME_MAX
1277 if (strlen(socknamebuf) > NAME_MAX)
1279 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1280 socknamebuf[NAME_MAX] = 0;
1282 #endif
1283 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1285 ServerSocket = MakeServerSocket();
1286 InitKeytab();
1288 LoadScripts();
1289 ScriptInit();
1290 if (script_file)
1292 ScriptSource(script_file);
1293 free(script_file);
1294 script_file = 0;
1297 #ifdef ETCSCREENRC
1298 # ifdef ALLOW_SYSSCREENRC
1299 if ((ap = getenv("SYSSCREENRC")))
1300 (void)StartRc(ap, 0);
1301 else
1302 # endif
1303 (void)StartRc(ETCSCREENRC, 0);
1304 #endif
1305 (void)StartRc(RcFileName, 0);
1306 # ifdef UTMPOK
1307 # ifndef UTNOKEEP
1308 InitUtmp();
1309 # endif /* UTNOKEEP */
1310 # endif /* UTMPOK */
1311 if (display)
1313 if (InitTermcap(0, 0))
1315 debug("Could not init termcap - exiting\n");
1316 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1317 freetty();
1318 if (D_userpid)
1319 Kill(D_userpid, SIG_BYE);
1320 eexit(1);
1322 MakeDefaultCanvas();
1323 InitTerm(0);
1324 #ifdef UTMPOK
1325 RemoveLoginSlot();
1326 #endif
1328 else
1329 MakeTermcap(1);
1330 #ifdef LOADAV
1331 InitLoadav();
1332 #endif /* LOADAV */
1333 MakeNewEnv();
1334 signal(SIGHUP, SigHup);
1335 signal(SIGINT, FinitHandler);
1336 signal(SIGQUIT, FinitHandler);
1337 signal(SIGTERM, FinitHandler);
1338 #ifdef BSDJOBS
1339 signal(SIGTTIN, SIG_IGN);
1340 signal(SIGTTOU, SIG_IGN);
1341 #endif
1343 if (display)
1345 brktty(D_userfd);
1346 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1347 /* Note: SetMode must be called _before_ FinishRc. */
1348 SetTTY(D_userfd, &D_NewMode);
1349 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1350 Msg(errno, "Warning: NBLOCK fcntl failed");
1352 else
1353 brktty(-1); /* just try */
1354 signal(SIGCHLD, SigChld);
1355 #ifdef ETCSCREENRC
1356 # ifdef ALLOW_SYSSCREENRC
1357 if ((ap = getenv("SYSSCREENRC")))
1358 FinishRc(ap);
1359 else
1360 # endif
1361 FinishRc(ETCSCREENRC);
1362 #endif
1363 FinishRc(RcFileName);
1365 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1366 if (windows == NULL)
1368 debug("We open one default window, as screenrc did not specify one.\n");
1369 if (MakeWindow(&nwin) == -1)
1371 Msg(0, "Sorry, could not find a PTY.");
1372 sleep(5);
1373 Finit(0);
1374 /* NOTREACHED */
1378 #ifdef HAVE_BRAILLE
1379 StartBraille();
1380 #endif
1382 if (display && default_startup)
1383 display_copyright();
1384 signal(SIGINT, SigInt);
1385 if (rflag && (rflag & 1) == 0 && !quietflag)
1387 Msg(0, "New screen...");
1388 rflag = 0;
1391 serv_read.type = EV_READ;
1392 serv_read.fd = ServerSocket;
1393 serv_read.handler = serv_read_fn;
1394 evenq(&serv_read);
1396 serv_select.pri = -10;
1397 serv_select.type = EV_ALWAYS;
1398 serv_select.handler = serv_select_fn;
1399 evenq(&serv_select);
1401 logflushev.type = EV_TIMEOUT;
1402 logflushev.handler = logflush_fn;
1404 sched();
1405 /* NOTREACHED */
1406 return 0;
1409 void
1410 WindowDied(p, wstat, wstat_valid)
1411 struct win *p;
1412 int wstat;
1413 int wstat_valid;
1415 int killit = 0;
1417 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1418 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1419 killit = 1;
1421 if (ZombieKey_destroy && !killit)
1423 char buf[100], *s, reason[100];
1424 time_t now;
1426 if (wstat_valid) {
1427 if (WIFEXITED(wstat))
1428 if (WEXITSTATUS(wstat))
1429 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1430 else
1431 sprintf(reason, "terminated normally");
1432 else if (WIFSIGNALED(wstat))
1433 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1434 #ifdef WCOREDUMP
1435 WCOREDUMP(wstat) ? " (core file generated)" : "");
1436 #else
1437 "");
1438 #endif
1439 } else
1440 sprintf(reason, "detached from window");
1442 (void) time(&now);
1443 s = ctime(&now);
1444 if (s && *s)
1445 s[strlen(s) - 1] = '\0';
1446 debug3("window %d (%s) going into zombie state fd %d",
1447 p->w_number, p->w_title, p->w_ptyfd);
1448 #ifdef UTMPOK
1449 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1451 RemoveUtmp(p);
1452 p->w_slot = 0; /* "detached" */
1454 #endif
1455 CloseDevice(p);
1457 p->w_deadpid = p->w_pid;
1458 p->w_pid = 0;
1459 ResetWindow(p);
1460 /* p->w_y = p->w_bot; */
1461 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1462 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1463 WriteString(p, buf, strlen(buf));
1464 WindowChanged(p, 'f');
1466 else
1467 KillWindow(p);
1468 #ifdef UTMPOK
1469 CarefulUtmp();
1470 #endif
1473 static void
1474 SigChldHandler()
1476 struct stat st;
1477 #ifdef DEBUG
1478 fds();
1479 #endif
1480 while (GotSigChld)
1482 GotSigChld = 0;
1483 DoWait();
1484 #ifdef SYSVSIGS
1485 signal(SIGCHLD, SigChld);
1486 #endif
1488 if (stat(SockPath, &st) == -1)
1490 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1491 if (!RecoverSocket())
1493 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1494 Finit(1);
1496 else
1497 debug1("'%s' reconstructed\n", SockPath);
1499 else
1500 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1503 static sigret_t
1504 SigChld SIGDEFARG
1506 debug("SigChld()\n");
1507 GotSigChld = 1;
1508 SIGRETURN;
1511 sigret_t
1512 SigHup SIGDEFARG
1514 /* Hangup all displays */
1515 while ((display = displays) != 0)
1516 Hangup();
1517 SIGRETURN;
1521 * the backend's Interrupt handler
1522 * we cannot insert the intrc directly, as we never know
1523 * if fore is valid.
1525 static sigret_t
1526 SigInt SIGDEFARG
1528 #if HAZARDOUS
1529 char ibuf;
1531 debug("SigInt()\n");
1532 if (fore && displays)
1534 # if defined(TERMIO) || defined(POSIX)
1535 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1536 # else
1537 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1538 # endif
1539 fore->w_inlen = 0;
1540 write(fore->w_ptyfd, &ibuf, 1);
1542 #else
1543 signal(SIGINT, SigInt);
1544 debug("SigInt() careful\n");
1545 InterruptPlease = 1;
1546 #endif
1547 SIGRETURN;
1550 static sigret_t
1551 CoreDump SIGDEFARG
1553 struct display *disp;
1554 char buf[80];
1556 #if defined(SYSVSIGS) && defined(SIGHASARG)
1557 signal(sigsig, SIG_IGN);
1558 #endif
1559 setgid(getgid());
1560 setuid(getuid());
1561 unlink("core");
1562 #ifdef SIGHASARG
1563 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1564 #else
1565 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1566 #endif
1567 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1569 #else /* SHADOWPW && !DEBUG */
1570 " (core dumped)"
1571 #endif /* SHADOWPW && !DEBUG */
1573 for (disp = displays; disp; disp = disp->d_next)
1575 fcntl(disp->d_userfd, F_SETFL, 0);
1576 SetTTY(disp->d_userfd, &D_OldMode);
1577 write(disp->d_userfd, buf, strlen(buf));
1578 Kill(disp->d_userpid, SIG_BYE);
1580 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1581 Kill(getpid(), SIGKILL);
1582 eexit(11);
1583 #else /* SHADOWPW && !DEBUG */
1584 abort();
1585 #endif /* SHADOWPW && !DEBUG */
1586 SIGRETURN;
1589 static void
1590 DoWait()
1592 register int pid;
1593 struct win *p, *next;
1594 #ifdef BSDWAIT
1595 union wait wstat;
1596 #else
1597 int wstat;
1598 #endif
1600 #ifdef BSDJOBS
1601 # ifndef BSDWAIT
1602 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1603 # else
1604 # ifdef USE_WAIT2
1606 * From: rouilj@sni-usa.com (John Rouillard)
1607 * note that WUNTRACED is not documented to work, but it is defined in
1608 * /usr/include/sys/wait.h, so it may work
1610 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1611 # else /* USE_WAIT2 */
1612 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1613 # endif /* USE_WAIT2 */
1614 # endif
1615 #else /* BSDJOBS */
1616 while ((pid = wait(&wstat)) < 0)
1617 if (errno != EINTR)
1618 break;
1619 if (pid > 0)
1620 #endif /* BSDJOBS */
1622 for (p = windows; p; p = next)
1624 next = p->w_next;
1625 if ( (p->w_pid && pid == p->w_pid) ||
1626 (p->w_deadpid && pid == p->w_deadpid) )
1628 /* child has ceased to exist */
1629 p->w_pid = 0;
1631 #ifdef BSDJOBS
1632 if (WIFSTOPPED(wstat))
1634 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1635 #ifdef SIGTTIN
1636 if (WSTOPSIG(wstat) == SIGTTIN)
1638 Msg(0, "Suspended (tty input)");
1639 continue;
1641 #endif
1642 #ifdef SIGTTOU
1643 if (WSTOPSIG(wstat) == SIGTTOU)
1645 Msg(0, "Suspended (tty output)");
1646 continue;
1648 #endif
1649 /* Try to restart process */
1650 Msg(0, "Child has been stopped, restarting.");
1651 if (killpg(pid, SIGCONT))
1652 kill(pid, SIGCONT);
1654 else
1655 #endif
1657 WindowDied(p, wstat, 1);
1659 break;
1661 #ifdef PSEUDOS
1662 if (p->w_pwin && pid == p->w_pwin->p_pid)
1664 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1665 FreePseudowin(p);
1666 break;
1668 #endif
1670 if (p == 0)
1672 debug1("pid %d not found - hope that's ok\n", pid);
1678 static sigret_t
1679 FinitHandler SIGDEFARG
1681 #ifdef SIGHASARG
1682 debug1("FinitHandler called, sig %d.\n", sigsig);
1683 #else
1684 debug("FinitHandler called.\n");
1685 #endif
1686 Finit(1);
1687 SIGRETURN;
1690 void
1691 Finit(i)
1692 int i;
1694 signal(SIGCHLD, SIG_DFL);
1695 signal(SIGHUP, SIG_IGN);
1696 debug1("Finit(%d);\n", i);
1698 ScriptFinit();
1700 while (windows)
1702 struct win *p = windows;
1703 windows = windows->w_next;
1704 FreeWindow(p);
1706 if (ServerSocket != -1)
1708 debug1("we unlink(%s)\n", SockPath);
1709 #ifdef USE_SETEUID
1710 xseteuid(real_uid);
1711 xsetegid(real_gid);
1712 #endif
1713 (void) unlink(SockPath);
1714 #ifdef USE_SETEUID
1715 xseteuid(eff_uid);
1716 xsetegid(eff_gid);
1717 #endif
1719 for (display = displays; display; display = display->d_next)
1721 if (D_status)
1722 RemoveStatus();
1723 FinitTerm();
1724 #ifdef UTMPOK
1725 RestoreLoginSlot();
1726 #endif
1727 AddStr("[screen is terminating]\r\n");
1728 Flush();
1729 SetTTY(D_userfd, &D_OldMode);
1730 fcntl(D_userfd, F_SETFL, 0);
1731 freetty();
1732 Kill(D_userpid, SIG_BYE);
1735 * we _cannot_ call eexit(i) here,
1736 * instead of playing with the Socket above. Sigh.
1738 exit(i);
1741 void
1742 eexit(e)
1743 int e;
1745 debug("eexit\n");
1746 if (ServerSocket != -1)
1748 debug1("we unlink(%s)\n", SockPath);
1749 setgid(real_gid);
1750 setuid(real_uid);
1751 (void) unlink(SockPath);
1753 exit(e);
1756 void
1757 Hangup()
1759 if (display == 0)
1760 return;
1761 debug1("Hangup %x\n", display);
1762 if (D_userfd >= 0)
1764 close(D_userfd);
1765 D_userfd = -1;
1767 if (auto_detach || displays->d_next)
1768 Detach(D_HANGUP);
1769 else
1770 Finit(0);
1774 * Detach now has the following modes:
1775 *D_DETACH SIG_BYE detach backend and exit attacher
1776 *D_HANGUP SIG_BYE detach backend and exit attacher
1777 *D_STOP SIG_STOP stop attacher (and detach backend)
1778 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1779 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1780 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1781 *D_LOCK SIG_LOCK lock the attacher
1782 * (jw)
1783 * we always remove our utmp slots. (even when "lock" or "stop")
1784 * Note: Take extra care here, we may be called by interrupt!
1786 void
1787 Detach(mode)
1788 int mode;
1790 int sign = 0, pid;
1791 struct canvas *cv;
1792 struct win *p;
1794 if (display == 0)
1795 return;
1797 signal(SIGHUP, SIG_IGN);
1798 debug1("Detach(%d)\n", mode);
1799 if (D_status)
1800 RemoveStatus();
1801 FinitTerm();
1802 if (!display)
1803 return;
1804 switch (mode)
1806 case D_HANGUP:
1807 sign = SIG_BYE;
1808 break;
1809 case D_DETACH:
1810 AddStr("[detached]\r\n");
1811 sign = SIG_BYE;
1812 break;
1813 #ifdef BSDJOBS
1814 case D_STOP:
1815 sign = SIG_STOP;
1816 break;
1817 #endif
1818 #ifdef REMOTE_DETACH
1819 case D_REMOTE:
1820 AddStr("[remote detached]\r\n");
1821 sign = SIG_BYE;
1822 break;
1823 #endif
1824 #ifdef POW_DETACH
1825 case D_POWER:
1826 AddStr("[power detached]\r\n");
1827 if (PowDetachString)
1829 AddStr(PowDetachString);
1830 AddStr("\r\n");
1832 sign = SIG_POWER_BYE;
1833 break;
1834 #ifdef REMOTE_DETACH
1835 case D_REMOTE_POWER:
1836 AddStr("[remote power detached]\r\n");
1837 if (PowDetachString)
1839 AddStr(PowDetachString);
1840 AddStr("\r\n");
1842 sign = SIG_POWER_BYE;
1843 break;
1844 #endif
1845 #endif
1846 case D_LOCK:
1847 ClearAll();
1848 sign = SIG_LOCK;
1849 /* tell attacher to lock terminal with a lockprg. */
1850 break;
1852 #ifdef UTMPOK
1853 if (displays->d_next == 0)
1855 for (p = windows; p; p = p->w_next)
1857 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1859 RemoveUtmp(p);
1861 * Set the slot to 0 to get the window
1862 * logged in again.
1864 p->w_slot = (slot_t) 0;
1868 if (mode != D_HANGUP)
1869 RestoreLoginSlot();
1870 #endif
1871 if (displays->d_next == 0 && console_window)
1873 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1875 debug("could not release console - killing window\n");
1876 KillWindow(console_window);
1877 display = displays; /* restore display */
1880 if (D_fore)
1882 #ifdef MULTIUSER
1883 ReleaseAutoWritelock(display, D_fore);
1884 #endif
1885 D_user->u_detachwin = D_fore->w_number;
1886 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1888 AutosaveLayout(D_layout);
1889 layout_last = D_layout;
1890 for (cv = D_cvlist; cv; cv = cv->c_next)
1892 p = Layer2Window(cv->c_layer);
1893 SetCanvasWindow(cv, 0);
1894 if (p)
1895 WindowChanged(p, 'u');
1898 pid = D_userpid;
1899 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1900 FreeDisplay();
1901 if (displays == 0)
1902 /* Flag detached-ness */
1903 (void) chsock();
1905 * tell father what to do. We do that after we
1906 * freed the tty, thus getty feels more comfortable on hpux
1907 * if it was a power detach.
1909 Kill(pid, sign);
1910 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1911 debug("Detach returns, we are successfully detached.\n");
1912 signal(SIGHUP, SigHup);
1915 static int
1916 IsSymbol(e, s)
1917 char *e, *s;
1919 register int l;
1921 l = strlen(s);
1922 return strncmp(e, s, l) == 0 && e[l] == '=';
1925 void
1926 MakeNewEnv()
1928 register char **op, **np;
1929 static char stybuf[MAXSTR];
1931 for (op = environ; *op; ++op)
1933 if (NewEnv)
1934 free((char *)NewEnv);
1935 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1936 if (!NewEnv)
1937 Panic(0, strnomem);
1938 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1939 *np++ = stybuf; /* NewEnv[0] */
1940 *np++ = Term; /* NewEnv[1] */
1941 np++; /* room for SHELL */
1942 #ifdef TIOCSWINSZ
1943 np += 2; /* room for TERMCAP and WINDOW */
1944 #else
1945 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
1946 #endif
1948 for (op = environ; *op; ++op)
1950 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
1951 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
1952 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
1953 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
1955 *np++ = *op;
1957 *np = 0;
1960 void
1961 /*VARARGS2*/
1962 #if defined(USEVARARGS) && defined(__STDC__)
1963 Msg(int err, char *fmt, VA_DOTS)
1964 #else
1965 Msg(err, fmt, VA_DOTS)
1966 int err;
1967 char *fmt;
1968 VA_DECL
1969 #endif
1971 VA_LIST(ap)
1972 char buf[MAXPATHLEN*2];
1973 char *p = buf;
1975 VA_START(ap, fmt);
1976 fmt = DoNLS(fmt);
1977 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1978 VA_END(ap);
1979 if (err)
1981 p += strlen(p);
1982 *p++ = ':';
1983 *p++ = ' ';
1984 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
1985 buf[sizeof(buf) - 1] = 0;
1987 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
1989 if (display && displays)
1990 MakeStatus(buf);
1991 else if (displays)
1993 for (display = displays; display; display = display->d_next)
1994 MakeStatus(buf);
1996 else if (display)
1998 /* no displays but a display - must have forked.
1999 * send message to backend!
2001 char *tty = D_usertty;
2002 struct display *olddisplay = display;
2003 display = 0; /* only send once */
2004 SendErrorMsg(tty, buf);
2005 display = olddisplay;
2007 else
2008 printf("%s\r\n", buf);
2012 * Call FinitTerm for all displays, write a message to each and call eexit();
2014 void
2015 /*VARARGS2*/
2016 #if defined(USEVARARGS) && defined(__STDC__)
2017 Panic(int err, char *fmt, VA_DOTS)
2018 #else
2019 Panic(err, fmt, VA_DOTS)
2020 int err;
2021 char *fmt;
2022 VA_DECL
2023 #endif
2025 VA_LIST(ap)
2026 char buf[MAXPATHLEN*2];
2027 char *p = buf;
2029 VA_START(ap, fmt);
2030 fmt = DoNLS(fmt);
2031 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2032 VA_END(ap);
2033 if (err)
2035 p += strlen(p);
2036 *p++ = ':';
2037 *p++ = ' ';
2038 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2039 buf[sizeof(buf) - 1] = 0;
2041 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2042 if (displays == 0 && display == 0)
2044 printf("%s\r\n", buf);
2045 if (PanicPid)
2046 Kill(PanicPid, SIG_BYE);
2048 else if (displays == 0)
2050 /* no displays but a display - must have forked.
2051 * send message to backend!
2053 char *tty = D_usertty;
2054 display = 0;
2055 SendErrorMsg(tty, buf);
2056 sleep(2);
2057 _exit(1);
2059 else
2060 for (display = displays; display; display = display->d_next)
2062 if (D_status)
2063 RemoveStatus();
2064 FinitTerm();
2065 Flush();
2066 #ifdef UTMPOK
2067 RestoreLoginSlot();
2068 #endif
2069 SetTTY(D_userfd, &D_OldMode);
2070 fcntl(D_userfd, F_SETFL, 0);
2071 write(D_userfd, buf, strlen(buf));
2072 write(D_userfd, "\n", 1);
2073 freetty();
2074 if (D_userpid)
2075 Kill(D_userpid, SIG_BYE);
2077 #ifdef MULTIUSER
2078 if (tty_oldmode >= 0)
2080 # ifdef USE_SETEUID
2081 if (setuid(own_uid))
2082 xseteuid(own_uid); /* may be a loop. sigh. */
2083 # else
2084 setuid(own_uid);
2085 # endif
2086 debug1("Panic: changing back modes from %s\n", attach_tty);
2087 chmod(attach_tty, tty_oldmode);
2089 #endif
2090 eexit(1);
2095 * '^' is allowed as an escape mechanism for control characters. jw.
2097 * Added time insertion using ideas/code from /\ndy Jones
2098 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2102 #ifndef USE_LOCALE
2103 static const char days[] = "SunMonTueWedThuFriSat";
2104 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2105 #endif
2107 static char winmsg_buf[MAXSTR];
2108 #define MAX_WINMSG_REND 16 /* rendition changes */
2109 static int winmsg_rend[MAX_WINMSG_REND];
2110 static int winmsg_rendpos[MAX_WINMSG_REND];
2111 static int winmsg_numrend;
2113 static char *
2114 pad_expand(buf, p, numpad, padlen)
2115 char *buf;
2116 char *p;
2117 int numpad;
2118 int padlen;
2120 char *pn, *pn2;
2121 int i, r;
2123 padlen = padlen - (p - buf); /* space for rent */
2124 if (padlen < 0)
2125 padlen = 0;
2126 pn2 = pn = p + padlen;
2127 r = winmsg_numrend;
2128 while (p >= buf)
2130 if (r && p - buf == winmsg_rendpos[r - 1])
2132 winmsg_rendpos[--r] = pn - buf;
2133 continue;
2135 *pn-- = *p;
2136 if (*p-- == 127)
2138 pn[1] = ' ';
2139 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2140 padlen -= i;
2141 while (i-- > 0)
2142 *pn-- = ' ';
2143 numpad--;
2146 return pn2;
2149 struct backtick {
2150 struct backtick *next;
2151 int num;
2152 int tick;
2153 int lifespan;
2154 time_t bestbefore;
2155 char result[MAXSTR];
2156 char **cmdv;
2157 struct event ev;
2158 char *buf;
2159 int bufi;
2162 struct backtick *backticks;
2164 static void
2165 backtick_filter(bt)
2166 struct backtick *bt;
2168 char *p, *q;
2169 int c;
2171 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2173 if (c == '\t')
2174 c = ' ';
2175 if (c >= ' ' || c == '\005')
2176 *q++ = c;
2178 *q = 0;
2181 static void
2182 backtick_fn(ev, data)
2183 struct event *ev;
2184 char *data;
2186 struct backtick *bt;
2187 int i, j, k, l;
2189 bt = (struct backtick *)data;
2190 debug1("backtick_fn for #%d\n", bt->num);
2191 i = bt->bufi;
2192 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2193 if (l <= 0)
2195 debug1("EOF on backtick #%d\n", bt->num);
2196 evdeq(ev);
2197 close(ev->fd);
2198 ev->fd = -1;
2199 return;
2201 debug1("read %d bytes\n", l);
2202 i += l;
2203 for (j = 0; j < l; j++)
2204 if (bt->buf[i - j - 1] == '\n')
2205 break;
2206 if (j < l)
2208 for (k = i - j - 2; k >= 0; k--)
2209 if (bt->buf[k] == '\n')
2210 break;
2211 k++;
2212 bcopy(bt->buf + k, bt->result, i - j - k);
2213 bt->result[i - j - k - 1] = 0;
2214 backtick_filter(bt);
2215 WindowChanged(0, '`');
2217 if (j == l && i == MAXSTR)
2219 j = MAXSTR/2;
2220 l = j + 1;
2222 if (j < l)
2224 if (j)
2225 bcopy(bt->buf + i - j, bt->buf, j);
2226 i = j;
2228 bt->bufi = i;
2231 void
2232 setbacktick(num, lifespan, tick, cmdv)
2233 int num;
2234 int lifespan;
2235 int tick;
2236 char **cmdv;
2238 struct backtick **btp, *bt;
2239 char **v;
2241 debug1("setbacktick called for backtick #%d\n", num);
2242 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2243 if (bt->num == num)
2244 break;
2245 if (!bt && !cmdv)
2246 return;
2247 if (bt)
2249 for (v = bt->cmdv; *v; v++)
2250 free(*v);
2251 free(bt->cmdv);
2252 if (bt->buf)
2253 free(bt->buf);
2254 if (bt->ev.fd >= 0)
2255 close(bt->ev.fd);
2256 evdeq(&bt->ev);
2258 if (bt && !cmdv)
2260 *btp = bt->next;
2261 free(bt);
2262 return;
2264 if (!bt)
2266 bt = (struct backtick *)malloc(sizeof *bt);
2267 if (!bt)
2269 Msg(0, strnomem);
2270 return;
2272 bzero(bt, sizeof(*bt));
2273 bt->next = 0;
2274 *btp = bt;
2276 bt->num = num;
2277 bt->tick = tick;
2278 bt->lifespan = lifespan;
2279 bt->bestbefore = 0;
2280 bt->result[0] = 0;
2281 bt->buf = 0;
2282 bt->bufi = 0;
2283 bt->cmdv = cmdv;
2284 bt->ev.fd = -1;
2285 if (bt->tick == 0 && bt->lifespan == 0)
2287 debug("setbacktick: continuous mode\n");
2288 bt->buf = (char *)malloc(MAXSTR);
2289 if (bt->buf == 0)
2291 Msg(0, strnomem);
2292 setbacktick(num, 0, 0, (char **)0);
2293 return;
2295 bt->ev.type = EV_READ;
2296 bt->ev.fd = readpipe(bt->cmdv);
2297 bt->ev.handler = backtick_fn;
2298 bt->ev.data = (char *)bt;
2299 if (bt->ev.fd >= 0)
2300 evenq(&bt->ev);
2304 static char *
2305 runbacktick(bt, tickp, now)
2306 struct backtick *bt;
2307 int *tickp;
2308 time_t now;
2310 int f, i, l, j;
2311 time_t now2;
2313 debug1("runbacktick called for backtick #%d\n", bt->num);
2314 if (bt->tick && (!*tickp || bt->tick < *tickp))
2315 *tickp = bt->tick;
2316 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2318 debug1("returning old result (%d)\n", bt->lifespan);
2319 return bt->result;
2321 f = readpipe(bt->cmdv);
2322 if (f == -1)
2323 return bt->result;
2324 i = 0;
2325 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2327 debug1("runbacktick: read %d bytes\n", l);
2328 i += l;
2329 for (j = 1; j < l; j++)
2330 if (bt->result[i - j - 1] == '\n')
2331 break;
2332 if (j == l && i == sizeof(bt->result))
2334 j = sizeof(bt->result) / 2;
2335 l = j + 1;
2337 if (j < l)
2339 bcopy(bt->result + i - j, bt->result, j);
2340 i = j;
2343 close(f);
2344 bt->result[sizeof(bt->result) - 1] = '\n';
2345 if (i && bt->result[i - 1] == '\n')
2346 i--;
2347 debug1("runbacktick: finished, %d bytes\n", i);
2348 bt->result[i] = 0;
2349 backtick_filter(bt);
2350 (void)time(&now2);
2351 bt->bestbefore = now2 + bt->lifespan;
2352 return bt->result;
2355 char *
2356 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2357 char *str;
2358 struct win *win;
2359 int esc;
2360 int padlen;
2361 struct event *ev;
2362 int rec;
2364 static int tick;
2365 char *s = str;
2366 register char *p = winmsg_buf;
2367 register int ctrl;
2368 struct timeval now;
2369 struct tm *tm;
2370 int l, i, r;
2371 int num;
2372 int zeroflg;
2373 int longflg;
2374 int minusflg;
2375 int plusflg;
2376 int qmflag = 0, omflag = 0, qmnumrend = 0;
2377 char *qmpos = 0;
2378 int numpad = 0;
2379 int lastpad = 0;
2380 int truncpos = -1;
2381 int truncper = 0;
2382 int trunclong = 0;
2383 struct backtick *bt;
2385 if (winmsg_numrend >= 0)
2386 winmsg_numrend = 0;
2387 else
2388 winmsg_numrend = -winmsg_numrend;
2390 tick = 0;
2391 tm = 0;
2392 ctrl = 0;
2393 gettimeofday(&now, NULL);
2394 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2396 *p = *s;
2397 if (ctrl)
2399 ctrl = 0;
2400 if (*s != '^' && *s >= 64)
2401 *p &= 0x1f;
2402 continue;
2404 if (*s != esc)
2406 if (esc == '%')
2408 switch (*s)
2410 #if 0
2411 case '~':
2412 *p = BELL;
2413 break;
2414 #endif
2415 case '^':
2416 ctrl = 1;
2417 *p-- = '^';
2418 break;
2419 default:
2420 break;
2423 continue;
2425 if (*++s == esc) /* double escape ? */
2426 continue;
2427 if ((plusflg = *s == '+') != 0)
2428 s++;
2429 if ((minusflg = *s == '-') != 0)
2430 s++;
2431 if ((zeroflg = *s == '0') != 0)
2432 s++;
2433 num = 0;
2434 while(*s >= '0' && *s <= '9')
2435 num = num * 10 + (*s++ - '0');
2436 if ((longflg = *s == 'L') != 0)
2437 s++;
2438 switch (*s)
2440 case '?':
2441 p--;
2442 if (qmpos)
2444 if ((!qmflag && !omflag) || omflag == 1)
2446 p = qmpos;
2447 if (qmnumrend < winmsg_numrend)
2448 winmsg_numrend = qmnumrend;
2450 qmpos = 0;
2451 break;
2453 qmpos = p;
2454 qmnumrend = winmsg_numrend;
2455 qmflag = omflag = 0;
2456 break;
2457 case ':':
2458 p--;
2459 if (!qmpos)
2460 break;
2461 if (qmflag && omflag != 1)
2463 omflag = 1;
2464 qmpos = p;
2465 qmnumrend = winmsg_numrend;
2467 else
2469 p = qmpos;
2470 if (qmnumrend < winmsg_numrend)
2471 winmsg_numrend = qmnumrend;
2472 omflag = -1;
2474 break;
2475 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2476 case 'a': case 'A': case 's': case 'c': case 'C':
2477 if (l < 4)
2478 break;
2479 if (tm == 0)
2481 time_t nowsec = now.tv_sec;
2482 tm = localtime(&nowsec);
2484 qmflag = 1;
2485 if (!tick || tick > 3600)
2486 tick = 3600;
2487 switch (*s)
2489 case 'd':
2490 sprintf(p, "%02d", tm->tm_mday % 100);
2491 break;
2492 case 'D':
2493 #ifdef USE_LOCALE
2494 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2495 #else
2496 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2497 #endif
2498 break;
2499 case 'm':
2500 sprintf(p, "%02d", tm->tm_mon + 1);
2501 break;
2502 case 'M':
2503 #ifdef USE_LOCALE
2504 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2505 #else
2506 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2507 #endif
2508 break;
2509 case 'y':
2510 sprintf(p, "%02d", tm->tm_year % 100);
2511 break;
2512 case 'Y':
2513 sprintf(p, "%04d", tm->tm_year + 1900);
2514 break;
2515 case 'a':
2516 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2517 break;
2518 case 'A':
2519 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2520 break;
2521 case 's':
2522 sprintf(p, "%02d", tm->tm_sec);
2523 tick = 1;
2524 break;
2525 case 'c':
2526 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2527 if (!tick || tick > 60)
2528 tick = 60;
2529 break;
2530 case 'C':
2531 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2532 if (!tick || tick > 60)
2533 tick = 60;
2534 break;
2535 default:
2536 break;
2538 p += strlen(p) - 1;
2539 break;
2540 case 'l':
2541 #ifdef LOADAV
2542 *p = 0;
2543 if (l > 20)
2544 AddLoadav(p);
2545 if (*p)
2547 qmflag = 1;
2548 p += strlen(p) - 1;
2550 else
2551 *p = '?';
2552 if (!tick || tick > 60)
2553 tick = 60;
2554 #else
2555 *p = '?';
2556 #endif
2557 p += strlen(p) - 1;
2558 break;
2559 case '`':
2560 case 'h':
2561 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2563 p--;
2564 break;
2566 if (*s == '`')
2568 for (bt = backticks; bt; bt = bt->next)
2569 if (bt->num == num)
2570 break;
2571 if (bt == 0)
2573 p--;
2574 break;
2578 char savebuf[sizeof(winmsg_buf)];
2579 int oldtick = tick;
2580 int oldnumrend = winmsg_numrend;
2582 *p = 0;
2583 strcpy(savebuf, winmsg_buf);
2584 winmsg_numrend = -winmsg_numrend;
2585 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2586 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2587 if (!tick || oldtick < tick)
2588 tick = oldtick;
2589 if ((int)strlen(winmsg_buf) < l)
2590 strcat(savebuf, winmsg_buf);
2591 strcpy(winmsg_buf, savebuf);
2592 while (oldnumrend < winmsg_numrend)
2593 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2594 if (*p)
2595 qmflag = 1;
2596 p += strlen(p) - 1;
2598 break;
2599 case 'w':
2600 case 'W':
2602 struct win *oldfore = 0;
2603 char *ss;
2605 if (display)
2607 oldfore = D_fore;
2608 D_fore = win;
2610 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2611 if (minusflg)
2612 *ss = 0;
2613 if (display)
2614 D_fore = oldfore;
2616 if (*p)
2617 qmflag = 1;
2618 p += strlen(p) - 1;
2619 break;
2620 case 'u':
2621 *p = 0;
2622 if (win)
2623 AddOtherUsers(p, l - 1, win);
2624 if (*p)
2625 qmflag = 1;
2626 p += strlen(p) - 1;
2627 break;
2628 case 'f':
2629 *p = 0;
2630 if (win)
2631 AddWindowFlags(p, l - 1, win);
2632 if (*p)
2633 qmflag = 1;
2634 p += strlen(p) - 1;
2635 break;
2636 case 't':
2637 *p = 0;
2638 if (win && (int)strlen(win->w_title) < l)
2640 strcpy(p, win->w_title);
2641 if (*p)
2642 qmflag = 1;
2644 p += strlen(p) - 1;
2645 break;
2646 case '{':
2648 char rbuf[128];
2649 s++;
2650 for (i = 0; i < 127; i++)
2651 if (s[i] && s[i] != '}')
2652 rbuf[i] = s[i];
2653 else
2654 break;
2655 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2657 r = -1;
2658 rbuf[i] = 0;
2659 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2660 if (i != 1 || rbuf[0] != '-')
2661 r = ParseAttrColor(rbuf, (char *)0, 0);
2662 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2664 winmsg_rend[winmsg_numrend] = r;
2665 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2666 winmsg_numrend++;
2669 s += i;
2670 p--;
2672 break;
2673 case 'H':
2674 *p = 0;
2675 if ((int)strlen(HostName) < l)
2677 strcpy(p, HostName);
2678 if (*p)
2679 qmflag = 1;
2681 p += strlen(p) - 1;
2682 break;
2683 case 'F':
2684 p--;
2685 /* small hack */
2686 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2687 minusflg = !minusflg;
2688 if (minusflg)
2689 qmflag = 1;
2690 break;
2691 case '>':
2692 truncpos = p - winmsg_buf;
2693 truncper = num > 100 ? 100 : num;
2694 trunclong = longflg;
2695 p--;
2696 break;
2697 case '=':
2698 case '<':
2699 *p = ' ';
2700 if (num || zeroflg || plusflg || longflg || (*s != '='))
2702 /* expand all pads */
2703 if (minusflg)
2705 num = (plusflg ? lastpad : padlen) - num;
2706 if (!plusflg && padlen == 0)
2707 num = p - winmsg_buf;
2708 plusflg = 0;
2710 else if (!zeroflg)
2712 if (*s != '=' && num == 0 && !plusflg)
2713 num = 100;
2714 if (num > 100)
2715 num = 100;
2716 if (padlen == 0)
2717 num = p - winmsg_buf;
2718 else
2719 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2721 if (num < 0)
2722 num = 0;
2723 if (plusflg)
2724 num += lastpad;
2725 if (num > MAXSTR - 1)
2726 num = MAXSTR - 1;
2727 if (numpad)
2728 p = pad_expand(winmsg_buf, p, numpad, num);
2729 numpad = 0;
2730 if (p - winmsg_buf > num && !longflg)
2732 int left, trunc;
2734 if (truncpos == -1)
2736 truncpos = lastpad;
2737 truncper = 0;
2739 trunc = lastpad + truncper * (num - lastpad) / 100;
2740 if (trunc > num)
2741 trunc = num;
2742 if (trunc < lastpad)
2743 trunc = lastpad;
2744 left = truncpos - trunc;
2745 if (left > p - winmsg_buf - num)
2746 left = p - winmsg_buf - num;
2747 debug1("lastpad = %d, ", lastpad);
2748 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2749 if (left > 0)
2751 if (left + lastpad > p - winmsg_buf)
2752 left = p - winmsg_buf - lastpad;
2753 if (p - winmsg_buf - lastpad - left > 0)
2754 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2755 p -= left;
2756 r = winmsg_numrend;
2757 while (r && winmsg_rendpos[r - 1] > lastpad)
2759 r--;
2760 winmsg_rendpos[r] -= left;
2761 if (winmsg_rendpos[r] < lastpad)
2762 winmsg_rendpos[r] = lastpad;
2764 if (trunclong)
2766 if (p - winmsg_buf > lastpad)
2767 winmsg_buf[lastpad] = '.';
2768 if (p - winmsg_buf > lastpad + 1)
2769 winmsg_buf[lastpad + 1] = '.';
2770 if (p - winmsg_buf > lastpad + 2)
2771 winmsg_buf[lastpad + 2] = '.';
2774 if (p - winmsg_buf > num)
2776 p = winmsg_buf + num;
2777 if (trunclong)
2779 if (num - 1 >= lastpad)
2780 p[-1] = '.';
2781 if (num - 2 >= lastpad)
2782 p[-2] = '.';
2783 if (num - 3 >= lastpad)
2784 p[-3] = '.';
2786 r = winmsg_numrend;
2787 while (r && winmsg_rendpos[r - 1] > num)
2788 winmsg_rendpos[--r] = num;
2790 truncpos = -1;
2791 trunclong = 0;
2792 if (lastpad > p - winmsg_buf)
2793 lastpad = p - winmsg_buf;
2794 debug1("lastpad now %d\n", lastpad);
2796 if (*s == '=')
2798 while (p - winmsg_buf < num)
2799 *p++ = ' ';
2800 lastpad = p - winmsg_buf;
2801 truncpos = -1;
2802 trunclong = 0;
2803 debug1("lastpad2 now %d\n", lastpad);
2805 p--;
2807 else if (padlen)
2809 *p = 127; /* internal pad representation */
2810 numpad++;
2812 break;
2813 case 'n':
2814 s++;
2815 /* FALLTHROUGH */
2816 default:
2817 s--;
2818 if (l > 10 + num)
2820 if (num == 0)
2821 num = 1;
2822 if (!win)
2823 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2824 else
2825 sprintf(p, "%*d", num, win->w_number);
2826 qmflag = 1;
2827 p += strlen(p) - 1;
2829 break;
2832 if (qmpos && !qmflag)
2833 p = qmpos + 1;
2834 *p = '\0';
2835 if (numpad)
2837 if (padlen > MAXSTR - 1)
2838 padlen = MAXSTR - 1;
2839 p = pad_expand(winmsg_buf, p, numpad, padlen);
2841 if (ev)
2843 evdeq(ev); /* just in case */
2844 ev->timeout.tv_sec = 0;
2845 ev->timeout.tv_usec = 0;
2847 if (ev && tick)
2849 now.tv_usec = 100000;
2850 if (tick == 1)
2851 now.tv_sec++;
2852 else
2853 now.tv_sec += tick - (now.tv_sec % tick);
2854 ev->timeout = now;
2855 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2857 return winmsg_buf;
2860 char *
2861 MakeWinMsg(s, win, esc)
2862 char *s;
2863 struct win *win;
2864 int esc;
2866 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2869 void
2870 PutWinMsg(s, start, max)
2871 char *s;
2872 int start, max;
2874 int i, p, l, r, n;
2875 struct mchar rend;
2876 struct mchar rendstack[MAX_WINMSG_REND];
2877 int rendstackn = 0;
2879 if (s != winmsg_buf)
2881 /* sorry, no fancy coloring available */
2882 debug1("PutWinMsg %s plain\n", s);
2883 l = strlen(s);
2884 if (l > max)
2885 l = max;
2886 l -= start;
2887 s += start;
2888 while (l-- > 0)
2889 PUTCHARLP(*s++);
2890 return;
2892 rend = D_rend;
2893 p = 0;
2894 l = strlen(s);
2895 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
2896 for (i = 0; i < winmsg_numrend && max > 0; i++)
2898 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
2899 break;
2900 if (p < winmsg_rendpos[i])
2902 n = winmsg_rendpos[i] - p;
2903 if (n > max)
2904 n = max;
2905 max -= n;
2906 p += n;
2907 while(n-- > 0)
2909 if (start-- > 0)
2910 s++;
2911 else
2912 PUTCHARLP(*s++);
2915 r = winmsg_rend[i];
2916 if (r == -1)
2918 if (rendstackn > 0)
2919 rend = rendstack[--rendstackn];
2921 else
2923 rendstack[rendstackn++] = rend;
2924 ApplyAttrColor(r, &rend);
2926 SetRendition(&rend);
2928 if (p < l)
2930 n = l - p;
2931 if (n > max)
2932 n = max;
2933 while(n-- > 0)
2935 if (start-- > 0)
2936 s++;
2937 else
2938 PUTCHARLP(*s++);
2944 #ifdef DEBUG
2945 static void
2946 fds1(i, j)
2947 int i, j;
2949 while (i < j)
2951 debug1("%d ", i);
2952 i++;
2954 if ((j = open("/dev/null", 0)) >= 0)
2956 fds1(i + 1, j);
2957 close(j);
2959 else
2961 while (dup(++i) < 0 && errno != EBADF)
2962 debug1("%d ", i);
2963 debug1(" [%d]\n", i);
2967 static void
2968 fds()
2970 debug("fds: ");
2971 fds1(-1, -1);
2973 #endif
2975 static void
2976 serv_read_fn(ev, data)
2977 struct event *ev;
2978 char *data;
2980 debug("Knock - knock!\n");
2981 ReceiveMsg();
2984 static void
2985 serv_select_fn(ev, data)
2986 struct event *ev;
2987 char *data;
2989 struct win *p;
2991 debug("serv_select_fn called\n");
2992 /* XXX: messages?? */
2993 if (GotSigChld)
2995 SigChldHandler();
2997 if (InterruptPlease)
2999 debug("Backend received interrupt\n");
3000 /* This approach is rather questionable in a multi-display
3001 * environment */
3002 if (fore && displays)
3004 #if defined(TERMIO) || defined(POSIX)
3005 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3006 #else
3007 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3008 #endif
3009 #ifdef PSEUDOS
3010 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3011 &ibuf, 1);
3012 debug1("Backend wrote interrupt to %d", fore->w_number);
3013 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3014 #else
3015 write(fore->w_ptyfd, &ibuf, 1);
3016 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3017 #endif
3019 InterruptPlease = 0;
3022 for (p = windows; p; p = p->w_next)
3024 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3026 struct canvas *cv;
3027 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3028 p->w_bell = BELL_ON;
3029 for (display = displays; display; display = display->d_next)
3031 for (cv = D_cvlist; cv; cv = cv->c_next)
3032 if (cv->c_layer->l_bottom == &p->w_layer)
3033 break;
3034 if (cv == 0)
3036 p->w_bell = BELL_DONE;
3037 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3039 else if (visual && !D_VB && (!D_status || !D_status_bell))
3041 Msg(0, "%s", VisualBellString);
3042 if (D_status)
3044 D_status_bell = 1;
3045 debug1("using vbell timeout %d\n", VBellWait);
3046 SetTimeout(&D_statusev, VBellWait );
3050 /* don't annoy the user with two messages */
3051 if (p->w_monitor == MON_FOUND)
3052 p->w_monitor = MON_DONE;
3053 WindowChanged(p, 'f');
3055 if (p->w_monitor == MON_FOUND)
3057 struct canvas *cv;
3058 p->w_monitor = MON_ON;
3059 for (display = displays; display; display = display->d_next)
3061 for (cv = D_cvlist; cv; cv = cv->c_next)
3062 if (cv->c_layer->l_bottom == &p->w_layer)
3063 break;
3064 if (cv)
3065 continue; /* user already sees window */
3066 #ifdef MULTIUSER
3067 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3068 continue; /* user doesn't care */
3069 #endif
3070 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3071 p->w_monitor = MON_DONE;
3073 WindowChanged(p, 'f');
3077 for (display = displays; display; display = display->d_next)
3079 struct canvas *cv;
3080 if (D_status == STATUS_ON_WIN)
3081 continue;
3082 /* XXX: should use display functions! */
3083 for (cv = D_cvlist; cv; cv = cv->c_next)
3085 int lx, ly;
3087 /* normalize window, see resize.c */
3088 lx = cv->c_layer->l_x;
3089 ly = cv->c_layer->l_y;
3090 if (lx == cv->c_layer->l_width)
3091 lx--;
3092 if (ly + cv->c_yoff < cv->c_ys)
3094 int i, n = cv->c_ys - (ly + cv->c_yoff);
3095 cv->c_yoff = cv->c_ys - ly;
3096 RethinkViewportOffsets(cv);
3097 if (n > cv->c_layer->l_height)
3098 n = cv->c_layer->l_height;
3099 CV_CALL(cv,
3100 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3101 LayRedisplayLine(-1, -1, -1, 1);
3102 for (i = 0; i < n; i++)
3103 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3104 if (cv == cv->c_display->d_forecv)
3105 LaySetCursor();
3108 else if (ly + cv->c_yoff > cv->c_ye)
3110 int i, n = ly + cv->c_yoff - cv->c_ye;
3111 cv->c_yoff = cv->c_ye - ly;
3112 RethinkViewportOffsets(cv);
3113 if (n > cv->c_layer->l_height)
3114 n = cv->c_layer->l_height;
3115 CV_CALL(cv,
3116 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3117 LayRedisplayLine(-1, -1, -1, 1);
3118 for (i = 0; i < n; i++)
3119 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3120 if (cv == cv->c_display->d_forecv)
3121 LaySetCursor();
3124 if (lx + cv->c_xoff < cv->c_xs)
3126 int i, n = cv->c_xs - (lx + cv->c_xoff);
3127 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3128 n = (cv->c_xe - cv->c_xs + 1) / 2;
3129 if (cv->c_xoff + n > cv->c_xs)
3130 n = cv->c_xs - cv->c_xoff;
3131 cv->c_xoff += n;
3132 RethinkViewportOffsets(cv);
3133 if (n > cv->c_layer->l_width)
3134 n = cv->c_layer->l_width;
3135 CV_CALL(cv,
3136 LayRedisplayLine(-1, -1, -1, 1);
3137 for (i = 0; i < flayer->l_height; i++)
3139 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3140 LayRedisplayLine(i, 0, n - 1, 1);
3142 if (cv == cv->c_display->d_forecv)
3143 LaySetCursor();
3146 else if (lx + cv->c_xoff > cv->c_xe)
3148 int i, n = lx + cv->c_xoff - cv->c_xe;
3149 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3150 n = (cv->c_xe - cv->c_xs + 1) / 2;
3151 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3152 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3153 cv->c_xoff -= n;
3154 RethinkViewportOffsets(cv);
3155 if (n > cv->c_layer->l_width)
3156 n = cv->c_layer->l_width;
3157 CV_CALL(cv,
3158 LayRedisplayLine(-1, -1, -1, 1);
3159 for (i = 0; i < flayer->l_height; i++)
3161 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3162 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3164 if (cv == cv->c_display->d_forecv)
3165 LaySetCursor();
3171 for (display = displays; display; display = display->d_next)
3173 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3174 continue;
3175 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3176 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3180 static void
3181 logflush_fn(ev, data)
3182 struct event *ev;
3183 char *data;
3185 struct win *p;
3186 char *buf;
3187 int n;
3189 if (!islogfile(NULL))
3190 return; /* no more logfiles */
3191 logfflush(NULL);
3192 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3193 if (n)
3195 SetTimeout(ev, n * 1000);
3196 evenq(ev); /* re-enqueue ourself */
3198 if (!logtstamp_on)
3199 return;
3200 /* write fancy time-stamp */
3201 for (p = windows; p; p = p->w_next)
3203 if (!p->w_log)
3204 continue;
3205 p->w_logsilence += n;
3206 if (p->w_logsilence < logtstamp_after)
3207 continue;
3208 if (p->w_logsilence - n >= logtstamp_after)
3209 continue;
3210 buf = MakeWinMsg(logtstamp_string, p, '%');
3211 logfwrite(p->w_log, buf, strlen(buf));
3216 * Interprets ^?, ^@ and other ^-control-char notation.
3217 * Interprets \ddd octal notation
3219 * The result is placed in *cp, p is advanced behind the parsed expression and
3220 * returned.
3222 static char *
3223 ParseChar(p, cp)
3224 char *p, *cp;
3226 if (*p == 0)
3227 return 0;
3228 if (*p == '^' && p[1])
3230 if (*++p == '?')
3231 *cp = '\177';
3232 else if (*p >= '@')
3233 *cp = Ctrl(*p);
3234 else
3235 return 0;
3236 ++p;
3238 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3240 *cp = 0;
3242 *cp = *cp * 8 + *p - '0';
3243 while (*++p <= '7' && *p >= '0');
3245 else
3246 *cp = *p++;
3247 return p;
3250 static int
3251 ParseEscape(p)
3252 char *p;
3254 unsigned char buf[2];
3256 if (*p == 0)
3257 SetEscape((struct acluser *)0, -1, -1);
3258 else
3260 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3261 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3262 return -1;
3263 SetEscape((struct acluser *)0, buf[0], buf[1]);
3265 return 0;