summaryrefslogtreecommitdiff
path: root/misc.c
diff options
context:
space:
mode:
authorPatrick McCarty <patrick.mccarty@linux.intel.com>2013-02-08 13:26:27 -0800
committerPatrick McCarty <patrick.mccarty@linux.intel.com>2013-02-08 13:26:27 -0800
commit9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7 (patch)
tree881eebfa461e4f8aa6b6f44b96ac0decd3bc887a /misc.c
downloadlsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.gz
lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.bz2
lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.zip
Imported Upstream version 4.87upstream/4.87
Diffstat (limited to 'misc.c')
-rw-r--r--misc.c1653
1 files changed, 1653 insertions, 0 deletions
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..20a6456
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,1653 @@
+/*
+ * misc.c - common miscellaneous functions for lsof
+ */
+
+
+/*
+ * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by Victor A. Abell
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: misc.c,v 1.27 2013/01/02 17:14:59 abe Exp $";
+#endif
+
+
+#include "lsof.h"
+
+#if defined(HASWIDECHAR)
+# if defined(WIDECHARINCL)
+#include WIDECHARINCL
+# endif /* defined(WIDECHARINCL) */
+# if defined(HASWCTYPE_H)
+#include <wctype.h>
+# endif /* defined(HASWCTYPE_H) */
+#endif /* defined(HASWIDECHAR) */
+
+
+/*
+ * Local definitions
+ */
+
+#if !defined(MAXSYMLINKS)
+#define MAXSYMLINKS 32
+#endif /* !defined(MAXSYMLINKS) */
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void closePipes,(void));
+_PROTOTYPE(static int dolstat,(char *path, char *buf, int len));
+_PROTOTYPE(static int dostat,(char *path, char *buf, int len));
+_PROTOTYPE(static int doreadlink,(char *path, char *buf, int len));
+_PROTOTYPE(static int doinchild,(int (*fn)(), char *fp, char *rbuf, int rbln));
+
+#if defined(HASINTSIGNAL)
+_PROTOTYPE(static int handleint,(int sig));
+#else /* !defined(HASINTSIGNAL) */
+_PROTOTYPE(static void handleint,(int sig));
+#endif /* defined(HASINTSIGNAL) */
+
+_PROTOTYPE(static char *safepup,(unsigned int c, int *cl));
+
+
+/*
+ * Local variables
+ */
+
+static pid_t Cpid = 0; /* child PID */
+static jmp_buf Jmp_buf; /* jump buffer */
+static int Pipes[] = /* pipes for child process */
+ { -1, -1, -1, -1 };
+static int CtSigs[] = { 0, SIGINT, SIGKILL };
+ /* child termination signals (in order
+ * of application) -- the first is a
+ * dummy to allow pipe closure to
+ * cause the child to exit */
+#define NCTSIGS (sizeof(CtSigs) / sizeof(int))
+
+
+#if defined(HASNLIST)
+/*
+ * build-Nl() - build kernel name list table
+ */
+
+static struct drive_Nl *Build_Nl = (struct drive_Nl *)NULL;
+ /* the default Drive_Nl address */
+
+void
+build_Nl(d)
+ struct drive_Nl *d; /* data to drive the construction */
+{
+ struct drive_Nl *dp;
+ int i, n;
+
+ for (dp = d, n = 0; dp->nn; dp++, n++)
+ ;
+ if (n < 1) {
+ (void) fprintf(stderr,
+ "%s: can't calculate kernel name list length\n", Pn);
+ Exit(1);
+ }
+ if (!(Nl = (struct NLIST_TYPE *)calloc((n + 1),
+ sizeof(struct NLIST_TYPE))))
+ {
+ (void) fprintf(stderr,
+ "%s: can't allocate %d bytes to kernel name list structure\n",
+ Pn, (int)((n + 1) * sizeof(struct NLIST_TYPE)));
+ Exit(1);
+ }
+ for (dp = d, i = 0; i < n; dp++, i++) {
+ Nl[i].NL_NAME = dp->knm;
+ }
+ Nll = (int)((n + 1) * sizeof(struct NLIST_TYPE));
+ Build_Nl = d;
+}
+#endif /* defined(HASNLIST) */
+
+
+/*
+ * childx() - make child process exit (if possible)
+ */
+
+void
+childx()
+{
+ static int at, sx;
+ pid_t wpid;
+
+ if (Cpid > 1) {
+
+ /*
+ * First close the pipes to and from the child. That should cause the
+ * child to exit. Compute alarm time shares.
+ */
+ (void) closePipes();
+ if ((at = TmLimit / NCTSIGS) < TMLIMMIN)
+ at = TMLIMMIN;
+ /*
+ * Loop, waiting for the child to exit. After the first pass, help
+ * the child exit by sending it signals.
+ */
+ for (sx = 0; sx < NCTSIGS; sx++) {
+ if (setjmp(Jmp_buf)) {
+
+ /*
+ * An alarm has rung. Disable further alarms.
+ *
+ * If there are more signals to send, continue the signal loop.
+ *
+ * If the last signal has been sent, issue a warning (unless
+ * warninge have been suppressed) and exit the signal loop.
+ */
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ if (sx < (NCTSIGS - 1))
+ continue;
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING -- child process %d may be hung.\n",
+ Pn, (int)Cpid);
+ break;
+ }
+ /*
+ * Send the next signal to the child process, after the first pass
+ * through the loop.
+ *
+ * Wrap the wait() with an alarm.
+ */
+ if (sx)
+ (void) kill(Cpid, CtSigs[sx]);
+ (void) signal(SIGALRM, handleint);
+ (void) alarm(at);
+ wpid = (pid_t) wait(NULL);
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ if (wpid == Cpid)
+ break;
+ }
+ Cpid = 0;
+ }
+}
+
+
+/*
+ * closePipes() - close open pipe file descriptors
+ */
+
+static void
+closePipes()
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (Pipes[i] >= 0) {
+ (void) close(Pipes[i]);
+ Pipes[i] = -1;
+ }
+ }
+}
+
+
+/*
+ * compdev() - compare Devtp[] entries
+ */
+
+int
+compdev(a1, a2)
+ COMP_P *a1, *a2;
+{
+ struct l_dev **p1 = (struct l_dev **)a1;
+ struct l_dev **p2 = (struct l_dev **)a2;
+
+ if ((dev_t)((*p1)->rdev) < (dev_t)((*p2)->rdev))
+ return(-1);
+ if ((dev_t)((*p1)->rdev) > (dev_t)((*p2)->rdev))
+ return(1);
+ if ((INODETYPE)((*p1)->inode) < (INODETYPE)((*p2)->inode))
+ return(-1);
+ if ((INODETYPE)((*p1)->inode) > (INODETYPE)((*p2)->inode))
+ return(1);
+ return(strcmp((*p1)->name, (*p2)->name));
+}
+
+
+/*
+ * doinchild() -- do a function in a child process
+ */
+
+static int
+doinchild(fn, fp, rbuf, rbln)
+ int (*fn)(); /* function to perform */
+ char *fp; /* function parameter */
+ char *rbuf; /* response buffer */
+ int rbln; /* response buffer length */
+{
+ int en, rv;
+/*
+ * Check reply buffer size.
+ */
+ if (!Fovhd && rbln > MAXPATHLEN) {
+ (void) fprintf(stderr,
+ "%s: doinchild error; response buffer too large: %d\n",
+ Pn, rbln);
+ Exit(1);
+ }
+/*
+ * Set up to handle an alarm signal; handle an alarm signal; build
+ * pipes for exchanging information with a child process; start the
+ * child process; and perform functions in the child process.
+ */
+ if (!Fovhd) {
+ if (setjmp(Jmp_buf)) {
+
+ /*
+ * Process an alarm that has rung.
+ */
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ (void) childx();
+ errno = ETIMEDOUT;
+ return(1);
+ } else if (!Cpid) {
+
+ /*
+ * Create pipes to exchange function information with a child
+ * process.
+ */
+ if (pipe(Pipes) < 0 || pipe(&Pipes[2]) < 0) {
+ (void) fprintf(stderr, "%s: can't open pipes: %s\n",
+ Pn, strerror(errno));
+ Exit(1);
+ }
+ /*
+ * Fork a child to execute functions.
+ */
+ if ((Cpid = fork()) == 0) {
+
+ /*
+ * Begin the child process.
+ */
+
+ int fd, nd, r_al, r_rbln;
+ char r_arg[MAXPATHLEN+1], r_rbuf[MAXPATHLEN+1];
+ int (*r_fn)();
+ /*
+ * Close all open file descriptors except Pipes[0] and
+ * Pipes[3].
+ */
+ for (fd = 0, nd = GET_MAX_FD(); fd < nd; fd++) {
+ if (fd == Pipes[0] || fd == Pipes[3])
+ continue;
+ (void) close(fd);
+ if (fd == Pipes[1])
+ Pipes[1] = -1;
+ else if (fd == Pipes[2])
+ Pipes[2] = -1;
+ }
+ if (Pipes[1] >= 0) {
+ (void) close(Pipes[1]);
+ Pipes[1] = -1;
+ }
+ if (Pipes[2] >= 0) {
+ (void) close(Pipes[2]);
+ Pipes[2] = -1;
+ }
+ /*
+ * Read function requests, process them, and return replies.
+ */
+ for (;;) {
+ if (read(Pipes[0], (char *)&r_fn, sizeof(r_fn))
+ != (int)sizeof(r_fn)
+ || read(Pipes[0], (char *)&r_al, sizeof(int))
+ != (int)sizeof(int)
+ || r_al < 1
+ || r_al > (int)sizeof(r_arg)
+ || read(Pipes[0], r_arg, r_al) != r_al
+ || read(Pipes[0], (char *)&r_rbln, sizeof(r_rbln))
+ != (int)sizeof(r_rbln)
+ || r_rbln < 1 || r_rbln > (int)sizeof(r_rbuf))
+ break;
+ rv = r_fn(r_arg, r_rbuf, r_rbln);
+ en = errno;
+ if (write(Pipes[3], (char *)&rv, sizeof(rv))
+ != sizeof(rv)
+ || write(Pipes[3], (char *)&en, sizeof(en))
+ != sizeof(en)
+ || write(Pipes[3], r_rbuf, r_rbln) != r_rbln)
+ break;
+ }
+ (void) _exit(0);
+ }
+ /*
+ * Continue in the parent process to finish the setup.
+ */
+ if (Cpid < 0) {
+ (void) fprintf(stderr, "%s: can't fork: %s\n",
+ Pn, strerror(errno));
+ Exit(1);
+ }
+ (void) close(Pipes[0]);
+ (void) close(Pipes[3]);
+ Pipes[0] = Pipes[3] = -1;
+ }
+ }
+ if (!Fovhd) {
+ int len;
+
+ /*
+ * Send a function to the child and wait for the response.
+ */
+ len = strlen(fp) + 1;
+ (void) signal(SIGALRM, handleint);
+ (void) alarm(TmLimit);
+ if (write(Pipes[1], (char *)&fn, sizeof(fn)) != sizeof(fn)
+ || write(Pipes[1], (char *)&len, sizeof(len)) != sizeof(len)
+ || write(Pipes[1], fp, len) != len
+ || write(Pipes[1], (char *)&rbln, sizeof(rbln)) != sizeof(rbln)
+ || read(Pipes[2], (char *)&rv, sizeof(rv)) != sizeof(rv)
+ || read(Pipes[2], (char *)&en, sizeof(en)) != sizeof(en)
+ || read(Pipes[2], rbuf, rbln) != rbln) {
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ (void) childx();
+ errno = ECHILD;
+ return(-1);
+ }
+ } else {
+
+ /*
+ * Do the operation directly -- not in a child.
+ */
+ (void) signal(SIGALRM, handleint);
+ (void) alarm(TmLimit);
+ rv = fn(fp, rbuf, rbln);
+ en = errno;
+ }
+/*
+ * Function completed, response collected -- complete the operation.
+ */
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ errno = en;
+ return(rv);
+}
+
+
+/*
+ * dolstat() - do an lstat() function
+ */
+
+static int
+dolstat(path, rbuf, rbln)
+ char *path; /* path */
+ char *rbuf; /* response buffer */
+ int rbln; /* response buffer length */
+
+/* ARGSUSED */
+
+{
+ return(lstat(path, (struct stat *)rbuf));
+}
+
+
+/*
+ * doreadlink() -- do a readlink() function
+ */
+
+static int
+doreadlink(path, rbuf, rbln)
+ char *path; /* path */
+ char *rbuf; /* response buffer */
+ int rbln; /* response buffer length */
+{
+ return(readlink(path, rbuf, rbln));
+}
+
+
+/*
+ * dostat() - do a stat() function
+ */
+
+static int
+dostat(path, rbuf, rbln)
+ char *path; /* path */
+ char *rbuf; /* response buffer */
+ int rbln; /* response buffer length */
+
+/* ARGSUSED */
+
+{
+ return(stat(path, (struct stat *)rbuf));
+}
+
+
+#if defined(WILLDROPGID)
+/*
+ * dropgid() - drop setgid permission
+ */
+
+void
+dropgid()
+{
+ if (!Setuidroot && Setgid) {
+ if (setgid(Mygid) < 0) {
+ (void) fprintf(stderr, "%s: can't setgid(%d): %s\n",
+ Pn, (int)Mygid, strerror(errno));
+ Exit(1);
+ }
+ Setgid = 0;
+ }
+}
+#endif /* defined(WILLDROPGID) */
+
+
+/*
+ * enter_dev_ch() - enter device characters in file structure
+ */
+
+void
+enter_dev_ch(m)
+ char *m;
+{
+ char *mp;
+
+ if (!m || *m == '\0')
+ return;
+ if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr, "%s: no more dev_ch space at PID %d: \n",
+ Pn, Lp->pid);
+ safestrprt(m, stderr, 1);
+ Exit(1);
+ }
+ if (Lf->dev_ch)
+ (void) free((FREE_P *)Lf->dev_ch);
+ Lf->dev_ch = mp;
+}
+
+
+/*
+ * enter_IPstate() -- enter a TCP or UDP state
+ */
+
+void
+enter_IPstate(ty, nm, nr)
+ char *ty; /* type -- TCP or UDP */
+ char *nm; /* state name (may be NULL) */
+ int nr; /* state number */
+{
+
+#if defined(USE_LIB_PRINT_TCPTPI)
+ TcpNstates = nr;
+#else /* !defined(USE_LIB_PRINT_TCPTPI) */
+
+ int al, i, j, oc, nn, ns, off, tx;
+ char *cp;
+ MALLOC_S len;
+/*
+ * Check the type name and set the type index.
+ */
+ if (!ty) {
+ (void) fprintf(stderr,
+ "%s: no type specified to enter_IPstate()\n", Pn);
+ Exit(1);
+ }
+ if (!strcmp(ty, "TCP"))
+ tx = 0;
+ else if (!strcmp(ty, "UDP"))
+ tx = 1;
+ else {
+ (void) fprintf(stderr, "%s: unknown type for enter_IPstate: %s\n",
+ Pn, ty);
+ Exit(1);
+ }
+/*
+ * If the name argument is NULL, reduce the allocated table to its minimum
+ * size.
+ */
+ if (!nm) {
+ if (tx) {
+ if (UdpSt) {
+ if (!UdpNstates) {
+ (void) free((MALLOC_P *)UdpSt);
+ UdpSt = (char **)NULL;
+ }
+ if (UdpNstates < UdpStAlloc) {
+ len = (MALLOC_S)(UdpNstates * sizeof(char *));
+ if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len)))
+ {
+ (void) fprintf(stderr,
+ "%s: can't reduce UdpSt[]\n", Pn);
+ Exit(1);
+ }
+ }
+ UdpStAlloc = UdpNstates;
+ }
+ } else {
+ if (TcpSt) {
+ if (!TcpNstates) {
+ (void) free((MALLOC_P *)TcpSt);
+ TcpSt = (char **)NULL;
+ }
+ if (TcpNstates < TcpStAlloc) {
+ len = (MALLOC_S)(TcpNstates * sizeof(char *));
+ if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len)))
+ {
+ (void) fprintf(stderr,
+ "%s: can't reduce TcpSt[]\n", Pn);
+ Exit(1);
+ }
+ }
+ TcpStAlloc = TcpNstates;
+ }
+ }
+ return;
+ }
+/*
+ * Check the name and number.
+ */
+ if ((len = (size_t)strlen(nm)) < 1) {
+ (void) fprintf(stderr,
+ "%s: bad %s name (\"%s\"), number=%d\n", Pn, ty, nm, nr);
+ Exit(1);
+ }
+/*
+ * Make a copy of the name.
+ */
+ if (!(cp = mkstrcpy(nm, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: enter_IPstate(): no %s space for %s\n",
+ Pn, ty, nm);
+ Exit(1);
+ }
+/*
+ * Set the necessary offset for using nr as an index. If it is
+ * a new offset, adjust previous entries.
+ */
+ if ((nr < 0) && ((off = -nr) > (tx ? UdpStOff : TcpStOff))) {
+ if (tx ? UdpSt : TcpSt) {
+
+ /*
+ * A new, larger offset (smaller negative state number) could mean
+ * a previously allocated state table must be enlarged and its
+ * previous entries moved.
+ */
+ oc = off - (tx ? UdpStOff : TcpStOff);
+ al = tx ? UdpStAlloc : TcpStAlloc;
+ ns = tx ? UdpNstates : TcpNstates;
+ if ((nn = ns + oc) >= al) {
+ while ((nn + 5) > al) {
+ al += TCPUDPALLOC;
+ }
+ len = (MALLOC_S)(al * sizeof(char *));
+ if (tx) {
+ if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len)))
+ goto no_IP_space;
+ UdpStAlloc = al;
+ } else {
+ if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len)))
+ goto no_IP_space;
+ TcpStAlloc = al;
+ }
+ for (i = 0, j = oc; i < oc; i++, j++) {
+ if (tx) {
+ if (i < UdpNstates)
+ UdpSt[j] = UdpSt[i];
+ UdpSt[i] = (char *)NULL;
+ } else {
+ if (i < TcpNstates)
+ TcpSt[j] = TcpSt[i];
+ TcpSt[i] = (char *)NULL;
+ }
+ }
+ if (tx)
+ UdpNstates += oc;
+ else
+ TcpNstates += oc;
+ }
+ }
+ if (tx)
+ UdpStOff = off;
+ else
+ TcpStOff = off;
+ }
+/*
+ * Enter name as {Tc|Ud}pSt[nr + {Tc|Ud}pStOff].
+ *
+ * Allocate space, as required.
+ */
+ al = tx ? UdpStAlloc : TcpStAlloc;
+ off = tx ? UdpStOff : TcpStOff;
+ nn = nr + off + 1;
+ if (nn > al) {
+ i = tx ? UdpNstates : TcpNstates;
+ while ((nn + 5) > al) {
+ al += TCPUDPALLOC;
+ }
+ len = (MALLOC_S)(al * sizeof(char *));
+ if (tx) {
+ if (UdpSt)
+ UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len);
+ else
+ UdpSt = (char **)malloc(len);
+ if (!UdpSt) {
+
+no_IP_space:
+
+ (void) fprintf(stderr, "%s: no %s state space\n", Pn, ty);
+ Exit(1);
+ }
+ UdpNstates = nn;
+ UdpStAlloc = al;
+ } else {
+ if (TcpSt)
+ TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len);
+ else
+ TcpSt = (char **)malloc(len);
+ if (!TcpSt)
+ goto no_IP_space;
+ TcpNstates = nn;
+ TcpStAlloc = al;
+ }
+ while (i < al) {
+ if (tx)
+ UdpSt[i] = (char *)NULL;
+ else
+ TcpSt[i] = (char *)NULL;
+ i++;
+ }
+ } else {
+ if (tx) {
+ if (nn > UdpNstates)
+ UdpNstates = nn;
+ } else {
+ if (nn > TcpNstates)
+ TcpNstates = nn;
+ }
+ }
+ if (tx) {
+ if (UdpSt[nr + UdpStOff]) {
+
+dup_IP_state:
+
+ (void) fprintf(stderr,
+ "%s: duplicate %s state %d (already %s): %s\n",
+ Pn, ty, nr,
+ tx ? UdpSt[nr + UdpStOff] : TcpSt[nr + TcpStOff],
+ nm);
+ Exit(1);
+ }
+ UdpSt[nr + UdpStOff] = cp;
+ } else {
+ if (TcpSt[nr + TcpStOff])
+ goto dup_IP_state;
+ TcpSt[nr + TcpStOff] = cp;
+ }
+#endif /* defined(USE_LIB_PRINT_TCPTPI) */
+
+}
+
+
+/*
+ * enter_nm() - enter name in local file structure
+ */
+
+void
+enter_nm(m)
+ char *m;
+{
+ char *mp;
+
+ if (!m || *m == '\0')
+ return;
+ if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr, "%s: no more nm space at PID %d for: ",
+ Pn, Lp->pid);
+ safestrprt(m, stderr, 1);
+ Exit(1);
+ }
+ if (Lf->nm)
+ (void) free((FREE_P *)Lf->nm);
+ Lf->nm = mp;
+}
+
+
+/*
+ * Exit() - do a clean exit()
+ */
+
+void
+Exit(xv)
+ int xv; /* exit() value */
+{
+ (void) childx();
+
+#if defined(HASDCACHE)
+ if (DCrebuilt && !Fwarn)
+ (void) fprintf(stderr, "%s: WARNING: %s was updated.\n",
+ Pn, DCpath[DCpathX]);
+#endif /* defined(HASDCACHE) */
+
+ exit(xv);
+}
+
+
+#if defined(HASNLIST)
+/*
+ * get_Nl_value() - get Nl value for nickname
+ */
+
+int
+get_Nl_value(nn, d, v)
+ char *nn; /* nickname of requested entry */
+ struct drive_Nl *d; /* drive_Nl table that built Nl
+ * (if NULL, use Build_Nl) */
+ KA_T *v; /* returned value (if NULL,
+ * return nothing) */
+{
+ int i;
+
+ if (!Nl || !Nll)
+ return(-1);
+ if (!d)
+ d = Build_Nl;
+ for (i = 0; d->nn; d++, i++) {
+ if (strcmp(d->nn, nn) == 0) {
+ if (v)
+ *v = (KA_T)Nl[i].n_value;
+ return(i);
+ }
+ }
+ return(-1);
+}
+#endif /* defined(HASNLIST) */
+
+
+/*
+ * handleint() - handle an interrupt
+ */
+
+#if defined(HASINTSIGNAL)
+static int
+#else
+static void
+#endif
+
+/* ARGSUSED */
+
+handleint(sig)
+ int sig;
+{
+ longjmp(Jmp_buf, 1);
+}
+
+
+/*
+ * hashbyname() - hash by name
+ */
+
+int
+hashbyname(nm, mod)
+ char *nm; /* pointer to NUL-terminated name */
+ int mod; /* hash modulus */
+{
+ int i, j;
+
+ for (i = j = 0; *nm; nm++) {
+ i ^= (int)*nm << j;
+ if (++j > 7)
+ j = 0;
+ }
+ return(((int)(i * 31415)) & (mod - 1));
+}
+
+
+/*
+ * is_nw_addr() - is this network address selected?
+ */
+
+int
+is_nw_addr(ia, p, af)
+ unsigned char *ia; /* Internet address */
+ int p; /* port */
+ int af; /* address family -- e.g., AF_INET,
+ * AF_INET6 */
+{
+ struct nwad *n;
+
+ if (!(n = Nwad))
+ return(0);
+ for (; n; n = n->next) {
+ if (n->proto) {
+ if (strcasecmp(n->proto, Lf->iproto) != 0)
+ continue;
+ }
+ if (af && n->af && af != n->af)
+ continue;
+
+#if defined(HASIPv6)
+ if (af == AF_INET6) {
+ if (n->a[15] || n->a[14] || n->a[13] || n->a[12]
+ || n->a[11] || n->a[10] || n->a[9] || n->a[8]
+ || n->a[7] || n->a[6] || n->a[5] || n->a[4]
+ || n->a[3] || n->a[2] || n->a[1] || n->a[0]) {
+ if (ia[15] != n->a[15] || ia[14] != n->a[14]
+ || ia[13] != n->a[13] || ia[12] != n->a[12]
+ || ia[11] != n->a[11] || ia[10] != n->a[10]
+ || ia[9] != n->a[9] || ia[8] != n->a[8]
+ || ia[7] != n->a[7] || ia[6] != n->a[6]
+ || ia[5] != n->a[5] || ia[4] != n->a[4]
+ || ia[3] != n->a[3] || ia[2] != n->a[2]
+ || ia[1] != n->a[1] || ia[0] != n->a[0])
+ continue;
+ }
+ } else if (af == AF_INET)
+#endif /* defined(HASIPv6) */
+
+ {
+ if (n->a[3] || n->a[2] || n->a[1] || n->a[0]) {
+ if (ia[3] != n->a[3] || ia[2] != n->a[2]
+ || ia[1] != n->a[1] || ia[0] != n->a[0])
+ continue;
+ }
+ }
+
+#if defined(HASIPv6)
+ else
+ continue;
+#endif /* defined(HASIPv6) */
+
+ if (n->sport == -1 || (p >= n->sport && p <= n->eport)) {
+ n->f = 1;
+ return(1);
+ }
+ }
+ return(0);
+}
+
+
+/*
+ * mkstrcpy() - make a string copy in malloc()'d space
+ *
+ * return: copy pointer
+ * copy length (optional)
+ */
+
+char *
+mkstrcpy(src, rlp)
+ char *src; /* source */
+ MALLOC_S *rlp; /* returned length pointer (optional)
+ * The returned length is an strlen()
+ * equivalent */
+{
+ MALLOC_S len;
+ char *ns;
+
+ len = (MALLOC_S)(src ? strlen(src) : 0);
+ ns = (char *)malloc(len + 1);
+ if (ns) {
+ if (src)
+ (void) snpf(ns, len + 1, "%s", src);
+ else
+ *ns = '\0';
+ }
+ if (rlp)
+ *rlp = len;
+ return(ns);
+}
+
+
+/*
+ * mkstrcat() - make a catenated copy of up to three strings under optional
+ * string-by-string count control
+ *
+ * return: copy pointer
+ * copy string length (optional)
+ */
+
+char *
+mkstrcat(s1, l1, s2, l2, s3, l3, clp)
+ char *s1; /* source string 1 */
+ int l1; /* length of string 1 (-1 if none) */
+ char *s2; /* source string 2 */
+ int l2; /* length of string 2 (-1 if none) */
+ char *s3; /* source string 3 (optional) */
+ int l3 ; /* length of string 3 (-1 if none) */
+ MALLOC_S *clp; /* pointer to return of copy length
+ * (optional) */
+{
+ MALLOC_S cl, len1, len2, len3;
+ char *cp;
+
+ if (s1)
+ len1 = (MALLOC_S)((l1 >= 0) ? l1 : strlen(s1));
+ else
+ len1 = (MALLOC_S)0;
+ if (s2)
+ len2 = (MALLOC_S)((l2 >= 0) ? l2 : strlen(s2));
+ else
+ len2 = (MALLOC_S)0;
+ if (s3)
+ len3 = (MALLOC_S)((l3 >= 0) ? l3 : strlen(s3));
+ else
+ len3 = (MALLOC_S)0;
+ cl = len1 + len2 + len3;
+ if ((cp = (char *)malloc(cl + 1))) {
+ char *tp = cp;
+
+ if (s1 && len1) {
+ (void) strncpy(tp, s1, len1);
+ tp += len1;
+ }
+ if (s2 && len2) {
+ (void) strncpy(tp, s2, len2);
+ tp += len2;
+ }
+ if (s3 && len3) {
+ (void) strncpy(tp, s3, len3);
+ tp += len3;
+ }
+ *tp = '\0';
+ }
+ if (clp)
+ *clp = cl;
+ return(cp);
+}
+
+
+/*
+ * is_readable() -- is file readable
+ */
+
+int
+is_readable(path, msg)
+ char *path; /* file path */
+ int msg; /* issue warning message if 1 */
+{
+ if (access(path, R_OK) < 0) {
+ if (!Fwarn && msg == 1)
+ (void) fprintf(stderr, ACCESSERRFMT, Pn, path, strerror(errno));
+ return(0);
+ }
+ return(1);
+}
+
+
+/*
+ * lstatsafely() - lstat path safely (i. e., with timeout)
+ */
+
+int
+lstatsafely(path, buf)
+ char *path; /* file path */
+ struct stat *buf; /* stat buffer address */
+{
+ if (Fblock) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: avoiding stat(%s): -b was specified.\n",
+ Pn, path);
+ errno = EWOULDBLOCK;
+ return(1);
+ }
+ return(doinchild(dolstat, path, (char *)buf, sizeof(struct stat)));
+}
+
+
+/*
+ * Readlink() - read and interpret file system symbolic links
+ */
+
+char *
+Readlink(arg)
+ char *arg; /* argument to be interpreted */
+{
+ char abuf[MAXPATHLEN+1];
+ int alen;
+ char *ap;
+ char *argp1, *argp2;
+ int i, len, llen, slen;
+ char lbuf[MAXPATHLEN+1];
+ static char *op = (char *)NULL;
+ static int ss = 0;
+ char *s1;
+ static char **stk = (char **)NULL;
+ static int sx = 0;
+ char tbuf[MAXPATHLEN+1];
+/*
+ * See if avoiding kernel blocks.
+ */
+ if (Fblock) {
+ if (!Fwarn) {
+ (void) fprintf(stderr, "%s: avoiding readlink(", Pn);
+ safestrprt(arg, stderr, 0);
+ (void) fprintf(stderr, "): -b was specified.\n");
+ }
+ op = (char *)NULL;
+ return(arg);
+ }
+/*
+ * Save the original path.
+ */
+ if (!op)
+ op = arg;
+/*
+ * Evaluate each component of the argument for a symbolic link.
+ */
+ for (alen = 0, ap = abuf, argp1 = argp2 = arg; *argp2; argp1 = argp2 ) {
+ for (argp2 = argp1 + 1; *argp2 && *argp2 != '/'; argp2++)
+ ;
+ if ((len = argp2 - arg) >= (int)sizeof(tbuf)) {
+
+path_too_long:
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: readlink() path too long: ", Pn);
+ safestrprt(op ? op : arg, stderr, 1);
+ }
+ op = (char *)NULL;
+ return((char *)NULL);
+ }
+ (void) strncpy(tbuf, arg, len);
+ tbuf[len] = '\0';
+ /*
+ * Dereference a symbolic link.
+ */
+ if ((llen=doinchild(doreadlink,tbuf,lbuf,sizeof(lbuf) - 1)) >= 0) {
+
+ /*
+ * If the link is a new absolute path, replace
+ * the previous assembly with it.
+ */
+ if (lbuf[0] == '/') {
+ (void) strncpy(abuf, lbuf, llen);
+ ap = &abuf[llen];
+ *ap = '\0';
+ alen = llen;
+ continue;
+ }
+ lbuf[llen] = '\0';
+ s1 = lbuf;
+ } else {
+ llen = argp2 - argp1;
+ s1 = argp1;
+ }
+ /*
+ * Make sure two components are separated by a `/'.
+ *
+ * If the first component is not a link, don't force
+ * a leading '/'.
+ *
+ * If the first component is a link and the source of
+ * the link has a leading '/', force a leading '/'.
+ */
+ if (*s1 == '/')
+ slen = 1;
+ else {
+ if (alen > 0) {
+
+ /*
+ * This is not the first component.
+ */
+ if (abuf[alen - 1] == '/')
+ slen = 1;
+ else
+ slen = 2;
+ } else {
+
+ /*
+ * This is the first component.
+ */
+ if (s1 == lbuf && tbuf[0] == '/')
+ slen = 2;
+ else
+ slen = 1;
+ }
+ }
+ /*
+ * Add to the path assembly.
+ */
+ if ((alen + llen + slen) >= (int)sizeof(abuf))
+ goto path_too_long;
+ if (slen == 2)
+ *ap++ = '/';
+ (void) strncpy(ap, s1, llen);
+ ap += llen;
+ *ap = '\0';
+ alen += (llen + slen - 1);
+ }
+/*
+ * If the assembled path and argument are the same, free all but the
+ * last string in the stack, and return the argument.
+ */
+ if (strcmp(arg, abuf) == 0) {
+ for (i = 0; i < sx; i++) {
+ if (i < (sx - 1))
+ (void) free((FREE_P *)stk[i]);
+ stk[i] = (char *)NULL;
+ }
+ sx = 0;
+ op = (char *)NULL;
+ return(arg);
+ }
+/*
+ * If the assembled path and argument are different, add it to the
+ * string stack, then Readlink() it.
+ */
+ if (!(s1 = mkstrcpy(abuf, (MALLOC_S *)NULL))) {
+
+no_readlink_space:
+
+ (void) fprintf(stderr, "%s: no Readlink string space for ", Pn);
+ safestrprt(abuf, stderr, 1);
+ Exit(1);
+ }
+ if (sx >= MAXSYMLINKS) {
+
+ /*
+ * If there are too many symbolic links, report an error, clear
+ * the stack, and return no path.
+ */
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: too many (> %d) symbolic links in readlink() path: ",
+ Pn, MAXSYMLINKS);
+ safestrprt(op ? op : arg, stderr, 1);
+ }
+ for (i = 0; i < sx; i++) {
+ (void) free((FREE_P *)stk[i]);
+ stk[i] = (char *)NULL;
+ }
+ (void) free((FREE_P *)stk);
+ stk = (char **)NULL;
+ ss = sx = 0;
+ op = (char *)NULL;
+ return((char *)NULL);
+ }
+ if (++sx > ss) {
+ if (!stk)
+ stk = (char **)malloc((MALLOC_S)(sizeof(char *) * sx));
+ else
+ stk = (char **)realloc((MALLOC_P *)stk,
+ (MALLOC_S)(sizeof(char *) * sx));
+ if (!stk)
+ goto no_readlink_space;
+ ss = sx;
+ }
+ stk[sx - 1] = s1;
+ return(Readlink(s1));
+}
+
+
+#if defined(HASSTREAMS)
+/*
+ * readstdata() - read stream's stdata structure
+ */
+
+int
+readstdata(addr, buf)
+ KA_T addr; /* stdata address in kernel*/
+ struct stdata *buf; /* buffer addess */
+{
+ if (!addr
+ || kread(addr, (char *)buf, sizeof(struct stdata))) {
+ (void) snpf(Namech, Namechl, "no stream data in %s",
+ print_kptr(addr, (char *)NULL, 0));
+ return(1);
+ }
+ return(0);
+}
+
+
+/*
+ * readsthead() - read stream head
+ */
+
+int
+readsthead(addr, buf)
+ KA_T addr; /* starting queue pointer in kernel */
+ struct queue *buf; /* buffer for queue head */
+{
+ KA_T qp;
+
+ if (!addr) {
+ (void) snpf(Namech, Namechl, "no stream queue head");
+ return(1);
+ }
+ for (qp = addr; qp; qp = (KA_T)buf->q_next) {
+ if (kread(qp, (char *)buf, sizeof(struct queue))) {
+ (void) snpf(Namech, Namechl, "bad stream queue link at %s",
+ print_kptr(qp, (char *)NULL, 0));
+ return(1);
+ }
+ }
+ return(0);
+}
+
+
+/*
+ * readstidnm() - read stream module ID name
+ */
+
+int
+readstidnm(addr, buf, len)
+ KA_T addr; /* module ID name address in kernel */
+ char *buf; /* receiving buffer address */
+ READLEN_T len; /* buffer length */
+{
+ if (!addr || kread(addr, buf, len)) {
+ (void) snpf(Namech, Namechl, "can't read module ID name from %s",
+ print_kptr(addr, (char *)NULL, 0));
+ return(1);
+ }
+ return(0);
+}
+
+
+/*
+ * readstmin() - read stream's module info
+ */
+
+int
+readstmin(addr, buf)
+ KA_T addr; /* module info address in kernel */
+ struct module_info *buf; /* receiving buffer address */
+{
+ if (!addr || kread(addr, (char *)buf, sizeof(struct module_info))) {
+ (void) snpf(Namech, Namechl, "can't read module info from %s",
+ print_kptr(addr, (char *)NULL, 0));
+ return(1);
+ }
+ return(0);
+}
+
+
+/*
+ * readstqinit() - read stream's queue information structure
+ */
+
+int
+readstqinit(addr, buf)
+ KA_T addr; /* queue info address in kernel */
+ struct qinit *buf; /* receiving buffer address */
+{
+ if (!addr || kread(addr, (char *)buf, sizeof(struct qinit))) {
+ (void) snpf(Namech, Namechl, "can't read queue info from %s",
+ print_kptr(addr, (char *)NULL, 0));
+ return(1);
+ }
+ return(0);
+}
+#endif /* HASSTREAMS */
+
+
+/*
+ * safepup() - safely print an unprintable character -- i.e., print it in a
+ * printable form
+ *
+ * return: char * to printable equivalent
+ * cl = strlen(printable equivalent)
+ */
+
+static char *
+safepup(c, cl)
+ unsigned int c; /* unprintable (i.e., !isprint())
+ * character */
+ int *cl; /* returned printable strlen -- NULL if
+ * no return needed */
+{
+ int len;
+ char *rp;
+ static char up[8];
+
+ if (c < 0x20) {
+ switch (c) {
+ case '\b':
+ rp = "\\b";
+ break;
+ case '\f':
+ rp = "\\f";
+ break;
+ case '\n':
+ rp = "\\n";
+ break;
+ case '\r':
+ rp = "\\r";
+ break;
+ case '\t':
+ rp = "\\t";
+ break;
+ default:
+ (void) snpf(up, sizeof(up), "^%c", c + 0x40);
+ rp = up;
+ }
+ len = 2;
+ } else if (c == 0xff) {
+ rp = "^?";
+ len = 2;
+ } else {
+ (void) snpf(up, sizeof(up), "\\x%02x", (int)(c & 0xff));
+ rp = up;
+ len = 4;
+ }
+ if (cl)
+ *cl = len;
+ return(rp);
+}
+
+
+/*
+ * safestrlen() - calculate a "safe" string length -- i.e., compute space for
+ * non-printable characters when printed in a printable form
+ */
+
+int
+safestrlen(sp, flags)
+ char *sp; /* string pointer */
+ int flags; /* flags:
+ * bit 0: 0 (0) = no NL
+ * 1 (1) = add trailing NL
+ * 1: 0 (0) = ' ' printable
+ * 1 (2) = ' ' not printable
+ */
+{
+ char c;
+ int len = 0;
+
+ c = (flags & 2) ? ' ' : '\0';
+ if (sp) {
+ for (; *sp; sp++) {
+ if (!isprint((unsigned char)*sp) || *sp == c) {
+ if (*sp < 0x20 || (unsigned char)*sp == 0xff)
+ len += 2; /* length of \. or ^. form */
+ else
+ len += 4; /* length of "\x%02x" printf */
+ } else
+ len++;
+ }
+ }
+ return(len);
+}
+
+
+/*
+ * safestrprt() - print a string "safely" to the indicated stream -- i.e.,
+ * print unprintable characters in a printable form
+ */
+
+void
+safestrprt(sp, fs, flags)
+ char *sp; /* string to print pointer pointer */
+ FILE *fs; /* destination stream -- e.g., stderr
+ * or stdout */
+ int flags; /* flags:
+ * bit 0: 0 (0) = no NL
+ * 1 (1) = add trailing NL
+ * 1: 0 (0) = ' ' printable
+ * 1 (2) = ' ' not printable
+ * 2: 0 (0) = print string as is
+ * 1 (4) = surround string
+ * with '"'
+ * 4: 0 (0) = print ending '\n'
+ * 1 (8) = don't print ending
+ * '\n'
+ */
+{
+ char c;
+ int lnc, lnt, sl;
+
+#if defined(HASWIDECHAR)
+ wchar_t w;
+ int wcmx = MB_CUR_MAX;
+#else /* !defined(HASWIDECHAR) */
+ static int wcmx = 1;
+#endif /* defined(HASWIDECHAR) */
+
+ c = (flags & 2) ? ' ' : '\0';
+ if (flags & 4)
+ putc('"', fs);
+ if (sp) {
+ for (sl = strlen(sp); *sp; sl -= lnc, sp += lnc) {
+
+#if defined(HASWIDECHAR)
+ if (wcmx > 1) {
+ lnc = mblen(sp, sl);
+ if (lnc > 1) {
+ if ((mbtowc(&w, sp, sl) == lnc) && iswprint(w)) {
+ for (lnt = 0; lnt < lnc; lnt++) {
+ putc((int)*(sp + lnt), fs);
+ }
+ } else {
+ for (lnt = 0; lnt < lnc; lnt++) {
+ fputs(safepup((unsigned int)*(sp + lnt),
+ (int *)NULL), fs);
+ }
+ }
+ continue;
+ } else
+ lnc = 1;
+ } else
+ lnc = 1;
+#else /* !defined(HASWIDECHAR) */
+ lnc = 1;
+#endif /* defined(HASWIDECHAR) */
+
+ if (isprint((unsigned char)*sp) && *sp != c)
+ putc((int)(*sp & 0xff), fs);
+ else {
+ if ((flags & 8) && (*sp == '\n') && !*(sp + 1))
+ break;
+ fputs(safepup((unsigned int)*sp, (int *)NULL), fs);
+ }
+ }
+ }
+ if (flags & 4)
+ putc('"', fs);
+ if (flags & 1)
+ putc('\n', fs);
+}
+
+
+/*
+ * safestrprtn() - print a specified number of characters from a string
+ * "safely" to the indicated stream
+ */
+
+void
+safestrprtn(sp, len, fs, flags)
+ char *sp; /* string to print pointer pointer */
+ int len; /* safe number of characters to
+ * print */
+ FILE *fs; /* destination stream -- e.g., stderr
+ * or stdout */
+ int flags; /* flags:
+ * bit 0: 0 (0) = no NL
+ * 1 (1) = add trailing NL
+ * 1: 0 (0) = ' ' printable
+ * 1 (2) = ' ' not printable
+ * 2: 0 (0) = print string as is
+ * 1 (4) = surround string
+ * with '"'
+ * 4: 0 (0) = print ending '\n'
+ * 1 (8) = don't print ending
+ * '\n'
+ */
+{
+ char c, *up;
+ int cl, i;
+
+ if (flags & 4)
+ putc('"', fs);
+ if (sp) {
+ c = (flags & 2) ? ' ' : '\0';
+ for (i = 0; i < len && *sp; sp++) {
+ if (isprint((unsigned char)*sp) && *sp != c) {
+ putc((int)(*sp & 0xff), fs);
+ i++;
+ } else {
+ if ((flags & 8) && (*sp == '\n') && !*(sp + 1))
+ break;
+ up = safepup((unsigned int)*sp, &cl);
+ if ((i + cl) > len)
+ break;
+ fputs(up, fs);
+ i += cl;
+ }
+ }
+ } else
+ i = 0;
+ for (; i < len; i++)
+ putc(' ', fs);
+ if (flags & 4)
+ putc('"', fs);
+ if (flags & 1)
+ putc('\n', fs);
+}
+
+
+/*
+ * statsafely() - stat path safely (i. e., with timeout)
+ */
+
+int
+statsafely(path, buf)
+ char *path; /* file path */
+ struct stat *buf; /* stat buffer address */
+{
+ if (Fblock) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: avoiding stat(%s): -b was specified.\n",
+ Pn, path);
+ errno = EWOULDBLOCK;
+ return(1);
+ }
+ return(doinchild(dostat, path, (char *)buf, sizeof(struct stat)));
+}
+
+
+/*
+ * stkdir() - stack directory name
+ */
+
+void
+stkdir(p)
+ char *p; /* directory path */
+{
+ MALLOC_S len;
+/*
+ * Provide adequate space for directory stack pointers.
+ */
+ if (Dstkx >= Dstkn) {
+ Dstkn += 128;
+ len = (MALLOC_S)(Dstkn * sizeof(char *));
+ if (!Dstk)
+ Dstk = (char **)malloc(len);
+ else
+ Dstk = (char **)realloc((MALLOC_P *)Dstk, len);
+ if (!Dstk) {
+ (void) fprintf(stderr,
+ "%s: no space for directory stack at: ", Pn);
+ safestrprt(p, stderr, 1);
+ Exit(1);
+ }
+ }
+/*
+ * Allocate space for the name, copy it there and put its pointer on the stack.
+ */
+ if (!(Dstk[Dstkx] = mkstrcpy(p, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr, "%s: no space for: ", Pn);
+ safestrprt(p, stderr, 1);
+ Exit(1);
+ }
+ Dstkx++;
+}
+
+
+/*
+ * x2dev() - convert hexadecimal ASCII string to device number
+ */
+
+char *
+x2dev(s, d)
+ char *s; /* ASCII string */
+ dev_t *d; /* device receptacle */
+{
+ char *cp, *cp1;
+ int n;
+ dev_t r;
+
+/*
+ * Skip an optional leading 0x. Count the number of hex digits up to the end
+ * of the string, or to a space, or to a comma. Return an error if an unknown
+ * character is encountered. If the count is larger than (2 * sizeof(dev_t))
+ * -- e.g., because of sign extension -- ignore excess leading hex 0xf digits,
+ * but return an error if an excess leading digit isn't 0xf.
+ */
+ if (strncasecmp(s, "0x", 2) == 0)
+ s += 2;
+ for (cp = s, n = 0; *cp; cp++, n++) {
+ if (isdigit((unsigned char)*cp))
+ continue;
+ if ((unsigned char)*cp >= 'a' && (unsigned char)*cp <= 'f')
+ continue;
+ if ((unsigned char)*cp >= 'A' && (unsigned char)*cp <= 'F')
+ continue;
+ if (*cp == ' ' || *cp == ',')
+ break;
+ return((char *)NULL);
+ }
+ if (!n)
+ return((char *)NULL);
+ if (n > (2 * (int)sizeof(dev_t))) {
+ cp1 = s;
+ s += (n - (2 * sizeof(dev_t)));
+ while (cp1 < s) {
+ if (*cp1 != 'f' && *cp1 != 'F')
+ return((char *)NULL);
+ cp1++;
+ }
+ }
+/*
+ * Assemble the validated hex digits of the device number, starting at a point
+ * in the string relevant to sizeof(dev_t).
+ */
+ for (r = 0; s < cp; s++) {
+ r = r << 4;
+ if (isdigit((unsigned char)*s))
+ r |= (unsigned char)(*s - '0') & 0xf;
+ else {
+ if (isupper((unsigned char)*s))
+ r |= ((unsigned char)(*s - 'A') + 10) & 0xf;
+ else
+ r |= ((unsigned char)(*s - 'a') + 10) & 0xf;
+ }
+ }
+ *d = r;
+ return(s);
+}