Do not allow resizing scrollback buffer in copy mode.
[screen-lua.git] / src / screen.c
blobd8e82f30b3cec917d158625a35e5358b5261afa7
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
362 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
363 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
364 #endif
365 #if defined(sun) && defined(SVR4)
367 /* Solaris' login blocks SIGHUP! This is _very bad_ */
368 sigset_t sset;
369 sigemptyset(&sset);
370 sigprocmask(SIG_SETMASK, &sset, 0);
372 #endif
375 * First, close all unused descriptors
376 * (otherwise, we might have problems with the select() call)
378 closeallfiles(0);
379 #ifdef DEBUG
380 opendebug(1, 0);
381 #endif
382 sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
383 PATCHLEVEL, STATE, ORIGIN, DATE);
384 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
385 debug2("-- screen debug started %s (%s)\n", *av, version);
386 #ifdef POSIX
387 debug("POSIX\n");
388 #endif
389 #ifdef TERMIO
390 debug("TERMIO\n");
391 #endif
392 #ifdef SYSV
393 debug("SYSV\n");
394 #endif
395 #ifdef SYSVSIGS
396 debug("SYSVSIGS\n");
397 #endif
398 #ifdef NAMEDPIPE
399 debug("NAMEDPIPE\n");
400 #endif
401 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
402 debug("Window size changing enabled\n");
403 #endif
404 #ifdef HAVE_SETREUID
405 debug("SETREUID\n");
406 #endif
407 #ifdef HAVE_SETEUID
408 debug("SETEUID\n");
409 #endif
410 #ifdef hpux
411 debug("hpux\n");
412 #endif
413 #ifdef USEBCOPY
414 debug("USEBCOPY\n");
415 #endif
416 #ifdef UTMPOK
417 debug("UTMPOK\n");
418 #endif
419 #ifdef LOADAV
420 debug("LOADAV\n");
421 #endif
422 #ifdef NETHACK
423 debug("NETHACK\n");
424 #endif
425 #ifdef TERMINFO
426 debug("TERMINFO\n");
427 #endif
428 #ifdef SHADOWPW
429 debug("SHADOWPW\n");
430 #endif
431 #ifdef NAME_MAX
432 debug1("NAME_MAX = %d\n", NAME_MAX);
433 #endif
435 BellString = SaveStr("Bell in window %n");
436 VisualBellString = SaveStr(" Wuff, Wuff!! ");
437 ActivityString = SaveStr("Activity in window %n");
438 screenlogfile = SaveStr("screenlog.%n");
439 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
440 hstatusstring = SaveStr("%h");
441 captionstring = SaveStr("%3n %t");
442 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
443 wlisttit = SaveStr("Num Name%=Flags");
444 wliststr = SaveStr("%3n %t%=%f");
445 #ifdef COPY_PASTE
446 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
447 #endif
448 ShellProg = NULL;
449 #ifdef POW_DETACH
450 PowDetachString = 0;
451 #endif
452 default_startup = (ac > 1) ? 0 : 1;
453 adaptflag = 0;
454 VBellWait = VBELLWAIT * 1000;
455 MsgWait = MSGWAIT * 1000;
456 MsgMinWait = MSGMINWAIT * 1000;
457 SilenceWait = SILENCEWAIT;
458 #ifdef HAVE_BRAILLE
459 InitBraille();
460 #endif
461 #ifdef ZMODEM
462 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
463 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
464 #endif
466 #ifdef COPY_PASTE
467 CompileKeys((char *)0, 0, mark_key_tab);
468 #endif
469 #ifdef UTF8
470 InitBuiltinTabs();
471 screenencodings = SaveStr(SCREENENCODINGS);
472 #endif
473 nwin = nwin_undef;
474 nwin_options = nwin_undef;
475 strcpy(screenterm, "screen");
477 logreopen_register(lf_secreopen);
479 av0 = *av;
480 /* if this is a login screen, assume -RR */
481 if (*av0 == '-')
483 rflag = 4;
484 #ifdef MULTI
485 xflag = 1;
486 #else
487 dflag = 1;
488 #endif
489 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
491 while (ac > 0)
493 ap = *++av;
494 if (--ac > 0 && *ap == '-')
496 if (ap[1] == '-' && ap[2] == 0)
498 av++;
499 ac--;
500 break;
502 if (ap[1] == '-' && !strcmp(ap, "--version"))
503 Panic(0, "Screen version %s", version);
504 if (ap[1] == '-' && !strcmp(ap, "--help"))
505 exit_with_usage(myname, NULL, NULL);
506 while (ap && *ap && *++ap)
508 switch (*ap)
510 case 'a':
511 nwin_options.aflag = 1;
512 break;
513 case 'A':
514 adaptflag = 1;
515 break;
516 case 'p': /* preselect */
517 if (*++ap)
518 preselect = ap;
519 else
521 if (!--ac)
522 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
523 preselect = *++av;
525 ap = NULL;
526 break;
527 #ifdef HAVE_BRAILLE
528 case 'B':
529 bd.bd_start_braille = 1;
530 break;
531 #endif
532 case 'c':
533 if (*++ap)
534 RcFileName = ap;
535 else
537 if (--ac == 0)
538 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
539 RcFileName = *++av;
541 ap = NULL;
542 break;
543 case 'e':
544 if (!*++ap)
546 if (--ac == 0)
547 exit_with_usage(myname, "Specify command characters with -e", NULL);
548 ap = *++av;
550 if (ParseEscape(ap))
551 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
552 ap = NULL;
553 break;
554 case 'f':
555 ap++;
556 switch (*ap++)
558 case 'n':
559 case '0':
560 nwin_options.flowflag = FLOW_NOW * 0;
561 break;
562 case '\0':
563 ap--;
564 /* FALLTHROUGH */
565 case 'y':
566 case '1':
567 nwin_options.flowflag = FLOW_NOW * 1;
568 break;
569 case 'a':
570 nwin_options.flowflag = FLOW_AUTOFLAG;
571 break;
572 default:
573 exit_with_usage(myname, "Unknown flow option -%s", --ap);
575 break;
576 case 'h':
577 if (--ac == 0)
578 exit_with_usage(myname, NULL, NULL);
579 nwin_options.histheight = atoi(*++av);
580 if (nwin_options.histheight < 0)
581 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
582 break;
583 case 'i':
584 iflag = 1;
585 break;
586 case 't': /* title, the former AkA == -k */
587 if (--ac == 0)
588 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
589 nwin_options.aka = *++av;
590 break;
591 case 'l':
592 ap++;
593 switch (*ap++)
595 case 'n':
596 case '0':
597 nwin_options.lflag = 0;
598 break;
599 case '\0':
600 ap--;
601 /* FALLTHROUGH */
602 case 'y':
603 case '1':
604 nwin_options.lflag = 1;
605 break;
606 case 'a':
607 nwin_options.lflag = 3;
608 break;
609 case 's': /* -ls */
610 case 'i': /* -list */
611 lsflag = 1;
612 if (ac > 1 && !SockMatch)
614 SockMatch = *++av;
615 ac--;
617 ap = NULL;
618 break;
619 default:
620 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
622 break;
623 case 'w':
624 lsflag = 1;
625 wipeflag = 1;
626 if (ac > 1 && !SockMatch)
628 SockMatch = *++av;
629 ac--;
631 break;
632 case 'L':
633 nwin_options.Lflag = 1;
634 break;
635 case 'm':
636 mflag = 1;
637 break;
638 case 'O': /* to be (or not to be?) deleted. jw. */
639 force_vt = 0;
640 break;
641 case 'T':
642 if (--ac == 0)
643 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
644 if (strlen(*++av) < 20)
645 strcpy(screenterm, *av);
646 else
647 Panic(0, "-T: terminal name too long. (max. 20 char)");
648 nwin_options.term = screenterm;
649 break;
650 case 'q':
651 quietflag = 1;
652 break;
653 case 'r':
654 case 'R':
655 #ifdef MULTI
656 case 'x':
657 #endif
658 if (ac > 1 && *av[1] != '-' && !SockMatch)
660 SockMatch = *++av;
661 ac--;
662 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
664 #ifdef MULTI
665 if (*ap == 'x')
666 xflag = 1;
667 #endif
668 if (rflag)
669 rflag = 2;
670 rflag += (*ap == 'R') ? 2 : 1;
671 break;
672 #ifdef REMOTE_DETACH
673 case 'd':
674 dflag = 1;
675 /* FALLTHROUGH */
676 case 'D':
677 if (!dflag)
678 dflag = 2;
679 if (ac == 2)
681 if (*av[1] != '-' && !SockMatch)
683 SockMatch = *++av;
684 ac--;
685 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
688 break;
689 #endif
690 case 's':
691 if (--ac == 0)
692 exit_with_usage(myname, "Specify shell with -s", NULL);
693 if (ShellProg)
694 free(ShellProg);
695 ShellProg = SaveStr(*++av);
696 debug1("ShellProg: '%s'\n", ShellProg);
697 break;
698 case 'S':
699 if (!SockMatch)
701 if (--ac == 0)
702 exit_with_usage(myname, "Specify session-name with -S", NULL);
703 SockMatch = *++av;
705 if (!*SockMatch)
706 exit_with_usage(myname, "Empty session-name?", NULL);
707 break;
708 case 'X':
709 cmdflag = 1;
710 break;
711 case 'v':
712 Panic(0, "Screen version %s", version);
713 /* NOTREACHED */
714 #ifdef UTF8
715 case 'U':
716 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
717 break;
718 #endif
719 default:
720 exit_with_usage(myname, "Unknown option %s", --ap);
724 else
725 break;
728 real_uid = getuid();
729 real_gid = getgid();
730 eff_uid = geteuid();
731 eff_gid = getegid();
732 if (eff_uid != real_uid)
734 /* if running with s-bit, we must install a special signal
735 * handler routine that resets the s-bit, so that we get a
736 * core file anyway.
738 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
739 signal(SIGBUS, CoreDump);
740 #endif /* SIGBUS */
741 signal(SIGSEGV, CoreDump);
744 #ifdef USE_LOCALE
745 setlocale(LC_ALL, "");
746 #endif
747 #ifdef ENCODINGS
748 if (nwin_options.encoding == -1)
750 /* ask locale if we should start in UTF-8 mode */
751 # ifdef HAVE_NL_LANGINFO
752 # ifndef USE_LOCALE
753 setlocale(LC_CTYPE, "");
754 # endif
755 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
756 debug1("locale says encoding = %d\n", nwin_options.encoding);
757 # else
758 # ifdef UTF8
759 char *s;
760 if (((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
761 (s = getenv("LANG"))) && InStr(s, "UTF-8"))
762 nwin_options.encoding = UTF8;
763 # endif
764 debug1("environment says encoding=%d\n", nwin_options.encoding);
765 #endif
767 #endif
768 if (SockMatch && strlen(SockMatch) >= MAXSTR)
769 Panic(0, "Ridiculously long socketname - try again.");
770 if (cmdflag && !rflag && !dflag && !xflag)
771 xflag = 1;
772 if (!cmdflag && dflag && mflag && !(rflag || xflag))
773 detached = 1;
774 nwin = nwin_options;
775 #ifdef ENCODINGS
776 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
777 #endif
778 if (ac)
779 nwin.args = av;
781 /* make the write() calls return -1 on all errors */
782 #ifdef SIGXFSZ
784 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
785 * It appears that in System V Release 4, UNIX, if you are writing
786 * an output file and you exceed the currently set file size limit,
787 * you _don't_ just get the call to `write' returning with a
788 * failure code. Rather, you get a signal called `SIGXFSZ' which,
789 * if neither handled nor ignored, will cause your program to crash
790 * with a core dump.
792 signal(SIGXFSZ, SIG_IGN);
793 #endif /* SIGXFSZ */
795 #ifdef SIGPIPE
796 signal(SIGPIPE, SIG_IGN);
797 #endif
799 if (!ShellProg)
801 register char *sh;
803 sh = getenv("SHELL");
804 ShellProg = SaveStr(sh ? sh : DefaultShell);
806 ShellArgs[0] = ShellProg;
807 home = getenv("HOME");
809 #ifdef NETHACK
810 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
812 char nethackrc[MAXPATHLEN];
814 if (home && (strlen(home) < (MAXPATHLEN - 20)))
816 sprintf(nethackrc,"%s/.nethackrc", home);
817 nethackflag = !access(nethackrc, F_OK);
820 #endif
822 #ifdef MULTIUSER
823 own_uid = multi_uid = real_uid;
824 if (SockMatch && (sockp = index(SockMatch, '/')))
826 if (eff_uid)
827 Panic(0, "Must run suid root for multiuser support.");
828 *sockp = 0;
829 multi = SockMatch;
830 SockMatch = sockp + 1;
831 if (*multi)
833 struct passwd *mppp;
834 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
835 Panic(0, "Cannot identify account '%s'.", multi);
836 multi_uid = mppp->pw_uid;
837 multi_home = SaveStr(mppp->pw_dir);
838 if (strlen(multi_home) > MAXPATHLEN - 10)
839 Panic(0, "home directory path too long");
840 # ifdef MULTI
841 /* always fake multi attach mode */
842 if (rflag || lsflag)
843 xflag = 1;
844 # endif /* MULTI */
845 detached = 0;
846 multiattach = 1;
849 if (SockMatch && *SockMatch == 0)
850 SockMatch = 0;
851 #endif /* MULTIUSER */
853 if ((LoginName = getlogin()) && LoginName[0] != '\0')
855 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
856 if ((int)ppp->pw_uid != real_uid)
857 ppp = (struct passwd *) 0;
859 if (ppp == 0)
861 if ((ppp = getpwuid(real_uid)) == 0)
863 Panic(0, "getpwuid() can't identify your account!");
864 exit(1);
866 LoginName = ppp->pw_name;
868 LoginName = SaveStr(LoginName);
870 ppp = getpwbyname(LoginName, ppp);
872 #if !defined(SOCKDIR) && defined(MULTIUSER)
873 if (multi && !multiattach)
875 if (home && strcmp(home, ppp->pw_dir))
876 Panic(0, "$HOME must match passwd entry for multiuser screens.");
878 #endif
880 if (home == 0 || *home == '\0')
881 home = ppp->pw_dir;
882 if (strlen(LoginName) > 20)
883 Panic(0, "LoginName too long - sorry.");
884 #ifdef MULTIUSER
885 if (multi && strlen(multi) > 20)
886 Panic(0, "Screen owner name too long - sorry.");
887 #endif
888 if (strlen(home) > MAXPATHLEN - 25)
889 Panic(0, "$HOME too long - sorry.");
891 attach_tty = "";
892 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag))
894 #ifndef NAMEDPIPE
895 int fl;
896 #endif
898 /* ttyname implies isatty */
899 if (!(attach_tty = ttyname(0)))
900 Panic(0, "Must be connected to a terminal.");
901 if (strlen(attach_tty) >= MAXPATHLEN)
902 Panic(0, "TtyName too long - sorry.");
903 if (stat(attach_tty, &st))
904 Panic(errno, "Cannot access '%s'", attach_tty);
905 #ifdef MULTIUSER
906 tty_mode = (int)st.st_mode & 0777;
907 #endif
909 #ifndef NAMEDPIPE
910 fl = fcntl(0, F_GETFL, 0);
911 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
912 attach_fd = 0;
913 #endif
914 if (attach_fd == -1)
916 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
917 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
918 close(n);
920 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
922 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
923 Panic(0, "Please set a terminal type.");
924 if (strlen(attach_term) > sizeof(D_termname) - 1)
925 Panic(0, "$TERM too long - sorry.");
926 GetTTY(0, &attach_Mode);
927 #ifdef DEBUGGGGGGGGGGGGGGG
928 DebugTTY(&attach_Mode);
929 #endif /* DEBUG */
932 #ifdef _MODE_T
933 oumask = umask(0); /* well, unsigned never fails? jw. */
934 #else
935 if ((oumask = (int)umask(0)) == -1)
936 Panic(errno, "Cannot change umask to zero");
937 #endif
938 SockDir = getenv("SCREENDIR");
939 if (SockDir)
941 if (strlen(SockDir) >= MAXPATHLEN - 1)
942 Panic(0, "Ridiculously long $SCREENDIR - try again.");
943 #ifdef MULTIUSER
944 if (multi)
945 Panic(0, "No $SCREENDIR with multi screens, please.");
946 #endif
948 #ifdef MULTIUSER
949 if (multiattach)
951 # ifndef SOCKDIR
952 sprintf(SockPath, "%s/.screen", multi_home);
953 SockDir = SockPath;
954 # else
955 SockDir = SOCKDIR;
956 sprintf(SockPath, "%s/S-%s", SockDir, multi);
957 # endif
959 else
960 #endif
962 #ifndef SOCKDIR
963 if (SockDir == 0)
965 sprintf(SockPath, "%s/.screen", home);
966 SockDir = SockPath;
968 #endif
969 if (SockDir)
971 if (access(SockDir, F_OK))
973 debug1("SockDir '%s' missing ...\n", SockDir);
974 if (UserContext() > 0)
976 if (mkdir(SockDir, 0700))
977 UserReturn(0);
978 UserReturn(1);
980 if (UserStatus() <= 0)
981 Panic(0, "Cannot make directory '%s'.", SockDir);
983 if (SockDir != SockPath)
984 strcpy(SockPath, SockDir);
986 #ifdef SOCKDIR
987 else
989 SockDir = SOCKDIR;
990 if (lstat(SockDir, &st))
992 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
993 (eff_gid != real_gid) ? 0775 :
994 #ifdef S_ISVTX
995 0777|S_ISVTX;
996 #else
997 0777;
998 #endif
999 if (mkdir(SockDir, n) == -1)
1000 Panic(errno, "Cannot make directory '%s'", SockDir);
1002 else
1004 if (!S_ISDIR(st.st_mode))
1005 Panic(0, "'%s' must be a directory.", SockDir);
1006 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1007 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1008 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1009 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1010 0777;
1011 if (((int)st.st_mode & 0777) != n)
1012 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1014 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1015 if (access(SockPath, F_OK))
1017 if (mkdir(SockPath, 0700) == -1)
1018 Panic(errno, "Cannot make directory '%s'", SockPath);
1019 (void) chown(SockPath, real_uid, real_gid);
1022 #endif
1025 if (stat(SockPath, &st) == -1)
1026 Panic(errno, "Cannot access %s", SockPath);
1027 else
1028 if (!S_ISDIR(st.st_mode))
1029 Panic(0, "%s is not a directory.", SockPath);
1030 #ifdef MULTIUSER
1031 if (multi)
1033 if ((int)st.st_uid != multi_uid)
1034 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1036 else
1037 #endif
1039 if ((int)st.st_uid != real_uid)
1040 Panic(0, "You are not the owner of %s.", SockPath);
1042 if ((st.st_mode & 0777) != 0700)
1043 Panic(0, "Directory %s must have mode 700.", SockPath);
1044 if (SockMatch && index(SockMatch, '/'))
1045 Panic(0, "Bad session name '%s'", SockMatch);
1046 SockName = SockPath + strlen(SockPath) + 1;
1047 *SockName = 0;
1048 (void) umask(oumask);
1049 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1051 #if defined(SYSV) && !defined(ISC)
1052 if (uname(&utsnam) == -1)
1053 Panic(errno, "uname");
1054 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1055 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1056 #else
1057 (void) gethostname(HostName, MAXSTR);
1058 HostName[MAXSTR - 1] = '\0';
1059 #endif
1060 if ((ap = index(HostName, '.')) != NULL)
1061 *ap = '\0';
1063 if (lsflag)
1065 int i, fo, oth;
1067 #ifdef MULTIUSER
1068 if (multi)
1069 real_uid = multi_uid;
1070 #endif
1071 setgid(real_gid);
1072 setuid(real_uid);
1073 eff_uid = real_uid;
1074 eff_gid = real_gid;
1075 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1076 if (quietflag)
1077 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1078 if (fo == 0)
1079 Panic(0, "No Sockets found in %s.\n", SockPath);
1080 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1081 /* NOTREACHED */
1083 signal(SIG_BYE, AttacherFinit); /* prevent races */
1084 if (cmdflag)
1086 char *sty = 0;
1088 /* attach_tty is not mandatory */
1089 if ((attach_tty = ttyname(0)) == 0)
1090 attach_tty = "";
1091 if (strlen(attach_tty) >= MAXPATHLEN)
1092 Panic(0, "TtyName too long - sorry.");
1093 if (!*av)
1094 Panic(0, "Please specify a command.");
1095 setgid(real_gid);
1096 setuid(real_uid);
1097 eff_uid = real_uid;
1098 eff_gid = real_gid;
1099 if (!mflag && !SockMatch)
1101 sty = getenv("STY");
1102 if (sty && *sty == 0)
1103 sty = 0;
1105 SendCmdMessage(sty, SockMatch, av);
1106 exit(0);
1108 else if (rflag || xflag)
1110 debug("screen -r: - is there anybody out there?\n");
1111 if (Attach(MSG_ATTACH))
1113 Attacher();
1114 /* NOTREACHED */
1116 #ifdef MULTIUSER
1117 if (multiattach)
1118 Panic(0, "Can't create sessions of other users.");
1119 #endif
1120 debug("screen -r: backend not responding -- still crying\n");
1122 else if (dflag && !mflag)
1124 (void) Attach(MSG_DETACH);
1125 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1126 eexit(0);
1127 /* NOTREACHED */
1129 if (!SockMatch && !mflag)
1131 register char *sty;
1133 if ((sty = getenv("STY")) != 0 && *sty != '\0')
1135 setgid(real_gid);
1136 setuid(real_uid);
1137 eff_uid = real_uid;
1138 eff_gid = real_gid;
1139 nwin_options.args = av;
1140 SendCreateMsg(sty, &nwin);
1141 exit(0);
1142 /* NOTREACHED */
1145 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1147 if (!detached || dflag != 2)
1148 MasterPid = fork();
1149 else
1150 MasterPid = 0;
1152 switch (MasterPid)
1154 case -1:
1155 Panic(errno, "fork");
1156 /* NOTREACHED */
1157 case 0:
1158 break;
1159 default:
1160 if (detached)
1161 exit(0);
1162 if (SockMatch)
1163 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1164 else
1165 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1166 for (ap = socknamebuf; *ap; ap++)
1167 if (*ap == '/')
1168 *ap = '-';
1169 #ifdef NAME_MAX
1170 if (strlen(socknamebuf) > NAME_MAX)
1171 socknamebuf[NAME_MAX] = 0;
1172 #endif
1173 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1174 setgid(real_gid);
1175 setuid(real_uid);
1176 eff_uid = real_uid;
1177 eff_gid = real_gid;
1178 Attacher();
1179 /* NOTREACHED */
1182 if (!detached)
1183 PanicPid = getppid();
1185 if (DefaultEsc == -1)
1186 DefaultEsc = Ctrl('a');
1187 if (DefaultMetaEsc == -1)
1188 DefaultMetaEsc = 'a';
1190 ap = av0 + strlen(av0) - 1;
1191 while (ap >= av0)
1193 if (!strncmp("screen", ap, 6))
1195 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1196 break;
1198 ap--;
1200 if (ap < av0)
1201 *av0 = 'S';
1203 #ifdef DEBUG
1205 char buf[256];
1207 if (dfp && dfp != stderr)
1208 fclose(dfp);
1209 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1210 if ((dfp = fopen(buf, "w")) == NULL)
1211 dfp = stderr;
1212 else
1213 (void) chmod(buf, 0666);
1215 #endif
1216 if (!detached)
1218 if (attach_fd == -1)
1220 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1221 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1223 else
1224 n = dup(attach_fd);
1226 else
1227 n = -1;
1228 freopen("/dev/null", "r", stdin);
1229 freopen("/dev/null", "w", stdout);
1231 #ifdef DEBUG
1232 if (dfp != stderr)
1233 #endif
1234 freopen("/dev/null", "w", stderr);
1235 debug("-- screen.back debug started\n");
1238 * This guarantees that the session owner is listed, even when we
1239 * start detached. From now on we should not refer to 'LoginName'
1240 * any more, use users->u_name instead.
1242 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1243 Panic(0, "Could not create user info");
1244 if (!detached)
1246 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1247 Panic(0, "Could not alloc display");
1248 PanicPid = 0;
1249 #ifdef ENCODINGS
1250 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1251 debug1("D_encoding = %d\n", D_encoding);
1252 #endif
1255 if (SockMatch)
1257 /* user started us with -S option */
1258 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1260 else
1262 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1263 HostName);
1265 for (ap = socknamebuf; *ap; ap++)
1266 if (*ap == '/')
1267 *ap = '-';
1268 #ifdef NAME_MAX
1269 if (strlen(socknamebuf) > NAME_MAX)
1271 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1272 socknamebuf[NAME_MAX] = 0;
1274 #endif
1275 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1277 ServerSocket = MakeServerSocket();
1278 InitKeytab();
1279 #ifdef ETCSCREENRC
1280 # ifdef ALLOW_SYSSCREENRC
1281 if ((ap = getenv("SYSSCREENRC")))
1282 (void)StartRc(ap, 0);
1283 else
1284 # endif
1285 (void)StartRc(ETCSCREENRC, 0);
1286 #endif
1287 (void)StartRc(RcFileName, 0);
1288 # ifdef UTMPOK
1289 # ifndef UTNOKEEP
1290 InitUtmp();
1291 # endif /* UTNOKEEP */
1292 # endif /* UTMPOK */
1293 if (display)
1295 if (InitTermcap(0, 0))
1297 debug("Could not init termcap - exiting\n");
1298 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1299 freetty();
1300 if (D_userpid)
1301 Kill(D_userpid, SIG_BYE);
1302 eexit(1);
1304 MakeDefaultCanvas();
1305 InitTerm(0);
1306 #ifdef UTMPOK
1307 RemoveLoginSlot();
1308 #endif
1310 else
1311 MakeTermcap(1);
1312 #ifdef LOADAV
1313 InitLoadav();
1314 #endif /* LOADAV */
1315 MakeNewEnv();
1316 signal(SIGHUP, SigHup);
1317 signal(SIGINT, FinitHandler);
1318 signal(SIGQUIT, FinitHandler);
1319 signal(SIGTERM, FinitHandler);
1320 #ifdef BSDJOBS
1321 signal(SIGTTIN, SIG_IGN);
1322 signal(SIGTTOU, SIG_IGN);
1323 #endif
1325 if (display)
1327 brktty(D_userfd);
1328 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1329 /* Note: SetMode must be called _before_ FinishRc. */
1330 SetTTY(D_userfd, &D_NewMode);
1331 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1332 Msg(errno, "Warning: NBLOCK fcntl failed");
1334 else
1335 brktty(-1); /* just try */
1336 signal(SIGCHLD, SigChld);
1337 #ifdef ETCSCREENRC
1338 # ifdef ALLOW_SYSSCREENRC
1339 if ((ap = getenv("SYSSCREENRC")))
1340 FinishRc(ap);
1341 else
1342 # endif
1343 FinishRc(ETCSCREENRC);
1344 #endif
1345 FinishRc(RcFileName);
1347 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1348 if (windows == NULL)
1350 debug("We open one default window, as screenrc did not specify one.\n");
1351 if (MakeWindow(&nwin) == -1)
1353 Msg(0, "Sorry, could not find a PTY.");
1354 sleep(5);
1355 Finit(0);
1356 /* NOTREACHED */
1360 #ifdef HAVE_BRAILLE
1361 StartBraille();
1362 #endif
1364 if (display && default_startup)
1365 display_copyright();
1366 signal(SIGINT, SigInt);
1367 if (rflag && (rflag & 1) == 0 && !quietflag)
1369 Msg(0, "New screen...");
1370 rflag = 0;
1373 serv_read.type = EV_READ;
1374 serv_read.fd = ServerSocket;
1375 serv_read.handler = serv_read_fn;
1376 evenq(&serv_read);
1378 serv_select.pri = -10;
1379 serv_select.type = EV_ALWAYS;
1380 serv_select.handler = serv_select_fn;
1381 evenq(&serv_select);
1383 logflushev.type = EV_TIMEOUT;
1384 logflushev.handler = logflush_fn;
1386 sched();
1387 /* NOTREACHED */
1388 return 0;
1391 void
1392 WindowDied(p, wstat, wstat_valid)
1393 struct win *p;
1394 int wstat;
1395 int wstat_valid;
1397 int killit = 0;
1399 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1400 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1401 killit = 1;
1403 if (ZombieKey_destroy && !killit)
1405 char buf[100], *s, reason[100];
1406 time_t now;
1408 if (wstat_valid) {
1409 if (WIFEXITED(wstat))
1410 if (WEXITSTATUS(wstat))
1411 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1412 else
1413 sprintf(reason, "terminated normally");
1414 else if (WIFSIGNALED(wstat))
1415 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1416 #ifdef WCOREDUMP
1417 WCOREDUMP(wstat) ? " (core file generated)" : "");
1418 #else
1419 "");
1420 #endif
1421 } else
1422 sprintf(reason, "detached from window");
1424 (void) time(&now);
1425 s = ctime(&now);
1426 if (s && *s)
1427 s[strlen(s) - 1] = '\0';
1428 debug3("window %d (%s) going into zombie state fd %d",
1429 p->w_number, p->w_title, p->w_ptyfd);
1430 #ifdef UTMPOK
1431 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1433 RemoveUtmp(p);
1434 p->w_slot = 0; /* "detached" */
1436 #endif
1437 CloseDevice(p);
1439 p->w_deadpid = p->w_pid;
1440 p->w_pid = 0;
1441 ResetWindow(p);
1442 /* p->w_y = p->w_bot; */
1443 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1444 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1445 WriteString(p, buf, strlen(buf));
1446 WindowChanged(p, 'f');
1448 else
1449 KillWindow(p);
1450 #ifdef UTMPOK
1451 CarefulUtmp();
1452 #endif
1455 static void
1456 SigChldHandler()
1458 struct stat st;
1459 #ifdef DEBUG
1460 fds();
1461 #endif
1462 while (GotSigChld)
1464 GotSigChld = 0;
1465 DoWait();
1466 #ifdef SYSVSIGS
1467 signal(SIGCHLD, SigChld);
1468 #endif
1470 if (stat(SockPath, &st) == -1)
1472 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1473 if (!RecoverSocket())
1475 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1476 Finit(1);
1478 else
1479 debug1("'%s' reconstructed\n", SockPath);
1481 else
1482 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1485 static sigret_t
1486 SigChld SIGDEFARG
1488 debug("SigChld()\n");
1489 GotSigChld = 1;
1490 SIGRETURN;
1493 sigret_t
1494 SigHup SIGDEFARG
1496 /* Hangup all displays */
1497 while ((display = displays) != 0)
1498 Hangup();
1499 SIGRETURN;
1503 * the backend's Interrupt handler
1504 * we cannot insert the intrc directly, as we never know
1505 * if fore is valid.
1507 static sigret_t
1508 SigInt SIGDEFARG
1510 #if HAZARDOUS
1511 char ibuf;
1513 debug("SigInt()\n");
1514 if (fore && displays)
1516 # if defined(TERMIO) || defined(POSIX)
1517 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1518 # else
1519 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1520 # endif
1521 fore->w_inlen = 0;
1522 write(fore->w_ptyfd, &ibuf, 1);
1524 #else
1525 signal(SIGINT, SigInt);
1526 debug("SigInt() careful\n");
1527 InterruptPlease = 1;
1528 #endif
1529 SIGRETURN;
1532 static sigret_t
1533 CoreDump SIGDEFARG
1535 struct display *disp;
1536 char buf[80];
1538 #if defined(SYSVSIGS) && defined(SIGHASARG)
1539 signal(sigsig, SIG_IGN);
1540 #endif
1541 setgid(getgid());
1542 setuid(getuid());
1543 unlink("core");
1544 #ifdef SIGHASARG
1545 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1546 #else
1547 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1548 #endif
1549 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1551 #else /* SHADOWPW && !DEBUG */
1552 " (core dumped)"
1553 #endif /* SHADOWPW && !DEBUG */
1555 for (disp = displays; disp; disp = disp->d_next)
1557 fcntl(disp->d_userfd, F_SETFL, 0);
1558 SetTTY(disp->d_userfd, &D_OldMode);
1559 write(disp->d_userfd, buf, strlen(buf));
1560 Kill(disp->d_userpid, SIG_BYE);
1562 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1563 Kill(getpid(), SIGKILL);
1564 eexit(11);
1565 #else /* SHADOWPW && !DEBUG */
1566 abort();
1567 #endif /* SHADOWPW && !DEBUG */
1568 SIGRETURN;
1571 static void
1572 DoWait()
1574 register int pid;
1575 struct win *p, *next;
1576 #ifdef BSDWAIT
1577 union wait wstat;
1578 #else
1579 int wstat;
1580 #endif
1582 #ifdef BSDJOBS
1583 # ifndef BSDWAIT
1584 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1585 # else
1586 # ifdef USE_WAIT2
1588 * From: rouilj@sni-usa.com (John Rouillard)
1589 * note that WUNTRACED is not documented to work, but it is defined in
1590 * /usr/include/sys/wait.h, so it may work
1592 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1593 # else /* USE_WAIT2 */
1594 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1595 # endif /* USE_WAIT2 */
1596 # endif
1597 #else /* BSDJOBS */
1598 while ((pid = wait(&wstat)) < 0)
1599 if (errno != EINTR)
1600 break;
1601 if (pid > 0)
1602 #endif /* BSDJOBS */
1604 for (p = windows; p; p = next)
1606 next = p->w_next;
1607 if ( (p->w_pid && pid == p->w_pid) ||
1608 (p->w_deadpid && pid == p->w_deadpid) )
1610 /* child has ceased to exist */
1611 p->w_pid = 0;
1613 #ifdef BSDJOBS
1614 if (WIFSTOPPED(wstat))
1616 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1617 #ifdef SIGTTIN
1618 if (WSTOPSIG(wstat) == SIGTTIN)
1620 Msg(0, "Suspended (tty input)");
1621 continue;
1623 #endif
1624 #ifdef SIGTTOU
1625 if (WSTOPSIG(wstat) == SIGTTOU)
1627 Msg(0, "Suspended (tty output)");
1628 continue;
1630 #endif
1631 /* Try to restart process */
1632 Msg(0, "Child has been stopped, restarting.");
1633 if (killpg(pid, SIGCONT))
1634 kill(pid, SIGCONT);
1636 else
1637 #endif
1639 WindowDied(p, wstat, 1);
1641 break;
1643 #ifdef PSEUDOS
1644 if (p->w_pwin && pid == p->w_pwin->p_pid)
1646 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1647 FreePseudowin(p);
1648 break;
1650 #endif
1652 if (p == 0)
1654 debug1("pid %d not found - hope that's ok\n", pid);
1660 static sigret_t
1661 FinitHandler SIGDEFARG
1663 #ifdef SIGHASARG
1664 debug1("FinitHandler called, sig %d.\n", sigsig);
1665 #else
1666 debug("FinitHandler called.\n");
1667 #endif
1668 Finit(1);
1669 SIGRETURN;
1672 void
1673 Finit(i)
1674 int i;
1676 struct win *p, *next;
1678 signal(SIGCHLD, SIG_DFL);
1679 signal(SIGHUP, SIG_IGN);
1680 debug1("Finit(%d);\n", i);
1681 for (p = windows; p; p = next)
1683 next = p->w_next;
1684 FreeWindow(p);
1686 if (ServerSocket != -1)
1688 debug1("we unlink(%s)\n", SockPath);
1689 #ifdef USE_SETEUID
1690 xseteuid(real_uid);
1691 xsetegid(real_gid);
1692 #endif
1693 (void) unlink(SockPath);
1694 #ifdef USE_SETEUID
1695 xseteuid(eff_uid);
1696 xsetegid(eff_gid);
1697 #endif
1699 for (display = displays; display; display = display->d_next)
1701 if (D_status)
1702 RemoveStatus();
1703 FinitTerm();
1704 #ifdef UTMPOK
1705 RestoreLoginSlot();
1706 #endif
1707 AddStr("[screen is terminating]\r\n");
1708 Flush();
1709 SetTTY(D_userfd, &D_OldMode);
1710 fcntl(D_userfd, F_SETFL, 0);
1711 freetty();
1712 Kill(D_userpid, SIG_BYE);
1715 * we _cannot_ call eexit(i) here,
1716 * instead of playing with the Socket above. Sigh.
1718 exit(i);
1721 void
1722 eexit(e)
1723 int e;
1725 debug("eexit\n");
1726 if (ServerSocket != -1)
1728 debug1("we unlink(%s)\n", SockPath);
1729 setgid(real_gid);
1730 setuid(real_uid);
1731 (void) unlink(SockPath);
1733 exit(e);
1736 void
1737 Hangup()
1739 if (display == 0)
1740 return;
1741 debug1("Hangup %x\n", display);
1742 if (D_userfd >= 0)
1744 close(D_userfd);
1745 D_userfd = -1;
1747 if (auto_detach || displays->d_next)
1748 Detach(D_HANGUP);
1749 else
1750 Finit(0);
1754 * Detach now has the following modes:
1755 *D_DETACH SIG_BYE detach backend and exit attacher
1756 *D_HANGUP SIG_BYE detach backend and exit attacher
1757 *D_STOP SIG_STOP stop attacher (and detach backend)
1758 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1759 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1760 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1761 *D_LOCK SIG_LOCK lock the attacher
1762 * (jw)
1763 * we always remove our utmp slots. (even when "lock" or "stop")
1764 * Note: Take extra care here, we may be called by interrupt!
1766 void
1767 Detach(mode)
1768 int mode;
1770 int sign = 0, pid;
1771 struct canvas *cv;
1772 struct win *p;
1774 if (display == 0)
1775 return;
1777 signal(SIGHUP, SIG_IGN);
1778 debug1("Detach(%d)\n", mode);
1779 if (D_status)
1780 RemoveStatus();
1781 FinitTerm();
1782 if (!display)
1783 return;
1784 switch (mode)
1786 case D_HANGUP:
1787 sign = SIG_BYE;
1788 break;
1789 case D_DETACH:
1790 AddStr("[detached]\r\n");
1791 sign = SIG_BYE;
1792 break;
1793 #ifdef BSDJOBS
1794 case D_STOP:
1795 sign = SIG_STOP;
1796 break;
1797 #endif
1798 #ifdef REMOTE_DETACH
1799 case D_REMOTE:
1800 AddStr("[remote detached]\r\n");
1801 sign = SIG_BYE;
1802 break;
1803 #endif
1804 #ifdef POW_DETACH
1805 case D_POWER:
1806 AddStr("[power detached]\r\n");
1807 if (PowDetachString)
1809 AddStr(PowDetachString);
1810 AddStr("\r\n");
1812 sign = SIG_POWER_BYE;
1813 break;
1814 #ifdef REMOTE_DETACH
1815 case D_REMOTE_POWER:
1816 AddStr("[remote power detached]\r\n");
1817 if (PowDetachString)
1819 AddStr(PowDetachString);
1820 AddStr("\r\n");
1822 sign = SIG_POWER_BYE;
1823 break;
1824 #endif
1825 #endif
1826 case D_LOCK:
1827 ClearAll();
1828 sign = SIG_LOCK;
1829 /* tell attacher to lock terminal with a lockprg. */
1830 break;
1832 #ifdef UTMPOK
1833 if (displays->d_next == 0)
1835 for (p = windows; p; p = p->w_next)
1837 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1839 RemoveUtmp(p);
1841 * Set the slot to 0 to get the window
1842 * logged in again.
1844 p->w_slot = (slot_t) 0;
1848 if (mode != D_HANGUP)
1849 RestoreLoginSlot();
1850 #endif
1851 if (displays->d_next == 0 && console_window)
1853 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1855 debug("could not release console - killing window\n");
1856 KillWindow(console_window);
1857 display = displays; /* restore display */
1860 if (D_fore)
1862 #ifdef MULTIUSER
1863 ReleaseAutoWritelock(display, D_fore);
1864 #endif
1865 D_user->u_detachwin = D_fore->w_number;
1866 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1868 AutosaveLayout(D_layout);
1869 layout_last = D_layout;
1870 for (cv = D_cvlist; cv; cv = cv->c_next)
1872 p = Layer2Window(cv->c_layer);
1873 SetCanvasWindow(cv, 0);
1874 if (p)
1875 WindowChanged(p, 'u');
1878 pid = D_userpid;
1879 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1880 FreeDisplay();
1881 if (displays == 0)
1882 /* Flag detached-ness */
1883 (void) chsock();
1885 * tell father what to do. We do that after we
1886 * freed the tty, thus getty feels more comfortable on hpux
1887 * if it was a power detach.
1889 Kill(pid, sign);
1890 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1891 debug("Detach returns, we are successfully detached.\n");
1892 signal(SIGHUP, SigHup);
1895 static int
1896 IsSymbol(e, s)
1897 char *e, *s;
1899 register int l;
1901 l = strlen(s);
1902 return strncmp(e, s, l) == 0 && e[l] == '=';
1905 void
1906 MakeNewEnv()
1908 register char **op, **np;
1909 static char stybuf[MAXSTR];
1911 for (op = environ; *op; ++op)
1913 if (NewEnv)
1914 free((char *)NewEnv);
1915 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1916 if (!NewEnv)
1917 Panic(0, strnomem);
1918 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1919 *np++ = stybuf; /* NewEnv[0] */
1920 *np++ = Term; /* NewEnv[1] */
1921 np++; /* room for SHELL */
1922 #ifdef TIOCSWINSZ
1923 np += 2; /* room for TERMCAP and WINDOW */
1924 #else
1925 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
1926 #endif
1928 for (op = environ; *op; ++op)
1930 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
1931 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
1932 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
1933 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
1935 *np++ = *op;
1937 *np = 0;
1940 void
1941 /*VARARGS2*/
1942 #if defined(USEVARARGS) && defined(__STDC__)
1943 Msg(int err, char *fmt, VA_DOTS)
1944 #else
1945 Msg(err, fmt, VA_DOTS)
1946 int err;
1947 char *fmt;
1948 VA_DECL
1949 #endif
1951 VA_LIST(ap)
1952 char buf[MAXPATHLEN*2];
1953 char *p = buf;
1955 VA_START(ap, fmt);
1956 fmt = DoNLS(fmt);
1957 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1958 VA_END(ap);
1959 if (err)
1961 p += strlen(p);
1962 *p++ = ':';
1963 *p++ = ' ';
1964 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
1965 buf[sizeof(buf) - 1] = 0;
1967 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
1969 if (display && displays)
1970 MakeStatus(buf);
1971 else if (displays)
1973 for (display = displays; display; display = display->d_next)
1974 MakeStatus(buf);
1976 else if (display)
1978 /* no displays but a display - must have forked.
1979 * send message to backend!
1981 char *tty = D_usertty;
1982 struct display *olddisplay = display;
1983 display = 0; /* only send once */
1984 SendErrorMsg(tty, buf);
1985 display = olddisplay;
1987 else
1988 printf("%s\r\n", buf);
1992 * Call FinitTerm for all displays, write a message to each and call eexit();
1994 void
1995 /*VARARGS2*/
1996 #if defined(USEVARARGS) && defined(__STDC__)
1997 Panic(int err, char *fmt, VA_DOTS)
1998 #else
1999 Panic(err, fmt, VA_DOTS)
2000 int err;
2001 char *fmt;
2002 VA_DECL
2003 #endif
2005 VA_LIST(ap)
2006 char buf[MAXPATHLEN*2];
2007 char *p = buf;
2009 VA_START(ap, fmt);
2010 fmt = DoNLS(fmt);
2011 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2012 VA_END(ap);
2013 if (err)
2015 p += strlen(p);
2016 *p++ = ':';
2017 *p++ = ' ';
2018 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2019 buf[sizeof(buf) - 1] = 0;
2021 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2022 if (displays == 0 && display == 0)
2024 printf("%s\r\n", buf);
2025 if (PanicPid)
2026 Kill(PanicPid, SIG_BYE);
2028 else if (displays == 0)
2030 /* no displays but a display - must have forked.
2031 * send message to backend!
2033 char *tty = D_usertty;
2034 display = 0;
2035 SendErrorMsg(tty, buf);
2036 sleep(2);
2037 _exit(1);
2039 else
2040 for (display = displays; display; display = display->d_next)
2042 if (D_status)
2043 RemoveStatus();
2044 FinitTerm();
2045 Flush();
2046 #ifdef UTMPOK
2047 RestoreLoginSlot();
2048 #endif
2049 SetTTY(D_userfd, &D_OldMode);
2050 fcntl(D_userfd, F_SETFL, 0);
2051 write(D_userfd, buf, strlen(buf));
2052 write(D_userfd, "\n", 1);
2053 freetty();
2054 if (D_userpid)
2055 Kill(D_userpid, SIG_BYE);
2057 #ifdef MULTIUSER
2058 if (tty_oldmode >= 0)
2060 # ifdef USE_SETEUID
2061 if (setuid(own_uid))
2062 xseteuid(own_uid); /* may be a loop. sigh. */
2063 # else
2064 setuid(own_uid);
2065 # endif
2066 debug1("Panic: changing back modes from %s\n", attach_tty);
2067 chmod(attach_tty, tty_oldmode);
2069 #endif
2070 eexit(1);
2075 * '^' is allowed as an escape mechanism for control characters. jw.
2077 * Added time insertion using ideas/code from /\ndy Jones
2078 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2082 #ifndef USE_LOCALE
2083 static const char days[] = "SunMonTueWedThuFriSat";
2084 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2085 #endif
2087 static char winmsg_buf[MAXSTR];
2088 #define MAX_WINMSG_REND 16 /* rendition changes */
2089 static int winmsg_rend[MAX_WINMSG_REND];
2090 static int winmsg_rendpos[MAX_WINMSG_REND];
2091 static int winmsg_numrend;
2093 static char *
2094 pad_expand(buf, p, numpad, padlen)
2095 char *buf;
2096 char *p;
2097 int numpad;
2098 int padlen;
2100 char *pn, *pn2;
2101 int i, r;
2103 padlen = padlen - (p - buf); /* space for rent */
2104 if (padlen < 0)
2105 padlen = 0;
2106 pn2 = pn = p + padlen;
2107 r = winmsg_numrend;
2108 while (p >= buf)
2110 if (r && p - buf == winmsg_rendpos[r - 1])
2112 winmsg_rendpos[--r] = pn - buf;
2113 continue;
2115 *pn-- = *p;
2116 if (*p-- == 127)
2118 pn[1] = ' ';
2119 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2120 padlen -= i;
2121 while (i-- > 0)
2122 *pn-- = ' ';
2123 numpad--;
2126 return pn2;
2129 struct backtick {
2130 struct backtick *next;
2131 int num;
2132 int tick;
2133 int lifespan;
2134 time_t bestbefore;
2135 char result[MAXSTR];
2136 char **cmdv;
2137 struct event ev;
2138 char *buf;
2139 int bufi;
2142 struct backtick *backticks;
2144 static void
2145 backtick_filter(bt)
2146 struct backtick *bt;
2148 char *p, *q;
2149 int c;
2151 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2153 if (c == '\t')
2154 c = ' ';
2155 if (c >= ' ' || c == '\005')
2156 *q++ = c;
2158 *q = 0;
2161 static void
2162 backtick_fn(ev, data)
2163 struct event *ev;
2164 char *data;
2166 struct backtick *bt;
2167 int i, j, k, l;
2169 bt = (struct backtick *)data;
2170 debug1("backtick_fn for #%d\n", bt->num);
2171 i = bt->bufi;
2172 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2173 if (l <= 0)
2175 debug1("EOF on backtick #%d\n", bt->num);
2176 evdeq(ev);
2177 close(ev->fd);
2178 ev->fd = -1;
2179 return;
2181 debug1("read %d bytes\n", l);
2182 i += l;
2183 for (j = 0; j < l; j++)
2184 if (bt->buf[i - j - 1] == '\n')
2185 break;
2186 if (j < l)
2188 for (k = i - j - 2; k >= 0; k--)
2189 if (bt->buf[k] == '\n')
2190 break;
2191 k++;
2192 bcopy(bt->buf + k, bt->result, i - j - k);
2193 bt->result[i - j - k - 1] = 0;
2194 backtick_filter(bt);
2195 WindowChanged(0, '`');
2197 if (j == l && i == MAXSTR)
2199 j = MAXSTR/2;
2200 l = j + 1;
2202 if (j < l)
2204 if (j)
2205 bcopy(bt->buf + i - j, bt->buf, j);
2206 i = j;
2208 bt->bufi = i;
2211 void
2212 setbacktick(num, lifespan, tick, cmdv)
2213 int num;
2214 int lifespan;
2215 int tick;
2216 char **cmdv;
2218 struct backtick **btp, *bt;
2219 char **v;
2221 debug1("setbacktick called for backtick #%d\n", num);
2222 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2223 if (bt->num == num)
2224 break;
2225 if (!bt && !cmdv)
2226 return;
2227 if (bt)
2229 for (v = bt->cmdv; *v; v++)
2230 free(*v);
2231 free(bt->cmdv);
2232 if (bt->buf)
2233 free(bt->buf);
2234 if (bt->ev.fd >= 0)
2235 close(bt->ev.fd);
2236 evdeq(&bt->ev);
2238 if (bt && !cmdv)
2240 *btp = bt->next;
2241 free(bt);
2242 return;
2244 if (!bt)
2246 bt = (struct backtick *)malloc(sizeof *bt);
2247 if (!bt)
2249 Msg(0, strnomem);
2250 return;
2252 bzero(bt, sizeof(*bt));
2253 bt->next = 0;
2254 *btp = bt;
2256 bt->num = num;
2257 bt->tick = tick;
2258 bt->lifespan = lifespan;
2259 bt->bestbefore = 0;
2260 bt->result[0] = 0;
2261 bt->buf = 0;
2262 bt->bufi = 0;
2263 bt->cmdv = cmdv;
2264 bt->ev.fd = -1;
2265 if (bt->tick == 0 && bt->lifespan == 0)
2267 debug("setbacktick: continuous mode\n");
2268 bt->buf = (char *)malloc(MAXSTR);
2269 if (bt->buf == 0)
2271 Msg(0, strnomem);
2272 setbacktick(num, 0, 0, (char **)0);
2273 return;
2275 bt->ev.type = EV_READ;
2276 bt->ev.fd = readpipe(bt->cmdv);
2277 bt->ev.handler = backtick_fn;
2278 bt->ev.data = (char *)bt;
2279 if (bt->ev.fd >= 0)
2280 evenq(&bt->ev);
2284 static char *
2285 runbacktick(bt, tickp, now)
2286 struct backtick *bt;
2287 int *tickp;
2288 time_t now;
2290 int f, i, l, j;
2291 time_t now2;
2293 debug1("runbacktick called for backtick #%d\n", bt->num);
2294 if (bt->tick && (!*tickp || bt->tick < *tickp))
2295 *tickp = bt->tick;
2296 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2298 debug1("returning old result (%d)\n", bt->lifespan);
2299 return bt->result;
2301 f = readpipe(bt->cmdv);
2302 if (f == -1)
2303 return bt->result;
2304 i = 0;
2305 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2307 debug1("runbacktick: read %d bytes\n", l);
2308 i += l;
2309 for (j = 1; j < l; j++)
2310 if (bt->result[i - j - 1] == '\n')
2311 break;
2312 if (j == l && i == sizeof(bt->result))
2314 j = sizeof(bt->result) / 2;
2315 l = j + 1;
2317 if (j < l)
2319 bcopy(bt->result + i - j, bt->result, j);
2320 i = j;
2323 close(f);
2324 bt->result[sizeof(bt->result) - 1] = '\n';
2325 if (i && bt->result[i - 1] == '\n')
2326 i--;
2327 debug1("runbacktick: finished, %d bytes\n", i);
2328 bt->result[i] = 0;
2329 backtick_filter(bt);
2330 (void)time(&now2);
2331 bt->bestbefore = now2 + bt->lifespan;
2332 return bt->result;
2335 char *
2336 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2337 char *str;
2338 struct win *win;
2339 int esc;
2340 int padlen;
2341 struct event *ev;
2342 int rec;
2344 static int tick;
2345 char *s = str;
2346 register char *p = winmsg_buf;
2347 register int ctrl;
2348 struct timeval now;
2349 struct tm *tm;
2350 int l, i, r;
2351 int num;
2352 int zeroflg;
2353 int longflg;
2354 int minusflg;
2355 int plusflg;
2356 int qmflag = 0, omflag = 0, qmnumrend = 0;
2357 char *qmpos = 0;
2358 int numpad = 0;
2359 int lastpad = 0;
2360 int truncpos = -1;
2361 int truncper = 0;
2362 int trunclong = 0;
2363 struct backtick *bt;
2365 if (winmsg_numrend >= 0)
2366 winmsg_numrend = 0;
2367 else
2368 winmsg_numrend = -winmsg_numrend;
2370 tick = 0;
2371 tm = 0;
2372 ctrl = 0;
2373 gettimeofday(&now, NULL);
2374 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2376 *p = *s;
2377 if (ctrl)
2379 ctrl = 0;
2380 if (*s != '^' && *s >= 64)
2381 *p &= 0x1f;
2382 continue;
2384 if (*s != esc)
2386 if (esc == '%')
2388 switch (*s)
2390 #if 0
2391 case '~':
2392 *p = BELL;
2393 break;
2394 #endif
2395 case '^':
2396 ctrl = 1;
2397 *p-- = '^';
2398 break;
2399 default:
2400 break;
2403 continue;
2405 if (*++s == esc) /* double escape ? */
2406 continue;
2407 if ((plusflg = *s == '+') != 0)
2408 s++;
2409 if ((minusflg = *s == '-') != 0)
2410 s++;
2411 if ((zeroflg = *s == '0') != 0)
2412 s++;
2413 num = 0;
2414 while(*s >= '0' && *s <= '9')
2415 num = num * 10 + (*s++ - '0');
2416 if ((longflg = *s == 'L') != 0)
2417 s++;
2418 switch (*s)
2420 case '?':
2421 p--;
2422 if (qmpos)
2424 if ((!qmflag && !omflag) || omflag == 1)
2426 p = qmpos;
2427 if (qmnumrend < winmsg_numrend)
2428 winmsg_numrend = qmnumrend;
2430 qmpos = 0;
2431 break;
2433 qmpos = p;
2434 qmnumrend = winmsg_numrend;
2435 qmflag = omflag = 0;
2436 break;
2437 case ':':
2438 p--;
2439 if (!qmpos)
2440 break;
2441 if (qmflag && omflag != 1)
2443 omflag = 1;
2444 qmpos = p;
2445 qmnumrend = winmsg_numrend;
2447 else
2449 p = qmpos;
2450 if (qmnumrend < winmsg_numrend)
2451 winmsg_numrend = qmnumrend;
2452 omflag = -1;
2454 break;
2455 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2456 case 'a': case 'A': case 's': case 'c': case 'C':
2457 if (l < 4)
2458 break;
2459 if (tm == 0)
2461 time_t nowsec = now.tv_sec;
2462 tm = localtime(&nowsec);
2464 qmflag = 1;
2465 if (!tick || tick > 3600)
2466 tick = 3600;
2467 switch (*s)
2469 case 'd':
2470 sprintf(p, "%02d", tm->tm_mday % 100);
2471 break;
2472 case 'D':
2473 #ifdef USE_LOCALE
2474 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2475 #else
2476 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2477 #endif
2478 break;
2479 case 'm':
2480 sprintf(p, "%02d", tm->tm_mon + 1);
2481 break;
2482 case 'M':
2483 #ifdef USE_LOCALE
2484 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2485 #else
2486 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2487 #endif
2488 break;
2489 case 'y':
2490 sprintf(p, "%02d", tm->tm_year % 100);
2491 break;
2492 case 'Y':
2493 sprintf(p, "%04d", tm->tm_year + 1900);
2494 break;
2495 case 'a':
2496 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2497 break;
2498 case 'A':
2499 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2500 break;
2501 case 's':
2502 sprintf(p, "%02d", tm->tm_sec);
2503 tick = 1;
2504 break;
2505 case 'c':
2506 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2507 if (!tick || tick > 60)
2508 tick = 60;
2509 break;
2510 case 'C':
2511 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2512 if (!tick || tick > 60)
2513 tick = 60;
2514 break;
2515 default:
2516 break;
2518 p += strlen(p) - 1;
2519 break;
2520 case 'l':
2521 #ifdef LOADAV
2522 *p = 0;
2523 if (l > 20)
2524 AddLoadav(p);
2525 if (*p)
2527 qmflag = 1;
2528 p += strlen(p) - 1;
2530 else
2531 *p = '?';
2532 if (!tick || tick > 60)
2533 tick = 60;
2534 #else
2535 *p = '?';
2536 #endif
2537 p += strlen(p) - 1;
2538 break;
2539 case '`':
2540 case 'h':
2541 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2543 p--;
2544 break;
2546 if (*s == '`')
2548 for (bt = backticks; bt; bt = bt->next)
2549 if (bt->num == num)
2550 break;
2551 if (bt == 0)
2553 p--;
2554 break;
2558 char savebuf[sizeof(winmsg_buf)];
2559 int oldtick = tick;
2560 int oldnumrend = winmsg_numrend;
2562 *p = 0;
2563 strcpy(savebuf, winmsg_buf);
2564 winmsg_numrend = -winmsg_numrend;
2565 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2566 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2567 if (!tick || oldtick < tick)
2568 tick = oldtick;
2569 if ((int)strlen(winmsg_buf) < l)
2570 strcat(savebuf, winmsg_buf);
2571 strcpy(winmsg_buf, savebuf);
2572 while (oldnumrend < winmsg_numrend)
2573 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2574 if (*p)
2575 qmflag = 1;
2576 p += strlen(p) - 1;
2578 break;
2579 case 'w':
2580 case 'W':
2582 struct win *oldfore = 0;
2583 char *ss;
2585 if (display)
2587 oldfore = D_fore;
2588 D_fore = win;
2590 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2591 if (minusflg)
2592 *ss = 0;
2593 if (display)
2594 D_fore = oldfore;
2596 if (*p)
2597 qmflag = 1;
2598 p += strlen(p) - 1;
2599 break;
2600 case 'u':
2601 *p = 0;
2602 if (win)
2603 AddOtherUsers(p, l - 1, win);
2604 if (*p)
2605 qmflag = 1;
2606 p += strlen(p) - 1;
2607 break;
2608 case 'f':
2609 *p = 0;
2610 if (win)
2611 AddWindowFlags(p, l - 1, win);
2612 if (*p)
2613 qmflag = 1;
2614 p += strlen(p) - 1;
2615 break;
2616 case 't':
2617 *p = 0;
2618 if (win && (int)strlen(win->w_title) < l)
2620 strcpy(p, win->w_title);
2621 if (*p)
2622 qmflag = 1;
2624 p += strlen(p) - 1;
2625 break;
2626 case '{':
2628 char rbuf[128];
2629 s++;
2630 for (i = 0; i < 127; i++)
2631 if (s[i] && s[i] != '}')
2632 rbuf[i] = s[i];
2633 else
2634 break;
2635 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2637 r = -1;
2638 rbuf[i] = 0;
2639 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2640 if (i != 1 || rbuf[0] != '-')
2641 r = ParseAttrColor(rbuf, (char *)0, 0);
2642 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2644 winmsg_rend[winmsg_numrend] = r;
2645 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2646 winmsg_numrend++;
2649 s += i;
2650 p--;
2652 break;
2653 case 'H':
2654 *p = 0;
2655 if ((int)strlen(HostName) < l)
2657 strcpy(p, HostName);
2658 if (*p)
2659 qmflag = 1;
2661 p += strlen(p) - 1;
2662 break;
2663 case 'F':
2664 p--;
2665 /* small hack */
2666 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2667 minusflg = !minusflg;
2668 if (minusflg)
2669 qmflag = 1;
2670 break;
2671 case '>':
2672 truncpos = p - winmsg_buf;
2673 truncper = num > 100 ? 100 : num;
2674 trunclong = longflg;
2675 p--;
2676 break;
2677 case '=':
2678 case '<':
2679 *p = ' ';
2680 if (num || zeroflg || plusflg || longflg || (*s != '='))
2682 /* expand all pads */
2683 if (minusflg)
2685 num = (plusflg ? lastpad : padlen) - num;
2686 if (!plusflg && padlen == 0)
2687 num = p - winmsg_buf;
2688 plusflg = 0;
2690 else if (!zeroflg)
2692 if (*s != '=' && num == 0 && !plusflg)
2693 num = 100;
2694 if (num > 100)
2695 num = 100;
2696 if (padlen == 0)
2697 num = p - winmsg_buf;
2698 else
2699 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2701 if (num < 0)
2702 num = 0;
2703 if (plusflg)
2704 num += lastpad;
2705 if (num > MAXSTR - 1)
2706 num = MAXSTR - 1;
2707 if (numpad)
2708 p = pad_expand(winmsg_buf, p, numpad, num);
2709 numpad = 0;
2710 if (p - winmsg_buf > num && !longflg)
2712 int left, trunc;
2714 if (truncpos == -1)
2716 truncpos = lastpad;
2717 truncper = 0;
2719 trunc = lastpad + truncper * (num - lastpad) / 100;
2720 if (trunc > num)
2721 trunc = num;
2722 if (trunc < lastpad)
2723 trunc = lastpad;
2724 left = truncpos - trunc;
2725 if (left > p - winmsg_buf - num)
2726 left = p - winmsg_buf - num;
2727 debug1("lastpad = %d, ", lastpad);
2728 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2729 if (left > 0)
2731 if (left + lastpad > p - winmsg_buf)
2732 left = p - winmsg_buf - lastpad;
2733 if (p - winmsg_buf - lastpad - left > 0)
2734 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2735 p -= left;
2736 r = winmsg_numrend;
2737 while (r && winmsg_rendpos[r - 1] > lastpad)
2739 r--;
2740 winmsg_rendpos[r] -= left;
2741 if (winmsg_rendpos[r] < lastpad)
2742 winmsg_rendpos[r] = lastpad;
2744 if (trunclong)
2746 if (p - winmsg_buf > lastpad)
2747 winmsg_buf[lastpad] = '.';
2748 if (p - winmsg_buf > lastpad + 1)
2749 winmsg_buf[lastpad + 1] = '.';
2750 if (p - winmsg_buf > lastpad + 2)
2751 winmsg_buf[lastpad + 2] = '.';
2754 if (p - winmsg_buf > num)
2756 p = winmsg_buf + num;
2757 if (trunclong)
2759 if (num - 1 >= lastpad)
2760 p[-1] = '.';
2761 if (num - 2 >= lastpad)
2762 p[-2] = '.';
2763 if (num - 3 >= lastpad)
2764 p[-3] = '.';
2766 r = winmsg_numrend;
2767 while (r && winmsg_rendpos[r - 1] > num)
2768 winmsg_rendpos[--r] = num;
2770 truncpos = -1;
2771 trunclong = 0;
2772 if (lastpad > p - winmsg_buf)
2773 lastpad = p - winmsg_buf;
2774 debug1("lastpad now %d\n", lastpad);
2776 if (*s == '=')
2778 while (p - winmsg_buf < num)
2779 *p++ = ' ';
2780 lastpad = p - winmsg_buf;
2781 truncpos = -1;
2782 trunclong = 0;
2783 debug1("lastpad2 now %d\n", lastpad);
2785 p--;
2787 else if (padlen)
2789 *p = 127; /* internal pad representation */
2790 numpad++;
2792 break;
2793 case 'n':
2794 s++;
2795 /* FALLTHROUGH */
2796 default:
2797 s--;
2798 if (l > 10 + num)
2800 if (num == 0)
2801 num = 1;
2802 if (!win)
2803 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2804 else
2805 sprintf(p, "%*d", num, win->w_number);
2806 qmflag = 1;
2807 p += strlen(p) - 1;
2809 break;
2812 if (qmpos && !qmflag)
2813 p = qmpos + 1;
2814 *p = '\0';
2815 if (numpad)
2817 if (padlen > MAXSTR - 1)
2818 padlen = MAXSTR - 1;
2819 p = pad_expand(winmsg_buf, p, numpad, padlen);
2821 if (ev)
2823 evdeq(ev); /* just in case */
2824 ev->timeout.tv_sec = 0;
2825 ev->timeout.tv_usec = 0;
2827 if (ev && tick)
2829 now.tv_usec = 100000;
2830 if (tick == 1)
2831 now.tv_sec++;
2832 else
2833 now.tv_sec += tick - (now.tv_sec % tick);
2834 ev->timeout = now;
2835 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2837 return winmsg_buf;
2840 char *
2841 MakeWinMsg(s, win, esc)
2842 char *s;
2843 struct win *win;
2844 int esc;
2846 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2849 void
2850 PutWinMsg(s, start, max)
2851 char *s;
2852 int start, max;
2854 int i, p, l, r, n;
2855 struct mchar rend;
2856 struct mchar rendstack[MAX_WINMSG_REND];
2857 int rendstackn = 0;
2859 if (s != winmsg_buf)
2861 /* sorry, no fancy coloring available */
2862 debug1("PutWinMsg %s plain\n", s);
2863 l = strlen(s);
2864 if (l > max)
2865 l = max;
2866 l -= start;
2867 s += start;
2868 while (l-- > 0)
2869 PUTCHARLP(*s++);
2870 return;
2872 rend = D_rend;
2873 p = 0;
2874 l = strlen(s);
2875 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
2876 for (i = 0; i < winmsg_numrend && max > 0; i++)
2878 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
2879 break;
2880 if (p < winmsg_rendpos[i])
2882 n = winmsg_rendpos[i] - p;
2883 if (n > max)
2884 n = max;
2885 max -= n;
2886 p += n;
2887 while(n-- > 0)
2889 if (start-- > 0)
2890 s++;
2891 else
2892 PUTCHARLP(*s++);
2895 r = winmsg_rend[i];
2896 if (r == -1)
2898 if (rendstackn > 0)
2899 rend = rendstack[--rendstackn];
2901 else
2903 rendstack[rendstackn++] = rend;
2904 ApplyAttrColor(r, &rend);
2906 SetRendition(&rend);
2908 if (p < l)
2910 n = l - p;
2911 if (n > max)
2912 n = max;
2913 while(n-- > 0)
2915 if (start-- > 0)
2916 s++;
2917 else
2918 PUTCHARLP(*s++);
2924 #ifdef DEBUG
2925 static void
2926 fds1(i, j)
2927 int i, j;
2929 while (i < j)
2931 debug1("%d ", i);
2932 i++;
2934 if ((j = open("/dev/null", 0)) >= 0)
2936 fds1(i + 1, j);
2937 close(j);
2939 else
2941 while (dup(++i) < 0 && errno != EBADF)
2942 debug1("%d ", i);
2943 debug1(" [%d]\n", i);
2947 static void
2948 fds()
2950 debug("fds: ");
2951 fds1(-1, -1);
2953 #endif
2955 static void
2956 serv_read_fn(ev, data)
2957 struct event *ev;
2958 char *data;
2960 debug("Knock - knock!\n");
2961 ReceiveMsg();
2964 static void
2965 serv_select_fn(ev, data)
2966 struct event *ev;
2967 char *data;
2969 struct win *p;
2971 debug("serv_select_fn called\n");
2972 /* XXX: messages?? */
2973 if (GotSigChld)
2975 SigChldHandler();
2977 if (InterruptPlease)
2979 debug("Backend received interrupt\n");
2980 /* This approach is rather questionable in a multi-display
2981 * environment */
2982 if (fore && displays)
2984 #if defined(TERMIO) || defined(POSIX)
2985 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
2986 #else
2987 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
2988 #endif
2989 #ifdef PSEUDOS
2990 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
2991 &ibuf, 1);
2992 debug1("Backend wrote interrupt to %d", fore->w_number);
2993 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
2994 #else
2995 write(fore->w_ptyfd, &ibuf, 1);
2996 debug1("Backend wrote interrupt to %d\n", fore->w_number);
2997 #endif
2999 InterruptPlease = 0;
3002 for (p = windows; p; p = p->w_next)
3004 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3006 struct canvas *cv;
3007 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3008 p->w_bell = BELL_ON;
3009 for (display = displays; display; display = display->d_next)
3011 for (cv = D_cvlist; cv; cv = cv->c_next)
3012 if (cv->c_layer->l_bottom == &p->w_layer)
3013 break;
3014 if (cv == 0)
3016 p->w_bell = BELL_DONE;
3017 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3019 else if (visual && !D_VB && (!D_status || !D_status_bell))
3021 Msg(0, "%s", VisualBellString);
3022 if (D_status)
3024 D_status_bell = 1;
3025 debug1("using vbell timeout %d\n", VBellWait);
3026 SetTimeout(&D_statusev, VBellWait );
3030 /* don't annoy the user with two messages */
3031 if (p->w_monitor == MON_FOUND)
3032 p->w_monitor = MON_DONE;
3033 WindowChanged(p, 'f');
3035 if (p->w_monitor == MON_FOUND)
3037 struct canvas *cv;
3038 p->w_monitor = MON_ON;
3039 for (display = displays; display; display = display->d_next)
3041 for (cv = D_cvlist; cv; cv = cv->c_next)
3042 if (cv->c_layer->l_bottom == &p->w_layer)
3043 break;
3044 if (cv)
3045 continue; /* user already sees window */
3046 #ifdef MULTIUSER
3047 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3048 continue; /* user doesn't care */
3049 #endif
3050 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3051 p->w_monitor = MON_DONE;
3053 WindowChanged(p, 'f');
3057 for (display = displays; display; display = display->d_next)
3059 struct canvas *cv;
3060 if (D_status == STATUS_ON_WIN)
3061 continue;
3062 /* XXX: should use display functions! */
3063 for (cv = D_cvlist; cv; cv = cv->c_next)
3065 int lx, ly;
3067 /* normalize window, see resize.c */
3068 lx = cv->c_layer->l_x;
3069 ly = cv->c_layer->l_y;
3070 if (lx == cv->c_layer->l_width)
3071 lx--;
3072 if (ly + cv->c_yoff < cv->c_ys)
3074 int i, n = cv->c_ys - (ly + cv->c_yoff);
3075 cv->c_yoff = cv->c_ys - ly;
3076 RethinkViewportOffsets(cv);
3077 if (n > cv->c_layer->l_height)
3078 n = cv->c_layer->l_height;
3079 CV_CALL(cv,
3080 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3081 LayRedisplayLine(-1, -1, -1, 1);
3082 for (i = 0; i < n; i++)
3083 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3084 if (cv == cv->c_display->d_forecv)
3085 LaySetCursor();
3088 else if (ly + cv->c_yoff > cv->c_ye)
3090 int i, n = ly + cv->c_yoff - cv->c_ye;
3091 cv->c_yoff = cv->c_ye - ly;
3092 RethinkViewportOffsets(cv);
3093 if (n > cv->c_layer->l_height)
3094 n = cv->c_layer->l_height;
3095 CV_CALL(cv,
3096 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3097 LayRedisplayLine(-1, -1, -1, 1);
3098 for (i = 0; i < n; i++)
3099 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3100 if (cv == cv->c_display->d_forecv)
3101 LaySetCursor();
3104 if (lx + cv->c_xoff < cv->c_xs)
3106 int i, n = cv->c_xs - (lx + cv->c_xoff);
3107 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3108 n = (cv->c_xe - cv->c_xs + 1) / 2;
3109 if (cv->c_xoff + n > cv->c_xs)
3110 n = cv->c_xs - cv->c_xoff;
3111 cv->c_xoff += n;
3112 RethinkViewportOffsets(cv);
3113 if (n > cv->c_layer->l_width)
3114 n = cv->c_layer->l_width;
3115 CV_CALL(cv,
3116 LayRedisplayLine(-1, -1, -1, 1);
3117 for (i = 0; i < flayer->l_height; i++)
3119 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3120 LayRedisplayLine(i, 0, n - 1, 1);
3122 if (cv == cv->c_display->d_forecv)
3123 LaySetCursor();
3126 else if (lx + cv->c_xoff > cv->c_xe)
3128 int i, n = lx + cv->c_xoff - cv->c_xe;
3129 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3130 n = (cv->c_xe - cv->c_xs + 1) / 2;
3131 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3132 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3133 cv->c_xoff -= n;
3134 RethinkViewportOffsets(cv);
3135 if (n > cv->c_layer->l_width)
3136 n = cv->c_layer->l_width;
3137 CV_CALL(cv,
3138 LayRedisplayLine(-1, -1, -1, 1);
3139 for (i = 0; i < flayer->l_height; i++)
3141 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3142 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3144 if (cv == cv->c_display->d_forecv)
3145 LaySetCursor();
3151 for (display = displays; display; display = display->d_next)
3153 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3154 continue;
3155 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3156 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3160 static void
3161 logflush_fn(ev, data)
3162 struct event *ev;
3163 char *data;
3165 struct win *p;
3166 char *buf;
3167 int n;
3169 if (!islogfile(NULL))
3170 return; /* no more logfiles */
3171 logfflush(NULL);
3172 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3173 if (n)
3175 SetTimeout(ev, n * 1000);
3176 evenq(ev); /* re-enqueue ourself */
3178 if (!logtstamp_on)
3179 return;
3180 /* write fancy time-stamp */
3181 for (p = windows; p; p = p->w_next)
3183 if (!p->w_log)
3184 continue;
3185 p->w_logsilence += n;
3186 if (p->w_logsilence < logtstamp_after)
3187 continue;
3188 if (p->w_logsilence - n >= logtstamp_after)
3189 continue;
3190 buf = MakeWinMsg(logtstamp_string, p, '%');
3191 logfwrite(p->w_log, buf, strlen(buf));
3196 * Interprets ^?, ^@ and other ^-control-char notation.
3197 * Interprets \ddd octal notation
3199 * The result is placed in *cp, p is advanced behind the parsed expression and
3200 * returned.
3202 static char *
3203 ParseChar(p, cp)
3204 char *p, *cp;
3206 if (*p == 0)
3207 return 0;
3208 if (*p == '^' && p[1])
3210 if (*++p == '?')
3211 *cp = '\177';
3212 else if (*p >= '@')
3213 *cp = Ctrl(*p);
3214 else
3215 return 0;
3216 ++p;
3218 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3220 *cp = 0;
3222 *cp = *cp * 8 + *p - '0';
3223 while (*++p <= '7' && *p >= '0');
3225 else
3226 *cp = *p++;
3227 return p;
3230 static int
3231 ParseEscape(p)
3232 char *p;
3234 unsigned char buf[2];
3236 if (*p == 0)
3237 SetEscape((struct acluser *)0, -1, -1);
3238 else
3240 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3241 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3242 return -1;
3243 SetEscape((struct acluser *)0, buf[0], buf[1]);
3245 return 0;