#include <sys/types.h>
#include <ctype.h>
#include <fcntl.h>
#ifdef sgi
# include <sys/sysmacros.h>
#endif
#include <sys/stat.h>
#ifndef sun
# include <sys/ioctl.h>
#endif
#ifndef SIGINT
# include <signal.h>
#endif
#include "config.h"
#ifdef SVR4
# include <sys/stropts.h>
#endif
#if defined(SYSV) && !defined(ISC)
# include <sys/utsname.h>
#endif
#if defined(sequent) || defined(SVR4)
# include <sys/resource.h>
#endif
#ifdef ISC
# include <sys/tty.h>
# include <sys/sioctl.h>
# include <sys/pty.h>
#endif
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
# include <compat.h>
#endif
#if defined(USE_LOCALE) || defined(ENCODINGS)
# include <locale.h>
#endif
#if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
# include <langinfo.h>
#endif
#include "screen.h"
#ifdef HAVE_BRAILLE
# include "braille.h"
#endif
#include "patchlevel.h"
#ifndef LOCK
# undef SHADOWPW
#endif
#include <pwd.h>
#ifdef SHADOWPW
# include <shadow.h>
#endif
#include "logfile.h"
#ifdef __APPLE__
#include <TargetConditionals.h>
#if !TARGET_OS_EMBEDDED
#include <vproc.h>
#include <vproc_priv.h>
#endif
#endif
#ifdef DEBUG
FILE *dfp;
#endif
extern char Term[], screenterm[], **environ, Termcap[];
int force_vt = 1;
int VBellWait, MsgWait, MsgMinWait, SilenceWait;
extern struct acluser *users;
extern struct display *displays, *display;
extern int visual_bell;
#ifdef COPY_PASTE
extern unsigned char mark_key_tab[];
#endif
extern char version[];
extern char DefaultShell[];
#ifdef ZMODEM
extern char *zmodem_sendcmd;
extern char *zmodem_recvcmd;
#endif
char *ShellProg;
char *ShellArgs[2];
extern struct NewWindow nwin_undef, nwin_default, nwin_options;
struct backtick;
static struct passwd *getpwbyname __P((char *, struct passwd *));
static void SigChldHandler __P((void));
static sigret_t SigChld __P(SIGPROTOARG);
static sigret_t SigInt __P(SIGPROTOARG);
static sigret_t CoreDump __P(SIGPROTOARG);
static sigret_t FinitHandler __P(SIGPROTOARG);
static void DoWait __P((void));
static void serv_read_fn __P((struct event *, char *));
static void serv_select_fn __P((struct event *, char *));
static void logflush_fn __P((struct event *, char *));
static void backtick_filter __P((struct backtick *));
static void backtick_fn __P((struct event *, char *));
static char *runbacktick __P((struct backtick *, int *, time_t));
static int IsSymbol __P((char *, char *));
static char *ParseChar __P((char *, char *));
static int ParseEscape __P((char *));
static char *pad_expand __P((char *, char *, int, int));
#ifdef DEBUG
static void fds __P((void));
#endif
int nversion;
struct passwd *ppp;
char *attach_tty;
char *attach_term;
char *LoginName;
struct mode attach_Mode;
char SockPath[MAXPATHLEN + 2 * MAXSTR];
char *SockName;
char *SockMatch = NULL;
int ServerSocket = -1;
struct event serv_read;
struct event serv_select;
struct event logflushev;
char **NewEnv = NULL;
char *RcFileName = NULL;
char *home;
char *screenlogfile;
int log_flush = 10;
int logtstamp_on = 0;
char *logtstamp_string;
int logtstamp_after = 120;
char *hardcopydir = NULL;
char *BellString;
char *VisualBellString;
char *ActivityString;
#ifdef COPY_PASTE
char *BufferFile;
#endif
#ifdef POW_DETACH
char *PowDetachString;
#endif
char *hstatusstring;
char *captionstring;
char *timestring;
char *wliststr;
char *wlisttit;
int auto_detach = 1;
int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
int cmdflag;
int adaptflag;
#ifdef MULTIUSER
char *multi;
char *multi_home;
int multi_uid;
int own_uid;
int multiattach;
int tty_mode;
int tty_oldmode = -1;
#endif
char HostName[MAXSTR];
int MasterPid;
int real_uid, real_gid, eff_uid, eff_gid;
int default_startup;
int ZombieKey_destroy, ZombieKey_resurrect;
char *preselect = NULL;
#ifdef UTF8
char *screenencodings;
#endif
#ifdef NETHACK
int nethackflag = 0;
#endif
int maxwin = MAXWIN;
struct layer *flayer;
struct win *fore;
struct win *windows;
struct win *console_window;
#include "extern.h"
char strnomem[] = "Out of memory.";
static int InterruptPlease;
static int GotSigChld;
static int
lf_secreopen(name, wantfd, l)
char *name;
int wantfd;
struct logfile *l;
{
int got_fd;
close(wantfd);
if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
lf_move_fd(got_fd, wantfd) < 0)
{
logfclose(l);
debug1("lf_secreopen: failed for %s\n", name);
return -1;
}
l->st->st_ino = l->st->st_dev = 0;
debug2("lf_secreopen: %d = %s\n", wantfd, name);
return 0;
}
static struct passwd *
getpwbyname(name, ppp)
char *name;
struct passwd *ppp;
{
int n;
#ifdef SHADOWPW
struct spwd *sss = NULL;
static char *spw = NULL;
#endif
if (!ppp && !(ppp = getpwnam(name)))
return NULL;
#ifdef SHADOWPW
pw_try_again:
#endif
n = 0;
if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
n = 13;
for (; n < 13; n++)
{
char c = ppp->pw_passwd[n];
if (!(c == '.' || c == '/' || c == '$' ||
(c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z')))
break;
}
#ifdef SHADOWPW
if (n < 13 && sss == 0)
{
sss = getspnam(ppp->pw_name);
if (sss)
{
if (spw)
free(spw);
ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
endspent();
goto pw_try_again;
}
endspent();
}
#endif
if (n < 13)
ppp->pw_passwd = 0;
#ifdef linux
if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
ppp->pw_passwd[13] = 0;
#endif
return ppp;
}
int
main(ac, av)
int ac;
char **av;
{
register int n;
char *ap;
char *av0;
char socknamebuf[2 * MAXSTR];
int mflag = 0;
char *myname = (ac == 0) ? "screen" : av[0];
char *SockDir;
struct stat st;
#ifdef _MODE_T
mode_t oumask;
#else
int oumask;
#endif
#if defined(SYSV) && !defined(ISC)
struct utsname utsnam;
#endif
struct NewWindow nwin;
int detached = 0;
#ifdef MULTIUSER
char *sockp;
#endif
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
setcompat(COMPAT_POSIX|COMPAT_BSDPROT);
#endif
#if defined(sun) && defined(SVR4)
{
sigset_t sset;
sigemptyset(&sset);
sigprocmask(SIG_SETMASK, &sset, 0);
}
#endif
closeallfiles(0);
#ifdef DEBUG
opendebug(1, 0);
#endif
sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
PATCHLEVEL, STATE, ORIGIN, DATE);
nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
debug2("-- screen debug started %s (%s)\n", *av, version);
#ifdef POSIX
debug("POSIX\n");
#endif
#ifdef TERMIO
debug("TERMIO\n");
#endif
#ifdef SYSV
debug("SYSV\n");
#endif
#ifdef SYSVSIGS
debug("SYSVSIGS\n");
#endif
#ifdef NAMEDPIPE
debug("NAMEDPIPE\n");
#endif
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
debug("Window size changing enabled\n");
#endif
#ifdef HAVE_SETREUID
debug("SETREUID\n");
#endif
#ifdef HAVE_SETEUID
debug("SETEUID\n");
#endif
#ifdef hpux
debug("hpux\n");
#endif
#ifdef USEBCOPY
debug("USEBCOPY\n");
#endif
#ifdef UTMPOK
debug("UTMPOK\n");
#endif
#ifdef LOADAV
debug("LOADAV\n");
#endif
#ifdef NETHACK
debug("NETHACK\n");
#endif
#ifdef TERMINFO
debug("TERMINFO\n");
#endif
#ifdef SHADOWPW
debug("SHADOWPW\n");
#endif
#ifdef NAME_MAX
debug1("NAME_MAX = %d\n", NAME_MAX);
#endif
BellString = SaveStr("Bell in window %n");
VisualBellString = SaveStr(" Wuff, Wuff!! ");
ActivityString = SaveStr("Activity in window %n");
screenlogfile = SaveStr("screenlog.%n");
logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
hstatusstring = SaveStr("%h");
captionstring = SaveStr("%3n %t");
timestring = SaveStr("%c:%s %M %d %H%? %l%?");
wlisttit = SaveStr("Num Name%=Flags");
wliststr = SaveStr("%3n %t%=%f");
#ifdef COPY_PASTE
BufferFile = SaveStr(DEFAULT_BUFFERFILE);
#endif
ShellProg = NULL;
#ifdef POW_DETACH
PowDetachString = 0;
#endif
default_startup = (ac > 1) ? 0 : 1;
adaptflag = 0;
VBellWait = VBELLWAIT * 1000;
MsgWait = MSGWAIT * 1000;
MsgMinWait = MSGMINWAIT * 1000;
SilenceWait = SILENCEWAIT;
#ifdef HAVE_BRAILLE
InitBraille();
#endif
#ifdef ZMODEM
zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
#endif
#ifdef COPY_PASTE
CompileKeys((char *)0, 0, mark_key_tab);
#endif
#ifdef UTF8
InitBuiltinTabs();
screenencodings = SaveStr(SCREENENCODINGS);
#endif
nwin = nwin_undef;
nwin_options = nwin_undef;
strcpy(screenterm, "screen");
logreopen_register(lf_secreopen);
av0 = *av;
if (*av0 == '-')
{
rflag = 4;
#ifdef MULTI
xflag = 1;
#else
dflag = 1;
#endif
ShellProg = SaveStr(DefaultShell);
}
while (ac > 0)
{
ap = *++av;
if (--ac > 0 && *ap == '-')
{
if (ap[1] == '-' && ap[2] == 0)
{
av++;
ac--;
break;
}
if (ap[1] == '-' && !strcmp(ap, "--version"))
Panic(0, "Screen version %s", version);
if (ap[1] == '-' && !strcmp(ap, "--help"))
exit_with_usage(myname, NULL, NULL);
while (ap && *ap && *++ap)
{
switch (*ap)
{
case 'a':
nwin_options.aflag = 1;
break;
case 'A':
adaptflag = 1;
break;
case 'p':
if (*++ap)
preselect = ap;
else
{
if (!--ac)
exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
preselect = *++av;
}
ap = NULL;
break;
#ifdef HAVE_BRAILLE
case 'B':
bd.bd_start_braille = 1;
break;
#endif
case 'c':
if (*++ap)
RcFileName = ap;
else
{
if (--ac == 0)
exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
RcFileName = *++av;
}
ap = NULL;
break;
case 'e':
if (!*++ap)
{
if (--ac == 0)
exit_with_usage(myname, "Specify command characters with -e", NULL);
ap = *++av;
}
if (ParseEscape(ap))
Panic(0, "Two characters are required with -e option, not '%s'.", ap);
ap = NULL;
break;
case 'f':
ap++;
switch (*ap++)
{
case 'n':
case '0':
nwin_options.flowflag = FLOW_NOW * 0;
break;
case '\0':
ap--;
case 'y':
case '1':
nwin_options.flowflag = FLOW_NOW * 1;
break;
case 'a':
nwin_options.flowflag = FLOW_AUTOFLAG;
break;
default:
exit_with_usage(myname, "Unknown flow option -%s", --ap);
}
break;
case 'h':
if (--ac == 0)
exit_with_usage(myname, NULL, NULL);
nwin_options.histheight = atoi(*++av);
if (nwin_options.histheight < 0)
exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
break;
case 'i':
iflag = 1;
break;
case 't':
if (--ac == 0)
exit_with_usage(myname, "Specify a new window-name with -t", NULL);
nwin_options.aka = *++av;
break;
case 'l':
ap++;
switch (*ap++)
{
case 'n':
case '0':
nwin_options.lflag = 0;
break;
case '\0':
ap--;
case 'y':
case '1':
nwin_options.lflag = 1;
break;
case 'a':
nwin_options.lflag = 3;
break;
case 's':
case 'i':
lsflag = 1;
if (ac > 1 && !SockMatch)
{
SockMatch = *++av;
ac--;
}
ap = NULL;
break;
default:
exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
}
break;
case 'w':
lsflag = 1;
wipeflag = 1;
if (ac > 1 && !SockMatch)
{
SockMatch = *++av;
ac--;
}
break;
case 'L':
nwin_options.Lflag = 1;
break;
case 'm':
mflag = 1;
break;
case 'O':
force_vt = 0;
break;
case 'T':
if (--ac == 0)
exit_with_usage(myname, "Specify terminal-type with -T", NULL);
if (strlen(*++av) < 20)
strcpy(screenterm, *av);
else
Panic(0, "-T: terminal name too long. (max. 20 char)");
nwin_options.term = screenterm;
break;
case 'q':
quietflag = 1;
break;
case 'r':
case 'R':
#ifdef MULTI
case 'x':
#endif
if (ac > 1 && *av[1] != '-' && !SockMatch)
{
SockMatch = *++av;
ac--;
debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
}
#ifdef MULTI
if (*ap == 'x')
xflag = 1;
#endif
if (rflag)
rflag = 2;
rflag += (*ap == 'R') ? 2 : 1;
break;
#ifdef REMOTE_DETACH
case 'd':
dflag = 1;
case 'D':
if (!dflag)
dflag = 2;
if (ac == 2)
{
if (*av[1] != '-' && !SockMatch)
{
SockMatch = *++av;
ac--;
debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
}
}
break;
#endif
case 's':
if (--ac == 0)
exit_with_usage(myname, "Specify shell with -s", NULL);
if (ShellProg)
free(ShellProg);
ShellProg = SaveStr(*++av);
debug1("ShellProg: '%s'\n", ShellProg);
break;
case 'S':
if (!SockMatch)
{
if (--ac == 0)
exit_with_usage(myname, "Specify session-name with -S", NULL);
SockMatch = *++av;
}
if (!*SockMatch)
exit_with_usage(myname, "Empty session-name?", NULL);
break;
case 'X':
cmdflag = 1;
break;
case 'v':
Panic(0, "Screen version %s", version);
#ifdef UTF8
case 'U':
nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
break;
#endif
default:
exit_with_usage(myname, "Unknown option %s", --ap);
}
}
}
else
break;
}
real_uid = getuid();
real_gid = getgid();
eff_uid = geteuid();
eff_gid = getegid();
if (eff_uid != real_uid)
{
#ifdef SIGBUS
signal(SIGBUS, CoreDump);
#endif
signal(SIGSEGV, CoreDump);
}
#ifdef USE_LOCALE
setlocale(LC_ALL, "");
#endif
#ifdef ENCODINGS
if (nwin_options.encoding == -1)
{
# ifdef HAVE_NL_LANGINFO
# ifndef USE_LOCALE
setlocale(LC_CTYPE, "");
# endif
nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
debug1("locale says encoding = %d\n", nwin_options.encoding);
# else
# ifdef UTF8
char *s;
if (((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
(s = getenv("LANG"))) && InStr(s, "UTF-8"))
nwin_options.encoding = UTF8;
# endif
debug1("environment says encoding=%d\n", nwin_options.encoding);
#endif
}
#endif
if (SockMatch && strlen(SockMatch) >= MAXSTR)
Panic(0, "Ridiculously long socketname - try again.");
if (cmdflag && !rflag && !dflag && !xflag)
xflag = 1;
if (!cmdflag && dflag && mflag && !(rflag || xflag))
detached = 1;
nwin = nwin_options;
#ifdef ENCODINGS
nwin.encoding = nwin_undef.encoding;
#endif
if (ac)
nwin.args = av;
#ifdef SIGXFSZ
signal(SIGXFSZ, SIG_IGN);
#endif
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
if (!ShellProg)
{
register char *sh;
sh = getenv("SHELL");
ShellProg = SaveStr(sh ? sh : DefaultShell);
}
ShellArgs[0] = ShellProg;
home = getenv("HOME");
#ifdef NETHACK
if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
{
char nethackrc[MAXPATHLEN];
if (home && (strlen(home) < (MAXPATHLEN - 20)))
{
sprintf(nethackrc,"%s/.nethackrc", home);
nethackflag = !access(nethackrc, F_OK);
}
}
#endif
#ifdef MULTIUSER
own_uid = multi_uid = real_uid;
if (SockMatch && (sockp = index(SockMatch, '/')))
{
if (eff_uid)
Panic(0, "Must run suid root for multiuser support.");
*sockp = 0;
multi = SockMatch;
SockMatch = sockp + 1;
if (*multi)
{
struct passwd *mppp;
if ((mppp = getpwnam(multi)) == (struct passwd *)0)
Panic(0, "Cannot identify account '%s'.", multi);
multi_uid = mppp->pw_uid;
multi_home = SaveStr(mppp->pw_dir);
if (strlen(multi_home) > MAXPATHLEN - 10)
Panic(0, "home directory path too long");
# ifdef MULTI
if (rflag || lsflag)
xflag = 1;
# endif
detached = 0;
multiattach = 1;
}
}
if (SockMatch && *SockMatch == 0)
SockMatch = 0;
#endif
if ((LoginName = getlogin()) && LoginName[0] != '\0')
{
if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
if ((int)ppp->pw_uid != real_uid)
ppp = (struct passwd *) 0;
}
if (ppp == 0)
{
if ((ppp = getpwuid(real_uid)) == 0)
{
Panic(0, "getpwuid() can't identify your account!");
exit(1);
}
LoginName = ppp->pw_name;
}
LoginName = SaveStr(LoginName);
ppp = getpwbyname(LoginName, ppp);
#if !defined(SOCKDIR) && defined(MULTIUSER)
if (multi && !multiattach)
{
if (home && strcmp(home, ppp->pw_dir))
Panic(0, "$HOME must match passwd entry for multiuser screens.");
}
#endif
if (home == 0 || *home == '\0')
home = ppp->pw_dir;
if (strlen(LoginName) > 20)
Panic(0, "LoginName too long - sorry.");
#ifdef MULTIUSER
if (multi && strlen(multi) > 20)
Panic(0, "Screen owner name too long - sorry.");
#endif
if (strlen(home) > MAXPATHLEN - 25)
Panic(0, "$HOME too long - sorry.");
attach_tty = "";
if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag))
{
if (!(attach_tty = ttyname(0)))
Panic(0, "Must be connected to a terminal.");
if (strlen(attach_tty) >= MAXPATHLEN)
Panic(0, "TtyName too long - sorry.");
if (stat(attach_tty, &st))
Panic(errno, "Cannot access '%s'", attach_tty);
#ifdef MULTIUSER
tty_mode = (int)st.st_mode & 0777;
#endif
if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
close(n);
debug1("attach_tty is %s\n", attach_tty);
if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
Panic(0, "Please set a terminal type.");
if (strlen(attach_term) > sizeof(D_termname) - 1)
Panic(0, "$TERM too long - sorry.");
GetTTY(0, &attach_Mode);
#ifdef DEBUGGGGGGGGGGGGGGG
DebugTTY(&attach_Mode);
#endif
}
#ifdef _MODE_T
oumask = umask(0);
#else
if ((oumask = (int)umask(0)) == -1)
Panic(errno, "Cannot change umask to zero");
#endif
SockDir = getenv("SCREENDIR");
if (SockDir)
{
if (strlen(SockDir) >= MAXPATHLEN - 1)
Panic(0, "Ridiculously long $SCREENDIR - try again.");
#ifdef MULTIUSER
if (multi)
Panic(0, "No $SCREENDIR with multi screens, please.");
#endif
}
#ifdef __APPLE__
else if (!multi && real_uid == eff_uid) {
static char DarwinSockDir[PATH_MAX];
if (confstr(_CS_DARWIN_USER_TEMP_DIR, DarwinSockDir, sizeof(DarwinSockDir))) {
strlcat(DarwinSockDir, ".screen", sizeof(DarwinSockDir));
SockDir = DarwinSockDir;
}
}
#endif
#ifdef MULTIUSER
if (multiattach)
{
# ifndef SOCKDIR
sprintf(SockPath, "%s/.screen", multi_home);
SockDir = SockPath;
# else
SockDir = SOCKDIR;
sprintf(SockPath, "%s/S-%s", SockDir, multi);
# endif
}
else
#endif
{
#ifndef SOCKDIR
if (SockDir == 0)
{
sprintf(SockPath, "%s/.screen", home);
SockDir = SockPath;
}
#endif
if (SockDir)
{
if (access(SockDir, F_OK))
{
debug1("SockDir '%s' missing ...\n", SockDir);
if (UserContext() > 0)
{
if (mkdir(SockDir, 0700))
UserReturn(0);
UserReturn(1);
}
if (UserStatus() <= 0)
Panic(0, "Cannot make directory '%s'.", SockDir);
}
if (SockDir != SockPath)
strcpy(SockPath, SockDir);
}
#ifdef SOCKDIR
else
{
SockDir = SOCKDIR;
if (lstat(SockDir, &st))
{
n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
(eff_gid != real_gid) ? 0775 :
#ifdef S_ISVTX
0777|S_ISVTX;
#else
0777;
#endif
if (mkdir(SockDir, n) == -1)
Panic(errno, "Cannot make directory '%s'", SockDir);
}
else
{
if (!S_ISDIR(st.st_mode))
Panic(0, "'%s' must be a directory.", SockDir);
if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
Panic(0, "Directory '%s' must be owned by root.", SockDir);
n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
(eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
0777;
if (((int)st.st_mode & 0777) != n)
Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
}
sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
if (access(SockPath, F_OK))
{
if (mkdir(SockPath, 0700) == -1)
Panic(errno, "Cannot make directory '%s'", SockPath);
(void) chown(SockPath, real_uid, real_gid);
}
}
#endif
}
if (stat(SockPath, &st) == -1)
Panic(errno, "Cannot access %s", SockPath);
else
if (!S_ISDIR(st.st_mode))
Panic(0, "%s is not a directory.", SockPath);
#ifdef MULTIUSER
if (multi)
{
if ((int)st.st_uid != multi_uid)
Panic(0, "%s is not the owner of %s.", multi, SockPath);
}
else
#endif
{
if ((int)st.st_uid != real_uid)
Panic(0, "You are not the owner of %s.", SockPath);
}
if ((st.st_mode & 0777) != 0700)
Panic(0, "Directory %s must have mode 700.", SockPath);
if (SockMatch && index(SockMatch, '/'))
Panic(0, "Bad session name '%s'", SockMatch);
SockName = SockPath + strlen(SockPath) + 1;
*SockName = 0;
(void) umask(oumask);
debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
#if defined(SYSV) && !defined(ISC)
if (uname(&utsnam) == -1)
Panic(errno, "uname");
strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
#else
(void) gethostname(HostName, MAXSTR);
HostName[MAXSTR - 1] = '\0';
#endif
if ((ap = index(HostName, '.')) != NULL)
*ap = '\0';
if (lsflag)
{
int i, fo, oth;
#ifdef MULTIUSER
if (multi)
real_uid = multi_uid;
#endif
setgid(real_gid);
setuid(real_uid);
eff_uid = real_uid;
eff_gid = real_gid;
i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
if (quietflag)
exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
if (fo == 0)
Panic(0, "No Sockets found in %s.\n", SockPath);
Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
}
signal(SIG_BYE, AttacherFinit);
if (cmdflag)
{
char *sty = 0;
if ((attach_tty = ttyname(0)) == 0)
attach_tty = "";
if (strlen(attach_tty) >= MAXPATHLEN)
Panic(0, "TtyName too long - sorry.");
if (!*av)
Panic(0, "Please specify a command.");
setgid(real_gid);
setuid(real_uid);
eff_uid = real_uid;
eff_gid = real_gid;
if (!mflag && !SockMatch)
{
sty = getenv("STY");
if (sty && *sty == 0)
sty = 0;
}
SendCmdMessage(sty, SockMatch, av);
exit(0);
}
else if (rflag || xflag)
{
debug("screen -r: - is there anybody out there?\n");
if (Attach(MSG_ATTACH))
{
Attacher();
}
#ifdef MULTIUSER
if (multiattach)
Panic(0, "Can't create sessions of other users.");
#endif
debug("screen -r: backend not responding -- still crying\n");
}
else if (dflag && !mflag)
{
(void) Attach(MSG_DETACH);
Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
eexit(0);
}
if (!SockMatch && !mflag)
{
register char *sty;
if ((sty = getenv("STY")) != 0 && *sty != '\0')
{
setgid(real_gid);
setuid(real_uid);
eff_uid = real_uid;
eff_gid = real_gid;
nwin_options.args = av;
SendCreateMsg(sty, &nwin);
exit(0);
}
}
nwin_compose(&nwin_default, &nwin_options, &nwin_default);
if (!detached || dflag != 2)
MasterPid = fork();
else
MasterPid = 0;
switch (MasterPid)
{
case -1:
Panic(errno, "fork");
case 0:
break;
default:
if (detached)
exit(0);
if (SockMatch)
sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
else
sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
for (ap = socknamebuf; *ap; ap++)
if (*ap == '/')
*ap = '-';
#ifdef NAME_MAX
if (strlen(socknamebuf) > NAME_MAX)
socknamebuf[NAME_MAX] = 0;
#endif
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
setgid(real_gid);
setuid(real_uid);
eff_uid = real_uid;
eff_gid = real_gid;
Attacher();
}
if (DefaultEsc == -1)
DefaultEsc = Ctrl('a');
if (DefaultMetaEsc == -1)
DefaultMetaEsc = 'a';
ap = av0 + strlen(av0) - 1;
while (ap >= av0)
{
if (!strncmp("screen", ap, 6))
{
strncpy(ap, "SCREEN", 6);
break;
}
ap--;
}
if (ap < av0)
*av0 = 'S';
#ifdef DEBUG
{
char buf[256];
if (dfp && dfp != stderr)
fclose(dfp);
sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
if ((dfp = fopen(buf, "w")) == NULL)
dfp = stderr;
else
(void) chmod(buf, 0666);
}
#endif
if (!detached)
{
if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
}
else
n = -1;
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
#ifdef DEBUG
if (dfp != stderr)
#endif
freopen("/dev/null", "w", stderr);
debug("-- screen.back debug started\n");
#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
if (_vprocmgr_detach_from_console(0) != NULL)
errx(1, "can't detach from console");
#endif
if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
Panic(0, "Could not create user info");
if (!detached)
{
if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
Panic(0, "Could not alloc display");
#ifdef ENCODINGS
D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
debug1("D_encoding = %d\n", D_encoding);
#endif
}
if (SockMatch)
{
sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
}
else
{
sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
HostName);
}
for (ap = socknamebuf; *ap; ap++)
if (*ap == '/')
*ap = '-';
#ifdef NAME_MAX
if (strlen(socknamebuf) > NAME_MAX)
{
debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
socknamebuf[NAME_MAX] = 0;
}
#endif
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
ServerSocket = MakeServerSocket();
InitKeytab();
#ifdef ETCSCREENRC
# ifdef ALLOW_SYSSCREENRC
if ((ap = getenv("SYSSCREENRC")))
StartRc(ap);
else
# endif
StartRc(ETCSCREENRC);
#endif
StartRc(RcFileName);
# ifdef UTMPOK
# ifndef UTNOKEEP
InitUtmp();
# endif
# endif
if (display)
{
if (InitTermcap(0, 0))
{
debug("Could not init termcap - exiting\n");
fcntl(D_userfd, F_SETFL, 0);
freetty();
if (D_userpid)
Kill(D_userpid, SIG_BYE);
eexit(1);
}
MakeDefaultCanvas();
InitTerm(0);
#ifdef UTMPOK
RemoveLoginSlot();
#endif
}
else
MakeTermcap(1);
#ifdef LOADAV
InitLoadav();
#endif
MakeNewEnv();
signal(SIGHUP, SigHup);
signal(SIGINT, FinitHandler);
signal(SIGQUIT, FinitHandler);
signal(SIGTERM, FinitHandler);
#ifdef BSDJOBS
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
#endif
if (display)
{
brktty(D_userfd);
SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
SetTTY(D_userfd, &D_NewMode);
if (fcntl(D_userfd, F_SETFL, FNBLOCK))
Msg(errno, "Warning: NBLOCK fcntl failed");
}
else
brktty(-1);
signal(SIGCHLD, SigChld);
#ifdef ETCSCREENRC
# ifdef ALLOW_SYSSCREENRC
if ((ap = getenv("SYSSCREENRC")))
FinishRc(ap);
else
# endif
FinishRc(ETCSCREENRC);
#endif
FinishRc(RcFileName);
debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
if (windows == NULL)
{
debug("We open one default window, as screenrc did not specify one.\n");
if (MakeWindow(&nwin) == -1)
{
Msg(0, "Sorry, could not find a PTY.");
sleep(5);
Finit(0);
}
}
#ifdef HAVE_BRAILLE
StartBraille();
#endif
if (display && default_startup)
display_copyright();
signal(SIGINT, SigInt);
if (rflag && (rflag & 1) == 0)
{
Msg(0, "New screen...");
rflag = 0;
}
serv_read.type = EV_READ;
serv_read.fd = ServerSocket;
serv_read.handler = serv_read_fn;
evenq(&serv_read);
serv_select.pri = -10;
serv_select.type = EV_ALWAYS;
serv_select.handler = serv_select_fn;
evenq(&serv_select);
logflushev.type = EV_TIMEOUT;
logflushev.handler = logflush_fn;
sched();
return 0;
}
void
WindowDied(p)
struct win *p;
{
if (ZombieKey_destroy)
{
char buf[100], *s;
time_t now;
(void) time(&now);
s = ctime(&now);
if (s && *s)
s[strlen(s) - 1] = '\0';
debug3("window %d (%s) going into zombie state fd %d",
p->w_number, p->w_title, p->w_ptyfd);
#ifdef UTMPOK
if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
{
RemoveUtmp(p);
p->w_slot = 0;
}
#endif
CloseDevice(p);
p->w_pid = 0;
ResetWindow(p);
p->w_y = MFindUsedLine(p, p->w_bot, 1);
sprintf(buf, "\n\r=== Window terminated (%s) ===", s ? s : "?");
WriteString(p, buf, strlen(buf));
WindowChanged(p, 'f');
}
else
KillWindow(p);
#ifdef UTMPOK
CarefulUtmp();
#endif
}
static void
SigChldHandler()
{
struct stat st;
#ifdef DEBUG
fds();
#endif
while (GotSigChld)
{
GotSigChld = 0;
DoWait();
#ifdef SYSVSIGS
signal(SIGCHLD, SigChld);
#endif
}
if (stat(SockPath, &st) == -1)
{
debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
if (!RecoverSocket())
{
debug("SCREEN cannot recover from corrupt Socket, bye\n");
Finit(1);
}
else
debug1("'%s' reconstructed\n", SockPath);
}
else
debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
}
static sigret_t
SigChld SIGDEFARG
{
debug("SigChld()\n");
GotSigChld = 1;
SIGRETURN;
}
sigret_t
SigHup SIGDEFARG
{
while ((display = displays) != 0)
Hangup();
SIGRETURN;
}
static sigret_t
SigInt SIGDEFARG
{
#if HAZARDOUS
char ibuf;
debug("SigInt()\n");
if (fore && displays)
{
# if defined(TERMIO) || defined(POSIX)
ibuf = displays->d_OldMode.tio.c_cc[VINTR];
# else
ibuf = displays->d_OldMode.m_tchars.t_intrc;
# endif
fore->w_inlen = 0;
write(fore->w_ptyfd, &ibuf, 1);
}
#else
signal(SIGINT, SigInt);
debug("SigInt() careful\n");
InterruptPlease = 1;
#endif
SIGRETURN;
}
static sigret_t
CoreDump SIGDEFARG
{
struct display *disp;
char buf[80];
#if defined(SYSVSIGS) && defined(SIGHASARG)
signal(sigsig, SIG_IGN);
#endif
setgid(getgid());
setuid(getuid());
unlink("core");
#ifdef SIGHASARG
sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
#else
sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
#endif
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
""
#else
" (core dumped)"
#endif
);
for (disp = displays; disp; disp = disp->d_next)
{
fcntl(disp->d_userfd, F_SETFL, 0);
SetTTY(disp->d_userfd, &D_OldMode);
write(disp->d_userfd, buf, strlen(buf));
Kill(disp->d_userpid, SIG_BYE);
}
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
Kill(getpid(), SIGKILL);
eexit(11);
#else
abort();
#endif
SIGRETURN;
}
static void
DoWait()
{
register int pid;
struct win *p, *next;
#ifdef BSDWAIT
union wait wstat;
#else
int wstat;
#endif
#ifdef BSDJOBS
# ifndef BSDWAIT
while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
# else
# ifdef USE_WAIT2
while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
# else
while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
# endif
# endif
#else
while ((pid = wait(&wstat)) < 0)
if (errno != EINTR)
break;
if (pid > 0)
#endif
{
for (p = windows; p; p = next)
{
next = p->w_next;
if (pid == p->w_pid)
{
#ifdef BSDJOBS
if (WIFSTOPPED(wstat))
{
debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, p->w_pid, WSTOPSIG(wstat));
#ifdef SIGTTIN
if (WSTOPSIG(wstat) == SIGTTIN)
{
Msg(0, "Suspended (tty input)");
continue;
}
#endif
#ifdef SIGTTOU
if (WSTOPSIG(wstat) == SIGTTOU)
{
Msg(0, "Suspended (tty output)");
continue;
}
#endif
Msg(0, "Child has been stopped, restarting.");
if (killpg(p->w_pid, SIGCONT))
kill(p->w_pid, SIGCONT);
}
else
#endif
{
WindowDied(p);
}
break;
}
#ifdef PSEUDOS
if (p->w_pwin && pid == p->w_pwin->p_pid)
{
debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
FreePseudowin(p);
break;
}
#endif
}
if (p == 0)
{
debug1("pid %d not found - hope that's ok\n", pid);
}
}
}
static sigret_t
FinitHandler SIGDEFARG
{
#ifdef SIGHASARG
debug1("FinitHandler called, sig %d.\n", sigsig);
#else
debug("FinitHandler called.\n");
#endif
Finit(1);
SIGRETURN;
}
void
Finit(i)
int i;
{
struct win *p, *next;
signal(SIGCHLD, SIG_DFL);
signal(SIGHUP, SIG_IGN);
debug1("Finit(%d);\n", i);
for (p = windows; p; p = next)
{
next = p->w_next;
FreeWindow(p);
}
if (ServerSocket != -1)
{
debug1("we unlink(%s)\n", SockPath);
#ifdef USE_SETEUID
xseteuid(real_uid);
xsetegid(real_gid);
#endif
(void) unlink(SockPath);
#ifdef USE_SETEUID
xseteuid(eff_uid);
xsetegid(eff_gid);
#endif
}
for (display = displays; display; display = display->d_next)
{
if (D_status)
RemoveStatus();
FinitTerm();
#ifdef UTMPOK
RestoreLoginSlot();
#endif
AddStr("[screen is terminating]\r\n");
Flush();
SetTTY(D_userfd, &D_OldMode);
fcntl(D_userfd, F_SETFL, 0);
freetty();
Kill(D_userpid, SIG_BYE);
}
exit(i);
}
void
eexit(e)
int e;
{
debug("eexit\n");
if (ServerSocket != -1)
{
debug1("we unlink(%s)\n", SockPath);
setgid(real_gid);
setuid(real_uid);
(void) unlink(SockPath);
}
exit(e);
}
void
Hangup()
{
if (display == 0)
return;
debug1("Hangup %x\n", display);
if (D_userfd >= 0)
{
close(D_userfd);
D_userfd = -1;
}
if (auto_detach || displays->d_next)
Detach(D_HANGUP);
else
Finit(0);
}
void
Detach(mode)
int mode;
{
int sign = 0, pid;
struct canvas *cv;
struct win *p;
if (display == 0)
return;
signal(SIGHUP, SIG_IGN);
debug1("Detach(%d)\n", mode);
if (D_status)
RemoveStatus();
FinitTerm();
if (!display)
return;
switch (mode)
{
case D_HANGUP:
sign = SIG_BYE;
break;
case D_DETACH:
AddStr("[detached]\r\n");
sign = SIG_BYE;
break;
#ifdef BSDJOBS
case D_STOP:
sign = SIG_STOP;
break;
#endif
#ifdef REMOTE_DETACH
case D_REMOTE:
AddStr("[remote detached]\r\n");
sign = SIG_BYE;
break;
#endif
#ifdef POW_DETACH
case D_POWER:
AddStr("[power detached]\r\n");
if (PowDetachString)
{
AddStr(PowDetachString);
AddStr("\r\n");
}
sign = SIG_POWER_BYE;
break;
#ifdef REMOTE_DETACH
case D_REMOTE_POWER:
AddStr("[remote power detached]\r\n");
if (PowDetachString)
{
AddStr(PowDetachString);
AddStr("\r\n");
}
sign = SIG_POWER_BYE;
break;
#endif
#endif
case D_LOCK:
ClearAll();
sign = SIG_LOCK;
break;
}
#ifdef UTMPOK
if (displays->d_next == 0)
{
for (p = windows; p; p = p->w_next)
{
if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
{
RemoveUtmp(p);
p->w_slot = (slot_t) 0;
}
}
}
if (mode != D_HANGUP)
RestoreLoginSlot();
#endif
if (displays->d_next == 0 && console_window)
{
if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
{
debug("could not release console - killing window\n");
KillWindow(console_window);
display = displays;
}
}
if (D_fore)
{
#ifdef MULTIUSER
ReleaseAutoWritelock(display, D_fore);
#endif
D_user->u_detachwin = D_fore->w_number;
D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
}
for (cv = D_cvlist; cv; cv = cv->c_next)
{
p = Layer2Window(cv->c_layer);
SetCanvasWindow(cv, 0);
if (p)
WindowChanged(p, 'u');
}
pid = D_userpid;
debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
FreeDisplay();
if (displays == 0)
(void) chsock();
Kill(pid, sign);
debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
debug("Detach returns, we are successfully detached.\n");
signal(SIGHUP, SigHup);
}
static int
IsSymbol(e, s)
char *e, *s;
{
register int l;
l = strlen(s);
return strncmp(e, s, l) == 0 && e[l] == '=';
}
void
MakeNewEnv()
{
register char **op, **np;
static char stybuf[MAXSTR];
for (op = environ; *op; ++op)
;
if (NewEnv)
free((char *)NewEnv);
NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
if (!NewEnv)
Panic(0, strnomem);
sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
*np++ = stybuf;
*np++ = Term;
np++;
#ifdef TIOCSWINSZ
np += 2;
#else
np += 4;
#endif
for (op = environ; *op; ++op)
{
if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
&& !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
&& !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
&& !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
)
*np++ = *op;
}
*np = 0;
}
void
#if defined(USEVARARGS) && defined(__STDC__)
Msg(int err, char *fmt, VA_DOTS)
#else
Msg(err, fmt, VA_DOTS)
int err;
char *fmt;
VA_DECL
#endif
{
VA_LIST(ap)
char buf[MAXPATHLEN*2];
char *p = buf;
VA_START(ap, fmt);
fmt = DoNLS(fmt);
(void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
VA_END(ap);
if (err)
{
p += strlen(p);
*p++ = ':';
*p++ = ' ';
strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
buf[sizeof(buf) - 1] = 0;
}
debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
if (display && displays)
MakeStatus(buf);
else if (displays)
{
for (display = displays; display; display = display->d_next)
MakeStatus(buf);
}
else if (display)
{
char *tty = D_usertty;
struct display *olddisplay = display;
display = 0;
SendErrorMsg(tty, buf);
display = olddisplay;
}
else
printf("%s\r\n", buf);
}
void
#if defined(USEVARARGS) && defined(__STDC__)
Panic(int err, char *fmt, VA_DOTS)
#else
Panic(err, fmt, VA_DOTS)
int err;
char *fmt;
VA_DECL
#endif
{
VA_LIST(ap)
char buf[MAXPATHLEN*2];
char *p = buf;
VA_START(ap, fmt);
fmt = DoNLS(fmt);
(void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
VA_END(ap);
if (err)
{
p += strlen(p);
*p++ = ':';
*p++ = ' ';
strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
buf[sizeof(buf) - 1] = 0;
}
debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
if (displays == 0 && display == 0)
printf("%s\r\n", buf);
else if (displays == 0)
{
char *tty = D_usertty;
display = 0;
SendErrorMsg(tty, buf);
sleep(2);
_exit(1);
}
else
for (display = displays; display; display = display->d_next)
{
if (D_status)
RemoveStatus();
FinitTerm();
Flush();
#ifdef UTMPOK
RestoreLoginSlot();
#endif
SetTTY(D_userfd, &D_OldMode);
fcntl(D_userfd, F_SETFL, 0);
write(D_userfd, buf, strlen(buf));
write(D_userfd, "\n", 1);
freetty();
if (D_userpid)
Kill(D_userpid, SIG_BYE);
}
#ifdef MULTIUSER
if (tty_oldmode >= 0)
{
# ifdef USE_SETEUID
if (setuid(own_uid))
xseteuid(own_uid);
# else
setuid(own_uid);
# endif
debug1("Panic: changing back modes from %s\n", attach_tty);
chmod(attach_tty, tty_oldmode);
}
#endif
eexit(1);
}
#ifndef USE_LOCALE
static const char days[] = "SunMonTueWedThuFriSat";
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
#endif
static char winmsg_buf[MAXSTR];
#define MAX_WINMSG_REND 16
static int winmsg_rend[MAX_WINMSG_REND];
static int winmsg_rendpos[MAX_WINMSG_REND];
static int winmsg_numrend;
static char *
pad_expand(buf, p, numpad, padlen)
char *buf;
char *p;
int numpad;
int padlen;
{
char *pn, *pn2;
int i, r;
padlen = padlen - (p - buf);
if (padlen < 0)
padlen = 0;
pn2 = pn = p + padlen;
r = winmsg_numrend;
while (p >= buf)
{
if (r && p - buf == winmsg_rendpos[r - 1])
{
winmsg_rendpos[--r] = pn - buf;
continue;
}
*pn-- = *p;
if (*p-- == 127)
{
pn[1] = ' ';
i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
padlen -= i;
while (i-- > 0)
*pn-- = ' ';
numpad--;
}
}
return pn2;
}
struct backtick {
struct backtick *next;
int num;
int tick;
int lifespan;
time_t bestbefore;
char result[MAXSTR];
char **cmdv;
struct event ev;
char *buf;
int bufi;
};
struct backtick *backticks;
static void
backtick_filter(bt)
struct backtick *bt;
{
char *p, *q;
int c;
for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
{
if (c == '\t')
c = ' ';
if (c >= ' ' || c == '\005')
*q++ = c;
}
*q = 0;
}
static void
backtick_fn(ev, data)
struct event *ev;
char *data;
{
struct backtick *bt;
int i, j, k, l;
bt = (struct backtick *)data;
debug1("backtick_fn for #%d\n", bt->num);
i = bt->bufi;
l = read(ev->fd, bt->buf + i, MAXSTR - i);
if (l <= 0)
{
debug1("EOF on backtick #%d\n", bt->num);
evdeq(ev);
close(ev->fd);
ev->fd = -1;
return;
}
debug1("read %d bytes\n", l);
i += l;
for (j = 0; j < l; j++)
if (bt->buf[i - j - 1] == '\n')
break;
if (j < l)
{
for (k = i - j - 2; k >= 0; k--)
if (bt->buf[k] == '\n')
break;
k++;
bcopy(bt->buf + k, bt->result, i - j - k);
bt->result[i - j - k - 1] = 0;
backtick_filter(bt);
WindowChanged(0, '`');
}
if (j == l && i == MAXSTR)
{
j = MAXSTR/2;
l = j + 1;
}
if (j < l)
{
if (j)
bcopy(bt->buf + i - j, bt->buf, j);
i = j;
}
bt->bufi = i;
}
void
setbacktick(num, lifespan, tick, cmdv)
int num;
int lifespan;
int tick;
char **cmdv;
{
struct backtick **btp, *bt;
char **v;
debug1("setbacktick called for backtick #%d\n", num);
for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
if (bt->num == num)
break;
if (!bt && !cmdv)
return;
if (bt)
{
for (v = bt->cmdv; *v; v++)
free(*v);
free(bt->cmdv);
if (bt->buf)
free(bt->buf);
if (bt->ev.fd >= 0)
close(bt->ev.fd);
evdeq(&bt->ev);
}
if (bt && !cmdv)
{
*btp = bt->next;
free(bt);
return;
}
if (!bt)
{
bt = (struct backtick *)malloc(sizeof *bt);
if (!bt)
{
Msg(0, strnomem);
return;
}
bzero(bt, sizeof(*bt));
bt->next = 0;
*btp = bt;
}
bt->num = num;
bt->tick = tick;
bt->lifespan = lifespan;
bt->bestbefore = 0;
bt->result[0] = 0;
bt->buf = 0;
bt->bufi = 0;
bt->cmdv = cmdv;
bt->ev.fd = -1;
if (bt->tick == 0 && bt->lifespan == 0)
{
debug("setbacktick: continuous mode\n");
bt->buf = (char *)malloc(MAXSTR);
if (bt->buf == 0)
{
Msg(0, strnomem);
setbacktick(num, 0, 0, (char **)0);
return;
}
bt->ev.type = EV_READ;
bt->ev.fd = readpipe(bt->cmdv);
bt->ev.handler = backtick_fn;
bt->ev.data = (char *)bt;
if (bt->ev.fd >= 0)
evenq(&bt->ev);
}
}
static char *
runbacktick(bt, tickp, now)
struct backtick *bt;
int *tickp;
time_t now;
{
int f, i, l, j;
time_t now2;
debug1("runbacktick called for backtick #%d\n", bt->num);
if (bt->tick && (!*tickp || bt->tick < *tickp))
*tickp = bt->tick;
if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
{
debug1("returning old result (%d)\n", bt->lifespan);
return bt->result;
}
f = readpipe(bt->cmdv);
if (f == -1)
return bt->result;
i = 0;
while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
{
debug1("runbacktick: read %d bytes\n", l);
i += l;
for (j = 1; j < l; j++)
if (bt->result[i - j - 1] == '\n')
break;
if (j == l && i == sizeof(bt->result))
{
j = sizeof(bt->result) / 2;
l = j + 1;
}
if (j < l)
{
bcopy(bt->result + i - j, bt->result, j);
i = j;
}
}
close(f);
bt->result[sizeof(bt->result) - 1] = '\n';
if (i && bt->result[i - 1] == '\n')
i--;
debug1("runbacktick: finished, %d bytes\n", i);
bt->result[i] = 0;
backtick_filter(bt);
(void)time(&now2);
bt->bestbefore = now2 + bt->lifespan;
return bt->result;
}
char *
MakeWinMsgEv(str, win, esc, padlen, ev, rec)
char *str;
struct win *win;
int esc;
int padlen;
struct event *ev;
int rec;
{
static int tick;
char *s = str;
register char *p = winmsg_buf;
register int ctrl;
struct timeval now;
struct tm *tm;
int l, i, r;
int num;
int zeroflg;
int longflg;
int minusflg;
int plusflg;
int qmflag = 0, omflag = 0, qmnumrend = 0;
char *qmpos = 0;
int numpad = 0;
int lastpad = 0;
int truncpos = -1;
int truncper = 0;
int trunclong = 0;
struct backtick *bt;
if (winmsg_numrend >= 0)
winmsg_numrend = 0;
else
winmsg_numrend = -winmsg_numrend;
tick = 0;
tm = 0;
ctrl = 0;
gettimeofday(&now, NULL);
for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
{
*p = *s;
if (ctrl)
{
ctrl = 0;
if (*s != '^' && *s >= 64)
*p &= 0x1f;
continue;
}
if (*s != esc)
{
if (esc == '%')
{
switch (*s)
{
#if 0
case '~':
*p = BELL;
break;
#endif
case '^':
ctrl = 1;
*p-- = '^';
break;
default:
break;
}
}
continue;
}
if (*++s == esc)
continue;
if ((plusflg = *s == '+') != 0)
s++;
if ((minusflg = *s == '-') != 0)
s++;
if ((zeroflg = *s == '0') != 0)
s++;
num = 0;
while(*s >= '0' && *s <= '9')
num = num * 10 + (*s++ - '0');
if ((longflg = *s == 'L') != 0)
s++;
switch (*s)
{
case '?':
p--;
if (qmpos)
{
if ((!qmflag && !omflag) || omflag == 1)
{
p = qmpos;
if (qmnumrend < winmsg_numrend)
winmsg_numrend = qmnumrend;
}
qmpos = 0;
break;
}
qmpos = p;
qmnumrend = winmsg_numrend;
qmflag = omflag = 0;
break;
case ':':
p--;
if (!qmpos)
break;
if (qmflag && omflag != 1)
{
omflag = 1;
qmpos = p;
qmnumrend = winmsg_numrend;
}
else
{
p = qmpos;
if (qmnumrend < winmsg_numrend)
winmsg_numrend = qmnumrend;
omflag = -1;
}
break;
case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
case 'a': case 'A': case 's': case 'c': case 'C':
if (l < 4)
break;
if (tm == 0)
{
time_t nowsec = now.tv_sec;
tm = localtime(&nowsec);
}
qmflag = 1;
if (!tick || tick > 3600)
tick = 3600;
switch (*s)
{
case 'd':
sprintf(p, "%02d", tm->tm_mday % 100);
break;
case 'D':
#ifdef USE_LOCALE
strftime(p, l, (longflg ? "%A" : "%a"), tm);
#else
sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
#endif
break;
case 'm':
sprintf(p, "%02d", tm->tm_mon + 1);
break;
case 'M':
#ifdef USE_LOCALE
strftime(p, l, (longflg ? "%B" : "%b"), tm);
#else
sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
#endif
break;
case 'y':
sprintf(p, "%02d", tm->tm_year % 100);
break;
case 'Y':
sprintf(p, "%04d", tm->tm_year + 1900);
break;
case 'a':
sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
break;
case 'A':
sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
break;
case 's':
sprintf(p, "%02d", tm->tm_sec);
tick = 1;
break;
case 'c':
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
if (!tick || tick > 60)
tick = 60;
break;
case 'C':
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
if (!tick || tick > 60)
tick = 60;
break;
default:
break;
}
p += strlen(p) - 1;
break;
case 'l':
#ifdef LOADAV
*p = 0;
if (l > 20)
AddLoadav(p);
if (*p)
{
qmflag = 1;
p += strlen(p) - 1;
}
else
*p = '?';
if (!tick || tick > 60)
tick = 60;
#else
*p = '?';
#endif
p += strlen(p) - 1;
break;
case '`':
case 'h':
if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
{
p--;
break;
}
if (*s == '`')
{
for (bt = backticks; bt; bt = bt->next)
if (bt->num == num)
break;
if (bt == 0)
{
p--;
break;
}
}
{
char savebuf[sizeof(winmsg_buf)];
int oldtick = tick;
int oldnumrend = winmsg_numrend;
*p = 0;
strcpy(savebuf, winmsg_buf);
winmsg_numrend = -winmsg_numrend;
MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
debug2("oldtick=%d tick=%d\n", oldtick, tick);
if (!tick || oldtick < tick)
tick = oldtick;
if ((int)strlen(winmsg_buf) < l)
strcat(savebuf, winmsg_buf);
strcpy(winmsg_buf, savebuf);
while (oldnumrend < winmsg_numrend)
winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
if (*p)
qmflag = 1;
p += strlen(p) - 1;
}
break;
case 'w':
case 'W':
{
struct win *oldfore = 0;
char *ss;
if (display)
{
oldfore = D_fore;
D_fore = win;
}
ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
if (minusflg)
*ss = 0;
if (display)
D_fore = oldfore;
}
if (*p)
qmflag = 1;
p += strlen(p) - 1;
break;
case 'u':
*p = 0;
if (win)
AddOtherUsers(p, l - 1, win);
if (*p)
qmflag = 1;
p += strlen(p) - 1;
break;
case 'f':
*p = 0;
if (win)
AddWindowFlags(p, l - 1, win);
if (*p)
qmflag = 1;
p += strlen(p) - 1;
break;
case 't':
*p = 0;
if (win && (int)strlen(win->w_title) < l)
{
strcpy(p, win->w_title);
if (*p)
qmflag = 1;
}
p += strlen(p) - 1;
break;
case '{':
{
char rbuf[128];
s++;
for (i = 0; i < 127; i++)
if (s[i] && s[i] != '}')
rbuf[i] = s[i];
else
break;
if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
{
r = -1;
rbuf[i] = 0;
debug1("MakeWinMsg attrcolor %s\n", rbuf);
if (i != 1 || rbuf[0] != '-')
r = ParseAttrColor(rbuf, (char *)0, 0);
if (r != -1 || (i == 1 && rbuf[0] == '-'))
{
winmsg_rend[winmsg_numrend] = r;
winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
winmsg_numrend++;
}
}
s += i;
p--;
}
break;
case 'H':
*p = 0;
if ((int)strlen(HostName) < l)
{
strcpy(p, HostName);
if (*p)
qmflag = 1;
}
p += strlen(p) - 1;
break;
case 'F':
p--;
if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
qmflag = 1;
break;
case '>':
truncpos = p - winmsg_buf;
truncper = num > 100 ? 100 : num;
trunclong = longflg;
p--;
break;
case '=':
case '<':
*p = ' ';
if (num || zeroflg || plusflg || longflg || (*s != '='))
{
if (minusflg)
{
num = (plusflg ? lastpad : padlen) - num;
if (!plusflg && padlen == 0)
num = p - winmsg_buf;
plusflg = 0;
}
else if (!zeroflg)
{
if (*s != '=' && num == 0 && !plusflg)
num = 100;
if (num > 100)
num = 100;
if (padlen == 0)
num = p - winmsg_buf;
else
num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
}
if (num < 0)
num = 0;
if (plusflg)
num += lastpad;
if (num > MAXSTR - 1)
num = MAXSTR - 1;
if (numpad)
p = pad_expand(winmsg_buf, p, numpad, num);
numpad = 0;
if (p - winmsg_buf > num && !longflg)
{
int left, trunc;
if (truncpos == -1)
{
truncpos = lastpad;
truncper = 0;
}
trunc = lastpad + truncper * (num - lastpad) / 100;
if (trunc > num)
trunc = num;
if (trunc < lastpad)
trunc = lastpad;
left = truncpos - trunc;
if (left > p - winmsg_buf - num)
left = p - winmsg_buf - num;
debug1("lastpad = %d, ", lastpad);
debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
if (left > 0)
{
if (left + lastpad > p - winmsg_buf)
left = p - winmsg_buf - lastpad;
if (p - winmsg_buf - lastpad - left > 0)
bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
p -= left;
r = winmsg_numrend;
while (r && winmsg_rendpos[r - 1] > lastpad)
{
r--;
winmsg_rendpos[r] -= left;
if (winmsg_rendpos[r] < lastpad)
winmsg_rendpos[r] = lastpad;
}
if (trunclong)
{
if (p - winmsg_buf > lastpad)
winmsg_buf[lastpad] = '.';
if (p - winmsg_buf > lastpad + 1)
winmsg_buf[lastpad + 1] = '.';
if (p - winmsg_buf > lastpad + 2)
winmsg_buf[lastpad + 2] = '.';
}
}
if (p - winmsg_buf > num)
{
p = winmsg_buf + num;
if (trunclong)
{
if (num - 1 >= lastpad)
p[-1] = '.';
if (num - 2 >= lastpad)
p[-2] = '.';
if (num - 3 >= lastpad)
p[-3] = '.';
}
r = winmsg_numrend;
while (r && winmsg_rendpos[r - 1] > num)
winmsg_rendpos[--r] = num;
}
truncpos = -1;
trunclong = 0;
if (lastpad > p - winmsg_buf)
lastpad = p - winmsg_buf;
debug1("lastpad now %d\n", lastpad);
}
if (*s == '=')
{
while (p - winmsg_buf < num)
*p++ = ' ';
lastpad = p - winmsg_buf;
truncpos = -1;
trunclong = 0;
debug1("lastpad2 now %d\n", lastpad);
}
p--;
}
else if (padlen)
{
*p = 127;
numpad++;
}
break;
case 'n':
s++;
default:
s--;
if (l > 10 + num)
{
if (num == 0)
num = 1;
if (!win)
sprintf(p, "%*s", num, num > 1 ? "--" : "-");
else
sprintf(p, "%*d", num, win->w_number);
qmflag = 1;
p += strlen(p) - 1;
}
break;
}
}
if (qmpos && !qmflag)
p = qmpos + 1;
*p = '\0';
if (numpad)
{
if (padlen > MAXSTR - 1)
padlen = MAXSTR - 1;
p = pad_expand(winmsg_buf, p, numpad, padlen);
}
if (ev)
{
evdeq(ev);
ev->timeout.tv_sec = 0;
ev->timeout.tv_usec = 0;
}
if (ev && tick)
{
now.tv_usec = 100000;
if (tick == 1)
now.tv_sec++;
else
now.tv_sec += tick - (now.tv_sec % tick);
ev->timeout = now;
debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
}
return winmsg_buf;
}
char *
MakeWinMsg(s, win, esc)
char *s;
struct win *win;
int esc;
{
return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
}
int
PutWinMsg(s, start, max)
char *s;
int start, max;
{
int i, p, l, r, n;
struct mchar rend;
struct mchar rendstack[MAX_WINMSG_REND];
int rendstackn = 0;
if (s != winmsg_buf)
return 0;
rend = D_rend;
p = 0;
l = strlen(s);
debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
for (i = 0; i < winmsg_numrend && max > 0; i++)
{
if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
break;
if (p < winmsg_rendpos[i])
{
n = winmsg_rendpos[i] - p;
if (n > max)
n = max;
max -= n;
p += n;
while(n-- > 0)
{
if (start-- > 0)
s++;
else
PUTCHARLP(*s++);
}
}
r = winmsg_rend[i];
if (r == -1)
{
if (rendstackn > 0)
rend = rendstack[--rendstackn];
}
else
{
rendstack[rendstackn++] = rend;
ApplyAttrColor(r, &rend);
}
SetRendition(&rend);
}
if (p < l)
{
n = l - p;
if (n > max)
n = max;
while(n-- > 0)
{
if (start-- > 0)
s++;
else
PUTCHARLP(*s++);
}
}
return 1;
}
#ifdef DEBUG
static void
fds1(i, j)
int i, j;
{
while (i < j)
{
debug1("%d ", i);
i++;
}
if ((j = open("/dev/null", 0)) >= 0)
{
fds1(i + 1, j);
close(j);
}
else
{
while (dup(++i) < 0 && errno != EBADF)
debug1("%d ", i);
debug1(" [%d]\n", i);
}
}
static void
fds()
{
debug("fds: ");
fds1(-1, -1);
}
#endif
static void
serv_read_fn(ev, data)
struct event *ev;
char *data;
{
debug("Knock - knock!\n");
ReceiveMsg();
}
static void
serv_select_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p;
debug("serv_select_fn called\n");
if (GotSigChld)
{
SigChldHandler();
}
if (InterruptPlease)
{
debug("Backend received interrupt\n");
if (fore && displays)
{
#if defined(TERMIO) || defined(POSIX)
char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
#else
char ibuf = displays->d_OldMode.m_tchars.t_intrc;
#endif
#ifdef PSEUDOS
write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
&ibuf, 1);
debug1("Backend wrote interrupt to %d", fore->w_number);
debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
#else
write(fore->w_ptyfd, &ibuf, 1);
debug1("Backend wrote interrupt to %d\n", fore->w_number);
#endif
}
InterruptPlease = 0;
}
for (p = windows; p; p = p->w_next)
{
if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
{
struct canvas *cv;
int visual = p->w_bell == BELL_VISUAL || visual_bell;
p->w_bell = BELL_ON;
for (display = displays; display; display = display->d_next)
{
for (cv = D_cvlist; cv; cv = cv->c_next)
if (cv->c_layer->l_bottom == &p->w_layer)
break;
if (cv == 0)
{
p->w_bell = BELL_DONE;
Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
}
else if (visual && !D_VB && (!D_status || !D_status_bell))
{
Msg(0, "%s", VisualBellString);
if (D_status)
{
D_status_bell = 1;
debug1("using vbell timeout %d\n", VBellWait);
SetTimeout(&D_statusev, VBellWait );
}
}
}
if (p->w_monitor == MON_FOUND)
p->w_monitor = MON_DONE;
WindowChanged(p, 'f');
}
if (p->w_monitor == MON_FOUND)
{
struct canvas *cv;
p->w_monitor = MON_ON;
for (display = displays; display; display = display->d_next)
{
for (cv = D_cvlist; cv; cv = cv->c_next)
if (cv->c_layer->l_bottom == &p->w_layer)
break;
if (cv)
continue;
#ifdef MULTIUSER
if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
continue;
#endif
Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
p->w_monitor = MON_DONE;
}
WindowChanged(p, 'f');
}
}
for (display = displays; display; display = display->d_next)
{
struct canvas *cv;
if (D_status == STATUS_ON_WIN)
continue;
for (cv = D_cvlist; cv; cv = cv->c_next)
{
int lx, ly;
lx = cv->c_layer->l_x;
ly = cv->c_layer->l_y;
if (lx == cv->c_layer->l_width)
lx--;
if (ly + cv->c_yoff < cv->c_ys)
{
int i, n = cv->c_ys - (ly + cv->c_yoff);
cv->c_yoff = cv->c_ys - ly;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_height)
n = cv->c_layer->l_height;
CV_CALL(cv,
LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < n; i++)
LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
else if (ly + cv->c_yoff > cv->c_ye)
{
int i, n = ly + cv->c_yoff - cv->c_ye;
cv->c_yoff = cv->c_ye - ly;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_height)
n = cv->c_layer->l_height;
CV_CALL(cv,
LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < n; i++)
LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
if (lx + cv->c_xoff < cv->c_xs)
{
int i, n = cv->c_xs - (lx + cv->c_xoff);
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
n = (cv->c_xe - cv->c_xs + 1) / 2;
if (cv->c_xoff + n > cv->c_xs)
n = cv->c_xs - cv->c_xoff;
cv->c_xoff += n;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_width)
n = cv->c_layer->l_width;
CV_CALL(cv,
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < flayer->l_height; i++)
{
LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
LayRedisplayLine(i, 0, n - 1, 1);
}
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
else if (lx + cv->c_xoff > cv->c_xe)
{
int i, n = lx + cv->c_xoff - cv->c_xe;
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
n = (cv->c_xe - cv->c_xs + 1) / 2;
if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
cv->c_xoff -= n;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_width)
n = cv->c_layer->l_width;
CV_CALL(cv,
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < flayer->l_height; i++)
{
LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
}
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
}
}
for (display = displays; display; display = display->d_next)
{
if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
continue;
debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
CV_CALL(D_forecv, LayRestore();LaySetCursor());
}
}
static void
logflush_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p;
char *buf;
int n;
if (!islogfile(NULL))
return;
logfflush(NULL);
n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
if (n)
{
SetTimeout(ev, n * 1000);
evenq(ev);
}
if (!logtstamp_on)
return;
for (p = windows; p; p = p->w_next)
{
if (!p->w_log)
continue;
p->w_logsilence += n;
if (p->w_logsilence < logtstamp_after)
continue;
if (p->w_logsilence - n >= logtstamp_after)
continue;
buf = MakeWinMsg(logtstamp_string, p, '%');
logfwrite(p->w_log, buf, strlen(buf));
}
}
static char *
ParseChar(p, cp)
char *p, *cp;
{
if (*p == 0)
return 0;
if (*p == '^' && p[1])
{
if (*++p == '?')
*cp = '\177';
else if (*p >= '@')
*cp = Ctrl(*p);
else
return 0;
++p;
}
else if (*p == '\\' && *++p <= '7' && *p >= '0')
{
*cp = 0;
do
*cp = *cp * 8 + *p - '0';
while (*++p <= '7' && *p >= '0');
}
else
*cp = *p++;
return p;
}
static int
ParseEscape(p)
char *p;
{
unsigned char buf[2];
if (*p == 0)
SetEscape((struct acluser *)0, -1, -1);
else
{
if ((p = ParseChar(p, (char *)buf)) == NULL ||
(p = ParseChar(p, (char *)buf+1)) == NULL || *p)
return -1;
SetEscape((struct acluser *)0, buf[0], buf[1]);
}
return 0;
}