Merge branch 'master' into lua-scripting
[screen-lua.git] / src / screen.c
blob7dc00d909bf10e88c6f40339b4ced465ecffe11b
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;
362 char *sty = 0;
364 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
365 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
366 #endif
367 #if defined(sun) && defined(SVR4)
369 /* Solaris' login blocks SIGHUP! This is _very bad_ */
370 sigset_t sset;
371 sigemptyset(&sset);
372 sigprocmask(SIG_SETMASK, &sset, 0);
374 #endif
377 * First, close all unused descriptors
378 * (otherwise, we might have problems with the select() call)
380 closeallfiles(0);
381 #ifdef DEBUG
382 opendebug(1, 0);
383 #endif
384 sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
385 PATCHLEVEL, STATE, ORIGIN, DATE);
386 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
387 debug2("-- screen debug started %s (%s)\n", *av, version);
388 #ifdef POSIX
389 debug("POSIX\n");
390 #endif
391 #ifdef TERMIO
392 debug("TERMIO\n");
393 #endif
394 #ifdef SYSV
395 debug("SYSV\n");
396 #endif
397 #ifdef SYSVSIGS
398 debug("SYSVSIGS\n");
399 #endif
400 #ifdef NAMEDPIPE
401 debug("NAMEDPIPE\n");
402 #endif
403 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
404 debug("Window size changing enabled\n");
405 #endif
406 #ifdef HAVE_SETREUID
407 debug("SETREUID\n");
408 #endif
409 #ifdef HAVE_SETEUID
410 debug("SETEUID\n");
411 #endif
412 #ifdef hpux
413 debug("hpux\n");
414 #endif
415 #ifdef USEBCOPY
416 debug("USEBCOPY\n");
417 #endif
418 #ifdef UTMPOK
419 debug("UTMPOK\n");
420 #endif
421 #ifdef LOADAV
422 debug("LOADAV\n");
423 #endif
424 #ifdef NETHACK
425 debug("NETHACK\n");
426 #endif
427 #ifdef TERMINFO
428 debug("TERMINFO\n");
429 #endif
430 #ifdef SHADOWPW
431 debug("SHADOWPW\n");
432 #endif
433 #ifdef NAME_MAX
434 debug1("NAME_MAX = %d\n", NAME_MAX);
435 #endif
437 BellString = SaveStr("Bell in window %n");
438 VisualBellString = SaveStr(" Wuff, Wuff!! ");
439 ActivityString = SaveStr("Activity in window %n");
440 screenlogfile = SaveStr("screenlog.%n");
441 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
442 hstatusstring = SaveStr("%h");
443 captionstring = SaveStr("%3n %t");
444 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
445 wlisttit = SaveStr("Num Name%=Flags");
446 wliststr = SaveStr("%3n %t%=%f");
447 #ifdef COPY_PASTE
448 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
449 #endif
450 ShellProg = NULL;
451 #ifdef POW_DETACH
452 PowDetachString = 0;
453 #endif
454 default_startup = (ac > 1) ? 0 : 1;
455 adaptflag = 0;
456 VBellWait = VBELLWAIT * 1000;
457 MsgWait = MSGWAIT * 1000;
458 MsgMinWait = MSGMINWAIT * 1000;
459 SilenceWait = SILENCEWAIT;
460 #ifdef HAVE_BRAILLE
461 InitBraille();
462 #endif
463 #ifdef ZMODEM
464 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
465 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
466 #endif
468 #ifdef COPY_PASTE
469 CompileKeys((char *)0, 0, mark_key_tab);
470 #endif
471 #ifdef UTF8
472 InitBuiltinTabs();
473 screenencodings = SaveStr(SCREENENCODINGS);
474 #endif
475 nwin = nwin_undef;
476 nwin_options = nwin_undef;
477 strcpy(screenterm, "screen");
479 logreopen_register(lf_secreopen);
481 av0 = *av;
482 /* if this is a login screen, assume -RR */
483 if (*av0 == '-')
485 rflag = 4;
486 #ifdef MULTI
487 xflag = 1;
488 #else
489 dflag = 1;
490 #endif
491 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
493 while (ac > 0)
495 ap = *++av;
496 if (--ac > 0 && *ap == '-')
498 if (ap[1] == '-' && ap[2] == 0)
500 av++;
501 ac--;
502 break;
504 if (ap[1] == '-' && !strcmp(ap, "--version"))
505 Panic(0, "Screen version %s", version);
506 if (ap[1] == '-' && !strcmp(ap, "--help"))
507 exit_with_usage(myname, NULL, NULL);
508 while (ap && *ap && *++ap)
510 switch (*ap)
512 case 'a':
513 nwin_options.aflag = 1;
514 break;
515 case 'A':
516 adaptflag = 1;
517 break;
518 case 'p': /* preselect */
519 if (*++ap)
520 preselect = ap;
521 else
523 if (!--ac)
524 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
525 preselect = *++av;
527 ap = NULL;
528 break;
529 #ifdef HAVE_BRAILLE
530 case 'B':
531 bd.bd_start_braille = 1;
532 break;
533 #endif
534 case 'c':
535 if (*++ap)
536 RcFileName = ap;
537 else
539 if (--ac == 0)
540 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
541 RcFileName = *++av;
543 ap = NULL;
544 break;
545 case 'e':
546 if (!*++ap)
548 if (--ac == 0)
549 exit_with_usage(myname, "Specify command characters with -e", NULL);
550 ap = *++av;
552 if (ParseEscape(ap))
553 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
554 ap = NULL;
555 break;
556 case 'f':
557 ap++;
558 switch (*ap++)
560 case 'n':
561 case '0':
562 nwin_options.flowflag = FLOW_NOW * 0;
563 break;
564 case '\0':
565 ap--;
566 /* FALLTHROUGH */
567 case 'y':
568 case '1':
569 nwin_options.flowflag = FLOW_NOW * 1;
570 break;
571 case 'a':
572 nwin_options.flowflag = FLOW_AUTOFLAG;
573 break;
574 default:
575 exit_with_usage(myname, "Unknown flow option -%s", --ap);
577 break;
578 case 'h':
579 if (--ac == 0)
580 exit_with_usage(myname, NULL, NULL);
581 nwin_options.histheight = atoi(*++av);
582 if (nwin_options.histheight < 0)
583 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
584 break;
585 case 'i':
586 iflag = 1;
587 break;
588 case 't': /* title, the former AkA == -k */
589 if (--ac == 0)
590 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
591 nwin_options.aka = *++av;
592 break;
593 case 'l':
594 ap++;
595 switch (*ap++)
597 case 'n':
598 case '0':
599 nwin_options.lflag = 0;
600 break;
601 case '\0':
602 ap--;
603 /* FALLTHROUGH */
604 case 'y':
605 case '1':
606 nwin_options.lflag = 1;
607 break;
608 case 'a':
609 nwin_options.lflag = 3;
610 break;
611 case 's': /* -ls */
612 case 'i': /* -list */
613 lsflag = 1;
614 if (ac > 1 && !SockMatch)
616 SockMatch = *++av;
617 ac--;
619 ap = NULL;
620 break;
621 default:
622 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
624 break;
625 case 'w':
626 lsflag = 1;
627 wipeflag = 1;
628 if (ac > 1 && !SockMatch)
630 SockMatch = *++av;
631 ac--;
633 break;
634 case 'L':
635 nwin_options.Lflag = 1;
636 break;
637 case 'm':
638 mflag = 1;
639 break;
640 case 'O': /* to be (or not to be?) deleted. jw. */
641 force_vt = 0;
642 break;
643 case 'T':
644 if (--ac == 0)
645 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
646 if (strlen(*++av) < 20)
647 strcpy(screenterm, *av);
648 else
649 Panic(0, "-T: terminal name too long. (max. 20 char)");
650 nwin_options.term = screenterm;
651 break;
652 case 'q':
653 quietflag = 1;
654 break;
655 case 'r':
656 case 'R':
657 #ifdef MULTI
658 case 'x':
659 #endif
660 if (ac > 1 && *av[1] != '-' && !SockMatch)
662 SockMatch = *++av;
663 ac--;
664 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
666 #ifdef MULTI
667 if (*ap == 'x')
668 xflag = 1;
669 #endif
670 if (rflag)
671 rflag = 2;
672 rflag += (*ap == 'R') ? 2 : 1;
673 break;
674 #ifdef REMOTE_DETACH
675 case 'd':
676 dflag = 1;
677 /* FALLTHROUGH */
678 case 'D':
679 if (!dflag)
680 dflag = 2;
681 if (ac == 2)
683 if (*av[1] != '-' && !SockMatch)
685 SockMatch = *++av;
686 ac--;
687 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
690 break;
691 #endif
692 case 's':
693 if (--ac == 0)
694 exit_with_usage(myname, "Specify shell with -s", NULL);
695 if (ShellProg)
696 free(ShellProg);
697 ShellProg = SaveStr(*++av);
698 debug1("ShellProg: '%s'\n", ShellProg);
699 break;
700 case 'S':
701 if (!SockMatch)
703 if (--ac == 0)
704 exit_with_usage(myname, "Specify session-name with -S", NULL);
705 SockMatch = *++av;
707 if (!*SockMatch)
708 exit_with_usage(myname, "Empty session-name?", NULL);
709 break;
710 case 'X':
711 cmdflag = 1;
712 break;
713 case 'v':
714 Panic(0, "Screen version %s", version);
715 /* NOTREACHED */
716 #ifdef UTF8
717 case 'U':
718 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
719 break;
720 #endif
721 case 'u':
722 if (--ac == 0)
723 exit_with_usage(myname, "Specify lua script file with -u", NULL);
724 if (script_file)
725 free(script_file);
726 script_file = SaveStr(*++av);
727 break;
728 default:
729 exit_with_usage(myname, "Unknown option %s", --ap);
733 else
734 break;
737 real_uid = getuid();
738 real_gid = getgid();
739 eff_uid = geteuid();
740 eff_gid = getegid();
741 if (eff_uid != real_uid)
743 /* if running with s-bit, we must install a special signal
744 * handler routine that resets the s-bit, so that we get a
745 * core file anyway.
747 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
748 signal(SIGBUS, CoreDump);
749 #endif /* SIGBUS */
750 signal(SIGSEGV, CoreDump);
753 #ifdef USE_LOCALE
754 setlocale(LC_ALL, "");
755 #endif
756 #ifdef ENCODINGS
757 if (nwin_options.encoding == -1)
759 /* ask locale if we should start in UTF-8 mode */
760 # ifdef HAVE_NL_LANGINFO
761 # ifndef USE_LOCALE
762 setlocale(LC_CTYPE, "");
763 # endif
764 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
765 debug1("locale says encoding = %d\n", nwin_options.encoding);
766 # else
767 # ifdef UTF8
768 char *s;
769 if (((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
770 (s = getenv("LANG"))) && InStr(s, "UTF-8"))
771 nwin_options.encoding = UTF8;
772 # endif
773 debug1("environment says encoding=%d\n", nwin_options.encoding);
774 #endif
776 #endif
777 if (SockMatch && strlen(SockMatch) >= MAXSTR)
778 Panic(0, "Ridiculously long socketname - try again.");
779 if (cmdflag && !rflag && !dflag && !xflag)
780 xflag = 1;
781 if (!cmdflag && dflag && mflag && !(rflag || xflag))
782 detached = 1;
783 nwin = nwin_options;
784 #ifdef ENCODINGS
785 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
786 #endif
787 if (ac)
788 nwin.args = av;
790 /* make the write() calls return -1 on all errors */
791 #ifdef SIGXFSZ
793 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
794 * It appears that in System V Release 4, UNIX, if you are writing
795 * an output file and you exceed the currently set file size limit,
796 * you _don't_ just get the call to `write' returning with a
797 * failure code. Rather, you get a signal called `SIGXFSZ' which,
798 * if neither handled nor ignored, will cause your program to crash
799 * with a core dump.
801 signal(SIGXFSZ, SIG_IGN);
802 #endif /* SIGXFSZ */
804 #ifdef SIGPIPE
805 signal(SIGPIPE, SIG_IGN);
806 #endif
808 if (!ShellProg)
810 register char *sh;
812 sh = getenv("SHELL");
813 ShellProg = SaveStr(sh ? sh : DefaultShell);
815 ShellArgs[0] = ShellProg;
816 home = getenv("HOME");
817 if (!mflag && !SockMatch)
819 sty = getenv("STY");
820 if (sty && *sty == 0)
821 sty = 0;
824 #ifdef NETHACK
825 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
827 char nethackrc[MAXPATHLEN];
829 if (home && (strlen(home) < (MAXPATHLEN - 20)))
831 sprintf(nethackrc,"%s/.nethackrc", home);
832 nethackflag = !access(nethackrc, F_OK);
835 #endif
837 #ifdef MULTIUSER
838 own_uid = multi_uid = real_uid;
839 if (SockMatch && (sockp = index(SockMatch, '/')))
841 if (eff_uid)
842 Panic(0, "Must run suid root for multiuser support.");
843 *sockp = 0;
844 multi = SockMatch;
845 SockMatch = sockp + 1;
846 if (*multi)
848 struct passwd *mppp;
849 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
850 Panic(0, "Cannot identify account '%s'.", multi);
851 multi_uid = mppp->pw_uid;
852 multi_home = SaveStr(mppp->pw_dir);
853 if (strlen(multi_home) > MAXPATHLEN - 10)
854 Panic(0, "home directory path too long");
855 # ifdef MULTI
856 /* always fake multi attach mode */
857 if (rflag || lsflag)
858 xflag = 1;
859 # endif /* MULTI */
860 detached = 0;
861 multiattach = 1;
864 if (SockMatch && *SockMatch == 0)
865 SockMatch = 0;
866 #endif /* MULTIUSER */
868 if ((LoginName = getlogin()) && LoginName[0] != '\0')
870 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
871 if ((int)ppp->pw_uid != real_uid)
872 ppp = (struct passwd *) 0;
874 if (ppp == 0)
876 if ((ppp = getpwuid(real_uid)) == 0)
878 Panic(0, "getpwuid() can't identify your account!");
879 exit(1);
881 LoginName = ppp->pw_name;
883 LoginName = SaveStr(LoginName);
885 ppp = getpwbyname(LoginName, ppp);
887 #if !defined(SOCKDIR) && defined(MULTIUSER)
888 if (multi && !multiattach)
890 if (home && strcmp(home, ppp->pw_dir))
891 Panic(0, "$HOME must match passwd entry for multiuser screens.");
893 #endif
895 if (home == 0 || *home == '\0')
896 home = ppp->pw_dir;
897 if (strlen(LoginName) > 20)
898 Panic(0, "LoginName too long - sorry.");
899 #ifdef MULTIUSER
900 if (multi && strlen(multi) > 20)
901 Panic(0, "Screen owner name too long - sorry.");
902 #endif
903 if (strlen(home) > MAXPATHLEN - 25)
904 Panic(0, "$HOME too long - sorry.");
906 attach_tty = "";
907 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty))
909 #ifndef NAMEDPIPE
910 int fl;
911 #endif
913 /* ttyname implies isatty */
914 if (!(attach_tty = ttyname(0)))
915 Panic(0, "Must be connected to a terminal.");
916 if (strlen(attach_tty) >= MAXPATHLEN)
917 Panic(0, "TtyName too long - sorry.");
918 if (stat(attach_tty, &st))
919 Panic(errno, "Cannot access '%s'", attach_tty);
920 #ifdef MULTIUSER
921 tty_mode = (int)st.st_mode & 0777;
922 #endif
924 #ifndef NAMEDPIPE
925 fl = fcntl(0, F_GETFL, 0);
926 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
927 attach_fd = 0;
928 #endif
929 if (attach_fd == -1)
931 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
932 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
933 close(n);
935 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
937 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
938 Panic(0, "Please set a terminal type.");
939 if (strlen(attach_term) > sizeof(D_termname) - 1)
940 Panic(0, "$TERM too long - sorry.");
941 GetTTY(0, &attach_Mode);
942 #ifdef DEBUGGGGGGGGGGGGGGG
943 DebugTTY(&attach_Mode);
944 #endif /* DEBUG */
947 #ifdef _MODE_T
948 oumask = umask(0); /* well, unsigned never fails? jw. */
949 #else
950 if ((oumask = (int)umask(0)) == -1)
951 Panic(errno, "Cannot change umask to zero");
952 #endif
953 SockDir = getenv("SCREENDIR");
954 if (SockDir)
956 if (strlen(SockDir) >= MAXPATHLEN - 1)
957 Panic(0, "Ridiculously long $SCREENDIR - try again.");
958 #ifdef MULTIUSER
959 if (multi)
960 Panic(0, "No $SCREENDIR with multi screens, please.");
961 #endif
963 #ifdef MULTIUSER
964 if (multiattach)
966 # ifndef SOCKDIR
967 sprintf(SockPath, "%s/.screen", multi_home);
968 SockDir = SockPath;
969 # else
970 SockDir = SOCKDIR;
971 sprintf(SockPath, "%s/S-%s", SockDir, multi);
972 # endif
974 else
975 #endif
977 #ifndef SOCKDIR
978 if (SockDir == 0)
980 sprintf(SockPath, "%s/.screen", home);
981 SockDir = SockPath;
983 #endif
984 if (SockDir)
986 if (access(SockDir, F_OK))
988 debug1("SockDir '%s' missing ...\n", SockDir);
989 if (UserContext() > 0)
991 if (mkdir(SockDir, 0700))
992 UserReturn(0);
993 UserReturn(1);
995 if (UserStatus() <= 0)
996 Panic(0, "Cannot make directory '%s'.", SockDir);
998 if (SockDir != SockPath)
999 strcpy(SockPath, SockDir);
1001 #ifdef SOCKDIR
1002 else
1004 SockDir = SOCKDIR;
1005 if (lstat(SockDir, &st))
1007 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1008 (eff_gid != real_gid) ? 0775 :
1009 #ifdef S_ISVTX
1010 0777|S_ISVTX;
1011 #else
1012 0777;
1013 #endif
1014 if (mkdir(SockDir, n) == -1)
1015 Panic(errno, "Cannot make directory '%s'", SockDir);
1017 else
1019 if (!S_ISDIR(st.st_mode))
1020 Panic(0, "'%s' must be a directory.", SockDir);
1021 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1022 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1023 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1024 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1025 0777;
1026 if (((int)st.st_mode & 0777) != n)
1027 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1029 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1030 if (access(SockPath, F_OK))
1032 if (mkdir(SockPath, 0700) == -1)
1033 Panic(errno, "Cannot make directory '%s'", SockPath);
1034 (void) chown(SockPath, real_uid, real_gid);
1037 #endif
1040 if (stat(SockPath, &st) == -1)
1041 Panic(errno, "Cannot access %s", SockPath);
1042 else
1043 if (!S_ISDIR(st.st_mode))
1044 Panic(0, "%s is not a directory.", SockPath);
1045 #ifdef MULTIUSER
1046 if (multi)
1048 if ((int)st.st_uid != multi_uid)
1049 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1051 else
1052 #endif
1054 if ((int)st.st_uid != real_uid)
1055 Panic(0, "You are not the owner of %s.", SockPath);
1057 if ((st.st_mode & 0777) != 0700)
1058 Panic(0, "Directory %s must have mode 700.", SockPath);
1059 if (SockMatch && index(SockMatch, '/'))
1060 Panic(0, "Bad session name '%s'", SockMatch);
1061 SockName = SockPath + strlen(SockPath) + 1;
1062 *SockName = 0;
1063 (void) umask(oumask);
1064 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1066 #if defined(SYSV) && !defined(ISC)
1067 if (uname(&utsnam) == -1)
1068 Panic(errno, "uname");
1069 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1070 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1071 #else
1072 (void) gethostname(HostName, MAXSTR);
1073 HostName[MAXSTR - 1] = '\0';
1074 #endif
1075 if ((ap = index(HostName, '.')) != NULL)
1076 *ap = '\0';
1078 if (lsflag)
1080 int i, fo, oth;
1082 #ifdef MULTIUSER
1083 if (multi)
1084 real_uid = multi_uid;
1085 #endif
1086 setgid(real_gid);
1087 setuid(real_uid);
1088 eff_uid = real_uid;
1089 eff_gid = real_gid;
1090 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1091 if (quietflag)
1092 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1093 if (fo == 0)
1094 Panic(0, "No Sockets found in %s.\n", SockPath);
1095 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1096 /* NOTREACHED */
1098 signal(SIG_BYE, AttacherFinit); /* prevent races */
1099 if (cmdflag)
1101 /* attach_tty is not mandatory */
1102 if ((attach_tty = ttyname(0)) == 0)
1103 attach_tty = "";
1104 if (strlen(attach_tty) >= MAXPATHLEN)
1105 Panic(0, "TtyName too long - sorry.");
1106 if (!*av)
1107 Panic(0, "Please specify a command.");
1108 setgid(real_gid);
1109 setuid(real_uid);
1110 eff_uid = real_uid;
1111 eff_gid = real_gid;
1112 SendCmdMessage(sty, SockMatch, av);
1113 exit(0);
1115 else if (rflag || xflag)
1117 debug("screen -r: - is there anybody out there?\n");
1118 if (Attach(MSG_ATTACH))
1120 Attacher();
1121 /* NOTREACHED */
1123 #ifdef MULTIUSER
1124 if (multiattach)
1125 Panic(0, "Can't create sessions of other users.");
1126 #endif
1127 debug("screen -r: backend not responding -- still crying\n");
1129 else if (dflag && !mflag)
1131 (void) Attach(MSG_DETACH);
1132 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1133 eexit(0);
1134 /* NOTREACHED */
1136 if (!SockMatch && !mflag && sty)
1138 setgid(real_gid);
1139 setuid(real_uid);
1140 eff_uid = real_uid;
1141 eff_gid = real_gid;
1142 nwin_options.args = av;
1143 SendCreateMsg(sty, &nwin);
1144 exit(0);
1145 /* NOTREACHED */
1147 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1149 if (!detached || dflag != 2)
1150 MasterPid = fork();
1151 else
1152 MasterPid = 0;
1154 switch (MasterPid)
1156 case -1:
1157 Panic(errno, "fork");
1158 /* NOTREACHED */
1159 case 0:
1160 break;
1161 default:
1162 if (detached)
1163 exit(0);
1164 if (SockMatch)
1165 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1166 else
1167 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1168 for (ap = socknamebuf; *ap; ap++)
1169 if (*ap == '/')
1170 *ap = '-';
1171 #ifdef NAME_MAX
1172 if (strlen(socknamebuf) > NAME_MAX)
1173 socknamebuf[NAME_MAX] = 0;
1174 #endif
1175 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1176 setgid(real_gid);
1177 setuid(real_uid);
1178 eff_uid = real_uid;
1179 eff_gid = real_gid;
1180 Attacher();
1181 /* NOTREACHED */
1184 if (!detached)
1185 PanicPid = getppid();
1187 if (DefaultEsc == -1)
1188 DefaultEsc = Ctrl('a');
1189 if (DefaultMetaEsc == -1)
1190 DefaultMetaEsc = 'a';
1192 ap = av0 + strlen(av0) - 1;
1193 while (ap >= av0)
1195 if (!strncmp("screen", ap, 6))
1197 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1198 break;
1200 ap--;
1202 if (ap < av0)
1203 *av0 = 'S';
1205 #ifdef DEBUG
1207 char buf[256];
1209 if (dfp && dfp != stderr)
1210 fclose(dfp);
1211 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1212 if ((dfp = fopen(buf, "w")) == NULL)
1213 dfp = stderr;
1214 else
1215 (void) chmod(buf, 0666);
1217 #endif
1218 if (!detached)
1220 if (attach_fd == -1)
1222 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1223 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1225 else
1226 n = dup(attach_fd);
1228 else
1229 n = -1;
1230 freopen("/dev/null", "r", stdin);
1231 freopen("/dev/null", "w", stdout);
1233 #ifdef DEBUG
1234 if (dfp != stderr)
1235 #endif
1236 freopen("/dev/null", "w", stderr);
1237 debug("-- screen.back debug started\n");
1240 * This guarantees that the session owner is listed, even when we
1241 * start detached. From now on we should not refer to 'LoginName'
1242 * any more, use users->u_name instead.
1244 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1245 Panic(0, "Could not create user info");
1246 if (!detached)
1248 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1249 Panic(0, "Could not alloc display");
1250 PanicPid = 0;
1251 #ifdef ENCODINGS
1252 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1253 debug1("D_encoding = %d\n", D_encoding);
1254 #endif
1257 if (SockMatch)
1259 /* user started us with -S option */
1260 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1262 else
1264 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1265 HostName);
1267 for (ap = socknamebuf; *ap; ap++)
1268 if (*ap == '/')
1269 *ap = '-';
1270 #ifdef NAME_MAX
1271 if (strlen(socknamebuf) > NAME_MAX)
1273 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1274 socknamebuf[NAME_MAX] = 0;
1276 #endif
1277 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1279 ServerSocket = MakeServerSocket();
1280 InitKeytab();
1282 LoadScripts();
1283 ScriptInit();
1284 if (script_file)
1286 ScriptSource(script_file);
1287 free(script_file);
1288 script_file = 0;
1291 #ifdef ETCSCREENRC
1292 # ifdef ALLOW_SYSSCREENRC
1293 if ((ap = getenv("SYSSCREENRC")))
1294 (void)StartRc(ap, 0);
1295 else
1296 # endif
1297 (void)StartRc(ETCSCREENRC, 0);
1298 #endif
1299 (void)StartRc(RcFileName, 0);
1300 # ifdef UTMPOK
1301 # ifndef UTNOKEEP
1302 InitUtmp();
1303 # endif /* UTNOKEEP */
1304 # endif /* UTMPOK */
1305 if (display)
1307 if (InitTermcap(0, 0))
1309 debug("Could not init termcap - exiting\n");
1310 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1311 freetty();
1312 if (D_userpid)
1313 Kill(D_userpid, SIG_BYE);
1314 eexit(1);
1316 MakeDefaultCanvas();
1317 InitTerm(0);
1318 #ifdef UTMPOK
1319 RemoveLoginSlot();
1320 #endif
1322 else
1323 MakeTermcap(1);
1324 #ifdef LOADAV
1325 InitLoadav();
1326 #endif /* LOADAV */
1327 MakeNewEnv();
1328 signal(SIGHUP, SigHup);
1329 signal(SIGINT, FinitHandler);
1330 signal(SIGQUIT, FinitHandler);
1331 signal(SIGTERM, FinitHandler);
1332 #ifdef BSDJOBS
1333 signal(SIGTTIN, SIG_IGN);
1334 signal(SIGTTOU, SIG_IGN);
1335 #endif
1337 if (display)
1339 brktty(D_userfd);
1340 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1341 /* Note: SetMode must be called _before_ FinishRc. */
1342 SetTTY(D_userfd, &D_NewMode);
1343 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1344 Msg(errno, "Warning: NBLOCK fcntl failed");
1346 else
1347 brktty(-1); /* just try */
1348 signal(SIGCHLD, SigChld);
1349 #ifdef ETCSCREENRC
1350 # ifdef ALLOW_SYSSCREENRC
1351 if ((ap = getenv("SYSSCREENRC")))
1352 FinishRc(ap);
1353 else
1354 # endif
1355 FinishRc(ETCSCREENRC);
1356 #endif
1357 FinishRc(RcFileName);
1359 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1360 if (windows == NULL)
1362 debug("We open one default window, as screenrc did not specify one.\n");
1363 if (MakeWindow(&nwin) == -1)
1365 Msg(0, "Sorry, could not find a PTY.");
1366 sleep(5);
1367 Finit(0);
1368 /* NOTREACHED */
1372 #ifdef HAVE_BRAILLE
1373 StartBraille();
1374 #endif
1376 if (display && default_startup)
1377 display_copyright();
1378 signal(SIGINT, SigInt);
1379 if (rflag && (rflag & 1) == 0 && !quietflag)
1381 Msg(0, "New screen...");
1382 rflag = 0;
1385 serv_read.type = EV_READ;
1386 serv_read.fd = ServerSocket;
1387 serv_read.handler = serv_read_fn;
1388 evenq(&serv_read);
1390 serv_select.pri = -10;
1391 serv_select.type = EV_ALWAYS;
1392 serv_select.handler = serv_select_fn;
1393 evenq(&serv_select);
1395 logflushev.type = EV_TIMEOUT;
1396 logflushev.handler = logflush_fn;
1398 sched();
1399 /* NOTREACHED */
1400 return 0;
1403 void
1404 WindowDied(p, wstat, wstat_valid)
1405 struct win *p;
1406 int wstat;
1407 int wstat_valid;
1409 int killit = 0;
1411 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1412 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1413 killit = 1;
1415 if (ZombieKey_destroy && !killit)
1417 char buf[100], *s, reason[100];
1418 time_t now;
1420 if (wstat_valid) {
1421 if (WIFEXITED(wstat))
1422 if (WEXITSTATUS(wstat))
1423 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1424 else
1425 sprintf(reason, "terminated normally");
1426 else if (WIFSIGNALED(wstat))
1427 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1428 #ifdef WCOREDUMP
1429 WCOREDUMP(wstat) ? " (core file generated)" : "");
1430 #else
1431 "");
1432 #endif
1433 } else
1434 sprintf(reason, "detached from window");
1436 (void) time(&now);
1437 s = ctime(&now);
1438 if (s && *s)
1439 s[strlen(s) - 1] = '\0';
1440 debug3("window %d (%s) going into zombie state fd %d",
1441 p->w_number, p->w_title, p->w_ptyfd);
1442 #ifdef UTMPOK
1443 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1445 RemoveUtmp(p);
1446 p->w_slot = 0; /* "detached" */
1448 #endif
1449 CloseDevice(p);
1451 p->w_deadpid = p->w_pid;
1452 p->w_pid = 0;
1453 ResetWindow(p);
1454 /* p->w_y = p->w_bot; */
1455 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1456 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1457 WriteString(p, buf, strlen(buf));
1458 WindowChanged(p, 'f');
1460 else
1461 KillWindow(p);
1462 #ifdef UTMPOK
1463 CarefulUtmp();
1464 #endif
1467 static void
1468 SigChldHandler()
1470 struct stat st;
1471 #ifdef DEBUG
1472 fds();
1473 #endif
1474 while (GotSigChld)
1476 GotSigChld = 0;
1477 DoWait();
1478 #ifdef SYSVSIGS
1479 signal(SIGCHLD, SigChld);
1480 #endif
1482 if (stat(SockPath, &st) == -1)
1484 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1485 if (!RecoverSocket())
1487 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1488 Finit(1);
1490 else
1491 debug1("'%s' reconstructed\n", SockPath);
1493 else
1494 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1497 static sigret_t
1498 SigChld SIGDEFARG
1500 debug("SigChld()\n");
1501 GotSigChld = 1;
1502 SIGRETURN;
1505 sigret_t
1506 SigHup SIGDEFARG
1508 /* Hangup all displays */
1509 while ((display = displays) != 0)
1510 Hangup();
1511 SIGRETURN;
1515 * the backend's Interrupt handler
1516 * we cannot insert the intrc directly, as we never know
1517 * if fore is valid.
1519 static sigret_t
1520 SigInt SIGDEFARG
1522 #if HAZARDOUS
1523 char ibuf;
1525 debug("SigInt()\n");
1526 if (fore && displays)
1528 # if defined(TERMIO) || defined(POSIX)
1529 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1530 # else
1531 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1532 # endif
1533 fore->w_inlen = 0;
1534 write(fore->w_ptyfd, &ibuf, 1);
1536 #else
1537 signal(SIGINT, SigInt);
1538 debug("SigInt() careful\n");
1539 InterruptPlease = 1;
1540 #endif
1541 SIGRETURN;
1544 static sigret_t
1545 CoreDump SIGDEFARG
1547 struct display *disp;
1548 char buf[80];
1550 #if defined(SYSVSIGS) && defined(SIGHASARG)
1551 signal(sigsig, SIG_IGN);
1552 #endif
1553 setgid(getgid());
1554 setuid(getuid());
1555 unlink("core");
1556 #ifdef SIGHASARG
1557 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1558 #else
1559 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1560 #endif
1561 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1563 #else /* SHADOWPW && !DEBUG */
1564 " (core dumped)"
1565 #endif /* SHADOWPW && !DEBUG */
1567 for (disp = displays; disp; disp = disp->d_next)
1569 fcntl(disp->d_userfd, F_SETFL, 0);
1570 SetTTY(disp->d_userfd, &D_OldMode);
1571 write(disp->d_userfd, buf, strlen(buf));
1572 Kill(disp->d_userpid, SIG_BYE);
1574 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1575 Kill(getpid(), SIGKILL);
1576 eexit(11);
1577 #else /* SHADOWPW && !DEBUG */
1578 abort();
1579 #endif /* SHADOWPW && !DEBUG */
1580 SIGRETURN;
1583 static void
1584 DoWait()
1586 register int pid;
1587 struct win *p, *next;
1588 #ifdef BSDWAIT
1589 union wait wstat;
1590 #else
1591 int wstat;
1592 #endif
1594 #ifdef BSDJOBS
1595 # ifndef BSDWAIT
1596 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1597 # else
1598 # ifdef USE_WAIT2
1600 * From: rouilj@sni-usa.com (John Rouillard)
1601 * note that WUNTRACED is not documented to work, but it is defined in
1602 * /usr/include/sys/wait.h, so it may work
1604 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1605 # else /* USE_WAIT2 */
1606 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1607 # endif /* USE_WAIT2 */
1608 # endif
1609 #else /* BSDJOBS */
1610 while ((pid = wait(&wstat)) < 0)
1611 if (errno != EINTR)
1612 break;
1613 if (pid > 0)
1614 #endif /* BSDJOBS */
1616 for (p = windows; p; p = next)
1618 next = p->w_next;
1619 if ( (p->w_pid && pid == p->w_pid) ||
1620 (p->w_deadpid && pid == p->w_deadpid) )
1622 /* child has ceased to exist */
1623 p->w_pid = 0;
1625 #ifdef BSDJOBS
1626 if (WIFSTOPPED(wstat))
1628 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1629 #ifdef SIGTTIN
1630 if (WSTOPSIG(wstat) == SIGTTIN)
1632 Msg(0, "Suspended (tty input)");
1633 continue;
1635 #endif
1636 #ifdef SIGTTOU
1637 if (WSTOPSIG(wstat) == SIGTTOU)
1639 Msg(0, "Suspended (tty output)");
1640 continue;
1642 #endif
1643 /* Try to restart process */
1644 Msg(0, "Child has been stopped, restarting.");
1645 if (killpg(pid, SIGCONT))
1646 kill(pid, SIGCONT);
1648 else
1649 #endif
1651 WindowDied(p, wstat, 1);
1653 break;
1655 #ifdef PSEUDOS
1656 if (p->w_pwin && pid == p->w_pwin->p_pid)
1658 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1659 FreePseudowin(p);
1660 break;
1662 #endif
1664 if (p == 0)
1666 debug1("pid %d not found - hope that's ok\n", pid);
1672 static sigret_t
1673 FinitHandler SIGDEFARG
1675 #ifdef SIGHASARG
1676 debug1("FinitHandler called, sig %d.\n", sigsig);
1677 #else
1678 debug("FinitHandler called.\n");
1679 #endif
1680 Finit(1);
1681 SIGRETURN;
1684 void
1685 Finit(i)
1686 int i;
1688 signal(SIGCHLD, SIG_DFL);
1689 signal(SIGHUP, SIG_IGN);
1690 debug1("Finit(%d);\n", i);
1692 ScriptFinit();
1694 while (windows)
1696 struct win *p = windows;
1697 windows = windows->w_next;
1698 FreeWindow(p);
1700 if (ServerSocket != -1)
1702 debug1("we unlink(%s)\n", SockPath);
1703 #ifdef USE_SETEUID
1704 xseteuid(real_uid);
1705 xsetegid(real_gid);
1706 #endif
1707 (void) unlink(SockPath);
1708 #ifdef USE_SETEUID
1709 xseteuid(eff_uid);
1710 xsetegid(eff_gid);
1711 #endif
1713 for (display = displays; display; display = display->d_next)
1715 if (D_status)
1716 RemoveStatus();
1717 FinitTerm();
1718 #ifdef UTMPOK
1719 RestoreLoginSlot();
1720 #endif
1721 AddStr("[screen is terminating]\r\n");
1722 Flush();
1723 SetTTY(D_userfd, &D_OldMode);
1724 fcntl(D_userfd, F_SETFL, 0);
1725 freetty();
1726 Kill(D_userpid, SIG_BYE);
1729 * we _cannot_ call eexit(i) here,
1730 * instead of playing with the Socket above. Sigh.
1732 exit(i);
1735 void
1736 eexit(e)
1737 int e;
1739 debug("eexit\n");
1740 if (ServerSocket != -1)
1742 debug1("we unlink(%s)\n", SockPath);
1743 setgid(real_gid);
1744 setuid(real_uid);
1745 (void) unlink(SockPath);
1747 exit(e);
1750 void
1751 Hangup()
1753 if (display == 0)
1754 return;
1755 debug1("Hangup %x\n", display);
1756 if (D_userfd >= 0)
1758 close(D_userfd);
1759 D_userfd = -1;
1761 if (auto_detach || displays->d_next)
1762 Detach(D_HANGUP);
1763 else
1764 Finit(0);
1768 * Detach now has the following modes:
1769 *D_DETACH SIG_BYE detach backend and exit attacher
1770 *D_HANGUP SIG_BYE detach backend and exit attacher
1771 *D_STOP SIG_STOP stop attacher (and detach backend)
1772 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1773 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1774 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1775 *D_LOCK SIG_LOCK lock the attacher
1776 * (jw)
1777 * we always remove our utmp slots. (even when "lock" or "stop")
1778 * Note: Take extra care here, we may be called by interrupt!
1780 void
1781 Detach(mode)
1782 int mode;
1784 int sign = 0, pid;
1785 struct canvas *cv;
1786 struct win *p;
1788 if (display == 0)
1789 return;
1791 signal(SIGHUP, SIG_IGN);
1792 debug1("Detach(%d)\n", mode);
1793 if (D_status)
1794 RemoveStatus();
1795 FinitTerm();
1796 if (!display)
1797 return;
1798 switch (mode)
1800 case D_HANGUP:
1801 sign = SIG_BYE;
1802 break;
1803 case D_DETACH:
1804 AddStr("[detached]\r\n");
1805 sign = SIG_BYE;
1806 break;
1807 #ifdef BSDJOBS
1808 case D_STOP:
1809 sign = SIG_STOP;
1810 break;
1811 #endif
1812 #ifdef REMOTE_DETACH
1813 case D_REMOTE:
1814 AddStr("[remote detached]\r\n");
1815 sign = SIG_BYE;
1816 break;
1817 #endif
1818 #ifdef POW_DETACH
1819 case D_POWER:
1820 AddStr("[power detached]\r\n");
1821 if (PowDetachString)
1823 AddStr(PowDetachString);
1824 AddStr("\r\n");
1826 sign = SIG_POWER_BYE;
1827 break;
1828 #ifdef REMOTE_DETACH
1829 case D_REMOTE_POWER:
1830 AddStr("[remote power detached]\r\n");
1831 if (PowDetachString)
1833 AddStr(PowDetachString);
1834 AddStr("\r\n");
1836 sign = SIG_POWER_BYE;
1837 break;
1838 #endif
1839 #endif
1840 case D_LOCK:
1841 ClearAll();
1842 sign = SIG_LOCK;
1843 /* tell attacher to lock terminal with a lockprg. */
1844 break;
1846 #ifdef UTMPOK
1847 if (displays->d_next == 0)
1849 for (p = windows; p; p = p->w_next)
1851 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1853 RemoveUtmp(p);
1855 * Set the slot to 0 to get the window
1856 * logged in again.
1858 p->w_slot = (slot_t) 0;
1862 if (mode != D_HANGUP)
1863 RestoreLoginSlot();
1864 #endif
1865 if (displays->d_next == 0 && console_window)
1867 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1869 debug("could not release console - killing window\n");
1870 KillWindow(console_window);
1871 display = displays; /* restore display */
1874 if (D_fore)
1876 #ifdef MULTIUSER
1877 ReleaseAutoWritelock(display, D_fore);
1878 #endif
1879 D_user->u_detachwin = D_fore->w_number;
1880 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1882 AutosaveLayout(D_layout);
1883 layout_last = D_layout;
1884 for (cv = D_cvlist; cv; cv = cv->c_next)
1886 p = Layer2Window(cv->c_layer);
1887 SetCanvasWindow(cv, 0);
1888 if (p)
1889 WindowChanged(p, 'u');
1892 pid = D_userpid;
1893 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1894 FreeDisplay();
1895 if (displays == 0)
1896 /* Flag detached-ness */
1897 (void) chsock();
1899 * tell father what to do. We do that after we
1900 * freed the tty, thus getty feels more comfortable on hpux
1901 * if it was a power detach.
1903 Kill(pid, sign);
1904 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1905 debug("Detach returns, we are successfully detached.\n");
1906 signal(SIGHUP, SigHup);
1909 static int
1910 IsSymbol(e, s)
1911 char *e, *s;
1913 register int l;
1915 l = strlen(s);
1916 return strncmp(e, s, l) == 0 && e[l] == '=';
1919 void
1920 MakeNewEnv()
1922 register char **op, **np;
1923 static char stybuf[MAXSTR];
1925 for (op = environ; *op; ++op)
1927 if (NewEnv)
1928 free((char *)NewEnv);
1929 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1930 if (!NewEnv)
1931 Panic(0, strnomem);
1932 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1933 *np++ = stybuf; /* NewEnv[0] */
1934 *np++ = Term; /* NewEnv[1] */
1935 np++; /* room for SHELL */
1936 #ifdef TIOCSWINSZ
1937 np += 2; /* room for TERMCAP and WINDOW */
1938 #else
1939 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
1940 #endif
1942 for (op = environ; *op; ++op)
1944 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
1945 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
1946 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
1947 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
1949 *np++ = *op;
1951 *np = 0;
1954 void
1955 /*VARARGS2*/
1956 #if defined(USEVARARGS) && defined(__STDC__)
1957 Msg(int err, char *fmt, VA_DOTS)
1958 #else
1959 Msg(err, fmt, VA_DOTS)
1960 int err;
1961 char *fmt;
1962 VA_DECL
1963 #endif
1965 VA_LIST(ap)
1966 char buf[MAXPATHLEN*2];
1967 char *p = buf;
1969 VA_START(ap, fmt);
1970 fmt = DoNLS(fmt);
1971 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1972 VA_END(ap);
1973 if (err)
1975 p += strlen(p);
1976 *p++ = ':';
1977 *p++ = ' ';
1978 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
1979 buf[sizeof(buf) - 1] = 0;
1981 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
1983 if (display && displays)
1984 MakeStatus(buf);
1985 else if (displays)
1987 for (display = displays; display; display = display->d_next)
1988 MakeStatus(buf);
1990 else if (display)
1992 /* no displays but a display - must have forked.
1993 * send message to backend!
1995 char *tty = D_usertty;
1996 struct display *olddisplay = display;
1997 display = 0; /* only send once */
1998 SendErrorMsg(tty, buf);
1999 display = olddisplay;
2001 else
2002 printf("%s\r\n", buf);
2006 * Call FinitTerm for all displays, write a message to each and call eexit();
2008 void
2009 /*VARARGS2*/
2010 #if defined(USEVARARGS) && defined(__STDC__)
2011 Panic(int err, char *fmt, VA_DOTS)
2012 #else
2013 Panic(err, fmt, VA_DOTS)
2014 int err;
2015 char *fmt;
2016 VA_DECL
2017 #endif
2019 VA_LIST(ap)
2020 char buf[MAXPATHLEN*2];
2021 char *p = buf;
2023 VA_START(ap, fmt);
2024 fmt = DoNLS(fmt);
2025 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2026 VA_END(ap);
2027 if (err)
2029 p += strlen(p);
2030 *p++ = ':';
2031 *p++ = ' ';
2032 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2033 buf[sizeof(buf) - 1] = 0;
2035 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2036 if (displays == 0 && display == 0)
2038 printf("%s\r\n", buf);
2039 if (PanicPid)
2040 Kill(PanicPid, SIG_BYE);
2042 else if (displays == 0)
2044 /* no displays but a display - must have forked.
2045 * send message to backend!
2047 char *tty = D_usertty;
2048 display = 0;
2049 SendErrorMsg(tty, buf);
2050 sleep(2);
2051 _exit(1);
2053 else
2054 for (display = displays; display; display = display->d_next)
2056 if (D_status)
2057 RemoveStatus();
2058 FinitTerm();
2059 Flush();
2060 #ifdef UTMPOK
2061 RestoreLoginSlot();
2062 #endif
2063 SetTTY(D_userfd, &D_OldMode);
2064 fcntl(D_userfd, F_SETFL, 0);
2065 write(D_userfd, buf, strlen(buf));
2066 write(D_userfd, "\n", 1);
2067 freetty();
2068 if (D_userpid)
2069 Kill(D_userpid, SIG_BYE);
2071 #ifdef MULTIUSER
2072 if (tty_oldmode >= 0)
2074 # ifdef USE_SETEUID
2075 if (setuid(own_uid))
2076 xseteuid(own_uid); /* may be a loop. sigh. */
2077 # else
2078 setuid(own_uid);
2079 # endif
2080 debug1("Panic: changing back modes from %s\n", attach_tty);
2081 chmod(attach_tty, tty_oldmode);
2083 #endif
2084 eexit(1);
2089 * '^' is allowed as an escape mechanism for control characters. jw.
2091 * Added time insertion using ideas/code from /\ndy Jones
2092 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2096 #ifndef USE_LOCALE
2097 static const char days[] = "SunMonTueWedThuFriSat";
2098 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2099 #endif
2101 static char winmsg_buf[MAXSTR];
2102 #define MAX_WINMSG_REND 16 /* rendition changes */
2103 static int winmsg_rend[MAX_WINMSG_REND];
2104 static int winmsg_rendpos[MAX_WINMSG_REND];
2105 static int winmsg_numrend;
2107 static char *
2108 pad_expand(buf, p, numpad, padlen)
2109 char *buf;
2110 char *p;
2111 int numpad;
2112 int padlen;
2114 char *pn, *pn2;
2115 int i, r;
2117 padlen = padlen - (p - buf); /* space for rent */
2118 if (padlen < 0)
2119 padlen = 0;
2120 pn2 = pn = p + padlen;
2121 r = winmsg_numrend;
2122 while (p >= buf)
2124 if (r && p - buf == winmsg_rendpos[r - 1])
2126 winmsg_rendpos[--r] = pn - buf;
2127 continue;
2129 *pn-- = *p;
2130 if (*p-- == 127)
2132 pn[1] = ' ';
2133 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2134 padlen -= i;
2135 while (i-- > 0)
2136 *pn-- = ' ';
2137 numpad--;
2140 return pn2;
2143 struct backtick {
2144 struct backtick *next;
2145 int num;
2146 int tick;
2147 int lifespan;
2148 time_t bestbefore;
2149 char result[MAXSTR];
2150 char **cmdv;
2151 struct event ev;
2152 char *buf;
2153 int bufi;
2156 struct backtick *backticks;
2158 static void
2159 backtick_filter(bt)
2160 struct backtick *bt;
2162 char *p, *q;
2163 int c;
2165 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2167 if (c == '\t')
2168 c = ' ';
2169 if (c >= ' ' || c == '\005')
2170 *q++ = c;
2172 *q = 0;
2175 static void
2176 backtick_fn(ev, data)
2177 struct event *ev;
2178 char *data;
2180 struct backtick *bt;
2181 int i, j, k, l;
2183 bt = (struct backtick *)data;
2184 debug1("backtick_fn for #%d\n", bt->num);
2185 i = bt->bufi;
2186 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2187 if (l <= 0)
2189 debug1("EOF on backtick #%d\n", bt->num);
2190 evdeq(ev);
2191 close(ev->fd);
2192 ev->fd = -1;
2193 return;
2195 debug1("read %d bytes\n", l);
2196 i += l;
2197 for (j = 0; j < l; j++)
2198 if (bt->buf[i - j - 1] == '\n')
2199 break;
2200 if (j < l)
2202 for (k = i - j - 2; k >= 0; k--)
2203 if (bt->buf[k] == '\n')
2204 break;
2205 k++;
2206 bcopy(bt->buf + k, bt->result, i - j - k);
2207 bt->result[i - j - k - 1] = 0;
2208 backtick_filter(bt);
2209 WindowChanged(0, '`');
2211 if (j == l && i == MAXSTR)
2213 j = MAXSTR/2;
2214 l = j + 1;
2216 if (j < l)
2218 if (j)
2219 bcopy(bt->buf + i - j, bt->buf, j);
2220 i = j;
2222 bt->bufi = i;
2225 void
2226 setbacktick(num, lifespan, tick, cmdv)
2227 int num;
2228 int lifespan;
2229 int tick;
2230 char **cmdv;
2232 struct backtick **btp, *bt;
2233 char **v;
2235 debug1("setbacktick called for backtick #%d\n", num);
2236 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2237 if (bt->num == num)
2238 break;
2239 if (!bt && !cmdv)
2240 return;
2241 if (bt)
2243 for (v = bt->cmdv; *v; v++)
2244 free(*v);
2245 free(bt->cmdv);
2246 if (bt->buf)
2247 free(bt->buf);
2248 if (bt->ev.fd >= 0)
2249 close(bt->ev.fd);
2250 evdeq(&bt->ev);
2252 if (bt && !cmdv)
2254 *btp = bt->next;
2255 free(bt);
2256 return;
2258 if (!bt)
2260 bt = (struct backtick *)malloc(sizeof *bt);
2261 if (!bt)
2263 Msg(0, strnomem);
2264 return;
2266 bzero(bt, sizeof(*bt));
2267 bt->next = 0;
2268 *btp = bt;
2270 bt->num = num;
2271 bt->tick = tick;
2272 bt->lifespan = lifespan;
2273 bt->bestbefore = 0;
2274 bt->result[0] = 0;
2275 bt->buf = 0;
2276 bt->bufi = 0;
2277 bt->cmdv = cmdv;
2278 bt->ev.fd = -1;
2279 if (bt->tick == 0 && bt->lifespan == 0)
2281 debug("setbacktick: continuous mode\n");
2282 bt->buf = (char *)malloc(MAXSTR);
2283 if (bt->buf == 0)
2285 Msg(0, strnomem);
2286 setbacktick(num, 0, 0, (char **)0);
2287 return;
2289 bt->ev.type = EV_READ;
2290 bt->ev.fd = readpipe(bt->cmdv);
2291 bt->ev.handler = backtick_fn;
2292 bt->ev.data = (char *)bt;
2293 if (bt->ev.fd >= 0)
2294 evenq(&bt->ev);
2298 static char *
2299 runbacktick(bt, tickp, now)
2300 struct backtick *bt;
2301 int *tickp;
2302 time_t now;
2304 int f, i, l, j;
2305 time_t now2;
2307 debug1("runbacktick called for backtick #%d\n", bt->num);
2308 if (bt->tick && (!*tickp || bt->tick < *tickp))
2309 *tickp = bt->tick;
2310 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2312 debug1("returning old result (%d)\n", bt->lifespan);
2313 return bt->result;
2315 f = readpipe(bt->cmdv);
2316 if (f == -1)
2317 return bt->result;
2318 i = 0;
2319 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2321 debug1("runbacktick: read %d bytes\n", l);
2322 i += l;
2323 for (j = 1; j < l; j++)
2324 if (bt->result[i - j - 1] == '\n')
2325 break;
2326 if (j == l && i == sizeof(bt->result))
2328 j = sizeof(bt->result) / 2;
2329 l = j + 1;
2331 if (j < l)
2333 bcopy(bt->result + i - j, bt->result, j);
2334 i = j;
2337 close(f);
2338 bt->result[sizeof(bt->result) - 1] = '\n';
2339 if (i && bt->result[i - 1] == '\n')
2340 i--;
2341 debug1("runbacktick: finished, %d bytes\n", i);
2342 bt->result[i] = 0;
2343 backtick_filter(bt);
2344 (void)time(&now2);
2345 bt->bestbefore = now2 + bt->lifespan;
2346 return bt->result;
2349 char *
2350 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2351 char *str;
2352 struct win *win;
2353 int esc;
2354 int padlen;
2355 struct event *ev;
2356 int rec;
2358 static int tick;
2359 char *s = str;
2360 register char *p = winmsg_buf;
2361 register int ctrl;
2362 struct timeval now;
2363 struct tm *tm;
2364 int l, i, r;
2365 int num;
2366 int zeroflg;
2367 int longflg;
2368 int minusflg;
2369 int plusflg;
2370 int qmflag = 0, omflag = 0, qmnumrend = 0;
2371 char *qmpos = 0;
2372 int numpad = 0;
2373 int lastpad = 0;
2374 int truncpos = -1;
2375 int truncper = 0;
2376 int trunclong = 0;
2377 struct backtick *bt;
2379 if (winmsg_numrend >= 0)
2380 winmsg_numrend = 0;
2381 else
2382 winmsg_numrend = -winmsg_numrend;
2384 tick = 0;
2385 tm = 0;
2386 ctrl = 0;
2387 gettimeofday(&now, NULL);
2388 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2390 *p = *s;
2391 if (ctrl)
2393 ctrl = 0;
2394 if (*s != '^' && *s >= 64)
2395 *p &= 0x1f;
2396 continue;
2398 if (*s != esc)
2400 if (esc == '%')
2402 switch (*s)
2404 #if 0
2405 case '~':
2406 *p = BELL;
2407 break;
2408 #endif
2409 case '^':
2410 ctrl = 1;
2411 *p-- = '^';
2412 break;
2413 default:
2414 break;
2417 continue;
2419 if (*++s == esc) /* double escape ? */
2420 continue;
2421 if ((plusflg = *s == '+') != 0)
2422 s++;
2423 if ((minusflg = *s == '-') != 0)
2424 s++;
2425 if ((zeroflg = *s == '0') != 0)
2426 s++;
2427 num = 0;
2428 while(*s >= '0' && *s <= '9')
2429 num = num * 10 + (*s++ - '0');
2430 if ((longflg = *s == 'L') != 0)
2431 s++;
2432 switch (*s)
2434 case '?':
2435 p--;
2436 if (qmpos)
2438 if ((!qmflag && !omflag) || omflag == 1)
2440 p = qmpos;
2441 if (qmnumrend < winmsg_numrend)
2442 winmsg_numrend = qmnumrend;
2444 qmpos = 0;
2445 break;
2447 qmpos = p;
2448 qmnumrend = winmsg_numrend;
2449 qmflag = omflag = 0;
2450 break;
2451 case ':':
2452 p--;
2453 if (!qmpos)
2454 break;
2455 if (qmflag && omflag != 1)
2457 omflag = 1;
2458 qmpos = p;
2459 qmnumrend = winmsg_numrend;
2461 else
2463 p = qmpos;
2464 if (qmnumrend < winmsg_numrend)
2465 winmsg_numrend = qmnumrend;
2466 omflag = -1;
2468 break;
2469 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2470 case 'a': case 'A': case 's': case 'c': case 'C':
2471 if (l < 4)
2472 break;
2473 if (tm == 0)
2475 time_t nowsec = now.tv_sec;
2476 tm = localtime(&nowsec);
2478 qmflag = 1;
2479 if (!tick || tick > 3600)
2480 tick = 3600;
2481 switch (*s)
2483 case 'd':
2484 sprintf(p, "%02d", tm->tm_mday % 100);
2485 break;
2486 case 'D':
2487 #ifdef USE_LOCALE
2488 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2489 #else
2490 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2491 #endif
2492 break;
2493 case 'm':
2494 sprintf(p, "%02d", tm->tm_mon + 1);
2495 break;
2496 case 'M':
2497 #ifdef USE_LOCALE
2498 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2499 #else
2500 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2501 #endif
2502 break;
2503 case 'y':
2504 sprintf(p, "%02d", tm->tm_year % 100);
2505 break;
2506 case 'Y':
2507 sprintf(p, "%04d", tm->tm_year + 1900);
2508 break;
2509 case 'a':
2510 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2511 break;
2512 case 'A':
2513 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2514 break;
2515 case 's':
2516 sprintf(p, "%02d", tm->tm_sec);
2517 tick = 1;
2518 break;
2519 case 'c':
2520 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2521 if (!tick || tick > 60)
2522 tick = 60;
2523 break;
2524 case 'C':
2525 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2526 if (!tick || tick > 60)
2527 tick = 60;
2528 break;
2529 default:
2530 break;
2532 p += strlen(p) - 1;
2533 break;
2534 case 'l':
2535 #ifdef LOADAV
2536 *p = 0;
2537 if (l > 20)
2538 AddLoadav(p);
2539 if (*p)
2541 qmflag = 1;
2542 p += strlen(p) - 1;
2544 else
2545 *p = '?';
2546 if (!tick || tick > 60)
2547 tick = 60;
2548 #else
2549 *p = '?';
2550 #endif
2551 p += strlen(p) - 1;
2552 break;
2553 case '`':
2554 case 'h':
2555 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2557 p--;
2558 break;
2560 if (*s == '`')
2562 for (bt = backticks; bt; bt = bt->next)
2563 if (bt->num == num)
2564 break;
2565 if (bt == 0)
2567 p--;
2568 break;
2572 char savebuf[sizeof(winmsg_buf)];
2573 int oldtick = tick;
2574 int oldnumrend = winmsg_numrend;
2576 *p = 0;
2577 strcpy(savebuf, winmsg_buf);
2578 winmsg_numrend = -winmsg_numrend;
2579 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2580 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2581 if (!tick || oldtick < tick)
2582 tick = oldtick;
2583 if ((int)strlen(winmsg_buf) < l)
2584 strcat(savebuf, winmsg_buf);
2585 strcpy(winmsg_buf, savebuf);
2586 while (oldnumrend < winmsg_numrend)
2587 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2588 if (*p)
2589 qmflag = 1;
2590 p += strlen(p) - 1;
2592 break;
2593 case 'w':
2594 case 'W':
2596 struct win *oldfore = 0;
2597 char *ss;
2599 if (display)
2601 oldfore = D_fore;
2602 D_fore = win;
2604 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2605 if (minusflg)
2606 *ss = 0;
2607 if (display)
2608 D_fore = oldfore;
2610 if (*p)
2611 qmflag = 1;
2612 p += strlen(p) - 1;
2613 break;
2614 case 'u':
2615 *p = 0;
2616 if (win)
2617 AddOtherUsers(p, l - 1, win);
2618 if (*p)
2619 qmflag = 1;
2620 p += strlen(p) - 1;
2621 break;
2622 case 'f':
2623 *p = 0;
2624 if (win)
2625 AddWindowFlags(p, l - 1, win);
2626 if (*p)
2627 qmflag = 1;
2628 p += strlen(p) - 1;
2629 break;
2630 case 't':
2631 *p = 0;
2632 if (win && (int)strlen(win->w_title) < l)
2634 strcpy(p, win->w_title);
2635 if (*p)
2636 qmflag = 1;
2638 p += strlen(p) - 1;
2639 break;
2640 case '{':
2642 char rbuf[128];
2643 s++;
2644 for (i = 0; i < 127; i++)
2645 if (s[i] && s[i] != '}')
2646 rbuf[i] = s[i];
2647 else
2648 break;
2649 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2651 r = -1;
2652 rbuf[i] = 0;
2653 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2654 if (i != 1 || rbuf[0] != '-')
2655 r = ParseAttrColor(rbuf, (char *)0, 0);
2656 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2658 winmsg_rend[winmsg_numrend] = r;
2659 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2660 winmsg_numrend++;
2663 s += i;
2664 p--;
2666 break;
2667 case 'H':
2668 *p = 0;
2669 if ((int)strlen(HostName) < l)
2671 strcpy(p, HostName);
2672 if (*p)
2673 qmflag = 1;
2675 p += strlen(p) - 1;
2676 break;
2677 case 'F':
2678 p--;
2679 /* small hack */
2680 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2681 minusflg = !minusflg;
2682 if (minusflg)
2683 qmflag = 1;
2684 break;
2685 case '>':
2686 truncpos = p - winmsg_buf;
2687 truncper = num > 100 ? 100 : num;
2688 trunclong = longflg;
2689 p--;
2690 break;
2691 case '=':
2692 case '<':
2693 *p = ' ';
2694 if (num || zeroflg || plusflg || longflg || (*s != '='))
2696 /* expand all pads */
2697 if (minusflg)
2699 num = (plusflg ? lastpad : padlen) - num;
2700 if (!plusflg && padlen == 0)
2701 num = p - winmsg_buf;
2702 plusflg = 0;
2704 else if (!zeroflg)
2706 if (*s != '=' && num == 0 && !plusflg)
2707 num = 100;
2708 if (num > 100)
2709 num = 100;
2710 if (padlen == 0)
2711 num = p - winmsg_buf;
2712 else
2713 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2715 if (num < 0)
2716 num = 0;
2717 if (plusflg)
2718 num += lastpad;
2719 if (num > MAXSTR - 1)
2720 num = MAXSTR - 1;
2721 if (numpad)
2722 p = pad_expand(winmsg_buf, p, numpad, num);
2723 numpad = 0;
2724 if (p - winmsg_buf > num && !longflg)
2726 int left, trunc;
2728 if (truncpos == -1)
2730 truncpos = lastpad;
2731 truncper = 0;
2733 trunc = lastpad + truncper * (num - lastpad) / 100;
2734 if (trunc > num)
2735 trunc = num;
2736 if (trunc < lastpad)
2737 trunc = lastpad;
2738 left = truncpos - trunc;
2739 if (left > p - winmsg_buf - num)
2740 left = p - winmsg_buf - num;
2741 debug1("lastpad = %d, ", lastpad);
2742 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2743 if (left > 0)
2745 if (left + lastpad > p - winmsg_buf)
2746 left = p - winmsg_buf - lastpad;
2747 if (p - winmsg_buf - lastpad - left > 0)
2748 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2749 p -= left;
2750 r = winmsg_numrend;
2751 while (r && winmsg_rendpos[r - 1] > lastpad)
2753 r--;
2754 winmsg_rendpos[r] -= left;
2755 if (winmsg_rendpos[r] < lastpad)
2756 winmsg_rendpos[r] = lastpad;
2758 if (trunclong)
2760 if (p - winmsg_buf > lastpad)
2761 winmsg_buf[lastpad] = '.';
2762 if (p - winmsg_buf > lastpad + 1)
2763 winmsg_buf[lastpad + 1] = '.';
2764 if (p - winmsg_buf > lastpad + 2)
2765 winmsg_buf[lastpad + 2] = '.';
2768 if (p - winmsg_buf > num)
2770 p = winmsg_buf + num;
2771 if (trunclong)
2773 if (num - 1 >= lastpad)
2774 p[-1] = '.';
2775 if (num - 2 >= lastpad)
2776 p[-2] = '.';
2777 if (num - 3 >= lastpad)
2778 p[-3] = '.';
2780 r = winmsg_numrend;
2781 while (r && winmsg_rendpos[r - 1] > num)
2782 winmsg_rendpos[--r] = num;
2784 truncpos = -1;
2785 trunclong = 0;
2786 if (lastpad > p - winmsg_buf)
2787 lastpad = p - winmsg_buf;
2788 debug1("lastpad now %d\n", lastpad);
2790 if (*s == '=')
2792 while (p - winmsg_buf < num)
2793 *p++ = ' ';
2794 lastpad = p - winmsg_buf;
2795 truncpos = -1;
2796 trunclong = 0;
2797 debug1("lastpad2 now %d\n", lastpad);
2799 p--;
2801 else if (padlen)
2803 *p = 127; /* internal pad representation */
2804 numpad++;
2806 break;
2807 case 'n':
2808 s++;
2809 /* FALLTHROUGH */
2810 default:
2811 s--;
2812 if (l > 10 + num)
2814 if (num == 0)
2815 num = 1;
2816 if (!win)
2817 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2818 else
2819 sprintf(p, "%*d", num, win->w_number);
2820 qmflag = 1;
2821 p += strlen(p) - 1;
2823 break;
2826 if (qmpos && !qmflag)
2827 p = qmpos + 1;
2828 *p = '\0';
2829 if (numpad)
2831 if (padlen > MAXSTR - 1)
2832 padlen = MAXSTR - 1;
2833 p = pad_expand(winmsg_buf, p, numpad, padlen);
2835 if (ev)
2837 evdeq(ev); /* just in case */
2838 ev->timeout.tv_sec = 0;
2839 ev->timeout.tv_usec = 0;
2841 if (ev && tick)
2843 now.tv_usec = 100000;
2844 if (tick == 1)
2845 now.tv_sec++;
2846 else
2847 now.tv_sec += tick - (now.tv_sec % tick);
2848 ev->timeout = now;
2849 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2851 return winmsg_buf;
2854 char *
2855 MakeWinMsg(s, win, esc)
2856 char *s;
2857 struct win *win;
2858 int esc;
2860 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2863 void
2864 PutWinMsg(s, start, max)
2865 char *s;
2866 int start, max;
2868 int i, p, l, r, n;
2869 struct mchar rend;
2870 struct mchar rendstack[MAX_WINMSG_REND];
2871 int rendstackn = 0;
2873 if (s != winmsg_buf)
2875 /* sorry, no fancy coloring available */
2876 debug1("PutWinMsg %s plain\n", s);
2877 l = strlen(s);
2878 if (l > max)
2879 l = max;
2880 l -= start;
2881 s += start;
2882 while (l-- > 0)
2883 PUTCHARLP(*s++);
2884 return;
2886 rend = D_rend;
2887 p = 0;
2888 l = strlen(s);
2889 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
2890 for (i = 0; i < winmsg_numrend && max > 0; i++)
2892 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
2893 break;
2894 if (p < winmsg_rendpos[i])
2896 n = winmsg_rendpos[i] - p;
2897 if (n > max)
2898 n = max;
2899 max -= n;
2900 p += n;
2901 while(n-- > 0)
2903 if (start-- > 0)
2904 s++;
2905 else
2906 PUTCHARLP(*s++);
2909 r = winmsg_rend[i];
2910 if (r == -1)
2912 if (rendstackn > 0)
2913 rend = rendstack[--rendstackn];
2915 else
2917 rendstack[rendstackn++] = rend;
2918 ApplyAttrColor(r, &rend);
2920 SetRendition(&rend);
2922 if (p < l)
2924 n = l - p;
2925 if (n > max)
2926 n = max;
2927 while(n-- > 0)
2929 if (start-- > 0)
2930 s++;
2931 else
2932 PUTCHARLP(*s++);
2938 #ifdef DEBUG
2939 static void
2940 fds1(i, j)
2941 int i, j;
2943 while (i < j)
2945 debug1("%d ", i);
2946 i++;
2948 if ((j = open("/dev/null", 0)) >= 0)
2950 fds1(i + 1, j);
2951 close(j);
2953 else
2955 while (dup(++i) < 0 && errno != EBADF)
2956 debug1("%d ", i);
2957 debug1(" [%d]\n", i);
2961 static void
2962 fds()
2964 debug("fds: ");
2965 fds1(-1, -1);
2967 #endif
2969 static void
2970 serv_read_fn(ev, data)
2971 struct event *ev;
2972 char *data;
2974 debug("Knock - knock!\n");
2975 ReceiveMsg();
2978 static void
2979 serv_select_fn(ev, data)
2980 struct event *ev;
2981 char *data;
2983 struct win *p;
2985 debug("serv_select_fn called\n");
2986 /* XXX: messages?? */
2987 if (GotSigChld)
2989 SigChldHandler();
2991 if (InterruptPlease)
2993 debug("Backend received interrupt\n");
2994 /* This approach is rather questionable in a multi-display
2995 * environment */
2996 if (fore && displays)
2998 #if defined(TERMIO) || defined(POSIX)
2999 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3000 #else
3001 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3002 #endif
3003 #ifdef PSEUDOS
3004 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3005 &ibuf, 1);
3006 debug1("Backend wrote interrupt to %d", fore->w_number);
3007 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3008 #else
3009 write(fore->w_ptyfd, &ibuf, 1);
3010 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3011 #endif
3013 InterruptPlease = 0;
3016 for (p = windows; p; p = p->w_next)
3018 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3020 struct canvas *cv;
3021 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3022 p->w_bell = BELL_ON;
3023 for (display = displays; display; display = display->d_next)
3025 for (cv = D_cvlist; cv; cv = cv->c_next)
3026 if (cv->c_layer->l_bottom == &p->w_layer)
3027 break;
3028 if (cv == 0)
3030 p->w_bell = BELL_DONE;
3031 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3033 else if (visual && !D_VB && (!D_status || !D_status_bell))
3035 Msg(0, "%s", VisualBellString);
3036 if (D_status)
3038 D_status_bell = 1;
3039 debug1("using vbell timeout %d\n", VBellWait);
3040 SetTimeout(&D_statusev, VBellWait );
3044 /* don't annoy the user with two messages */
3045 if (p->w_monitor == MON_FOUND)
3046 p->w_monitor = MON_DONE;
3047 WindowChanged(p, 'f');
3049 if (p->w_monitor == MON_FOUND)
3051 struct canvas *cv;
3052 p->w_monitor = MON_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)
3059 continue; /* user already sees window */
3060 #ifdef MULTIUSER
3061 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3062 continue; /* user doesn't care */
3063 #endif
3064 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3065 p->w_monitor = MON_DONE;
3067 WindowChanged(p, 'f');
3071 for (display = displays; display; display = display->d_next)
3073 struct canvas *cv;
3074 if (D_status == STATUS_ON_WIN)
3075 continue;
3076 /* XXX: should use display functions! */
3077 for (cv = D_cvlist; cv; cv = cv->c_next)
3079 int lx, ly;
3081 /* normalize window, see resize.c */
3082 lx = cv->c_layer->l_x;
3083 ly = cv->c_layer->l_y;
3084 if (lx == cv->c_layer->l_width)
3085 lx--;
3086 if (ly + cv->c_yoff < cv->c_ys)
3088 int i, n = cv->c_ys - (ly + cv->c_yoff);
3089 cv->c_yoff = cv->c_ys - ly;
3090 RethinkViewportOffsets(cv);
3091 if (n > cv->c_layer->l_height)
3092 n = cv->c_layer->l_height;
3093 CV_CALL(cv,
3094 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3095 LayRedisplayLine(-1, -1, -1, 1);
3096 for (i = 0; i < n; i++)
3097 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3098 if (cv == cv->c_display->d_forecv)
3099 LaySetCursor();
3102 else if (ly + cv->c_yoff > cv->c_ye)
3104 int i, n = ly + cv->c_yoff - cv->c_ye;
3105 cv->c_yoff = cv->c_ye - ly;
3106 RethinkViewportOffsets(cv);
3107 if (n > cv->c_layer->l_height)
3108 n = cv->c_layer->l_height;
3109 CV_CALL(cv,
3110 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3111 LayRedisplayLine(-1, -1, -1, 1);
3112 for (i = 0; i < n; i++)
3113 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3114 if (cv == cv->c_display->d_forecv)
3115 LaySetCursor();
3118 if (lx + cv->c_xoff < cv->c_xs)
3120 int i, n = cv->c_xs - (lx + cv->c_xoff);
3121 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3122 n = (cv->c_xe - cv->c_xs + 1) / 2;
3123 if (cv->c_xoff + n > cv->c_xs)
3124 n = cv->c_xs - cv->c_xoff;
3125 cv->c_xoff += n;
3126 RethinkViewportOffsets(cv);
3127 if (n > cv->c_layer->l_width)
3128 n = cv->c_layer->l_width;
3129 CV_CALL(cv,
3130 LayRedisplayLine(-1, -1, -1, 1);
3131 for (i = 0; i < flayer->l_height; i++)
3133 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3134 LayRedisplayLine(i, 0, n - 1, 1);
3136 if (cv == cv->c_display->d_forecv)
3137 LaySetCursor();
3140 else if (lx + cv->c_xoff > cv->c_xe)
3142 int i, n = lx + cv->c_xoff - cv->c_xe;
3143 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3144 n = (cv->c_xe - cv->c_xs + 1) / 2;
3145 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3146 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3147 cv->c_xoff -= n;
3148 RethinkViewportOffsets(cv);
3149 if (n > cv->c_layer->l_width)
3150 n = cv->c_layer->l_width;
3151 CV_CALL(cv,
3152 LayRedisplayLine(-1, -1, -1, 1);
3153 for (i = 0; i < flayer->l_height; i++)
3155 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3156 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3158 if (cv == cv->c_display->d_forecv)
3159 LaySetCursor();
3165 for (display = displays; display; display = display->d_next)
3167 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3168 continue;
3169 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3170 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3174 static void
3175 logflush_fn(ev, data)
3176 struct event *ev;
3177 char *data;
3179 struct win *p;
3180 char *buf;
3181 int n;
3183 if (!islogfile(NULL))
3184 return; /* no more logfiles */
3185 logfflush(NULL);
3186 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3187 if (n)
3189 SetTimeout(ev, n * 1000);
3190 evenq(ev); /* re-enqueue ourself */
3192 if (!logtstamp_on)
3193 return;
3194 /* write fancy time-stamp */
3195 for (p = windows; p; p = p->w_next)
3197 if (!p->w_log)
3198 continue;
3199 p->w_logsilence += n;
3200 if (p->w_logsilence < logtstamp_after)
3201 continue;
3202 if (p->w_logsilence - n >= logtstamp_after)
3203 continue;
3204 buf = MakeWinMsg(logtstamp_string, p, '%');
3205 logfwrite(p->w_log, buf, strlen(buf));
3210 * Interprets ^?, ^@ and other ^-control-char notation.
3211 * Interprets \ddd octal notation
3213 * The result is placed in *cp, p is advanced behind the parsed expression and
3214 * returned.
3216 static char *
3217 ParseChar(p, cp)
3218 char *p, *cp;
3220 if (*p == 0)
3221 return 0;
3222 if (*p == '^' && p[1])
3224 if (*++p == '?')
3225 *cp = '\177';
3226 else if (*p >= '@')
3227 *cp = Ctrl(*p);
3228 else
3229 return 0;
3230 ++p;
3232 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3234 *cp = 0;
3236 *cp = *cp * 8 + *p - '0';
3237 while (*++p <= '7' && *p >= '0');
3239 else
3240 *cp = *p++;
3241 return p;
3244 static int
3245 ParseEscape(p)
3246 char *p;
3248 unsigned char buf[2];
3250 if (*p == 0)
3251 SetEscape((struct acluser *)0, -1, -1);
3252 else
3254 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3255 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3256 return -1;
3257 SetEscape((struct acluser *)0, buf[0], buf[1]);
3259 return 0;