summaryrefslogtreecommitdiff
path: root/src/fileio.c
diff options
context:
space:
mode:
authorjnweiger <jnweiger>2005-12-16 16:38:49 +0000
committerjnweiger <jnweiger>2005-12-16 16:38:49 +0000
commit8cb4b1196ba0c9011910807092f59cbf2ca77f24 (patch)
treedb0c159183437e6a48d2d559a1d57e1d91107b45 /src/fileio.c
parent32462f2e30134ec5b785571151e848ab4c2824ee (diff)
downloadscreen-8cb4b1196ba0c9011910807092f59cbf2ca77f24.tar.gz
historic version screen-3.2.0 Feb 10 1992
Diffstat (limited to 'src/fileio.c')
-rw-r--r--src/fileio.c2154
1 files changed, 1987 insertions, 167 deletions
diff --git a/src/fileio.c b/src/fileio.c
index b54e335..1e76803 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -22,7 +22,7 @@
* Patrick Wolfe (pat@kai.com, kailand!pat)
* Bart Schaefer (schaefer@cse.ogi.edu)
* Nathan Glasser (nathan@brokaw.lcs.mit.edu)
- * Larry W. Virden (lvirden@cas.org)
+ * Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu)
* Howard Chu (hyc@hanauma.jpl.nasa.gov)
* Tim MacKenzie (tym@dibbler.cs.monash.edu.au)
* Markku Jarvinen (mta@{cc,cs,ee}.tut.fi)
@@ -31,9 +31,13 @@
****************************************************************
*/
-#include "rcs.h"
-RCS_ID("$Id$ FAU")
+#ifndef lint
+ static char rcs_id[] = "$Id$ FAU";
+#endif
+#if defined(pyr) || defined(MIPS) || defined(GOULD_NP1) || defined(B43)
+extern int errno;
+#endif
#include <sys/types.h>
#ifndef sgi
# include <sys/file.h>
@@ -41,37 +45,264 @@ RCS_ID("$Id$ FAU")
#include <sys/stat.h>
#include <fcntl.h>
-#include <signal.h>
+#ifdef BSDI
+# include <sys/signal.h>
+#endif /* BSDI */
#include "config.h"
#include "screen.h"
#include "extern.h"
+#ifdef _SEQUENT_
+# define UTHOST /* _SEQUENT_ has got ut_find_host() */
+#endif
+
+#ifndef GETUTENT
+# ifdef GETTTYENT
+# include <ttyent.h>
+# else
+struct ttyent
+{
+ char *ty_name;
+};
+static char *tt, *ttnext;
+static char ttys[] = "/etc/ttys";
+# endif
+#endif
+
+#ifdef LOADAV
+# ifndef NeXT
+# include <nlist.h>
+
+static char KmemName[] = "/dev/kmem";
+# if defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi)
+static char UnixName[] = "/unix";
+# else
+# ifdef sequent
+static char UnixName[] = "/dynix";
+# else
+# ifdef hpux
+static char UnixName[] = "/hp-ux";
+# else
+# ifdef xelos
+static char UnixName[] = "/xelos";
+# else
+static char UnixName[] = "/vmunix";
+# endif /* xelos */
+# endif /* hpux */
+# endif /* sequent */
+# endif /* _SEQUENT_ ... */
+
+# ifdef alliant
+static char AvenrunSym[] = "_Loadavg";
+# else
+# if defined(hpux) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi)
+static char AvenrunSym[] = "avenrun";
+# else
+static char AvenrunSym[] = "_avenrun";
+# endif
+# endif /* alliant */
+static struct nlist nl[2];
+int avenrun;
+static kmemf;
+# ifdef LOADAV_3LONGS
+long loadav[3];
+# else
+# ifdef LOADAV_4LONGS
+long loadav[4];
+# else
+double loadav[3];
+# endif
+# endif
+# else /* NeXT */
+# include <mach.h>
+kern_return_t error;
+host_t host;
+unsigned int info_count;
+struct processor_set_basic_info info;
+processor_set_t default_set;
+float loadav;
+int avenrun;
+# endif /* NeXT */
+#endif /* LOADAV */
+
+#if defined(UTMPOK) && defined(GETUTENT) && !defined(SVR4)
+# if defined(hpux) /* cruel hpux release 8.0 */
+# define pututline _pututline
+# endif /* hpux */
+extern struct utmp *getutline(), *pututline();
+# if defined(_SEQUENT_)
+extern struct utmp *ut_add_user(), *ut_delete_user();
+extern char *ut_find_host();
+# endif
+#endif
#ifdef NETHACK
extern nethackflag;
#endif
+int hardcopy_append = 0;
+int all_norefresh = 0;
-extern struct display *display;
+extern char *RcFileName, *home, *extra_incap, *extra_outcap;
+extern char *BellString, *ActivityString, *ShellProg, *ShellArgs[];
+extern char *BufferFile, *PowDetachString, *VisualBellString;
+extern int VBellWait, MsgWait, MsgMinWait;
+extern struct key ktab[];
+extern char Esc, MetaEsc;
+extern char *shellaka, SockPath[], *SockNamePtr, *LoginName;
+extern int loginflag, allflag, TtyMode, auto_detach;
+extern int iflag, rflag, dflag;
+extern int default_flow, wrap;
+extern HS, termcapHS, use_hardstatus, visual_bell, default_monitor;
+extern int default_histheight;
+extern int default_startup;
+extern int slowpaste;
+extern DeadlyMsg, HasWindow;
+extern ForeNum, screenwidth, screenheight;
+extern char display_tty[];
extern struct win *fore;
+extern char screenterm[];
+extern int join_with_cr;
+extern struct mode OldMode, NewMode;
+extern int HasWindow;
+extern char mark_key_tab[];
extern int real_uid, eff_uid;
extern int real_gid, eff_gid;
-extern char *extra_incap, *extra_outcap;
-extern char *home, *RcFileName;
-extern char SockPath[], *SockNamePtr;
+
+#ifdef PASSWORD
+int CheckPassword;
+char Password[20];
+#endif
+
#ifdef COPY_PASTE
-extern char *BufferFile;
+extern char *copybuffer;
+extern copylen;
#endif
-extern int hardcopy_append;
-extern char *hardcopydir;
static char *CatExtra __P((char *, char *));
+static char **SaveArgs __P((int, char **));
+static int Parse __P((char *, char *[]));
+static char *ParseChar __P((char *, char *));
+static void ParseNum __P((int, char *[], int*));
+static void ParseOnOff __P((int, char *[], int*));
+static void ParseSaveStr __P((int, char *[], char **, char *));
+static int IsNum __P((char *, int));
+static int IsNumColon __P((char *, int, char *, int));
+static slot_t TtyNameSlot __P((char *));
+
+#if !defined(GETTTYENT) && !defined(GETUTENT)
+static void setttyent __P((void));
+static struct ttyent *getttyent __P((void));
+#endif
+/*
+ * XXX: system
+ */
+extern time_t time __P((time_t *));
+#if !defined(BSDI) && !defined(SVR4)
+extern char *getpass __P((char *));
+#endif /* !BSDI && !SVR4 */
+#if defined(LOADAV) && !defined(NeXT) && !defined(NLIST_DECLARED)
+extern int nlist __P((char *, struct nlist *));
+#endif
+
+char *KeyNames[] =
+{
+ "screen",
+ "select0", "select1", "select2", "select3", "select4",
+ "select5", "select6", "select7", "select8", "select9",
+ "aka", "clear", "colon", "copy", "detach", "flow",
+ "hardcopy", "help", "histnext", "history", "info", "kill", "lastmsg",
+ "license",
+ "lockscreen", "log", "login", "monitor", "next", "other", "paste",
+ "pow_detach", "prev", "quit", "readbuf", "redisplay", "removebuf",
+ "reset", "set", "shell", "suspend", "termcap", "time", "vbell",
+ "version", "width", "windows", "wrap", "writebuf", "xoff", "xon",
+ 0,
+};
+
+
+/* Must be in alpha order !!! */
+
+char *RCNames[] =
+{
+ "activity", "all", "autodetach", "bell", "bind", "bufferfile", "chdir",
+ "crlf", "echo", "escape", "flow", "hardcopy_append", "hardstatus", "login",
+ "markkeys", "mode", "monitor", "msgminwait", "msgwait", "nethack", "password",
+ "pow_detach_msg", "redraw", "refresh", "screen", "scrollback", "shell",
+ "shellaka", "sleep", "slowpaste", "startup_message", "term", "termcap",
+ "terminfo", "vbell", "vbell_msg", "vbellwait", "visualbell",
+ "visualbell_msg", "wrap",
+};
+
+enum RCcases
+{
+ RC_ACTIVITY,
+ RC_ALL,
+ RC_AUTODETACH,
+ RC_BELL,
+ RC_BIND,
+ RC_BUFFERFILE,
+ RC_CHDIR,
+ RC_CRLF,
+ RC_ECHO,
+ RC_ESCAPE,
+ RC_FLOW,
+ RC_HARDCOPY_APP,
+ RC_HARDSTATUS,
+ RC_LOGIN,
+ RC_MARKKEYS,
+ RC_MODE,
+ RC_MONITOR,
+ RC_MSGMINWAIT,
+ RC_MSGWAIT,
+ RC_NETHACK,
+ RC_PASSWORD,
+ RC_POW_DETACH_MSG,
+ RC_REDRAW,
+ RC_REFRESH,
+ RC_SCREEN,
+ RC_SCROLLBACK,
+ RC_SHELL,
+ RC_SHELLAKA,
+ RC_SLEEP,
+ RC_SLOWPASTE,
+ RC_STARTUP_MESSAGE,
+ RC_TERM,
+ RC_TERMCAP,
+ RC_TERMINFO,
+ RC_VBELL,
+ RC_VBELL_MSG,
+ RC_VBELLWAIT,
+ RC_VISUALBELL,
+ RC_VISUALBELL_MSG,
+ RC_WRAP,
+ RC_RCEND
+};
+
+#ifdef UTMPOK
+static utmp, utmpf;
+static char UtmpName[] = UTMPFILE;
+# ifdef MIPS
+ static utmpfappend;
+# endif
+#endif
static FILE *fp = NULL;
-char *rc_name;
+static char *rc_name;
-static char *
-CatExtra(str1, str2)
+char *SaveStr(str)
+register char *str;
+{
+ register char *cp;
+
+ if ((cp = malloc(strlen(str) + 1)) == NULL)
+ Msg_nomem;
+ else
+ strcpy(cp, str);
+ return cp;
+}
+
+static char *CatExtra(str1, str2)
register char *str1, *str2;
{
register char *cp;
@@ -85,7 +316,7 @@ register char *str1, *str2;
{
len2 = strlen(str2);
if ((cp = realloc(str2, (unsigned) len1 + len2 + add_colon + 1)) == NULL)
- Panic(0, strnomem);
+ Msg_nomem;
bcopy(cp, cp + len1 + add_colon, len2 + 1);
}
else
@@ -93,7 +324,7 @@ register char *str1, *str2;
if (len1 == 0)
return 0;
if ((cp = malloc((unsigned) len1 + add_colon + 1)) == NULL)
- Panic(0, strnomem);
+ Msg_nomem;
cp[len1 + add_colon] = '\0';
}
bcopy(str1, cp, len1);
@@ -103,8 +334,7 @@ register char *str1, *str2;
return cp;
}
-static char *
-findrcfile(rcfile)
+static char *findrcfile(rcfile)
char *rcfile;
{
static char buf[256];
@@ -132,7 +362,7 @@ char *rcfile;
{
debug(" ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n");
if (strlen(home) > 244)
- Panic(0, "Rc: home too large");
+ Msg(0, "Rc: home too large");
sprintf(buf, "%s/.iscreenrc", home);
if (access(buf, R_OK))
sprintf(buf, "%s/.screenrc", home);
@@ -154,7 +384,7 @@ char *rcfilename;
register int argc, len;
register char *p, *cp;
char buf[256];
- char *args[MAXARGS];
+ char *args[MAXARGS], *t;
rc_name = findrcfile(rcfilename);
@@ -168,7 +398,7 @@ char *rcfilename;
* the file.
*/
debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name, rcfilename);
- Panic(0, "Unable to open \"%s\".", rc_name);
+ Msg(0, "Unable to open \"%s\".", rc_name);
/* NOTREACHED */
}
debug1("StartRc: '%s' no good. ignored\n", rc_name);
@@ -176,6 +406,9 @@ char *rcfilename;
rc_name = "";
return;
}
+ if ((t = getenv("TERM")) == NULL)
+ Msg(0, "No TERM in environment.");
+ debug1("startrc got termcp:%s\n", t);
while (fgets(buf, sizeof buf, fp) != NULL)
{
if ((p = rindex(buf, '\n')) != NULL)
@@ -186,25 +419,23 @@ char *rcfilename;
{
if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3)
{
+ DeadlyMsg = 0;
Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
- continue;
}
- AddStr(args[argc - 1]);
- if (argc != 3)
+ else
{
- AddStr("\r\n");
- Flush();
+ printf((argc == 3) ? "%s" : "%s\r\n", args[argc - 1]);
}
}
else if (strcmp(args[0], "sleep") == 0)
{
- debug("sleeeeeeep\n");
if (argc != 2)
{
+ DeadlyMsg = 0;
Msg(0, "%s: sleep: one numeric argument expected.", rc_name);
- continue;
}
- DisplaySleep(atoi(args[1]));
+ else
+ sleep(atoi(args[1]));
}
#ifdef TERMINFO
else if (strcmp(args[0], "terminfo") == 0)
@@ -212,13 +443,8 @@ char *rcfilename;
else if (strcmp(args[0], "termcap") == 0)
#endif
{
- if (!display)
- continue;
if (argc < 3 || argc > 4)
- {
- Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]);
- continue;
- }
+ Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]);
for (p = args[1]; p && *p; p = cp)
{
if ((cp = index(p, '|')) != 0)
@@ -226,10 +452,10 @@ char *rcfilename;
len = strlen(p);
if (p[len - 1] == '*')
{
- if (!(len - 1) || !strncmp(p, d_termname, len - 1))
+ if (!(len - 1) || !strncmp(p, t, len - 1))
break;
}
- else if (!strcmp(p, d_termname))
+ else if (!strcmp(p, t))
break;
}
if (!(p && *p))
@@ -244,10 +470,99 @@ char *rcfilename;
rc_name = "";
}
+static char *
+ParseChar(p, cp)
+char *p, *cp;
+{
+ if (*p == '^')
+ {
+ 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;
+}
+
+/*
+ * CompileKeys must be called before Markroutine is first used.
+ * to initialise the keys with defaults, call CompileKeys(NULL, mark_key_tab);
+ *
+ * s is an ascii string in a termcap-like syntax. It looks like
+ * "j=u:k=d:l=r:h=l: =.:" and so on...
+ * this example rebinds the cursormovement to the keys u (up), d (down),
+ * l (left), r (right). placing a mark will now be done with ".".
+ */
+int CompileKeys(s, array)
+char *s, *array;
+{
+ int i;
+ unsigned char key, value;
+
+ if (!s || !*s)
+ {
+ for (i = 0; i < 256; i++)
+ array[i] = i;
+ return 0;
+ }
+ while (*s)
+ {
+ s = ParseChar(s, (char *) &key);
+ if (*s != '=')
+ return -1;
+ do
+ {
+ s = ParseChar(++s, (char *) &value);
+ array[value] = key;
+ }
+ while (*s == '=');
+ if (!*s)
+ break;
+ if (*s++ != ':')
+ return -1;
+ }
+ return 0;
+}
+
+static char **SaveArgs(argc, argv)
+register int argc;
+register char **argv;
+{
+ register char **ap, **pp;
+
+ if ((pp = ap = (char **) malloc((unsigned) (argc + 1) * sizeof(char **))) == 0)
+ Msg_nomem;
+#ifdef notdef
+ debug("saveargs:\n");
+#endif
+ while (argc--)
+ {
+ debug1(" '%s'", *argv);
+ *pp++ = SaveStr(*argv++);
+ }
+ debug("\n");
+ *pp = 0;
+ return ap;
+}
+
void
FinishRc(rcfilename)
char *rcfilename;
{
+ /* in FinishRc screen is not yet open, thus Msg() is deadly here.
+ */
char buf[256];
rc_name = findrcfile(rcfilename);
@@ -262,7 +577,7 @@ char *rcfilename;
* the file.
*/
debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name, rcfilename);
- Panic(0, "Unable to open \"%s\".", rc_name);
+ Msg(0, "Unable to open \"%s\".", rc_name);
/* NOTREACHED */
}
debug1("FinishRc: '%s' no good. ignored\n", rc_name);
@@ -274,7 +589,6 @@ char *rcfilename;
debug("finishrc is going...\n");
while (fgets(buf, sizeof buf, fp) != NULL)
{
- debug1("RCFILE:%s", buf);
RcLine(buf);
}
(void) fclose(fp);
@@ -283,6 +597,34 @@ char *rcfilename;
}
/*
+ * this is a KEY_SET pressed
+ */
+void
+DoSet(argv)
+char **argv;
+{
+ char *p;
+ static char buf[256];
+
+ p = buf;
+ debug("DoSet\n");
+ if (!argv || !*argv || !**argv)
+ {
+ debug("empty DoSet\n");
+ sprintf(buf, "set ");
+ RcLine(buf);
+ return;
+ }
+ sprintf(p, "set"); p+=3;
+ while(*argv && (strlen(buf) + strlen(*argv) < 255))
+ {
+ sprintf(p, " %s", *argv++);
+ p += strlen(p);
+ }
+ RcLine(buf);
+}
+
+/*
* "$HOST blafoo" -> "localhost blafoo"
* "${HOST}blafoo" -> "localhostblafoo"
* "\$HOST blafoo" -> "$HOST blafoo"
@@ -290,24 +632,17 @@ char *rcfilename;
* "'$HOST ${HOST}'" -> "'$HOST ${HOST}'"
* "'\$HOST'" -> "'\$HOST'"
* "\'$HOST' $HOST" -> "'localhost' $HOST"
- *
- * "$:termcapname:" -> "termcapvalue"
- * "$:terminfoname:" -> "termcapvalue"
- *
- * "\101" -> "A"
*/
-char *
-expand_vars(ss)
+static char *expand_env_vars(ss)
char *ss;
{
static char ebuf[2048];
- register int esize = 2047, vtype, quofl = 0;
+ register int esize = 2047, quofl = 0;
register char *e = ebuf;
register char *s = ss;
register char *v;
- char xbuf[11];
- while (*s && *s != '\0' && *s != '\n' && esize > 0)
+ while (*s && *s != '\n' && esize > 0)
{
if (*s == '\'')
quofl ^= 1;
@@ -316,99 +651,45 @@ char *ss;
char *p, c;
p = ++s;
- switch (*s)
+ if (*s == '{')
{
- case '{':
p = ++s;
while (*p != '}')
if (*p++ == '\0')
return ss;
- vtype = 0; /* env var */
- break;
- case ':':
- p = ++s;
- while (*p != ':')
- if (*p++ == '\0')
- return ss;
- vtype = 1; /* termcap string */
- break;
- default:
+ }
+ else
+ {
while (*p != ' ' && *p != '\0' && *p != '\n')
p++;
- vtype = 0; /* env var */
}
c = *p;
debug1("exp: c='%c'\n", c);
*p = '\0';
- if (vtype == 0)
- {
- v = xbuf;
- if (strcmp(s, "TERM") == 0)
- v = display ? d_termname : "unknown";
- else if (strcmp(s, "COLUMNS") == 0)
- sprintf(xbuf, "%d", display ? d_width : -1);
- else if (strcmp(s, "LINES") == 0)
- sprintf(xbuf, "%d", display ? d_height : -1);
- else
- v = getenv(s);
- }
- else
- v = gettermcapstring(s);
- if (v)
+ if (v = getenv(s))
{
debug2("exp: $'%s'='%s'\n", s, v);
while (*v && esize-- > 0)
*e++ = *v++;
}
else
- debug1("exp: '%s' not env\n", s); /* {-: */
- if ((*p = c) == '}' || c == ':')
+ debug1("exp: '%s' not env\n", s);
+ if ((*p = c) == '}')
p++;
s = p;
}
else
{
- /*
- * \$, \\$, \\, \\\, \012 are reduced here,
- * d_other sequences starting whith \ are passed through.
- */
if (s[0] == '\\' && !quofl)
- {
- if (s[1] >= '0' && s[1] <= '7')
- {
- int i;
-
- s++;
- i = *s - '0';
- s++;
- if (*s >= '0' && *s <= '7')
- {
- i = i * 8 + *s - '0';
- s++;
- if (*s >= '0' && *s <= '7')
- {
- i = i * 8 + *s - '0';
- s++;
- }
- }
- debug2("expandvars: octal coded character %o (%d)\n", i, i);
- *e++ = i;
- }
- else
- {
- if (s[1] == '$' ||
- (s[1] == '\\' && s[2] == '$') ||
- s[1] == '\'' ||
- (s[1] == '\\' && s[2] == '\''))
- s++;
- }
- }
+ if (s[1] == '$' || (s[1] == '\\' && s[2] == '$') ||
+ s[1] == '\'' || (s[1] == '\\' && s[2] == '\''))
+ s++;
*e++ = *s++;
esize--;
}
}
if (esize <= 0)
- Msg(0, "expand_vars: buffer overflow\n");
+ Msg(0, "expand_env_vars: buffer overflow\n");
*e = '\0';
return ebuf;
}
@@ -417,21 +698,818 @@ void
RcLine(ubuf)
char *ubuf;
{
- char *args[MAXARGS], *buf;
- struct action act;
+ char *args[MAXARGS];
+ register char *buf, *p, **pp, **ap;
+ register int argc, setflag;
+ int q, qq;
+ char key;
+ int low, high, mid, x;
- buf = expand_vars(ubuf);
- if (Parse(buf, args) <= 0)
- return;
- if ((act.nr = FindCommnr(*args)) == RC_ILLEGAL)
+ buf = expand_env_vars(ubuf);
+
+ ap = args;
+
+ if ((p = rindex(buf, '\n')) != NULL)
+ *p = '\0';
+ if (strncmp("set ", buf, 4) == 0)
+ {
+ buf += 4;
+ setflag = 1;
+ debug1("RcLine: '%s' is a set command\n", buf);
+ }
+ else if (strncmp("se ", buf, 3) == 0)
{
- Msg(0, "%s: unknown command '%s'", rc_name, *args);
+ buf += 3;
+ setflag = 1;
+ debug1("RcLine: '%s' is a se command\n", buf);
+ }
+ else
+ {
+ setflag = 0;
+ debug1("RcLine: '%s'\n", buf);
+ }
+ if ((argc = Parse(buf, ap)) == 0)
+ {
+ if (setflag)
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: set what?\n", rc_name);
+ }
return;
}
- act.args = args + 1;
- DoAction(&act, -1);
+
+ low = 0;
+ high = (int)RC_RCEND - 1;
+ while (low <= high)
+ {
+ mid = (low + high) / 2;
+ x = strcmp(ap[0], RCNames[mid]);
+ if (x < 0)
+ high = mid - 1;
+ else if (x > 0)
+ low = mid + 1;
+ else
+ break;
+ }
+ if (low > high)
+ mid = (int)RC_RCEND;
+ switch ((enum RCcases) mid)
+ {
+ case RC_ESCAPE:
+ if (argc != 2 || !ParseEscape(ap[1]))
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: two characters required after escape.", rc_name);
+ return;
+ }
+ if (Esc != MetaEsc)
+ ktab[Esc].type = KEY_OTHER;
+ else
+ ktab[Esc].type = KEY_IGNORE;
+ return;
+ case RC_CHDIR:
+ if (setflag)
+ break;
+ p = argc < 2 ? home : ap[1];
+ if (chdir(p) == -1)
+ {
+ DeadlyMsg = 0;
+ Msg(errno, "%s", p);
+ }
+ return;
+ case RC_SHELL:
+ ParseSaveStr(argc, ap, &ShellProg, "shell");
+ ShellArgs[0] = ShellProg;
+ return;
+ case RC_SHELLAKA:
+ ParseSaveStr(argc, ap, &shellaka, "shellaka");
+ return;
+ case RC_SCREEN:
+ if (setflag)
+ break;
+ DoScreen(rc_name, ap + 1);
+ return;
+ case RC_SLEEP:
+ case RC_TERMCAP:
+ case RC_TERMINFO:
+ return; /* Already handled */
+ case RC_TERM:
+ {
+ char *tmp = NULL;
+
+ ParseSaveStr(argc, ap, &tmp, "term");
+ if (!tmp)
+ return;
+ if (strlen(tmp) >= 20)
+ {
+ DeadlyMsg = 0;
+ Msg(0,"%s: term: argument too long ( < 20)", rc_name);
+ Free(tmp);
+ return;
+ }
+ strcpy(screenterm, args[1]);
+ Free(tmp);
+ debug1("screenterm set to %s\n", screenterm);
+ MakeTermcap(0);
+ return;
+ }
+ case RC_ECHO:
+ if (HasWindow && *rc_name == '\0')
+ {
+ /*
+ * user typed ^A:echo... well, echo isn't FinishRc's job,
+ * but as he wanted to test us, we show good will
+ */
+ DeadlyMsg = 0;
+ if (argc == 2 || (argc == 3 && !strcmp(ap[1], "-n")))
+ Msg(0, "%s", ap[argc - 1]);
+ else
+ Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
+ }
+ return;
+ case RC_BELL:
+ ParseSaveStr(argc, ap, &BellString, "bell");
+ return;
+ case RC_BUFFERFILE:
+ ParseSaveStr(argc, ap, &BufferFile, "bufferfile");
+ return;
+ case RC_ACTIVITY:
+ ParseSaveStr(argc, ap, &ActivityString, "activity");
+ return;
+ case RC_POW_DETACH_MSG:
+ ParseSaveStr(argc, ap, &PowDetachString, "pow_detach");
+ return;
+ case RC_LOGIN:
+#ifdef UTMPOK
+ q = loginflag;
+ ParseOnOff(argc, ap, &loginflag);
+ if (fore && setflag)
+ {
+ SlotToggle(loginflag?(1):(-1));
+ loginflag = q;
+ }
+#endif
+ return;
+ case RC_FLOW:
+ if (argc == 3 && ap[2][0] == 'i')
+ {
+ iflag = 1;
+ argc--;
+ }
+ if (argc == 2 && ap[1][0] == 'a')
+ default_flow = FLOW_AUTOFLAG;
+ else
+ ParseOnOff(argc, ap, &default_flow);
+ return;
+ case RC_WRAP:
+ ParseOnOff(argc, ap, &wrap);
+ return;
+ case RC_HARDSTATUS:
+ ParseOnOff(argc, ap, &use_hardstatus);
+ if (use_hardstatus)
+ HS = termcapHS;
+ else
+ HS = 0;
+ return;
+ case RC_MONITOR:
+ {
+ int f;
+
+ ParseOnOff(argc, ap, &f);
+ if (fore && setflag)
+ fore->monitor = (f == 0) ? MON_OFF : MON_ON;
+ else
+ default_monitor = (f == 0) ? MON_OFF : MON_ON;
+ }
+ return;
+ case RC_REDRAW:
+ case RC_REFRESH:
+ {
+ int r;
+
+ ParseOnOff(argc, ap, &r);
+ if (fore && setflag)
+ fore->norefresh = (r) ? 0 : 1;
+ else
+ {
+ all_norefresh = (r) ? 0 : 1;
+ if (all_norefresh)
+ Msg(0, "No refresh on window change!\n");
+ else
+ Msg(0, "Window specific refresh\n");
+ }
+ }
+ return;
+ case RC_VBELL:
+ case RC_VISUALBELL:
+ ParseOnOff(argc, ap, &visual_bell);
+ return;
+ case RC_VBELLWAIT:
+ ParseNum(argc, ap, &VBellWait);
+ if (fore && rc_name[0] == '\0')
+ Msg(0, "vbellwait set to %d seconds", VBellWait);
+ return;
+ case RC_MSGWAIT:
+ ParseNum(argc, ap, &MsgWait);
+ if (fore && rc_name[0] == '\0')
+ Msg(0, "msgwait set to %d seconds", MsgWait);
+ return;
+ case RC_MSGMINWAIT:
+ ParseNum(argc, ap, &MsgMinWait);
+ if (fore && rc_name[0] == '\0')
+ Msg(0, "msgminwait set to %d seconds", MsgMinWait);
+ return;
+ case RC_SCROLLBACK:
+ if (fore && setflag)
+ {
+ int i;
+
+ ParseNum(argc, ap, &i);
+ ChangeScrollback(fore, i, fore->width);
+ if (fore && rc_name[0] == '\0')
+ Msg(0, "scrollback set to %d", fore->histheight);
+ }
+ else
+ ParseNum(argc, ap, &default_histheight);
+ return;
+ case RC_SLOWPASTE:
+ ParseNum(argc, ap, &slowpaste);
+ if (fore && rc_name[0] == '\0')
+ Msg(0, "slowpaste set to %d milliseconds", slowpaste);
+ return;
+ case RC_MARKKEYS:
+ {
+ char *tmp = NULL;
+
+ ParseSaveStr(argc, ap, &tmp, "markkeys");
+ if (CompileKeys(ap[1], mark_key_tab))
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: markkeys: syntax error.", rc_name);
+ Free(tmp);
+ return;
+ }
+ debug1("markkeys %s\n", ap[1]);
+ Free(tmp);
+ return;
+ }
+#ifdef NETHACK
+ case RC_NETHACK:
+ ParseOnOff(argc, ap, &nethackflag);
+ return;
+#endif
+ case RC_HARDCOPY_APP:
+ ParseOnOff(argc, ap, &hardcopy_append);
+ return;
+ case RC_VBELL_MSG:
+ case RC_VISUALBELL_MSG:
+ ParseSaveStr(argc, ap, &VisualBellString, "vbell_msg");
+ debug1(" new vbellstr '%s'\n", VisualBellString);
+ return;
+ case RC_MODE:
+ if (argc != 2)
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: mode: one argument required.", rc_name);
+ return;
+ }
+ if (!IsNum(ap[1], 7))
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: mode: octal number expected.", rc_name);
+ return;
+ }
+ (void) sscanf(ap[1], "%o", &TtyMode);
+ return;
+ case RC_CRLF:
+ ParseOnOff(argc, ap, &join_with_cr);
+ return;
+ case RC_AUTODETACH:
+ ParseOnOff(argc, ap, &auto_detach);
+ return;
+ case RC_STARTUP_MESSAGE:
+ ParseOnOff(argc, ap, &default_startup);
+ return;
+#ifdef PASSWORD
+ case RC_PASSWORD:
+ CheckPassword = 1;
+ if (argc >= 2)
+ {
+ strncpy(Password, ap[1], sizeof Password);
+ if (!strcmp(Password, "none"))
+ CheckPassword = 0;
+ }
+ else
+ {
+ char *mstr = 0;
+ int msleep = 0, st;
+ char salt[2];
+
+#ifdef POSIX
+ if (HasWindow)
+ {
+ Msg(0, "Cannot ask for password on POSIX systems");
+ return;
+ }
+#endif
+ /* there is a clear screen sequence in the buffer. */
+ fflush(stdout);
+ if (HasWindow)
+ {
+ ClearDisplay();
+ SetTTY(0, &OldMode);
+ }
+ strncpy(Password, getpass("New screen password:"),
+ sizeof(Password));
+ if (strcmp(Password, getpass("Retype new password:")))
+ {
+#ifdef NETHACK
+ if (nethackflag)
+ mstr = "[ Passwords don't match - your armor crumbles away ]";
+ else
+#endif
+ mstr = "[ Passwords don't match - checking turned off ]";
+ msleep = 1;
+ CheckPassword = 0;
+ }
+ if (Password[0] == '\0')
+ {
+ CheckPassword = 0;
+ mstr = "[ No password - no secure ]";
+ msleep = 1;
+ }
+ for (st=0; st<2; st++)
+ salt[st] = 'A' + (int)((time(0) >> 6*st) % 26);
+ strncpy(Password, crypt(Password, salt), sizeof(Password));
+ if (CheckPassword)
+ {
+#ifdef COPY_PASTE
+ if (copybuffer)
+
+ Free(copybuffer);
+ copylen = strlen(Password);
+ if ((copybuffer = (char *) malloc(copylen+1)) == NULL)
+ {
+ Msg_nomem;
+ return;
+ }
+ strcpy(copybuffer, Password);
+ mstr = "[ Password moved into copybuffer ]";
+ msleep = 1;
+#else /* COPY_PASTE */
+ mstr = "[ Crypted password is \"%s\" ]";
+ msleep = 5;
+#endif /* COPY_PASTE */
+ }
+ if (HasWindow)
+ {
+ SetTTY(0, &NewMode);
+ Activate(0); /* Redraw */
+ if (mstr)
+ {
+ Msg(0, mstr, Password);
+ }
+ }
+ else
+ {
+ if (mstr)
+ {
+ printf(mstr, Password);
+ putchar('\n');
+ sleep(msleep);
+ }
+ ClearDisplay();
+ }
+ }
+ debug1("finishrc: our password is: --%s%-- \n", Password);
+ return;
+#endif /* PASSWORD */
+ case RC_ALL:
+ if (!setflag || !HasWindow || *rc_name)
+ break;
+ display_help();
+ return;
+ case RC_BIND:
+ if (setflag)
+ break;
+ p = ap[1];
+ if (argc < 2 || *p == '\0')
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: key expected after bind.", rc_name);
+ return;
+ }
+ if ((p = ParseChar(p, &key)) == NULL || *p)
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: bind: character, ^x, or (octal) \\032 expected.",
+ rc_name);
+ return;
+ }
+ if (ktab[key].type != KEY_IGNORE)
+ {
+ ktab[key].type = KEY_IGNORE;
+ if ((pp = ktab[key].args) != NULL)
+ {
+ for (; *pp; pp++)
+ Free(*pp);
+ Free(ktab[key].args);
+ }
+ }
+ if (argc > 2)
+ {
+ for (pp = KeyNames; *pp; ++pp)
+ if (strcmp(ap[2], *pp) == 0)
+ break;
+ if (*pp)
+ {
+ ktab[key].type = (enum keytype) (pp - KeyNames + 1);
+ if (argc > 3)
+ {
+ ktab[key].args = SaveArgs(argc - 3, ap + 3);
+ }
+ else
+ ktab[key].args = NULL;
+ }
+ else
+ {
+ ktab[key].type = KEY_CREATE;
+ ktab[key].args = SaveArgs(argc - 2, ap + 2);
+ }
+ }
+ return;
+ case RC_RCEND:
+ default:
+ {
+ char ibuf[3];
+ /*
+ * now we are user-friendly:
+ * if anyone typed a key name like "help" or "next" ...
+ * we did not match anything above. so look in the KeyNames table.
+ */
+ debug1("--ap[0] %s\n", ap[0]);
+ for (pp = KeyNames; *pp; ++pp)
+ if (strcmp(ap[0], *pp) == 0)
+ break;
+ if (*pp == 0)
+ break;
+
+ ibuf[0] = Esc;
+ ibuf[1] = pp - KeyNames +1;
+ debug1("RcLine: it was a keyname: '%s'\n", *pp);
+ q = 2; qq = 0;
+ if (HasWindow)
+ ProcessInput(ibuf, &q, (char *)0, &qq, 0);
+ else
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: Key '%s' has no effect while no window open...\n",
+ rc_name, ap[0]);
+ }
+ }
+ return;
+ }
+ DeadlyMsg = 0;
+ Msg(0, "%s: unknown %skeyword \"%s\"", rc_name,
+ setflag?"'set' ":"", ap[0]);
+}
+
+static int
+Parse(buf, args)
+char *buf, **args;
+{
+ register char *p = buf, **ap = args;
+ register int delim, argc;
+
+ argc = 0;
+ for (;;)
+ {
+ while (*p && (*p == ' ' || *p == '\t'))
+ ++p;
+ if (*p == '\0' || *p == '#')
+ {
+ *p = '\0';
+ return argc;
+ }
+ if (argc > MAXARGS - 1)
+ Msg(0, "%s: too many tokens.", rc_name);
+ delim = 0;
+ if (*p == '"' || *p == '\'')
+ delim = *p++;
+ argc++;
+ *ap = p;
+ *++ap = 0;
+ while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
+ ++p;
+ if (*p == '\0')
+ {
+ if (delim)
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: Missing quote.", rc_name);
+ return 0;
+ }
+ return argc;
+ }
+ *p++ = '\0';
+ }
+}
+
+int
+ParseEscape(p)
+char *p;
+{
+ if ((p = ParseChar(p, &Esc)) == NULL ||
+ (p = ParseChar(p, &MetaEsc)) == NULL || *p)
+ return 0;
+ return 1;
+}
+
+static void
+ParseNum(argc, ap, var)
+int argc;
+char *ap[];
+int *var;
+{
+ int i;
+ char *p;
+
+ if (argc == 2 && ap[1][0] != '\0')
+ {
+ i = 0;
+ p = ap[1];
+ while (*p)
+ {
+ if (*p >= '0' && *p <= '9')
+ i = 10 * i + (*p - '0');
+ else
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: %s: invalid argument. Give numeric argument",
+ rc_name, ap[0]);
+ return;
+ }
+ p++;
+ }
+ }
+ else
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: %s: invalid argument. Give one argument",
+ rc_name, ap[0]);
+ return;
+ }
+ debug1("ParseNum got %d\n", i);
+ *var = i;
+}
+
+static void
+ParseSaveStr(argc, ap, var, title)
+int argc;
+char *ap[];
+char **var;
+char *title;
+{
+ if (argc != 2)
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: %s: one argument required.", rc_name, title);
+ return;
+ }
+ if (*var)
+ Free(*var);
+ *var = SaveStr(ap[1]);
+ return;
+}
+
+static void
+ParseOnOff(argc, ap, var)
+int argc;
+char *ap[];
+int *var;
+{
+ register int num = -1;
+
+ if (argc == 2 && ap[1][0] == 'o')
+ {
+ if (ap[1][1] == 'f')
+ num = 0;
+ else if (ap[1][1] == 'n')
+ num = 1;
+ }
+ if (num < 0)
+ {
+ DeadlyMsg = 0;
+ Msg(0, "%s: %s: invalid argument. Give 'on' or 'off'", rc_name, ap[0]);
+ return;
+ }
+ *var = num;
+}
+
+
+static int IsNum(s, base)
+register char *s;
+register int base;
+{
+ for (base += '0'; *s; ++s)
+ if (*s < '0' || *s > base)
+ return 0;
+ return 1;
}
+static int IsNumColon(s, base, p, psize)
+int base, psize;
+char *s, *p;
+{
+ char *q;
+ if ((q = rindex(s, ':')) != NULL)
+ {
+ strncpy(p, q + 1, psize - 1);
+ p[psize - 1] = '\0';
+ *q = '\0';
+ }
+ else
+ *p = '\0';
+ return IsNum(s, base);
+}
+
+void
+SlotToggle(how)
+int how;
+/*
+ * how = 0 real toggle mode
+ * how > 0 do try to set a utmp slot.
+ * how < 0 try to withdraw a utmp slot
+ *
+ * slot = -1 window not logged in.
+ * slot = 0 window not logged in, but should be logged in.
+ * (unable to write utmp, or detached).
+ */
+{
+ debug1("SlotToggle %d\n", how);
+ if (how == 0)
+ how = (fore->slot == (slot_t) -1)?(1):(-1);
+ /*
+ * slot 0 or active -> we try to log out.
+ * slot -1 -> we try to log in.
+ */
+#ifdef UTMPOK
+ if (how > 0)
+ {
+ debug(" try to log in\n");
+ if ((fore->slot == (slot_t) -1) || (fore->slot == (slot_t) 0))
+ {
+#ifdef USRLIMIT
+ if (CountUsers() >= USRLIMIT)
+ Msg(0, "User limit reached.");
+ else
+#endif
+ {
+ if (SetUtmp(fore, ForeNum) == 0)
+ Msg(0, "This window is now logged in.");
+ else
+ Msg(0, "This window should now be logged in.");
+ }
+ }
+ else
+ Msg(0, "This window is already logged in.");
+ }
+ else if (how < 0)
+ {
+ debug(" try to log out\n");
+ if (fore->slot == (slot_t) -1)
+ Msg(0, "This window is already logged out\n");
+ else if (fore->slot == (slot_t) 0)
+ {
+ debug("What a relief! In fact, it was not logged in\n");
+ Msg(0, "This window is not logged in.");
+ fore->slot = (slot_t) -1;
+ }
+ else
+ {
+ RemoveUtmp(fore);
+ if (fore->slot != (slot_t) -1)
+ Msg(0, "What? Cannot remove Utmp slot?");
+ else
+ Msg(0, "This window is no longer logged in.");
+ }
+ }
+#else /* !UTMPOK */
+ Msg(0, "Unable to modify %s.\n", UTMPFILE);
+#endif
+}
+
+void
+DoScreen(fn, av)
+char *fn, **av;
+{
+ register int flowflag, num, lflag = loginflag, aflag = 0;
+ register char *aka = NULL;
+ register int histheight = default_histheight;
+ char buf[20];
+ char termbuf[25];
+ char *termp;
+ char *args[2];
+
+ flowflag = default_flow;
+ termbuf[0] = '\0';
+ termp = NULL;
+ while (av && *av && av[0][0] == '-')
+ {
+ switch (av[0][1])
+ {
+ case 'f':
+ switch (av[0][2])
+ {
+ case 'n':
+ case '0':
+ flowflag = FLOW_NOW * 0;
+ break;
+ case 'y':
+ case '1':
+ case '\0':
+ flowflag = FLOW_NOW * 1;
+ break;
+ case 'a':
+ flowflag = FLOW_AUTOFLAG;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 'k':
+ case 't':
+ if (av[0][2])
+ aka = &av[0][2];
+ else if (*++av)
+ aka = *av;
+ else
+ --av;
+ break;
+ case 'T':
+ if (av[0][2])
+ termp = &av[0][2];
+ else if (*++av)
+ termp = *av;
+ else
+ --av;
+ break;
+ case 'h':
+ if (av[0][2])
+ histheight = atoi(av[0] + 2);
+ else if (*++av)
+ histheight = atoi(*av);
+ else
+ --av;
+ break;
+ case 'l':
+ switch (av[0][2])
+ {
+ case 'n':
+ case '0':
+ lflag = 0;
+ break;
+ case 'y':
+ case '1':
+ case '\0':
+ lflag = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ default:
+ Msg(0, "%s: screen: invalid option -%c.", fn, av[0][1]);
+ break;
+ }
+ ++av;
+ }
+ num = 0;
+ if (av && *av && IsNumColon(*av, 10, buf, sizeof(buf)))
+ {
+ if (*buf != '\0')
+ aka = buf;
+ num = atoi(*av);
+ if (num < 0 || num > MAXWIN - 1)
+ {
+ Msg(0, "%s: illegal screen number %d.", fn, num);
+ num = 0;
+ }
+ ++av;
+ }
+ if (!av || !*av)
+ {
+ av = args;
+ av[0] = ShellProg;
+ av[1] = NULL;
+ if (!aka)
+ aka = shellaka;
+ }
+ MakeWindow(aka, av, aflag, flowflag, num, (char *) 0, lflag, histheight, termp);
+}
void
WriteFile(dump)
@@ -457,19 +1535,14 @@ int dump;
strcpy(fn + i, ".termcap");
break;
case DUMP_HARDCOPY:
- if (hardcopydir)
- sprintf(fn, "%s/hardcopy.%d", hardcopydir, fore->w_number);
- else
- sprintf(fn, "hardcopy.%d", fore->w_number);
+ sprintf(fn, "hardcopy.%d", ForeNum);
if (hardcopy_append && !access(fn, W_OK))
mode = "a";
break;
-#ifdef COPY_PASTE
case DUMP_EXCHANGE:
sprintf(fn, "%s", BufferFile);
umask(0);
break;
-#endif
}
debug2("WriteFile(%d) %s\n", dump, fn);
@@ -489,22 +1562,22 @@ int dump;
if (*mode == 'a')
{
putc('>', f);
- for (j = d_width - 2; j > 0; j--)
+ for (j = screenwidth - 2; j > 0; j--)
putc('=', f);
fputs("<\n", f);
}
- for (i = 0; i < d_height; i++)
+ for (i = 0; i < screenheight; ++i)
{
- p = fore->w_image[i];
- for (k = d_width - 1; k >= 0 && p[k] == ' '; k--)
+ p = fore->image[i];
+ for (k = screenwidth - 1; k >= 0 && p[k] == ' '; --k)
;
- for (j = 0; j <= k; j++)
+ for (j = 0; j <= k; ++j)
putc(p[j], f);
putc('\n', f);
}
break;
case DUMP_TERMCAP:
- if ((p = index(MakeTermcap(fore->w_aflag), '=')) != NULL)
+ if ((p = index(MakeTermcap(fore->aflag), '=')) != NULL)
{
fputs(++p, f);
putc('\n', f);
@@ -512,8 +1585,8 @@ int dump;
break;
#ifdef COPY_PASTE
case DUMP_EXCHANGE:
- p = d_copybuffer;
- for (i = 0; i < d_copylen; i++)
+ p = copybuffer;
+ for (i = 0; i < copylen; i++)
putc(*p++, f);
break;
#endif
@@ -566,33 +1639,33 @@ ReadFile()
return;
}
size = stb.st_size;
- if (d_copybuffer)
- free(d_copybuffer);
- d_copylen = 0;
- if ((d_copybuffer = malloc(size)) == NULL)
+ if (copybuffer)
+ Free(copybuffer);
+ copylen = 0;
+ if ((copybuffer = malloc(size)) == NULL)
{
close(i);
- Msg(0, strnomem);
+ Msg_nomem;
return;
}
errno = 0;
- if ((l = read(i, d_copybuffer, size)) != size)
+ if ((l = read(i, copybuffer, size)) != size)
{
- d_copylen = (l > 0) ? l : 0;
+ copylen = (l > 0) ? l : 0;
#ifdef NETHACK
if (nethackflag)
- Msg(errno, "You choke on your food: %d bytes", d_copylen);
+ Msg(errno, "You choke on your food: %d bytes", copylen);
else
#endif
- Msg(errno, "Got only %d bytes from %s", d_copylen, fn);
+ Msg(errno, "Got only %d bytes from %s", copylen, fn);
close(i);
return;
}
- d_copylen = l;
+ copylen = l;
if (read(i, &c, 1) > 0)
- Msg(0, "Slurped only %d characters into buffer - try again", d_copylen);
+ Msg(0, "Slurped only %d characters into buffer - try again", copylen);
else
- Msg(0, "Slurped %d characters into buffer", d_copylen);
+ Msg(0, "Slurped %d characters into buffer", copylen);
close(i);
return;
}
@@ -603,28 +1676,743 @@ KillBuffers()
char fn[1024];
sprintf(fn, "%s", BufferFile);
errno = 0;
-#ifndef NOREUID
- setreuid(eff_uid, real_uid);
- setregid(eff_gid, real_gid);
-#else
if (access(fn, W_OK) == -1)
{
Msg(errno, "%s not removed", fn);
return;
}
+ else
+ {
+ unlink(fn);
+ Msg(errno, "%s removed", fn);
+ }
+}
+#endif /* COPY_PASTE */
+
+#ifdef USRLIMIT
+CountUsers()
+{
+#ifdef GETUTENT
+ struct utmp *ut, *getutent();
+#else
+ struct utmp utmpbuf;
#endif
- unlink(fn);
- Msg(errno, "%s removed", fn);
-#ifndef NOREUID
- setreuid(real_uid, eff_uid);
- setregid(real_gid, eff_gid);
+ int UserCount;
+
+ debug1("CountUsers() - utmp=%d\n",utmp);
+ if (!utmp)
+ return(0);
+ UserCount = 0;
+#ifdef GETUTENT
+ setutent();
+ while (ut = getutent())
+ if (ut->ut_type == USER_PROCESS)
+ UserCount++;
+#else
+ (void) lseek(utmpf, (off_t) 0, 0);
+ while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0)
+ {
+ if (utmpbuf.ut_name[0] != '\0')
+ UserCount++;
+ }
#endif
+ return(UserCount);
+}
+#endif
+
+#ifdef UTMPOK
+
+static slot_t loginslot;
+static struct utmp utmp_logintty;
+#ifdef _SEQUENT_
+static char loginhost[100+1];
+#endif
+
+void
+InitUtmp()
+{
+ debug("InitUtmp testing...\n");
+ if ((utmpf = open(UtmpName, O_RDWR)) == -1)
+ {
+ if (errno != EACCES)
+ Msg(errno, UtmpName);
+ debug("InitUtmp failed.\n");
+ utmp = 0;
+ return;
+ }
+#ifdef GETUTENT
+ close(utmpf);
+ utmpf= -1;
+#endif
+#ifdef MIPS
+ if ((utmpfappend = open(UtmpName, O_APPEND)) == -1)
+ {
+ if (errno != EACCES)
+ Msg(errno, UtmpName);
+ return;
+ }
+#endif
+ utmp = 1;
+#ifndef apollo
+ ReInitUtmp();
+#endif
+}
+
+void
+ReInitUtmp()
+{
+#ifndef apollo
+ if (!utmp)
+ {
+ debug("Reinitutmp: utmp == 0\n");
+ return;
+ }
+#endif
+ debug("(Re)InitUtmp: removing your logintty\n");
+ loginslot = TtyNameSlot(display_tty);
+ if (loginslot!=(slot_t)0 && loginslot!=(slot_t)-1)
+ {
+#ifdef _SEQUENT_
+ if (p=ut_find_host(loginslot))
+ strncpy(loginhost, p, 100);
+#endif
+ RemoveLoginSlot(loginslot, &utmp_logintty);
+ }
+ debug1(" slot %d zapped\n", loginslot);
+}
+
+void
+RestoreLoginSlot()
+{
+ debug("RestoreLoginSlot()\n");
+#ifdef apollo
+ InitUtmp();
+#endif
+ if (utmp && loginslot!=(slot_t)0 && loginslot!=(slot_t)-1)
+ {
+#ifdef GETUTENT
+# ifdef _SEQUENT_
+ int fail;
+ debug1(" logging you in again (slot %s)\n", loginslot);
+/*
+ * We have problems if we add the console and use ut_add_user()
+ * because the id will be 'scon' instead of 'co'. So we
+ * restore it with pututline(). The reason why we don't use
+ * pututline all the time is that we want to set the host field.
+ * Unfortunatelly this can only be done with ut_add_user().
+ */
+ if (*loginhost)
+ {
+ fail = (ut_add_user(LoginName, loginslot, utmp_logintty.ut_pid,
+ *loginhost?loginhost:(char *)0) == 0);
+ }
+ else
+ {
+ setutent();
+ fail = (pututline(&utmp_logintty) == 0);
+ }
+ if (fail)
+# else /* _SEQUENT_ */
+ debug1(" logging you in again (slot %s)\n", loginslot);
+ setutent();
+ if (pututline(&utmp_logintty)==0)
+# endif /* _SEQUENT */
+#else /* GETUTENT */
+ debug1(" logging you in again (slot %d)\n", loginslot);
+# ifdef sequent
+ /* call sequent undocumented routine to count logins and add utmp entry if possible */
+ if (add_utmp(loginslot, &utmp_logintty) == -1)
+# else
+ (void) lseek(utmpf, (off_t) (loginslot * sizeof(struct utmp)), 0);
+ if (write(utmpf, (char *) &utmp_logintty, sizeof(struct utmp))
+ != sizeof(struct utmp))
+# endif /* sequent */
+#endif /* GETUTENT */
+ {
+#ifdef NETHACK
+ if (nethackflag)
+ Msg(errno, "%s is too hard to dig in.", UTMPFILE);
+ else
+#endif
+ Msg(errno,"Could not write %s.", UTMPFILE);
+ }
+ }
+#ifdef apollo
+ close(utmpf);
+#endif
+ loginslot = (slot_t) 0;
+}
+
+void
+RemoveLoginSlot(slot, up)
+slot_t slot;
+struct utmp *up;
+{
+#ifdef GETUTENT
+ struct utmp *uu;
+#endif
+ struct utmp u;
+#ifdef apollo
+ struct utmp *uq;
+#endif
+
+#ifdef GETUTENT
+ debug2("RemoveLoginSlot(%s, %08x)\n", (slot == (slot_t) 0 ||
+ slot == (slot_t) -1 ) ? "no slot" : slot, up);
+#else
+ debug2("RemoveLoginSlot(%d, %08x)\n", slot, up);
+#endif
+#ifdef apollo
+ InitUtmp();
+ bzero((char *)up, sizeof(struct utmp));
+ uq = (struct utmp *)malloc(sizeof(struct utmp));
+ bzero((char *)uq, sizeof(struct utmp));
+#endif /* apollo */
+ if (!utmp)
+ return;
+ if (slot != (slot_t) 0 && slot != (slot_t) -1)
+ {
+ bzero((char *) &u, sizeof u);
+#ifdef GETUTENT
+ setutent();
+ strncpy(u.ut_line, slot, sizeof(u.ut_line));
+ if ((uu = getutline(&u)) == 0)
+ {
+ DeadlyMsg = 0;
+ Msg(0, "Utmp slot not found -> not removed");
+ return;
+ }
+ *up= *uu;
+# ifdef _SEQUENT_
+ if (ut_delete_user(slot, uu->ut_pid, 0, 0) == 0)
+# else
+ uu->ut_type = DEAD_PROCESS;
+ uu->ut_exit.e_termination = 0;
+ uu->ut_exit.e_exit= 0;
+ if (pututline(uu) == 0)
+# endif
+#else
+ (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
+ if (read(utmpf, (char *) up, sizeof u) != sizeof u)
+ {
+ DeadlyMsg = 0;
+ Msg(errno, "cannot read %s ???", UTMPFILE);
+ sleep(1);
+ }
+ (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
+# ifdef apollo
+ bcopy((char *)up, (char *)uq, sizeof(struct utmp));
+ bzero(uq->ut_name, sizeof(uq->ut_name));
+ bzero(uq->ut_host, sizeof(uq->ut_host));
+ if (write(utmpf, (char *)uq, sizeof(struct utmp)) != sizeof(struct utmp))
+# else
+ if (write(utmpf, (char *) &u, sizeof u) != sizeof u)
+# endif /* apollo */
+#endif
+ {
+#ifdef NETHACK
+ if (nethackflag)
+ {
+ DeadlyMsg = 0;
+ Msg(errno, "%s is too hard to dig in.", UTMPFILE);
+ }
+ else
+#endif
+ {
+ DeadlyMsg = 0;
+ Msg(errno, "Could not write %s.", UTMPFILE);
+ }
+ }
+ }
+ else
+ {
+ debug1("There is no utmp-slot to be removed(%d)\n", slot);
+ }
+#ifdef apollo
+ close(utmpf);
+ free(uq);
+#endif
+}
+
+char *
+stripdev(nam)
+char *nam;
+{
+#ifdef apollo
+ char *p;
+
+ if (nam == NULL)
+ return NULL;
+ if (p = strstr(nam,"/dev/"))
+ return p + 5;
+#else
+ if (nam == NULL)
+ return NULL;
+ if (strncmp(nam, "/dev/", 5) == 0)
+ return nam + 5;
+#endif
+ return nam;
}
-#endif /* COPY_PASTE */
+static slot_t TtyNameSlot(nam)
+char *nam;
+{
+ char *name;
+ register slot_t slot;
+#ifndef GETUTENT
+ register struct ttyent *tp;
+#endif
+#ifdef apollo
+ struct utmp *up;
+#endif
+
+ debug1("TtyNameSlot(%s)\n", nam);
+#ifdef apollo
+ InitUtmp();
+#endif
+ if (!utmp || nam == NULL)
+ return (slot_t)0;
+ name = stripdev(nam);
+#ifdef GETUTENT
+ slot = name;
+#else
+# ifdef apollo
+ slot = 0;
+ up = (struct utmp *)malloc(sizeof(struct utmp));
+ while (1)
+ {
+ if ((read(utmpf, (char *)up, sizeof(struct utmp)) ==
+ sizeof(struct utmp)) && (strcmp(up->ut_line, name)))
+ slot++;
+ else
+ break;
+ }
+ close(utmpf);
+ free(up);
+# else /* !apollo */
+ slot = 1;
+ setttyent();
+ while ((tp = getttyent()) != NULL && strcmp(name, tp->ty_name) != 0)
+ {
+ debug2("'%s' %d, ", tp->ty_name, slot);
+ ++slot;
+ }
+ debug("\n");
+# ifdef MIPS
+ if (tp == NULL)
+ {
+ slot = CreateUtmp(name);
+ }
+# endif /* MIPS */
+# endif /* apollo */
+#endif /* GETUTENT */
+ return slot;
+}
+
+int
+SetUtmp(wi, displaynumber)
+struct win *wi;
+int displaynumber;
+{
+ register char *p;
+ register slot_t slot;
+ char *line;
+ struct utmp u;
+#ifdef UTHOST
+# ifdef _SEQUENT_
+ char host[100+5];
+# else
+ char host[sizeof(utmp_logintty.ut_host)+5];
+# endif
+#endif
+
+ wi->slot = (slot_t) 0;
+ if (!utmp)
+ return -1;
+ if ((slot = TtyNameSlot(wi->tty)) == (slot_t) NULL)
+ {
+ debug1("SetUtmp failed (tty %s).\n",wi->tty);
+ return -1;
+ }
+ debug2("SetUtmp %d will get slot %d...\n", displaynumber, (int)slot);
+#ifdef apollo
+ InitUtmp();
+#endif
+
+#ifdef UTHOST
+ host[sizeof(host)-5] = '\0';
+# ifdef _SEQUENT_
+ strncpy(host, loginhost, sizeof(host) - 5);
+# else
+ strncpy(host, utmp_logintty.ut_host, sizeof(host) - 5);
+# endif
+ if (loginslot != (slot_t)0 && loginslot != (slot_t)-1 && host[0] != '\0')
+ {
+ /*
+ * we want to set our ut_host field to something like
+ * ":ttyhf:s.0" or
+ * "faui45:s.0" or
+ * "132.199.81.4:s.0" (even this may hurt..), but not
+ * "faui45.informati"......:s.0
+ */
+ for (p = host; *p; p++)
+ {
+ if ((*p < '0' || *p > '9') && (*p != '.'))
+ break;
+ }
+ if (*p)
+ {
+ for (p = host; *p; p++)
+ {
+ if (*p == '.')
+ {
+ *p = '\0';
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ strncpy(host + 1, stripdev(display_tty), sizeof(host) - 6);
+ host[0] = ':';
+ }
+ debug1("rlogin hostname: '%s'\n", host);
+ sprintf(host + strlen(host), ":S.%c", '0' + displaynumber);
+ debug1("rlogin hostname: '%s'\n", host);
+#endif /* UTHOST */
+
+ line = stripdev(wi->tty);
+ bzero((char *) &u, sizeof u);
+
+#ifdef GETUTENT
+# ifdef _SEQUENT_
+ if (ut_add_user(LoginName, slot, wi->wpid, host)==0)
+# else
+ strncpy(u.ut_user, LoginName, sizeof(u.ut_user));
+ strncpy(u.ut_id, line + strlen(line) - 2, sizeof(u.ut_id));
+ strncpy(u.ut_line, line, sizeof(u.ut_line));
+ u.ut_pid = wi->wpid;
+ u.ut_type = USER_PROCESS;
+# ifdef SVR4
+ (void) time(&u.ut_tv.tv_sec);
+ u.ut_tv.tv_usec=0;
+# else
+ (void) time(&u.ut_time);
+# endif /* SVR4 */
+# ifdef UTHOST
+ strncpy(u.ut_host, host, sizeof(u.ut_host));
+# endif /* UTHOST */
+ if (pututline(&u) == 0)
+# endif /* _SEQUENT_ */
+#else /* GETUTENT */
+ strncpy(u.ut_line, line, sizeof(u.ut_line));
+ strncpy(u.ut_name, LoginName, sizeof(u.ut_name));
+# ifdef UTHOST
+ strncpy(u.ut_host, host, sizeof(u.ut_host));
+# endif /* UTHOST */
+# ifdef MIPS
+ u.ut_type = 7; /* USER_PROCESS */
+ strncpy(u.ut_id, line + 3, 4);
+# endif /* MIPS */
+ (void) time(&u.ut_time);
+# ifdef sequent
+/* call sequent undocumented routine to count logins and add utmp entry if possible */
+ if (add_utmp(slot, &u) == -1)
+# else
+ (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
+ if (write(utmpf, (char *) &u, sizeof u) != sizeof u)
+# endif /* sequent */
+#endif /* GETUTENT */
+
+ {
+#ifdef NETHACK
+ if (nethackflag)
+ Msg(errno, "%s is too hard to dig in.", UTMPFILE);
+ else
+#endif
+ Msg(errno,"Could not write %s.", UTMPFILE);
+#ifdef apollo
+ close(utmpf);
+#endif
+ return -1;
+ }
+ debug("SetUtmp successful\n");
+ wi->slot = slot;
+#ifdef apollo
+ close(utmpf);
+#endif
+ return 0;
+}
+
+#ifdef MIPS
+
+#define GETTTYENT
+static int ttyfd = 0;
+
+static void setttyent()
+{
+ if (ttyfd)
+ close(ttyfd);
+ ttyfd = open(UtmpName, O_RDONLY);
+}
+
+static struct ttyent *getttyent()
+{
+ static struct utmp u;
+ static struct ttyent t;
+
+ if (!ttyfd)
+ return NULL;
+
+ if (read(ttyfd, &u, sizeof u))
+ {
+ t.ty_name = u.ut_line;
+ return &t;
+ }
+ return NULL;
+}
+
+CreateUtmp(name)
+char *name;
+{
+ int slot;
+ struct utmp u;
+
+ strncpy(u.ut_line, name, 8);
+ strncpy(u.ut_name, LoginName, 8);
+ u.ut_type = 7; /* USER_PROCESS */
+ strncpy(u.ut_id, name+3, 4);
+ (void) time(&u.ut_time);
+ slot = (lseek(utmpfappend, 0, 2) + 1) / sizeof u;
+ (void) write(utmpfappend, (char *)&u, sizeof u);
+ close(utmpfappend);
+ if ((utmpfappend = open(UtmpName, O_APPEND)) == -1)
+ {
+ if (errno != EACCES)
+ Msg(errno, UtmpName);
+ return;
+ }
+ return slot;
+}
+#endif /* MIPS */
/*
- * (Almost) secure open and fopen...
+ * if slot could be removed or was 0, wi->slot = -1;
+ * else not changed.
+ */
+int
+RemoveUtmp(wi)
+struct win *wi;
+{
+#ifdef GETUTENT
+ struct utmp *uu;
+#endif
+#ifdef apollo
+ struct utmp *up;
+#endif
+ struct utmp u;
+ slot_t slot;
+
+ slot = wi->slot;
+#ifdef GETUTENT
+ debug1("RemoveUtmp(%s)\n", (slot == (slot_t) 0) ?
+ "no slot (0)":((slot == (slot_t) -1) ? "no slot (-1)" : slot));
+#else
+ debug1("RemoveUtmp(wi.slot: %d)\n", slot);
+#endif
+#ifdef apollo
+ InitUtmp();
+ up = (struct utmp *)malloc(sizeof(struct utmp));
+ bzero((char *)up, sizeof(struct utmp));
+#endif /* apollo */
+ if (!utmp)
+ return -1;
+ if (slot == (slot_t) 0 || slot == (slot_t) -1)
+ {
+ debug1("There is no utmp-slot to be removed(%d)\n", slot);
+ wi->slot = (slot_t) -1;
+ return 0;
+ }
+ bzero((char *) &u, sizeof u);
+#ifdef GETUTENT
+ setutent();
+ strncpy(u.ut_line, slot, sizeof(u.ut_line));
+ if ((uu = getutline(&u)) == 0)
+ {
+ Msg(0, "Utmp slot not found -> not removed");
+ return -1;
+ }
+# ifdef _SEQUENT_
+ if (ut_delete_user(slot, uu->ut_pid, 0, 0) == 0)
+# else
+ uu->ut_type = DEAD_PROCESS;
+ uu->ut_exit.e_termination = 0;
+ uu->ut_exit.e_exit= 0;
+ if (pututline(uu) == 0)
+# endif
+#else /* GETUTENT */
+ (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
+# ifdef apollo
+ if (read(utmpf, (char *) up, sizeof u) != sizeof u)
+ {
+ DeadlyMsg = 0;
+ Msg(errno, "cannot read %s?", UTMPFILE);
+ sleep(1);
+ }
+ (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
+ bzero(up->ut_name, sizeof(u.ut_name));
+ bzero(up->ut_host, sizeof(u.ut_host));
+ if (write(utmpf, (char *)up, sizeof u) != sizeof u)
+# else
+ if (write(utmpf, (char *) &u, sizeof u) != sizeof u)
+# endif /* apollo */
+#endif
+ {
+#ifdef NETHACK
+ if (nethackflag)
+ Msg(errno, "%s is too hard to dig in.", UTMPFILE);
+ else
+#endif
+ Msg(errno,"Could not write %s.", UTMPFILE);
+#ifdef apollo
+ close(utmpf);
+ free(up);
+#endif
+ return -1;
+ }
+ debug("RemoveUtmp successfull\n");
+ wi->slot = (slot_t) -1;
+#ifdef apollo
+ close(utmpf);
+ free(up);
+#endif
+ return 0;
+}
+
+#endif /* UTMPOK */
+
+#if !defined(GETTTYENT) && !defined(GETUTENT)
+
+static void setttyent()
+{
+ struct stat s;
+ register int f;
+ register char *p, *ep;
+
+ if (ttnext)
+ {
+ ttnext = tt;
+ return;
+ }
+ if ((f = open(ttys, O_RDONLY)) == -1 || fstat(f, &s) == -1)
+ Msg(errno, ttys);
+ if ((tt = malloc((unsigned) s.st_size + 1)) == 0)
+ Msg_nomem;
+ if (read(f, tt, s.st_size) != s.st_size)
+ Msg(errno, ttys);
+ close(f);
+ for (p = tt, ep = p + s.st_size; p < ep; ++p)
+ if (*p == '\n')
+ *p = '\0';
+ *p = '\0';
+ ttnext = tt;
+}
+
+static struct ttyent *getttyent()
+{
+ static struct ttyent t;
+
+ if (*ttnext == '\0')
+ return NULL;
+ t.ty_name = ttnext + 2;
+ ttnext += strlen(ttnext) + 1;
+ return &t;
+}
+
+#endif /* GETTTYENT */
+
+#ifdef LOADAV
+# ifdef LOADAV_NEXT
+void
+InitNeXTLoadAvg()
+{
+ error = processor_set_default(host_self(), &default_set);
+ if (error != KERN_SUCCESS)
+ mach_error("Error calling processor_set_default", error);
+ else
+ avenrun = 1;
+}
+
+int
+GetAvenrun()
+{
+ info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
+ error = processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO, &host,
+ (processor_set_info_t)&info, &info_count);
+ if (error != KERN_SUCCESS)
+ {
+ mach_error("Error calling processor_set_info", error);
+ return 0;
+ }
+ else
+ {
+ loadav = (float)info.load_average / LOAD_SCALE;
+ return 1;
+ }
+}
+
+# else /* need kmem for load avg */
+
+void
+InitKmem()
+{
+ debug("Init Kmem...\n");
+# ifndef apollo
+ if ((kmemf = open(KmemName, O_RDONLY)) == -1)
+ return;
+ debug("Kmem opened\n");
+ nl[0].n_name = AvenrunSym;
+ debug2("Searching in %s for %s\n", UnixName, nl[0].n_name);
+ nlist(UnixName, nl);
+ if (/* nl[0].n_type == 0 || */ nl[0].n_value == 0)
+ {
+ close(kmemf);
+ return;
+ }
+# ifdef sgi
+ nl[0].n_value &= ~(1 << 31); /* clear upper bit */
+# endif /* sgi */
+ debug("AvenrunSym found!!\n");
+# endif /* apollo */
+ avenrun = 1;
+}
+
+int
+GetAvenrun()
+{
+# ifdef apollo
+ int load[3];
+ register int i;
+
+ proc1_$get_loadav(load);
+ for (i = 0; i < 3; i++)
+ loadav[i] = (double)load[i] / 65536.0;
+# else
+ if (lseek(kmemf, (off_t) nl[0].n_value, 0) == (off_t) - 1)
+ return 0;
+ if (read(kmemf, (char *) loadav, sizeof loadav) != sizeof loadav)
+ return 0;
+# endif /* apollo */
+
+ return 1;
+}
+
+# endif /* !NeXT, need kmem for load avg */
+#endif /* LOADAV */
+
+/*
+ * (Almost) secure open and fopen... mlschroe.
*/
FILE *
@@ -638,16 +2426,15 @@ char *mode;
#endif
debug2("secfopen(%s, %s)\n", name, mode);
+ if (eff_uid == real_uid)
+ return(fopen(name, mode));
#ifndef NOREUID
setreuid(eff_uid, real_uid);
setregid(eff_gid, real_gid);
fi = fopen(name, mode);
setreuid(real_uid, eff_uid);
setregid(real_gid, eff_gid);
- return fi;
#else
- if (eff_uid == real_uid)
- return(fopen(name, mode));
if (mode[0] && mode[1] == '+')
flags = O_RDWR;
else
@@ -668,8 +2455,8 @@ char *mode;
close(fd);
return(0);
}
- return(fi);
#endif
+ return(fi);
}
@@ -686,16 +2473,15 @@ int mode;
#endif
debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode);
+ if (eff_uid == real_uid)
+ return(open(name, flags, mode));
#ifndef NOREUID
setreuid(eff_uid, real_uid);
setregid(eff_gid, real_gid);
fd = open(name, flags, mode);
setreuid(real_uid, eff_uid);
setregid(real_gid, eff_gid);
- return fd;
#else
- if (eff_uid == real_uid)
- return(open(name, flags, mode));
/* Truncation/creation is done in UserContext */
if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK)))
{
@@ -750,7 +2536,41 @@ int mode;
return(-1);
}
}
+#endif
debug1("secopen ok - returning %d\n", fd);
return(fd);
+}
+
+#ifdef BUGGYGETLOGIN
+char *
+getlogin()
+{
+ char *tty;
+#ifdef utmp
+# undef utmp
#endif
+ struct utmp u;
+ static char retbuf[sizeof(u.ut_user)+1];
+ int fd;
+
+ for (fd = 0; fd <= 2 && (tty = ttyname(fd)) == NULL; fd++)
+ ;
+ if ((tty == NULL) || ((fd = open(UTMP_FILE, O_RDONLY)) < 0))
+ return NULL;
+ tty = stripdev(tty);
+ retbuf[0] = '\0';
+ while (read(fd, &u, sizeof(struct utmp)) == sizeof(struct utmp))
+ {
+ if (!strncmp(tty, u.ut_line, sizeof(u.ut_line)))
+ {
+ strncpy(retbuf, u.ut_user, sizeof(u.ut_user));
+ retbuf[sizeof(u.ut_user)] = '\0';
+ if (u.ut_type == USER_PROCESS)
+ break;
+ }
+ }
+ close(fd);
+
+ return *retbuf ? retbuf : NULL;
}
+#endif