2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
4 * Copyright (c) 2008, 2009
5 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
6 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
7 * Micah Cowan (micah@cowan.name)
8 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
9 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
10 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
11 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
12 * Copyright (c) 1987 Oliver Laumann
15 * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu
16 * Bill Barry barryb@dots.physics.orst.edu
17 * Randy Lundquist randyl@dots.physics.orst.edu
19 * Modifications Copyright (c) 1995 by
20 * Science Access Project, Oregon State University.
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 3, or (at your option)
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program (see the file COPYING); if not, see
35 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
36 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
38 ****************************************************************
41 #include <sys/types.h>
47 # include <sys/sysmacros.h>
52 # include <sys/ioctl.h>
62 # include <sys/stropts.h>
65 #if defined(SYSV) && !defined(ISC)
66 # include <sys/utsname.h>
69 #if defined(sequent) || defined(SVR4)
70 # include <sys/resource.h>
71 #endif /* sequent || SVR4 */
75 # include <sys/sioctl.h>
79 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
82 #if defined(USE_LOCALE) || defined(ENCODINGS)
85 #if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
86 # include <langinfo.h>
94 #include "patchlevel.h"
97 * At the moment we only need the real password if the
98 * builtin lock is used. Therefore disable SHADOWPW if
99 * we do not really need it (kind of security thing).
108 #endif /* SHADOWPW */
110 #include "logfile.h" /* islogfile, logfflush */
117 extern char Term
[], screenterm
[], **environ
, Termcap
[];
119 int VBellWait
, MsgWait
, MsgMinWait
, SilenceWait
;
121 extern struct acluser
*users
;
122 extern struct display
*displays
, *display
;
124 extern struct LayFuncs MarkLf
;
127 extern int visual_bell
;
129 extern unsigned char mark_key_tab
[];
131 extern char version
[];
132 extern char DefaultShell
[];
134 extern char *zmodem_sendcmd
;
135 extern char *zmodem_recvcmd
;
137 extern struct layout
*layout_last
;
143 extern struct NewWindow nwin_undef
, nwin_default
, nwin_options
;
146 static struct passwd
*getpwbyname
__P((char *, struct passwd
*));
147 static void SigChldHandler
__P((void));
148 static sigret_t SigChld
__P(SIGPROTOARG
);
149 static sigret_t SigInt
__P(SIGPROTOARG
);
150 static sigret_t CoreDump
__P(SIGPROTOARG
);
151 static sigret_t FinitHandler
__P(SIGPROTOARG
);
152 static void DoWait
__P((void));
153 static void serv_read_fn
__P((struct event
*, char *));
154 static void serv_select_fn
__P((struct event
*, char *));
155 static void logflush_fn
__P((struct event
*, char *));
156 static void backtick_filter
__P((struct backtick
*));
157 static void backtick_fn
__P((struct event
*, char *));
158 static char *runbacktick
__P((struct backtick
*, int *, time_t));
159 static int IsSymbol
__P((char *, char *));
160 static char *ParseChar
__P((char *, char *));
161 static int ParseEscape
__P((char *));
162 static char *pad_expand
__P((char *, char *, int, int));
164 static void fds
__P((void));
167 int nversion
; /* numerical version, used for secondary DA */
175 struct mode attach_Mode
;
177 char SockPath
[MAXPATHLEN
+ 2 * MAXSTR
];
178 char *SockName
; /* SockName is pointer in SockPath */
179 char *SockMatch
= NULL
; /* session id command line argument */
180 int ServerSocket
= -1;
181 struct event serv_read
;
182 struct event serv_select
;
183 struct event logflushev
;
185 char **NewEnv
= NULL
;
187 char *RcFileName
= NULL
;
190 char *screenlogfile
; /* filename layout */
191 int log_flush
= 10; /* flush interval in seconds */
192 int logtstamp_on
= 0; /* tstamp disabled */
193 char *logtstamp_string
; /* stamp layout */
194 int logtstamp_after
= 120; /* first tstamp after 120s */
195 char *hardcopydir
= NULL
;
197 char *VisualBellString
;
198 char *ActivityString
;
203 char *PowDetachString
;
211 int iflag
, rflag
, dflag
, lsflag
, quietflag
, wipeflag
, xflag
;
223 int tty_oldmode
= -1;
226 char HostName
[MAXSTR
];
227 int MasterPid
, PanicPid
;
228 int real_uid
, real_gid
, eff_uid
, eff_gid
;
230 int ZombieKey_destroy
, ZombieKey_resurrect
, ZombieKey_onerror
;
231 char *preselect
= NULL
; /* only used in Attach() */
234 char *screenencodings
;
247 struct layer
*flayer
;
250 struct win
*console_window
;
259 char strnomem
[] = "Out of memory.";
262 static int InterruptPlease
;
263 static int GotSigChld
;
266 lf_secreopen(name
, wantfd
, l
)
274 if (((got_fd
= secopen(name
, O_WRONLY
| O_CREAT
| O_APPEND
, 0666)) < 0) ||
275 lf_move_fd(got_fd
, wantfd
) < 0)
278 debug1("lf_secreopen: failed for %s\n", name
);
281 l
->st
->st_ino
= l
->st
->st_dev
= 0;
282 debug2("lf_secreopen: %d = %s\n", wantfd
, name
);
286 /********************************************************************/
287 /********************************************************************/
288 /********************************************************************/
291 static struct passwd
*
292 getpwbyname(name
, ppp
)
298 struct spwd
*sss
= NULL
;
299 static char *spw
= NULL
;
302 if (!ppp
&& !(ppp
= getpwnam(name
)))
305 /* Do password sanity check..., allow ##user for SUN_C2 security */
310 if (ppp
->pw_passwd
[0] == '#' && ppp
->pw_passwd
[1] == '#' &&
311 strcmp(ppp
->pw_passwd
+ 2, ppp
->pw_name
) == 0)
315 char c
= ppp
->pw_passwd
[n
];
316 if (!(c
== '.' || c
== '/' || c
== '$' ||
317 (c
>= '0' && c
<= '9') ||
318 (c
>= 'a' && c
<= 'z') ||
319 (c
>= 'A' && c
<= 'Z')))
324 /* try to determine real password */
325 if (n
< 13 && sss
== 0)
327 sss
= getspnam(ppp
->pw_name
);
332 ppp
->pw_passwd
= spw
= SaveStr(sss
->sp_pwdp
);
333 endspent(); /* this should delete all buffers ... */
336 endspent(); /* this should delete all buffers ... */
342 if (ppp
->pw_passwd
&& strlen(ppp
->pw_passwd
) == 13 + 11)
343 ppp
->pw_passwd
[13] = 0; /* beware of linux's long passwords */
356 s
= getenv("LC_ALL");
358 s
= getenv("LC_CTYPE");
373 char socknamebuf
[2 * MAXSTR
];
375 char *myname
= (ac
== 0) ? "screen" : av
[0];
378 #ifdef _MODE_T /* (jw) */
383 #if defined(SYSV) && !defined(ISC)
384 struct utsname utsnam
;
386 struct NewWindow nwin
;
387 int detached
= 0; /* start up detached */
393 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
394 setcompat(COMPAT_POSIX
|COMPAT_BSDPROT
); /* turn on seteuid support */
396 #if defined(sun) && defined(SVR4)
398 /* Solaris' login blocks SIGHUP! This is _very bad_ */
401 sigprocmask(SIG_SETMASK
, &sset
, 0);
406 * First, close all unused descriptors
407 * (otherwise, we might have problems with the select() call)
413 snprintf(version
, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV
, VERS
,
414 PATCHLEVEL
, STATE
, ORIGIN
, GIT_REV
, DATE
);
415 nversion
= REV
* 10000 + VERS
* 100 + PATCHLEVEL
;
416 debug2("-- screen debug started %s (%s)\n", *av
, version
);
430 debug("NAMEDPIPE\n");
432 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
433 debug("Window size changing enabled\n");
463 debug1("NAME_MAX = %d\n", NAME_MAX
);
466 BellString
= SaveStr("Bell in window %n");
467 VisualBellString
= SaveStr(" Wuff, Wuff!! ");
468 ActivityString
= SaveStr("Activity in window %n");
469 screenlogfile
= SaveStr("screenlog.%n");
470 logtstamp_string
= SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
471 hstatusstring
= SaveStr("%h");
472 captionstring
= SaveStr("%4n %t");
473 timestring
= SaveStr("%c:%s %M %d %H%? %l%?");
474 wlisttit
= SaveStr(" Num Name%=Flags");
475 wliststr
= SaveStr("%4n %t%=%f");
477 BufferFile
= SaveStr(DEFAULT_BUFFERFILE
);
483 default_startup
= (ac
> 1) ? 0 : 1;
485 VBellWait
= VBELLWAIT
* 1000;
486 MsgWait
= MSGWAIT
* 1000;
487 MsgMinWait
= MSGMINWAIT
* 1000;
488 SilenceWait
= SILENCEWAIT
;
493 zmodem_sendcmd
= SaveStr("!!! sz -vv -b ");
494 zmodem_recvcmd
= SaveStr("!!! rz -vv -b -E");
498 CompileKeys((char *)0, 0, mark_key_tab
);
502 screenencodings
= SaveStr(SCREENENCODINGS
);
508 nwin_options
= nwin_undef
;
509 strcpy(screenterm
, "screen");
511 logreopen_register(lf_secreopen
);
514 /* if this is a login screen, assume -RR */
523 ShellProg
= SaveStr(DefaultShell
); /* to prevent nasty circles */
528 if (--ac
> 0 && *ap
== '-')
530 if (ap
[1] == '-' && ap
[2] == 0)
536 if (ap
[1] == '-' && !strcmp(ap
, "--version"))
537 Panic(0, "Screen version %s", version
);
538 if (ap
[1] == '-' && !strcmp(ap
, "--help"))
539 exit_with_usage(myname
, NULL
, NULL
);
540 while (ap
&& *ap
&& *++ap
)
545 nwin_options
.aflag
= 1;
550 case 'p': /* preselect */
556 exit_with_usage(myname
, "Specify a window to preselect with -p", NULL
);
563 bd
.bd_start_braille
= 1;
572 exit_with_usage(myname
, "Specify an alternate rc-filename with -c", NULL
);
581 exit_with_usage(myname
, "Specify command characters with -e", NULL
);
585 Panic(0, "Two characters are required with -e option, not '%s'.", ap
);
594 nwin_options
.flowflag
= FLOW_NOW
* 0;
601 nwin_options
.flowflag
= FLOW_NOW
* 1;
604 nwin_options
.flowflag
= FLOW_AUTOFLAG
;
607 exit_with_usage(myname
, "Unknown flow option -%s", --ap
);
612 exit_with_usage(myname
, NULL
, NULL
);
613 nwin_options
.histheight
= atoi(*++av
);
614 if (nwin_options
.histheight
< 0)
615 exit_with_usage(myname
, "-h: %s: negative scrollback size?", *av
);
620 case 't': /* title, the former AkA == -k */
622 exit_with_usage(myname
, "Specify a new window-name with -t", NULL
);
623 nwin_options
.aka
= *++av
;
631 nwin_options
.lflag
= 0;
638 nwin_options
.lflag
= 1;
641 nwin_options
.lflag
= 3;
644 case 'i': /* -list */
646 if (ac
> 1 && !SockMatch
)
654 exit_with_usage(myname
, "%s: Unknown suboption to -l", --ap
);
660 if (ac
> 1 && !SockMatch
)
667 nwin_options
.Lflag
= 1;
672 case 'O': /* to be (or not to be?) deleted. jw. */
677 exit_with_usage(myname
, "Specify terminal-type with -T", NULL
);
678 if (strlen(*++av
) < 20)
679 strcpy(screenterm
, *av
);
681 Panic(0, "-T: terminal name too long. (max. 20 char)");
682 nwin_options
.term
= screenterm
;
696 if (ac
> 1 && *av
[1] != '-' && !SockMatch
)
700 debug2("rflag=%d, SockMatch=%s\n", dflag
, SockMatch
);
708 rflag
+= (*ap
== 'R') ? 2 : 1;
719 if (*av
[1] != '-' && !SockMatch
)
723 debug2("dflag=%d, SockMatch=%s\n", dflag
, SockMatch
);
730 exit_with_usage(myname
, "Specify shell with -s", NULL
);
733 ShellProg
= SaveStr(*++av
);
734 debug1("ShellProg: '%s'\n", ShellProg
);
740 exit_with_usage(myname
, "Specify session-name with -S", NULL
);
744 exit_with_usage(myname
, "Empty session-name?", NULL
);
750 Panic(0, "Screen version %s", version
);
754 nwin_options
.encoding
= nwin_options
.encoding
== -1 ? UTF8
: 0;
758 exit_with_usage(myname
, "Unknown option %s", --ap
);
771 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
772 signal(SIGBUS
, CoreDump
);
774 signal(SIGSEGV
, CoreDump
);
778 setlocale(LC_ALL
, "");
781 if (nwin_options
.encoding
== -1)
783 /* ask locale if we should start in UTF-8 mode */
784 # ifdef HAVE_NL_LANGINFO
786 setlocale(LC_CTYPE
, "");
788 nwin_options
.encoding
= FindEncoding(nl_langinfo(CODESET
));
789 debug1("locale says encoding = %d\n", nwin_options
.encoding
);
793 if ((s
= locale_name()) && InStr(s
, "UTF-8"))
794 nwin_options
.encoding
= UTF8
;
796 debug1("environment says encoding=%d\n", nwin_options
.encoding
);
802 if ((s
= locale_name()))
804 if(!strncmp(s
, "zh_", 3) || !strncmp(s
, "ja_", 3) || !strncmp(s
, "ko_", 3))
812 if (nwin_options
.aka
)
815 if (nwin_options
.encoding
> 0)
817 size_t len
= strlen(nwin_options
.aka
);
819 char *newbuf
= malloc(3 * len
);
821 Panic(0, "%s", strnomem
);
822 newsz
= RecodeBuf(nwin_options
.aka
, len
,
823 nwin_options
.encoding
, 0, newbuf
);
824 newbuf
[newsz
] = '\0';
825 nwin_options
.aka
= newbuf
;
830 /* If we just use the original value from av,
831 subsequent shelltitle invocations will attempt to free
832 space we don't own... */
833 nwin_options
.aka
= SaveStr(nwin_options
.aka
);
837 if (SockMatch
&& strlen(SockMatch
) >= MAXSTR
)
838 Panic(0, "Ridiculously long socketname - try again.");
839 if (cmdflag
&& !rflag
&& !dflag
&& !xflag
)
841 if (!cmdflag
&& dflag
&& mflag
&& !(rflag
|| xflag
))
845 nwin
.encoding
= nwin_undef
.encoding
; /* let screenrc overwrite it */
850 /* make the write() calls return -1 on all errors */
853 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
854 * It appears that in System V Release 4, UNIX, if you are writing
855 * an output file and you exceed the currently set file size limit,
856 * you _don't_ just get the call to `write' returning with a
857 * failure code. Rather, you get a signal called `SIGXFSZ' which,
858 * if neither handled nor ignored, will cause your program to crash
861 signal(SIGXFSZ
, SIG_IGN
);
865 signal(SIGPIPE
, SIG_IGN
);
872 sh
= getenv("SHELL");
873 ShellProg
= SaveStr(sh
? sh
: DefaultShell
);
875 ShellArgs
[0] = ShellProg
;
876 home
= getenv("HOME");
877 if (!mflag
&& !SockMatch
)
880 if (sty
&& *sty
== 0)
885 if (!(nethackflag
= (getenv("NETHACKOPTIONS") != NULL
)))
887 char nethackrc
[MAXPATHLEN
];
889 if (home
&& (strlen(home
) < (MAXPATHLEN
- 20)))
891 sprintf(nethackrc
,"%s/.nethackrc", home
);
892 nethackflag
= !access(nethackrc
, F_OK
);
898 own_uid
= multi_uid
= real_uid
;
899 if (SockMatch
&& (sockp
= index(SockMatch
, '/')))
903 SockMatch
= sockp
+ 1;
907 if ((mppp
= getpwnam(multi
)) == (struct passwd
*)0)
908 Panic(0, "Cannot identify account '%s'.", multi
);
909 multi_uid
= mppp
->pw_uid
;
910 multi_home
= SaveStr(mppp
->pw_dir
);
911 if (strlen(multi_home
) > MAXPATHLEN
- 10)
912 Panic(0, "home directory path too long");
914 /* always fake multi attach mode */
921 /* Special case: effective user is multiuser. */
922 if (eff_uid
&& (multi_uid
!= eff_uid
))
923 Panic(0, "Must run suid root for multiuser support.");
925 if (SockMatch
&& *SockMatch
== 0)
927 #endif /* MULTIUSER */
929 if ((LoginName
= getlogin()) && LoginName
[0] != '\0')
931 if ((ppp
= getpwnam(LoginName
)) != (struct passwd
*) 0)
932 if ((int)ppp
->pw_uid
!= real_uid
)
933 ppp
= (struct passwd
*) 0;
937 if ((ppp
= getpwuid(real_uid
)) == 0)
939 Panic(0, "getpwuid() can't identify your account!");
942 LoginName
= ppp
->pw_name
;
944 LoginName
= SaveStr(LoginName
);
946 ppp
= getpwbyname(LoginName
, ppp
);
948 #if !defined(SOCKDIR) && defined(MULTIUSER)
949 if (multi
&& !multiattach
)
951 if (home
&& strcmp(home
, ppp
->pw_dir
))
952 Panic(0, "$HOME must match passwd entry for multiuser screens.");
956 #define SET_GUID() do \
960 eff_uid = real_uid; \
961 eff_gid = real_gid; \
964 #define SET_TTYNAME(fatal) do \
966 if (!(attach_tty = ttyname(0))) \
969 Panic(0, "Must be connected to a terminal."); \
973 else if (stat(attach_tty, &st)) \
974 Panic(errno, "Cannot access '%s'", attach_tty); \
975 if (strlen(attach_tty) >= MAXPATHLEN) \
976 Panic(0, "TtyName too long - sorry."); \
979 if (home
== 0 || *home
== '\0')
981 if (strlen(LoginName
) > 20)
982 Panic(0, "LoginName too long - sorry.");
984 if (multi
&& strlen(multi
) > 20)
985 Panic(0, "Screen owner name too long - sorry.");
987 if (strlen(home
) > MAXPATHLEN
- 25)
988 Panic(0, "$HOME too long - sorry.");
991 if (!detached
&& !lsflag
&& !cmdflag
&& !(dflag
&& !mflag
&& !rflag
&& !xflag
) && !(!mflag
&& !SockMatch
&& sty
&& !xflag
))
997 /* ttyname implies isatty */
1000 tty_mode
= (int)st
.st_mode
& 0777;
1004 fl
= fcntl(0, F_GETFL
, 0);
1005 if (fl
!= -1 && (fl
& (O_RDWR
|O_RDONLY
|O_WRONLY
)) == O_RDWR
)
1008 if (attach_fd
== -1)
1010 if ((n
= secopen(attach_tty
, O_RDWR
| O_NONBLOCK
, 0)) < 0)
1011 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty
);
1014 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty
, attach_fd
);
1016 if ((attach_term
= getenv("TERM")) == 0 || *attach_term
== 0)
1017 Panic(0, "Please set a terminal type.");
1018 if (strlen(attach_term
) > sizeof(D_termname
) - 1)
1019 Panic(0, "$TERM too long - sorry.");
1020 GetTTY(0, &attach_Mode
);
1021 #ifdef DEBUGGGGGGGGGGGGGGG
1022 DebugTTY(&attach_Mode
);
1027 oumask
= umask(0); /* well, unsigned never fails? jw. */
1029 if ((oumask
= (int)umask(0)) == -1)
1030 Panic(errno
, "Cannot change umask to zero");
1032 SockDir
= getenv("SCREENDIR");
1035 if (strlen(SockDir
) >= MAXPATHLEN
- 1)
1036 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1039 Panic(0, "No $SCREENDIR with multi screens, please.");
1046 sprintf(SockPath
, "%s/.screen", multi_home
);
1050 sprintf(SockPath
, "%s/S-%s", SockDir
, multi
);
1059 sprintf(SockPath
, "%s/.screen", home
);
1065 if (access(SockDir
, F_OK
))
1067 debug1("SockDir '%s' missing ...\n", SockDir
);
1068 if (UserContext() > 0)
1070 if (mkdir(SockDir
, 0700))
1074 if (UserStatus() <= 0)
1075 Panic(0, "Cannot make directory '%s'.", SockDir
);
1077 if (SockDir
!= SockPath
)
1078 strcpy(SockPath
, SockDir
);
1084 if (stat(SockDir
, &st
))
1086 n
= (eff_uid
== 0 && (real_uid
|| eff_gid
== real_gid
)) ? 0755 :
1087 (eff_gid
!= real_gid
) ? 0775 :
1093 if (mkdir(SockDir
, n
) == -1)
1094 Panic(errno
, "Cannot make directory '%s'", SockDir
);
1098 if (!S_ISDIR(st
.st_mode
))
1099 Panic(0, "'%s' must be a directory.", SockDir
);
1100 if (eff_uid
== 0 && real_uid
&& (int)st
.st_uid
!= eff_uid
)
1101 Panic(0, "Directory '%s' must be owned by root.", SockDir
);
1102 n
= (eff_uid
== 0 && (real_uid
|| (st
.st_mode
& 0775) != 0775)) ? 0755 :
1103 (eff_gid
== (int)st
.st_gid
&& eff_gid
!= real_gid
) ? 0775 :
1105 if (((int)st
.st_mode
& 0777) != n
)
1106 Panic(0, "Directory '%s' must have mode %03o.", SockDir
, n
);
1108 sprintf(SockPath
, "%s/S-%s", SockDir
, LoginName
);
1109 if (access(SockPath
, F_OK
))
1111 if (mkdir(SockPath
, 0700) == -1)
1112 Panic(errno
, "Cannot make directory '%s'", SockPath
);
1113 (void) chown(SockPath
, real_uid
, real_gid
);
1119 if (stat(SockPath
, &st
) == -1)
1120 Panic(errno
, "Cannot access %s", SockPath
);
1122 if (!S_ISDIR(st
.st_mode
))
1123 Panic(0, "%s is not a directory.", SockPath
);
1127 if ((int)st
.st_uid
!= multi_uid
)
1128 Panic(0, "%s is not the owner of %s.", multi
, SockPath
);
1133 if ((int)st
.st_uid
!= real_uid
)
1134 Panic(0, "You are not the owner of %s.", SockPath
);
1136 if ((st
.st_mode
& 0777) != 0700)
1137 Panic(0, "Directory %s must have mode 700.", SockPath
);
1138 if (SockMatch
&& index(SockMatch
, '/'))
1139 Panic(0, "Bad session name '%s'", SockMatch
);
1140 SockName
= SockPath
+ strlen(SockPath
) + 1;
1142 (void) umask(oumask
);
1143 debug2("SockPath: %s SockMatch: %s\n", SockPath
, SockMatch
? SockMatch
: "NULL");
1145 #if defined(SYSV) && !defined(ISC)
1146 if (uname(&utsnam
) == -1)
1147 Panic(errno
, "uname");
1148 strncpy(HostName
, utsnam
.nodename
, sizeof(utsnam
.nodename
) < MAXSTR
? sizeof(utsnam
.nodename
) : MAXSTR
- 1);
1149 HostName
[sizeof(utsnam
.nodename
) < MAXSTR
? sizeof(utsnam
.nodename
) : MAXSTR
- 1] = '\0';
1151 (void) gethostname(HostName
, MAXSTR
);
1152 HostName
[MAXSTR
- 1] = '\0';
1154 if ((ap
= index(HostName
, '.')) != NULL
)
1163 real_uid
= multi_uid
;
1166 i
= FindSocket((int *)NULL
, &fo
, &oth
, SockMatch
);
1168 exit(8 + (fo
? ((oth
|| i
) ? 2 : 1) : 0) + i
);
1170 Panic(0, "No Sockets found in %s.\n", SockPath
);
1171 Panic(0, "%d Socket%s in %s.\n", fo
, fo
> 1 ? "s" : "", SockPath
);
1174 signal(SIG_BYE
, AttacherFinit
); /* prevent races */
1177 /* attach_tty is not mandatory */
1180 Panic(0, "Please specify a command.");
1182 SendCmdMessage(sty
, SockMatch
, av
, queryflag
>= 0);
1185 else if (rflag
|| xflag
)
1187 debug("screen -r: - is there anybody out there?\n");
1188 if (Attach(MSG_ATTACH
))
1195 Panic(0, "Can't create sessions of other users.");
1197 debug("screen -r: backend not responding -- still crying\n");
1199 else if (dflag
&& !mflag
)
1203 Msg(0, "[%s %sdetached.]\n", SockName
, (dflag
> 1 ? "power " : ""));
1207 if (!SockMatch
&& !mflag
&& sty
)
1209 /* attach_tty is not mandatory */
1212 nwin_options
.args
= av
;
1213 SendCreateMsg(sty
, &nwin
);
1217 nwin_compose(&nwin_default
, &nwin_options
, &nwin_default
);
1219 if (!detached
|| dflag
!= 2)
1227 Panic(errno
, "fork");
1235 sprintf(socknamebuf
, "%d.%s", MasterPid
, SockMatch
);
1237 sprintf(socknamebuf
, "%d.%s.%s", MasterPid
, stripdev(attach_tty
), HostName
);
1238 for (ap
= socknamebuf
; *ap
; ap
++)
1242 if (strlen(socknamebuf
) > NAME_MAX
)
1243 socknamebuf
[NAME_MAX
] = 0;
1245 sprintf(SockPath
+ strlen(SockPath
), "/%s", socknamebuf
);
1252 PanicPid
= getppid();
1254 if (DefaultEsc
== -1)
1255 DefaultEsc
= Ctrl('a');
1256 if (DefaultMetaEsc
== -1)
1257 DefaultMetaEsc
= 'a';
1259 ap
= av0
+ strlen(av0
) - 1;
1262 if (!strncmp("screen", ap
, 6))
1264 strncpy(ap
, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1276 if (dfp
&& dfp
!= stderr
)
1278 sprintf(buf
, "%s/SCREEN.%d", DEBUGDIR
, (int)getpid());
1279 if ((dfp
= fopen(buf
, "w")) == NULL
)
1282 (void) chmod(buf
, 0666);
1287 if (attach_fd
== -1)
1289 if ((n
= secopen(attach_tty
, O_RDWR
, 0)) < 0)
1290 Panic(0, "Cannot reopen '%s' - please check.", attach_tty
);
1297 freopen("/dev/null", "r", stdin
);
1298 freopen("/dev/null", "w", stdout
);
1303 freopen("/dev/null", "w", stderr
);
1304 debug("-- screen.back debug started\n");
1307 * This guarantees that the session owner is listed, even when we
1308 * start detached. From now on we should not refer to 'LoginName'
1309 * any more, use users->u_name instead.
1311 if (UserAdd(LoginName
, (char *)0, (struct acluser
**)0) < 0)
1312 Panic(0, "Could not create user info");
1315 if (MakeDisplay(LoginName
, attach_tty
, attach_term
, n
, getppid(), &attach_Mode
) == 0)
1316 Panic(0, "Could not alloc display");
1319 D_encoding
= nwin_options
.encoding
> 0 ? nwin_options
.encoding
: 0;
1320 debug1("D_encoding = %d\n", D_encoding
);
1326 /* user started us with -S option */
1327 sprintf(socknamebuf
, "%d.%s", (int)getpid(), SockMatch
);
1331 sprintf(socknamebuf
, "%d.%s.%s", (int)getpid(), stripdev(attach_tty
),
1334 for (ap
= socknamebuf
; *ap
; ap
++)
1338 if (strlen(socknamebuf
) > NAME_MAX
)
1340 debug2("Socketname %s truncated to %d chars\n", socknamebuf
, NAME_MAX
);
1341 socknamebuf
[NAME_MAX
] = 0;
1344 sprintf(SockPath
+ strlen(SockPath
), "/%s", socknamebuf
);
1346 ServerSocket
= MakeServerSocket();
1349 # ifdef ALLOW_SYSSCREENRC
1350 if ((ap
= getenv("SYSSCREENRC")))
1351 (void)StartRc(ap
, 0);
1354 (void)StartRc(ETCSCREENRC
, 0);
1356 (void)StartRc(RcFileName
, 0);
1360 # endif /* UTNOKEEP */
1361 # endif /* UTMPOK */
1364 if (InitTermcap(0, 0))
1366 debug("Could not init termcap - exiting\n");
1367 fcntl(D_userfd
, F_SETFL
, 0); /* Flush sets FNBLOCK */
1370 Kill(D_userpid
, SIG_BYE
);
1373 MakeDefaultCanvas();
1385 signal(SIGHUP
, SigHup
);
1386 signal(SIGINT
, FinitHandler
);
1387 signal(SIGQUIT
, FinitHandler
);
1388 signal(SIGTERM
, FinitHandler
);
1390 signal(SIGTTIN
, SIG_IGN
);
1391 signal(SIGTTOU
, SIG_IGN
);
1397 SetMode(&D_OldMode
, &D_NewMode
, D_flow
, iflag
);
1398 /* Note: SetMode must be called _before_ FinishRc. */
1399 SetTTY(D_userfd
, &D_NewMode
);
1400 if (fcntl(D_userfd
, F_SETFL
, FNBLOCK
))
1401 Msg(errno
, "Warning: NBLOCK fcntl failed");
1404 brktty(-1); /* just try */
1405 signal(SIGCHLD
, SigChld
);
1407 # ifdef ALLOW_SYSSCREENRC
1408 if ((ap
= getenv("SYSSCREENRC")))
1412 FinishRc(ETCSCREENRC
);
1414 FinishRc(RcFileName
);
1416 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1417 if (windows
== NULL
)
1419 debug("We open one default window, as screenrc did not specify one.\n");
1420 if (MakeWindow(&nwin
) == -1)
1423 struct timeval tv
= { MsgWait
/1000, 1000*(MsgWait
%1000) };
1426 Msg(0, "Sorry, could not find a PTY or TTY.");
1427 // allow user to exit early by pressing any key.
1428 select(1, &rfd
, NULL
, NULL
, &tv
);
1433 else if (ac
) /* Screen was invoked with a command */
1442 if (display
&& default_startup
)
1443 display_copyright();
1444 signal(SIGINT
, SigInt
);
1445 if (rflag
&& (rflag
& 1) == 0 && !quietflag
)
1447 Msg(0, "New screen...");
1451 serv_read
.type
= EV_READ
;
1452 serv_read
.fd
= ServerSocket
;
1453 serv_read
.handler
= serv_read_fn
;
1456 serv_select
.pri
= -10;
1457 serv_select
.type
= EV_ALWAYS
;
1458 serv_select
.handler
= serv_select_fn
;
1459 evenq(&serv_select
);
1461 logflushev
.type
= EV_TIMEOUT
;
1462 logflushev
.handler
= logflush_fn
;
1470 WindowDied(p
, wstat
, wstat_valid
)
1481 if (p
->w_destroyev
.data
== (char *)p
)
1483 wstat
= p
->w_exitstatus
;
1485 evdeq(&p
->w_destroyev
);
1486 p
->w_destroyev
.data
= 0;
1489 #if defined(BSDJOBS) && !defined(BSDWAIT)
1490 if (!wstat_valid
&& p
->w_pid
> 0)
1492 /* EOF on file descriptor. The process is probably also dead.
1494 if (waitpid(p
->w_pid
, &wstat
, WNOHANG
| WUNTRACED
) == p
->w_pid
)
1501 if (ZombieKey_destroy
&& ZombieKey_onerror
&& wstat_valid
&&
1502 WIFEXITED(wstat
) && WEXITSTATUS(wstat
) == 0)
1505 if (ZombieKey_destroy
&& !killit
)
1507 char buf
[100], *s
, reason
[100];
1511 if (WIFEXITED(wstat
))
1512 if (WEXITSTATUS(wstat
))
1513 sprintf(reason
, "terminated with exit status %d", WEXITSTATUS(wstat
));
1515 sprintf(reason
, "terminated normally");
1516 else if (WIFSIGNALED(wstat
))
1517 sprintf(reason
, "terminated with signal %d%s", WTERMSIG(wstat
),
1519 WCOREDUMP(wstat
) ? " (core file generated)" : "");
1524 sprintf(reason
, "detached from window");
1529 s
[strlen(s
) - 1] = '\0';
1530 debug3("window %d (%s) going into zombie state fd %d",
1531 p
->w_number
, p
->w_title
, p
->w_ptyfd
);
1533 if (p
->w_slot
!= (slot_t
)0 && p
->w_slot
!= (slot_t
)-1)
1536 p
->w_slot
= 0; /* "detached" */
1541 p
->w_deadpid
= p
->w_pid
;
1544 /* p->w_y = p->w_bot; */
1545 p
->w_y
= MFindUsedLine(p
, p
->w_bot
, 1);
1546 sprintf(buf
, "\n\r=== Command %s (%s) ===", reason
, s
? s
: "?");
1547 WriteString(p
, buf
, strlen(buf
));
1548 WindowChanged(p
, 'f');
1569 signal(SIGCHLD
, SigChld
);
1572 if (stat(SockPath
, &st
) == -1)
1574 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath
);
1575 if (!RecoverSocket())
1577 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1581 debug1("'%s' reconstructed\n", SockPath
);
1584 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath
, (int)st
.st_mode
);
1590 debug("SigChld()\n");
1598 /* Hangup all displays */
1599 while ((display
= displays
) != 0)
1605 * the backend's Interrupt handler
1606 * we cannot insert the intrc directly, as we never know
1615 debug("SigInt()\n");
1616 if (fore
&& displays
)
1618 # if defined(TERMIO) || defined(POSIX)
1619 ibuf
= displays
->d_OldMode
.tio
.c_cc
[VINTR
];
1621 ibuf
= displays
->d_OldMode
.m_tchars
.t_intrc
;
1624 write(fore
->w_ptyfd
, &ibuf
, 1);
1627 signal(SIGINT
, SigInt
);
1628 debug("SigInt() careful\n");
1629 InterruptPlease
= 1;
1637 /* if running with s-bit, we must reset the s-bit, so that we get a
1641 struct display
*disp
;
1644 char *dump_msg
= " (core dumped)";
1646 int running_w_s_bit
= getuid() != geteuid();
1647 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1648 if (running_w_s_bit
)
1652 #if defined(SYSVSIGS) && defined(SIGHASARG)
1653 signal(sigsig
, SIG_IGN
);
1660 sprintf(buf
, "\r\n[screen caught signal %d.%s]\r\n", sigsig
, dump_msg
);
1662 sprintf(buf
, "\r\n[screen caught a fatal signal.%s]\r\n", dump_msg
);
1665 for (disp
= displays
; disp
; disp
= disp
->d_next
)
1667 if (disp
->d_nonblock
< -1 || disp
->d_nonblock
> 1000000)
1669 fcntl(disp
->d_userfd
, F_SETFL
, 0);
1670 SetTTY(disp
->d_userfd
, &D_OldMode
);
1671 write(disp
->d_userfd
, buf
, strlen(buf
));
1672 Kill(disp
->d_userpid
, SIG_BYE
);
1675 if (running_w_s_bit
)
1677 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1678 Kill(getpid(), SIGKILL
);
1680 #else /* SHADOWPW && !DEBUG */
1682 #endif /* SHADOWPW && !DEBUG */
1693 struct win
*p
, *next
;
1702 while ((pid
= waitpid(-1, &wstat
, WNOHANG
| WUNTRACED
)) > 0)
1706 * From: rouilj@sni-usa.com (John Rouillard)
1707 * note that WUNTRACED is not documented to work, but it is defined in
1708 * /usr/include/sys/wait.h, so it may work
1710 while ((pid
= wait2(&wstat
, WNOHANG
| WUNTRACED
)) > 0)
1711 # else /* USE_WAIT2 */
1712 while ((pid
= wait3(&wstat
, WNOHANG
| WUNTRACED
, (struct rusage
*) 0)) > 0)
1713 # endif /* USE_WAIT2 */
1716 while ((pid
= wait(&wstat
)) < 0)
1720 #endif /* BSDJOBS */
1722 for (p
= windows
; p
; p
= next
)
1725 if ( (p
->w_pid
&& pid
== p
->w_pid
) ||
1726 (p
->w_deadpid
&& pid
== p
->w_deadpid
) )
1728 /* child has ceased to exist */
1732 if (WIFSTOPPED(wstat
))
1734 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p
->w_number
, pid
, WSTOPSIG(wstat
));
1736 if (WSTOPSIG(wstat
) == SIGTTIN
)
1738 Msg(0, "Suspended (tty input)");
1743 if (WSTOPSIG(wstat
) == SIGTTOU
)
1745 Msg(0, "Suspended (tty output)");
1749 /* Try to restart process */
1750 Msg(0, "Child has been stopped, restarting.");
1751 if (killpg(pid
, SIGCONT
))
1757 /* Screen will detect the window has died when the window's
1758 * file descriptor signals EOF (which it will do when the process in
1759 * the window terminates). So do this in a timeout of 10 seconds.
1760 * (not doing this at all might also work)
1761 * See #27061 for more details.
1763 p
->w_destroyev
.data
= (char *)p
;
1764 p
->w_exitstatus
= wstat
;
1765 SetTimeout(&p
->w_destroyev
, 10 * 1000);
1766 evenq(&p
->w_destroyev
);
1771 if (p
->w_pwin
&& pid
== p
->w_pwin
->p_pid
)
1773 debug2("pseudo of win Nr %d died. pid == %d\n", p
->w_number
, p
->w_pwin
->p_pid
);
1781 debug1("pid %d not found - hope that's ok\n", pid
);
1788 FinitHandler SIGDEFARG
1791 debug1("FinitHandler called, sig %d.\n", sigsig
);
1793 debug("FinitHandler called.\n");
1803 signal(SIGCHLD
, SIG_DFL
);
1804 signal(SIGHUP
, SIG_IGN
);
1805 debug1("Finit(%d);\n", i
);
1808 struct win
*p
= windows
;
1809 windows
= windows
->w_next
;
1812 if (ServerSocket
!= -1)
1814 debug1("we unlink(%s)\n", SockPath
);
1819 (void) unlink(SockPath
);
1825 for (display
= displays
; display
; display
= display
->d_next
)
1833 AddStr("[screen is terminating]\r\n");
1835 SetTTY(D_userfd
, &D_OldMode
);
1836 fcntl(D_userfd
, F_SETFL
, 0);
1838 Kill(D_userpid
, SIG_BYE
);
1841 * we _cannot_ call eexit(i) here,
1842 * instead of playing with the Socket above. Sigh.
1852 if (ServerSocket
!= -1)
1854 debug1("we unlink(%s)\n", SockPath
);
1857 (void) unlink(SockPath
);
1867 debug1("Hangup %x\n", display
);
1873 if (auto_detach
|| displays
->d_next
)
1880 * Detach now has the following modes:
1881 *D_DETACH SIG_BYE detach backend and exit attacher
1882 *D_HANGUP SIG_BYE detach backend and exit attacher
1883 *D_STOP SIG_STOP stop attacher (and detach backend)
1884 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1885 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1886 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1887 *D_LOCK SIG_LOCK lock the attacher
1889 * we always remove our utmp slots. (even when "lock" or "stop")
1890 * Note: Take extra care here, we may be called by interrupt!
1903 #define AddStrSock(msg) do { \
1906 AddStr("[" msg " from "); \
1911 AddStr("[" msg "]\r\n"); \
1914 signal(SIGHUP
, SIG_IGN
);
1915 debug1("Detach(%d)\n", mode
);
1927 AddStrSock("detached");
1935 #ifdef REMOTE_DETACH
1937 AddStrSock("remote detached");
1943 AddStrSock("power detached");
1944 if (PowDetachString
)
1946 AddStr(PowDetachString
);
1949 sign
= SIG_POWER_BYE
;
1951 #ifdef REMOTE_DETACH
1952 case D_REMOTE_POWER
:
1953 AddStrSock("remote power detached");
1954 if (PowDetachString
)
1956 AddStr(PowDetachString
);
1959 sign
= SIG_POWER_BYE
;
1966 /* tell attacher to lock terminal with a lockprg. */
1970 if (displays
->d_next
== 0)
1972 for (p
= windows
; p
; p
= p
->w_next
)
1974 if (p
->w_slot
!= (slot_t
) -1 && !(p
->w_lflag
& 2))
1978 * Set the slot to 0 to get the window
1981 p
->w_slot
= (slot_t
) 0;
1985 if (mode
!= D_HANGUP
)
1988 if (displays
->d_next
== 0 && console_window
)
1990 if (TtyGrabConsole(console_window
->w_ptyfd
, 0, "detach"))
1992 debug("could not release console - killing window\n");
1993 KillWindow(console_window
);
1994 display
= displays
; /* restore display */
2000 ReleaseAutoWritelock(display
, D_fore
);
2002 D_user
->u_detachwin
= D_fore
->w_number
;
2003 D_user
->u_detachotherwin
= D_other
? D_other
->w_number
: -1;
2005 AutosaveLayout(D_layout
);
2006 layout_last
= D_layout
;
2007 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
2009 p
= Layer2Window(cv
->c_layer
);
2010 SetCanvasWindow(cv
, 0);
2012 WindowChanged(p
, 'u');
2016 debug2("display: %#x displays: %#x\n", (unsigned int)display
, (unsigned int)displays
);
2019 /* Flag detached-ness */
2022 * tell father what to do. We do that after we
2023 * freed the tty, thus getty feels more comfortable on hpux
2024 * if it was a power detach.
2027 debug2("Detach: Signal %d to Attacher(%d)!\n", sign
, pid
);
2028 debug("Detach returns, we are successfully detached.\n");
2029 signal(SIGHUP
, SigHup
);
2040 return strncmp(e
, s
, l
) == 0 && e
[l
] == '=';
2046 register char **op
, **np
;
2047 static char stybuf
[MAXSTR
];
2049 for (op
= environ
; *op
; ++op
)
2052 free((char *)NewEnv
);
2053 NewEnv
= np
= (char **) malloc((unsigned) (op
- environ
+ 7 + 1) * sizeof(char **));
2055 Panic(0, "%s", strnomem
);
2056 sprintf(stybuf
, "STY=%s", strlen(SockName
) <= MAXSTR
- 5 ? SockName
: "?");
2057 *np
++ = stybuf
; /* NewEnv[0] */
2058 *np
++ = Term
; /* NewEnv[1] */
2059 np
++; /* room for SHELL */
2061 np
+= 2; /* room for TERMCAP and WINDOW */
2063 np
+= 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2066 for (op
= environ
; *op
; ++op
)
2068 if (!IsSymbol(*op
, "TERM") && !IsSymbol(*op
, "TERMCAP")
2069 && !IsSymbol(*op
, "STY") && !IsSymbol(*op
, "WINDOW")
2070 && !IsSymbol(*op
, "SCREENCAP") && !IsSymbol(*op
, "SHELL")
2071 && !IsSymbol(*op
, "LINES") && !IsSymbol(*op
, "COLUMNS")
2080 #if defined(USEVARARGS) && defined(__STDC__)
2081 Msg(int err
, const char *fmt
, VA_DOTS
)
2083 Msg(err
, fmt
, VA_DOTS
)
2090 char buf
[MAXPATHLEN
*2];
2095 (void)vsnprintf(p
, sizeof(buf
) - 100, fmt
, VA_ARGS(ap
));
2102 strncpy(p
, strerror(err
), buf
+ sizeof(buf
) - p
- 1);
2103 buf
[sizeof(buf
) - 1] = 0;
2105 debug2("Msg('%s') (%#x);\n", buf
, (unsigned int)display
);
2107 if (display
&& displays
)
2111 for (display
= displays
; display
; display
= display
->d_next
)
2116 /* no displays but a display - must have forked.
2117 * send message to backend!
2119 char *tty
= D_usertty
;
2120 struct display
*olddisplay
= display
;
2121 display
= 0; /* only send once */
2122 SendErrorMsg(tty
, buf
);
2123 display
= olddisplay
;
2126 printf("%s\r\n", buf
);
2129 write(queryflag
, buf
, strlen(buf
));
2133 * Call FinitTerm for all displays, write a message to each and call eexit();
2137 #if defined(USEVARARGS) && defined(__STDC__)
2138 Panic(int err
, const char *fmt
, VA_DOTS
)
2140 Panic(err
, fmt
, VA_DOTS
)
2147 char buf
[MAXPATHLEN
*2];
2152 (void)vsnprintf(p
, sizeof(buf
) - 100, fmt
, VA_ARGS(ap
));
2159 strncpy(p
, strerror(err
), buf
+ sizeof(buf
) - p
- 1);
2160 buf
[sizeof(buf
) - 1] = 0;
2162 debug3("Panic('%s'); display=%x displays=%x\n", buf
, display
, displays
);
2163 if (displays
== 0 && display
== 0)
2165 printf("%s\r\n", buf
);
2167 Kill(PanicPid
, SIG_BYE
);
2169 else if (displays
== 0)
2171 /* no displays but a display - must have forked.
2172 * send message to backend!
2174 char *tty
= D_usertty
;
2176 SendErrorMsg(tty
, buf
);
2181 for (display
= displays
; display
; display
= display
->d_next
)
2190 SetTTY(D_userfd
, &D_OldMode
);
2191 fcntl(D_userfd
, F_SETFL
, 0);
2192 write(D_userfd
, buf
, strlen(buf
));
2193 write(D_userfd
, "\n", 1);
2196 Kill(D_userpid
, SIG_BYE
);
2199 if (tty_oldmode
>= 0)
2202 if (setuid(own_uid
))
2203 xseteuid(own_uid
); /* may be a loop. sigh. */
2207 debug1("Panic: changing back modes from %s\n", attach_tty
);
2208 chmod(attach_tty
, tty_oldmode
);
2216 * '^' is allowed as an escape mechanism for control characters. jw.
2218 * Added time insertion using ideas/code from /\ndy Jones
2219 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2224 static const char days
[] = "SunMonTueWedThuFriSat";
2225 static const char months
[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2228 static char winmsg_buf
[MAXSTR
];
2229 #define MAX_WINMSG_REND 256 /* rendition changes */
2230 static int winmsg_rend
[MAX_WINMSG_REND
];
2231 static int winmsg_rendpos
[MAX_WINMSG_REND
];
2232 static int winmsg_numrend
;
2235 pad_expand(buf
, p
, numpad
, padlen
)
2244 padlen
= padlen
- (p
- buf
); /* space for rent */
2247 pn2
= pn
= p
+ padlen
;
2251 if (r
&& *p
!= 127 && p
- buf
== winmsg_rendpos
[r
- 1])
2253 winmsg_rendpos
[--r
] = pn
- buf
;
2260 i
= numpad
> 0 ? (padlen
+ numpad
- 1) / numpad
: 0;
2265 if (r
&& p
- buf
== winmsg_rendpos
[r
- 1])
2266 winmsg_rendpos
[--r
] = pn
- buf
;
2273 struct backtick
*next
;
2278 char result
[MAXSTR
];
2285 struct backtick
*backticks
;
2289 struct backtick
*bt
;
2294 for (p
= q
= bt
->result
; (c
= (unsigned char)*p
++) != 0;)
2298 if (c
>= ' ' || c
== '\005')
2305 backtick_fn(ev
, data
)
2309 struct backtick
*bt
;
2312 bt
= (struct backtick
*)data
;
2313 debug1("backtick_fn for #%d\n", bt
->num
);
2315 l
= read(ev
->fd
, bt
->buf
+ i
, MAXSTR
- i
);
2318 debug1("EOF on backtick #%d\n", bt
->num
);
2324 debug1("read %d bytes\n", l
);
2326 for (j
= 0; j
< l
; j
++)
2327 if (bt
->buf
[i
- j
- 1] == '\n')
2331 for (k
= i
- j
- 2; k
>= 0; k
--)
2332 if (bt
->buf
[k
] == '\n')
2335 bcopy(bt
->buf
+ k
, bt
->result
, i
- j
- k
);
2336 bt
->result
[i
- j
- k
- 1] = 0;
2337 backtick_filter(bt
);
2338 WindowChanged(0, '`');
2340 if (j
== l
&& i
== MAXSTR
)
2348 bcopy(bt
->buf
+ i
- j
, bt
->buf
, j
);
2355 setbacktick(num
, lifespan
, tick
, cmdv
)
2361 struct backtick
**btp
, *bt
;
2364 debug1("setbacktick called for backtick #%d\n", num
);
2365 for (btp
= &backticks
; (bt
= *btp
) != 0; btp
= &bt
->next
)
2372 for (v
= bt
->cmdv
; *v
; v
++)
2389 bt
= (struct backtick
*)malloc(sizeof *bt
);
2392 Msg(0, "%s", strnomem
);
2395 bzero(bt
, sizeof(*bt
));
2401 bt
->lifespan
= lifespan
;
2408 if (bt
->tick
== 0 && bt
->lifespan
== 0)
2410 debug("setbacktick: continuous mode\n");
2411 bt
->buf
= (char *)malloc(MAXSTR
);
2414 Msg(0, "%s", strnomem
);
2415 setbacktick(num
, 0, 0, (char **)0);
2418 bt
->ev
.type
= EV_READ
;
2419 bt
->ev
.fd
= readpipe(bt
->cmdv
);
2420 bt
->ev
.handler
= backtick_fn
;
2421 bt
->ev
.data
= (char *)bt
;
2428 runbacktick(bt
, tickp
, now
)
2429 struct backtick
*bt
;
2436 debug1("runbacktick called for backtick #%d\n", bt
->num
);
2437 if (bt
->tick
&& (!*tickp
|| bt
->tick
< *tickp
))
2439 if ((bt
->lifespan
== 0 && bt
->tick
== 0) || now
< bt
->bestbefore
)
2441 debug1("returning old result (%d)\n", bt
->lifespan
);
2444 f
= readpipe(bt
->cmdv
);
2448 while ((l
= read(f
, bt
->result
+ i
, sizeof(bt
->result
) - i
)) > 0)
2450 debug1("runbacktick: read %d bytes\n", l
);
2452 for (j
= 1; j
< l
; j
++)
2453 if (bt
->result
[i
- j
- 1] == '\n')
2455 if (j
== l
&& i
== sizeof(bt
->result
))
2457 j
= sizeof(bt
->result
) / 2;
2462 bcopy(bt
->result
+ i
- j
, bt
->result
, j
);
2467 bt
->result
[sizeof(bt
->result
) - 1] = '\n';
2468 if (i
&& bt
->result
[i
- 1] == '\n')
2470 debug1("runbacktick: finished, %d bytes\n", i
);
2472 backtick_filter(bt
);
2474 bt
->bestbefore
= now2
+ bt
->lifespan
;
2479 AddWinMsgRend(str
, r
)
2483 if (winmsg_numrend
>= MAX_WINMSG_REND
|| str
< winmsg_buf
||
2484 str
>= winmsg_buf
+ MAXSTR
)
2487 winmsg_rend
[winmsg_numrend
] = r
;
2488 winmsg_rendpos
[winmsg_numrend
] = str
- winmsg_buf
;
2495 MakeWinMsgEv(str
, win
, esc
, padlen
, ev
, rec
)
2505 register char *p
= winmsg_buf
;
2515 int qmflag
= 0, omflag
= 0, qmnumrend
= 0;
2522 struct backtick
*bt
;
2524 if (winmsg_numrend
>= 0)
2527 winmsg_numrend
= -winmsg_numrend
;
2532 gettimeofday(&now
, NULL
);
2533 for (; *s
&& (l
= winmsg_buf
+ MAXSTR
- 1 - p
) > 0; s
++, p
++)
2539 if (*s
!= '^' && *s
>= 64)
2564 if (*++s
== esc
) /* double escape ? */
2566 if ((plusflg
= *s
== '+') != 0)
2568 if ((minusflg
= *s
== '-') != 0)
2570 if ((zeroflg
= *s
== '0') != 0)
2573 while(*s
>= '0' && *s
<= '9')
2574 num
= num
* 10 + (*s
++ - '0');
2575 if ((longflg
= *s
== 'L') != 0)
2583 if ((!qmflag
&& !omflag
) || omflag
== 1)
2586 if (qmnumrend
< winmsg_numrend
)
2587 winmsg_numrend
= qmnumrend
;
2593 qmnumrend
= winmsg_numrend
;
2594 qmflag
= omflag
= 0;
2600 if (qmflag
&& omflag
!= 1)
2604 qmnumrend
= winmsg_numrend
;
2609 if (qmnumrend
< winmsg_numrend
)
2610 winmsg_numrend
= qmnumrend
;
2614 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2615 case 'a': case 'A': case 's': case 'c': case 'C':
2620 time_t nowsec
= now
.tv_sec
;
2621 tm
= localtime(&nowsec
);
2624 if (!tick
|| tick
> 3600)
2629 sprintf(p
, "%02d", tm
->tm_mday
% 100);
2633 strftime(p
, l
, (longflg
? "%A" : "%a"), tm
);
2635 sprintf(p
, "%3.3s", days
+ 3 * tm
->tm_wday
);
2639 sprintf(p
, "%02d", tm
->tm_mon
+ 1);
2643 strftime(p
, l
, (longflg
? "%B" : "%b"), tm
);
2645 sprintf(p
, "%3.3s", months
+ 3 * tm
->tm_mon
);
2649 sprintf(p
, "%02d", tm
->tm_year
% 100);
2652 sprintf(p
, "%04d", tm
->tm_year
+ 1900);
2655 sprintf(p
, tm
->tm_hour
>= 12 ? "pm" : "am");
2658 sprintf(p
, tm
->tm_hour
>= 12 ? "PM" : "AM");
2661 sprintf(p
, "%02d", tm
->tm_sec
);
2665 sprintf(p
, zeroflg
? "%02d:%02d" : "%2d:%02d", tm
->tm_hour
, tm
->tm_min
);
2666 if (!tick
|| tick
> 60)
2670 sprintf(p
, zeroflg
? "%02d:%02d" : "%2d:%02d", (tm
->tm_hour
+ 11) % 12 + 1, tm
->tm_min
);
2671 if (!tick
|| tick
> 60)
2691 if (!tick
|| tick
> 60)
2700 if (rec
>= 10 || (*s
== 'h' && (win
== 0 || win
->w_hstatus
== 0 || *win
->w_hstatus
== 0)))
2707 for (bt
= backticks
; bt
; bt
= bt
->next
)
2717 char savebuf
[sizeof(winmsg_buf
)];
2719 int oldnumrend
= winmsg_numrend
;
2722 strcpy(savebuf
, winmsg_buf
);
2723 winmsg_numrend
= -winmsg_numrend
;
2724 MakeWinMsgEv(*s
== 'h' ? win
->w_hstatus
: runbacktick(bt
, &oldtick
, now
.tv_sec
), win
, '\005', 0, (struct event
*)0, rec
+ 1);
2725 debug2("oldtick=%d tick=%d\n", oldtick
, tick
);
2726 if (!tick
|| oldtick
< tick
)
2728 if ((int)strlen(winmsg_buf
) < l
)
2729 strcat(savebuf
, winmsg_buf
);
2730 strcpy(winmsg_buf
, savebuf
);
2731 while (oldnumrend
< winmsg_numrend
)
2732 winmsg_rendpos
[oldnumrend
++] += p
- winmsg_buf
;
2741 struct win
*oldfore
= 0;
2749 ss
= AddWindows(p
, l
- 1, (*s
== 'w' ? 0 : 1) | (longflg
? 0 : 2) | (plusflg
? 4 : 0) | (minusflg
? 8 : 0), win
? win
->w_number
: -1);
2760 AddOtherUsers(p
, l
- 1, win
);
2768 AddWindowFlags(p
, l
- 1, win
);
2775 if (win
&& (int)strlen(win
->w_title
) < l
)
2777 strcpy(p
, win
->w_title
);
2787 for (i
= 0; i
< 127; i
++)
2788 if (s
[i
] && s
[i
] != '}')
2792 if (s
[i
] == '}' && winmsg_numrend
< MAX_WINMSG_REND
)
2796 debug1("MakeWinMsg attrcolor %s\n", rbuf
);
2797 if (i
!= 1 || rbuf
[0] != '-')
2798 r
= ParseAttrColor(rbuf
, (char *)0, 0);
2799 if (r
!= -1 || (i
== 1 && rbuf
[0] == '-'))
2801 winmsg_rend
[winmsg_numrend
] = r
;
2802 winmsg_rendpos
[winmsg_numrend
] = p
- winmsg_buf
;
2812 if ((int)strlen(HostName
) < l
)
2814 strcpy(p
, HostName
);
2824 session_name
= strchr(SockName
, '.') + 1;
2825 if ((int)strlen(session_name
) < l
)
2827 strcpy(p
, session_name
);
2836 sprintf(p
, "%d", (plusflg
&& display
) ? D_userpid
: getpid());
2843 if (display
&& ((ev
&& ev
== &D_forecv
->c_captev
) || (!ev
&& win
&& win
== D_fore
)))
2844 minusflg
= !minusflg
;
2850 if (display
&& ev
&& ev
!= &D_hstatusev
) /* Hack */
2852 /* Is the layer in the current canvas in copy mode? */
2853 struct canvas
*cv
= (struct canvas
*)ev
->data
;
2854 if (ev
== &cv
->c_captev
&& cv
->c_layer
->l_layfn
== &MarkLf
)
2859 truncpos
= p
- winmsg_buf
;
2860 truncper
= num
> 100 ? 100 : num
;
2861 trunclong
= longflg
;
2867 if (num
|| zeroflg
|| plusflg
|| longflg
|| (*s
!= '='))
2869 /* expand all pads */
2872 num
= (plusflg
? lastpad
: padlen
) - num
;
2873 if (!plusflg
&& padlen
== 0)
2874 num
= p
- winmsg_buf
;
2879 if (*s
!= '=' && num
== 0 && !plusflg
)
2884 num
= p
- winmsg_buf
;
2886 num
= (padlen
- (plusflg
? lastpad
: 0)) * num
/ 100;
2892 if (num
> MAXSTR
- 1)
2895 p
= pad_expand(winmsg_buf
, p
, numpad
, num
);
2897 if (p
- winmsg_buf
> num
&& !longflg
)
2906 trunc
= lastpad
+ truncper
* (num
- lastpad
) / 100;
2909 if (trunc
< lastpad
)
2911 left
= truncpos
- trunc
;
2912 if (left
> p
- winmsg_buf
- num
)
2913 left
= p
- winmsg_buf
- num
;
2914 debug1("lastpad = %d, ", lastpad
);
2915 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos
, trunc
, left
);
2918 if (left
+ lastpad
> p
- winmsg_buf
)
2919 left
= p
- winmsg_buf
- lastpad
;
2920 if (p
- winmsg_buf
- lastpad
- left
> 0)
2921 bcopy(winmsg_buf
+ lastpad
+ left
, winmsg_buf
+ lastpad
, p
- winmsg_buf
- lastpad
- left
);
2924 while (r
&& winmsg_rendpos
[r
- 1] > lastpad
)
2927 winmsg_rendpos
[r
] -= left
;
2928 if (winmsg_rendpos
[r
] < lastpad
)
2929 winmsg_rendpos
[r
] = lastpad
;
2933 if (p
- winmsg_buf
> lastpad
)
2934 winmsg_buf
[lastpad
] = '.';
2935 if (p
- winmsg_buf
> lastpad
+ 1)
2936 winmsg_buf
[lastpad
+ 1] = '.';
2937 if (p
- winmsg_buf
> lastpad
+ 2)
2938 winmsg_buf
[lastpad
+ 2] = '.';
2941 if (p
- winmsg_buf
> num
)
2943 p
= winmsg_buf
+ num
;
2946 if (num
- 1 >= lastpad
)
2948 if (num
- 2 >= lastpad
)
2950 if (num
- 3 >= lastpad
)
2954 while (r
&& winmsg_rendpos
[r
- 1] > num
)
2955 winmsg_rendpos
[--r
] = num
;
2959 if (lastpad
> p
- winmsg_buf
)
2960 lastpad
= p
- winmsg_buf
;
2961 debug1("lastpad now %d\n", lastpad
);
2965 while (p
- winmsg_buf
< num
)
2967 lastpad
= p
- winmsg_buf
;
2970 debug1("lastpad2 now %d\n", lastpad
);
2976 *p
= 127; /* internal pad representation */
2990 sprintf(p
, "%*s", num
, num
> 1 ? "--" : "-");
2992 sprintf(p
, "%*d", num
, win
->w_number
);
2999 if (qmpos
&& !qmflag
)
3004 if (padlen
> MAXSTR
- 1)
3005 padlen
= MAXSTR
- 1;
3006 p
= pad_expand(winmsg_buf
, p
, numpad
, padlen
);
3010 evdeq(ev
); /* just in case */
3011 ev
->timeout
.tv_sec
= 0;
3012 ev
->timeout
.tv_usec
= 0;
3016 now
.tv_usec
= 100000;
3020 now
.tv_sec
+= tick
- (now
.tv_sec
% tick
);
3022 debug2("NEW timeout %d %d\n", ev
->timeout
.tv_sec
, tick
);
3028 MakeWinMsg(s
, win
, esc
)
3033 return MakeWinMsgEv(s
, win
, esc
, 0, (struct event
*)0, 0);
3037 PutWinMsg(s
, start
, max
)
3043 struct mchar rendstack
[MAX_WINMSG_REND
];
3046 if (s
!= winmsg_buf
)
3048 /* sorry, no fancy coloring available */
3049 debug1("PutWinMsg %s plain\n", s
);
3062 debug2("PutWinMsg %s start attr %x\n", s
, rend
.attr
);
3063 for (i
= 0; i
< winmsg_numrend
&& max
> 0; i
++)
3065 if (p
> winmsg_rendpos
[i
] || winmsg_rendpos
[i
] > l
)
3067 if (p
< winmsg_rendpos
[i
])
3069 n
= winmsg_rendpos
[i
] - p
;
3086 rend
= rendstack
[--rendstackn
];
3090 rendstack
[rendstackn
++] = rend
;
3091 ApplyAttrColor(r
, &rend
);
3093 SetRendition(&rend
);
3121 if ((j
= open("/dev/null", 0)) >= 0)
3128 while (dup(++i
) < 0 && errno
!= EBADF
)
3130 debug1(" [%d]\n", i
);
3143 serv_read_fn(ev
, data
)
3147 debug("Knock - knock!\n");
3152 serv_select_fn(ev
, data
)
3158 debug("serv_select_fn called\n");
3159 /* XXX: messages?? */
3164 if (InterruptPlease
)
3166 debug("Backend received interrupt\n");
3167 /* This approach is rather questionable in a multi-display
3169 if (fore
&& displays
)
3171 #if defined(TERMIO) || defined(POSIX)
3172 char ibuf
= displays
->d_OldMode
.tio
.c_cc
[VINTR
];
3174 char ibuf
= displays
->d_OldMode
.m_tchars
.t_intrc
;
3177 write(W_UWP(fore
) ? fore
->w_pwin
->p_ptyfd
: fore
->w_ptyfd
,
3179 debug1("Backend wrote interrupt to %d", fore
->w_number
);
3180 debug1("%s\n", W_UWP(fore
) ? " (pseudowin)" : "");
3182 write(fore
->w_ptyfd
, &ibuf
, 1);
3183 debug1("Backend wrote interrupt to %d\n", fore
->w_number
);
3186 InterruptPlease
= 0;
3189 for (p
= windows
; p
; p
= p
->w_next
)
3191 if (p
->w_bell
== BELL_FOUND
|| p
->w_bell
== BELL_VISUAL
)
3194 int visual
= p
->w_bell
== BELL_VISUAL
|| visual_bell
;
3195 p
->w_bell
= BELL_ON
;
3196 for (display
= displays
; display
; display
= display
->d_next
)
3198 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
3199 if (cv
->c_layer
->l_bottom
== &p
->w_layer
)
3203 p
->w_bell
= BELL_DONE
;
3204 Msg(0, "%s", MakeWinMsg(BellString
, p
, '%'));
3206 else if (visual
&& !D_VB
&& (!D_status
|| !D_status_bell
))
3208 Msg(0, "%s", VisualBellString
);
3212 debug1("using vbell timeout %d\n", VBellWait
);
3213 SetTimeout(&D_statusev
, VBellWait
);
3217 /* don't annoy the user with two messages */
3218 if (p
->w_monitor
== MON_FOUND
)
3219 p
->w_monitor
= MON_DONE
;
3220 WindowChanged(p
, 'f');
3222 if (p
->w_monitor
== MON_FOUND
)
3225 p
->w_monitor
= MON_ON
;
3226 for (display
= displays
; display
; display
= display
->d_next
)
3228 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
3229 if (cv
->c_layer
->l_bottom
== &p
->w_layer
)
3232 continue; /* user already sees window */
3234 if (!(ACLBYTE(p
->w_mon_notify
, D_user
->u_id
) & ACLBIT(D_user
->u_id
)))
3235 continue; /* user doesn't care */
3237 Msg(0, "%s", MakeWinMsg(ActivityString
, p
, '%'));
3238 p
->w_monitor
= MON_DONE
;
3240 WindowChanged(p
, 'f');
3244 for (display
= displays
; display
; display
= display
->d_next
)
3247 if (D_status
== STATUS_ON_WIN
)
3249 /* XXX: should use display functions! */
3250 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
3254 /* normalize window, see resize.c */
3255 lx
= cv
->c_layer
->l_x
;
3256 ly
= cv
->c_layer
->l_y
;
3257 if (lx
== cv
->c_layer
->l_width
)
3259 if (ly
+ cv
->c_yoff
< cv
->c_ys
)
3261 int i
, n
= cv
->c_ys
- (ly
+ cv
->c_yoff
);
3262 cv
->c_yoff
= cv
->c_ys
- ly
;
3263 RethinkViewportOffsets(cv
);
3264 if (n
> cv
->c_layer
->l_height
)
3265 n
= cv
->c_layer
->l_height
;
3267 LScrollV(flayer
, -n
, 0, flayer
->l_height
- 1, 0);
3268 LayRedisplayLine(-1, -1, -1, 1);
3269 for (i
= 0; i
< n
; i
++)
3270 LayRedisplayLine(i
, 0, flayer
->l_width
- 1, 1);
3271 if (cv
== cv
->c_display
->d_forecv
)
3275 else if (ly
+ cv
->c_yoff
> cv
->c_ye
)
3277 int i
, n
= ly
+ cv
->c_yoff
- cv
->c_ye
;
3278 cv
->c_yoff
= cv
->c_ye
- ly
;
3279 RethinkViewportOffsets(cv
);
3280 if (n
> cv
->c_layer
->l_height
)
3281 n
= cv
->c_layer
->l_height
;
3283 LScrollV(flayer
, n
, 0, cv
->c_layer
->l_height
- 1, 0);
3284 LayRedisplayLine(-1, -1, -1, 1);
3285 for (i
= 0; i
< n
; i
++)
3286 LayRedisplayLine(i
+ flayer
->l_height
- n
, 0, flayer
->l_width
- 1, 1);
3287 if (cv
== cv
->c_display
->d_forecv
)
3291 if (lx
+ cv
->c_xoff
< cv
->c_xs
)
3293 int i
, n
= cv
->c_xs
- (lx
+ cv
->c_xoff
);
3294 if (n
< (cv
->c_xe
- cv
->c_xs
+ 1) / 2)
3295 n
= (cv
->c_xe
- cv
->c_xs
+ 1) / 2;
3296 if (cv
->c_xoff
+ n
> cv
->c_xs
)
3297 n
= cv
->c_xs
- cv
->c_xoff
;
3299 RethinkViewportOffsets(cv
);
3300 if (n
> cv
->c_layer
->l_width
)
3301 n
= cv
->c_layer
->l_width
;
3303 LayRedisplayLine(-1, -1, -1, 1);
3304 for (i
= 0; i
< flayer
->l_height
; i
++)
3306 LScrollH(flayer
, -n
, i
, 0, flayer
->l_width
- 1, 0, 0);
3307 LayRedisplayLine(i
, 0, n
- 1, 1);
3309 if (cv
== cv
->c_display
->d_forecv
)
3313 else if (lx
+ cv
->c_xoff
> cv
->c_xe
)
3315 int i
, n
= lx
+ cv
->c_xoff
- cv
->c_xe
;
3316 if (n
< (cv
->c_xe
- cv
->c_xs
+ 1) / 2)
3317 n
= (cv
->c_xe
- cv
->c_xs
+ 1) / 2;
3318 if (cv
->c_xoff
- n
+ cv
->c_layer
->l_width
- 1 < cv
->c_xe
)
3319 n
= cv
->c_xoff
+ cv
->c_layer
->l_width
- 1 - cv
->c_xe
;
3321 RethinkViewportOffsets(cv
);
3322 if (n
> cv
->c_layer
->l_width
)
3323 n
= cv
->c_layer
->l_width
;
3325 LayRedisplayLine(-1, -1, -1, 1);
3326 for (i
= 0; i
< flayer
->l_height
; i
++)
3328 LScrollH(flayer
, n
, i
, 0, flayer
->l_width
- 1, 0, 0);
3329 LayRedisplayLine(i
, flayer
->l_width
- n
, flayer
->l_width
- 1, 1);
3331 if (cv
== cv
->c_display
->d_forecv
)
3338 for (display
= displays
; display
; display
= display
->d_next
)
3340 if (D_status
== STATUS_ON_WIN
|| D_cvlist
== 0 || D_cvlist
->c_next
== 0)
3342 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv
);
3343 CV_CALL(D_forecv
, LayRestore();LaySetCursor());
3348 logflush_fn(ev
, data
)
3356 if (!islogfile(NULL
))
3357 return; /* no more logfiles */
3359 n
= log_flush
? log_flush
: (logtstamp_after
+ 4) / 5;
3362 SetTimeout(ev
, n
* 1000);
3363 evenq(ev
); /* re-enqueue ourself */
3367 /* write fancy time-stamp */
3368 for (p
= windows
; p
; p
= p
->w_next
)
3372 p
->w_logsilence
+= n
;
3373 if (p
->w_logsilence
< logtstamp_after
)
3375 if (p
->w_logsilence
- n
>= logtstamp_after
)
3377 buf
= MakeWinMsg(logtstamp_string
, p
, '%');
3378 logfwrite(p
->w_log
, buf
, strlen(buf
));
3383 * Interprets ^?, ^@ and other ^-control-char notation.
3384 * Interprets \ddd octal notation
3386 * The result is placed in *cp, p is advanced behind the parsed expression and
3395 if (*p
== '^' && p
[1])
3405 else if (*p
== '\\' && *++p
<= '7' && *p
>= '0')
3409 *cp
= *cp
* 8 + *p
- '0';
3410 while (*++p
<= '7' && *p
>= '0');
3421 unsigned char buf
[2];
3424 SetEscape((struct acluser
*)0, -1, -1);
3427 if ((p
= ParseChar(p
, (char *)buf
)) == NULL
||
3428 (p
= ParseChar(p
, (char *)buf
+1)) == NULL
|| *p
)
3430 SetEscape((struct acluser
*)0, buf
[0], buf
[1]);