summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.skel62
-rw-r--r--lib/ckkv.c93
-rw-r--r--lib/cvfs.c110
-rw-r--r--lib/dvch.c1413
-rw-r--r--lib/fino.c148
-rw-r--r--lib/isfn.c418
-rw-r--r--lib/lkud.c207
-rw-r--r--lib/pdvn.c182
-rw-r--r--lib/prfp.c212
-rw-r--r--lib/ptti.c1370
-rw-r--r--lib/rdev.c524
-rw-r--r--lib/regex.c6328
-rw-r--r--lib/rmnt.c243
-rw-r--r--lib/rnam.c669
-rw-r--r--lib/rnch.c811
-rw-r--r--lib/rnmh.c743
-rw-r--r--lib/snpf.c749
17 files changed, 14282 insertions, 0 deletions
diff --git a/lib/Makefile.skel b/lib/Makefile.skel
new file mode 100644
index 0000000..5c2b022
--- /dev/null
+++ b/lib/Makefile.skel
@@ -0,0 +1,62 @@
+# Lsof library Makefile skeleton
+#
+# This skeleton is added to definitions established by Configure.
+#
+# $Id: Makefile.skel,v 1.13 2001/02/13 02:12:16 abe Exp $
+
+LIB= liblsof.a
+
+CDEF= ${RC_CFLAGS}
+CDEFS= ${CDEF} ${CFGF}
+INCL= ${DINC}
+
+HDR= ../lsof.h ../proto.h ../dlsof.h ../dproto.h ../machine.h
+
+SRC= ckkv.c cvfs.c dvch.c fino.c isfn.c lkud.c pdvn.c prfp.c \
+ ptti.c rdev.c regex.c rmnt.c rnam.c rnch.c rnmh.c snpf.c
+
+OBJ= ckkv.o cvfs.o dvch.o fino.o isfn.o lkud.o pdvn.o prfp.o \
+ ptti.o rdev.o regex.o rmnt.o rnam.o rnch.o rnmh.o snpf.o
+
+all: ${LIB}
+
+${LIB}: ${OBJ}
+ ${AR}
+ ${RANLIB}
+
+clean: FRC
+ rm -f ${LIB} ${OBJ} errs Makefile.bak a.out core
+
+FRC:
+
+ckkv.o: ${HDR} ckkv.c
+
+cvfs.o: ${HDR} cvfs.c
+
+dvch.o: ${HDR} dvch.c
+
+fino.o: ${HDR} fino.c
+
+isfn.o: ${HDR} isfn.c
+
+lkud.o: ${HDR} lkud.c
+
+pdvn.o: ${HDR} pdvn.c
+
+prfp.o: ${HDR} prfp.c
+
+ptti.o: ${HDR} ptti.c
+
+rdev.o: ${HDR} rdev.c
+
+regex.o: ${HDR} ../regex.h regex.c
+
+rmnt.o: ${HDR} rmnt.c
+
+rnam.o: ${HDR} rnam.c
+
+rnch.o: ${HDR} rnch.c
+
+rnmh.o: ${HDR} rnmh.c
+
+snpf.o: ${HDR} snpf.c
diff --git a/lib/ckkv.c b/lib/ckkv.c
new file mode 100644
index 0000000..876758a
--- /dev/null
+++ b/lib/ckkv.c
@@ -0,0 +1,93 @@
+/*
+ * cvfs.c -- ckkv() function for lsof library
+ */
+
+
+/*
+ * Copyright 1998 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_CKKV)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1998 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: ckkv.c,v 1.3 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+#include <sys/utsname.h>
+
+
+/*
+ * ckkv() - check kernel version
+ */
+
+void
+ckkv(d, er, ev, ea)
+ char *d; /* dialect */
+ char *er; /* expected revision; NULL, no test */
+ char *ev; /* expected version; NULL, no test */
+ char *ea; /* expected architecture; NULL, no
+ * test */
+{
+
+# if defined(HASKERNIDCK)
+ struct utsname u;
+
+ if (Fwarn)
+ return;
+/*
+ * Read the system information via uname(2).
+ */
+ if (uname(&u) < 0) {
+ (void) fprintf(stderr, "%s: uname error: %s\n",
+ Pn, strerror(errno));
+ Exit(1);
+ }
+ if (er && strcmp(er, u.release)) {
+ (void) fprintf(stderr,
+ "%s: WARNING: compiled for %s release %s; this is %s.\n",
+ Pn, d, er, u.release);
+ }
+ if (ev && strcmp(ev, u.version)) {
+ (void) fprintf(stderr,
+ "%s: WARNING: compiled for %s version %s; this is %s.\n",
+ Pn, d, ev, u.version);
+ }
+ if (ea && strcmp(ea, u.machine)) {
+ (void) fprintf(stderr,
+ "%s: WARNING: compiled for %s architecture %s; this is %s.\n",
+ Pn, d, ea, u.machine);
+ }
+# endif /* defined(HASKERNIDCK) */
+
+}
+#else /* !defined(USE_LIB_CKKV) */
+char ckkv_d1[] = "d"; char *ckkv_d2 = ckkv_d1;
+#endif /* defined(USE_LIB_CKKV) */
diff --git a/lib/cvfs.c b/lib/cvfs.c
new file mode 100644
index 0000000..067e53e
--- /dev/null
+++ b/lib/cvfs.c
@@ -0,0 +1,110 @@
+/*
+ * cvfs.c -- completevfs() function for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+/*
+ * The caller must define CVFS_DEVSAVE to have the device number moved
+ * from the mounts entry to the local vfs structure.
+ *
+ * The caller must define CVFS_NLKSAVE to have the link count moved from
+ * the mounts entry to the local vfs structure.
+ *
+ * The caller must define CVFS_SZSAVE to have the size moved from the
+ * mounts entry to the local vfs structure.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_COMPLETEVFS)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: cvfs.c,v 1.6 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+/*
+ * completevfs() - complete local vfs structure
+ */
+
+void
+completevfs(vfs, dev)
+ struct l_vfs *vfs; /* local vfs structure pointer */
+ dev_t *dev; /* device */
+{
+ struct mounts *mp;
+/*
+ * If only Internet socket files are selected, don't bother completing the
+ * local vfs structure.
+ */
+ if (Selinet)
+ return;
+/*
+ * Search for a match on device number.
+ */
+ for (mp = readmnt(); mp; mp = mp->next) {
+ if (mp->dev == *dev) {
+
+# if defined(CVFS_DEVSAVE)
+ vfs->dev = mp->dev;
+# endif /* defined(CVFS_DEVSAVE) */
+
+# if defined(CVFS_NLKSAVE)
+ vfs->nlink = mp->nlink;
+# endif /* defined(CVFS_NLKSAVE) */
+
+# if defined(CVFS_SZSAVE)
+ vfs->size = mp->size;
+# endif /* defined(CVFS_SZSAVE) */
+
+ vfs->dir = mp->dir;
+ vfs->fsname = mp->fsname;
+
+# if defined(HASFSINO)
+ vfs->fs_ino = mp->inode;
+# endif /* defined(HASFSINO) */
+
+# if defined(HASMNTSTAT)
+ vfs->mnt_stat = mp->stat;
+# endif /* defined(HASMNTSTAT) */
+
+
+ return;
+ }
+ }
+}
+#else /* !defined(USE_LIB_COMPLETEVFS) */
+char cvfs_d1[] = "d"; char *cvfs_d2 = cvfs_d1;
+#endif /* defined(USE_LIB_COMPLETEVFS) */
diff --git a/lib/dvch.c b/lib/dvch.c
new file mode 100644
index 0000000..ffdc4b4
--- /dev/null
+++ b/lib/dvch.c
@@ -0,0 +1,1413 @@
+/*
+ * dvch.c -- device cache functions for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(HASDCACHE)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: dvch.c,v 1.16 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+/*
+ * dvch.c - module that contains common device cache functions
+ *
+ * The caller may define the following:
+ *
+ * DCACHE_CLONE is the name of the function that reads and writes the
+ * clone section of the device cache file. The clone
+ * section follows the device section. If DCACHE_CLONE
+ * isn't defined, but HAS_STD_CLONE is defined to be 1,
+ * DCACHE_CLONE defaults to the local static function
+ * rw_clone_sect() that reads and writes a standard
+ * clone cache.
+ *
+ * DCACHE_CLR is the name of the function that clears the clone and
+ * pseudo caches when reading the device cache fails. If
+ * DCACHE_CLR isn't defined, but HAS_STD_CLONE is defined
+ * to be 1, DCACHE_CLR defaults to the local static
+ * function clr_sect() that clears a standard clone cache.
+ *
+ * DCACHE_PSEUDO is the name of the function that reads and writes
+ * the pseudo section of the device cache file. The
+ * pseudo section follows the device section and the
+ * clone section, if there is one.
+ *
+ * DVCH_CHOWN if the dialect has no fchown() function, so
+ * chown() must be used instead.
+ *
+ * DVCH_DEVPATH if the path to the device directory isn't "/dev".
+ *
+ * DVCH_EXPDEV if st_rdev must be expanded with the expdev()
+ * macro before use. (This is an EP/IX artifact.)
+ *
+ * HASBLKDEV if block device information is stored in BDevtp[].
+ */
+
+
+/*
+ * Local definitions
+ */
+
+# if !defined(DVCH_DEVPATH)
+#define DVCH_DEVPATH "/dev"
+# endif /* !defined(DVCH_DEVPATH) */
+
+/*
+ * Local storage
+ */
+
+static int crctbl[CRC_TBLL]; /* crc partial results table */
+
+
+/*
+ * Local function prototypes
+ */
+
+#undef DCACHE_CLR_LOCAL
+# if !defined(DCACHE_CLR)
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+#define DCACHE_CLR clr_sect
+#define DCACHE_CLR_LOCAL 1
+_PROTOTYPE(static void clr_sect,(void));
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+# endif /* !defined(DCACHE_CLR) */
+
+#undef DCACHE_CLONE_LOCAL
+# if !defined(DCACHE_CLONE)
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+#define DCACHE_CLONE rw_clone_sect
+#define DCACHE_CLONE_LOCAL 1
+_PROTOTYPE(static int rw_clone_sect,(int m));
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+# endif /*!defined(DCACHE_CLONE) */
+
+
+# if defined(HASBLKDEV)
+/*
+ * alloc_bdcache() - allocate block device cache
+ */
+
+void
+alloc_bdcache()
+{
+ if (!(BDevtp = (struct l_dev *)calloc((MALLOC_S)BNdev,
+ sizeof(struct l_dev))))
+ {
+ (void) fprintf(stderr, "%s: no space for block devices\n", Pn);
+ Exit(1);
+ }
+ if (!(BSdev = (struct l_dev **)malloc((MALLOC_S)(sizeof(struct l_dev *)
+ * BNdev))))
+ {
+ (void) fprintf(stderr, "%s: no space for block device pointers\n",
+ Pn);
+ Exit(1);
+ }
+}
+# endif /* defined(HASBLKDEV) */
+
+
+/*
+ * alloc_dcache() - allocate device cache
+ */
+
+void
+alloc_dcache()
+{
+ if (!(Devtp = (struct l_dev *)calloc((MALLOC_S)Ndev,
+ sizeof(struct l_dev))))
+ {
+ (void) fprintf(stderr, "%s: no space for devices\n", Pn);
+ Exit(1);
+ }
+ if (!(Sdev = (struct l_dev **)malloc((MALLOC_S)(sizeof(struct l_dev *)
+ * Ndev))))
+ {
+ (void) fprintf(stderr, "%s: no space for device pointers\n",
+ Pn);
+ Exit(1);
+ }
+}
+
+
+/*
+ * clr_devtab() - clear the device tables and free their space
+ */
+
+void
+clr_devtab()
+{
+ int i;
+
+ if (Devtp) {
+ for (i = 0; i < Ndev; i++) {
+ if (Devtp[i].name) {
+ (void) free((FREE_P *)Devtp[i].name);
+ Devtp[i].name = (char *)NULL;
+ }
+ }
+ (void) free((FREE_P *)Devtp);
+ Devtp = (struct l_dev *)NULL;
+ }
+ if (Sdev) {
+ (void) free((FREE_P *)Sdev);
+ Sdev = (struct l_dev **)NULL;
+ }
+ Ndev = 0;
+
+# if defined(HASBLKDEV)
+ if (BDevtp) {
+ for (i = 0; i < BNdev; i++) {
+ if (BDevtp[i].name) {
+ (void) free((FREE_P *)BDevtp[i].name);
+ BDevtp[i].name = (char *)NULL;
+ }
+ }
+ (void) free((FREE_P *)BDevtp);
+ BDevtp = (struct l_dev *)NULL;
+ }
+ if (BSdev) {
+ (void) free((FREE_P *)BSdev);
+ BSdev = (struct l_dev **)NULL;
+ }
+ BNdev = 0;
+# endif /* defined(HASBLKDEV) */
+
+}
+
+
+# if defined(DCACHE_CLR_LOCAL)
+/*
+ * clr_sect() - clear cached standard clone sections
+ */
+
+static void
+clr_sect()
+{
+ struct clone *c, *c1;
+
+ if (Clone) {
+ for (c = Clone; c; c = c1) {
+ c1 = c->next;
+ (void) free((FREE_P *)c);
+ }
+ Clone = (struct clone *)NULL;
+ }
+}
+# endif /* defined(DCACHE_CLR_LOCAL) */
+
+
+/*
+ * crc(b, l, s) - compute a crc for a block of bytes
+ */
+
+void
+crc(b, l, s)
+ char *b; /* block address */
+ int l; /* length */
+ unsigned *s; /* sum */
+{
+ char *cp; /* character pointer */
+ char *lm; /* character limit pointer */
+ unsigned sum; /* check sum */
+
+ cp = b;
+ lm = cp + l;
+ sum = *s;
+ do {
+ sum ^= ((int) *cp++) & 0xff;
+ sum = (sum >> 8) ^ crctbl[sum & 0xff];
+ } while (cp < lm);
+ *s = sum;
+}
+
+
+/*
+ * crcbld - build the CRC-16 partial results table
+ */
+
+void
+crcbld()
+{
+ int bit; /* temporary bit value */
+ unsigned entry; /* entry under construction */
+ int i; /* polynomial table index */
+ int j; /* bit shift count */
+
+ for(i = 0; i < CRC_TBLL; i++) {
+ entry = i;
+ for (j = 1; j <= CRC_BITS; j++) {
+ bit = entry & 1;
+ entry >>= 1;
+ if (bit)
+ entry ^= CRC_POLY;
+ }
+ crctbl[i] = entry;
+ }
+}
+
+
+/*
+ * dcpath() - define device cache file paths
+ */
+
+int
+dcpath(rw, npw)
+ int rw; /* read (1) or write (2) mode */
+ int npw; /* inhibit (0) or enable (1) no
+ * path warning message */
+{
+ char buf[MAXPATHLEN+1], *cp1, *cp2, hn[MAXPATHLEN+1];
+ int endf;
+ int i, j;
+ int l = 0;
+ int ierr = 0; /* intermediate error state */
+ int merr = 0; /* malloc error state */
+ struct passwd *p = (struct passwd *)NULL;
+ static short wenv = 1; /* HASENVDC warning state */
+ static short wpp = 1; /* HASPERSDCPATH warning state */
+/*
+ * Release any space reserved by previous path calls to dcpath().
+ */
+ if (DCpath[1]) {
+ (void) free((FREE_P *)DCpath[1]);
+ DCpath[1] = (char *)NULL;
+ }
+ if (DCpath[3]) {
+ (void) free((FREE_P *)DCpath[3]);
+ DCpath[3] = (char *)NULL;
+ }
+/*
+ * If a path was specified via -D, it's character address will have been
+ * stored in DCpathArg by ctrl_dcache(). Use that address if the real UID
+ * of this process is root, or the mode is read, or the process is neither
+ * setuid-root nor setgid.
+ */
+ if (Myuid == 0 || rw == 1 || (!Setuidroot && !Setgid))
+ DCpath[0] = DCpathArg;
+ else
+ DCpath[0] = (char *)NULL;
+
+# if defined(HASENVDC)
+/*
+ * If HASENVDC is defined, get its value from the environment, unless this
+ * is a setuid-root process, or the real UID of the process is 0, or the
+ * mode is write and the process is setgid.
+ */
+ if ((cp1 = getenv(HASENVDC)) && (l = strlen(cp1)) > 0
+ && !Setuidroot && Myuid && (rw == 1 || !Setgid)) {
+ if (!(cp2 = mkstrcpy(cp1, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: no space for device cache path: %s=", Pn, HASENVDC);
+ safestrprt(cp1, stderr, 1);
+ merr = 1;
+ } else
+ DCpath[1] = cp2;
+ } else if (cp1 && l > 0) {
+ if (!Fwarn && wenv) {
+ (void) fprintf(stderr,
+ "%s: WARNING: ignoring environment: %s=", Pn, HASENVDC);
+ safestrprt(cp1, stderr, 1);
+ }
+ wenv = 0;
+ }
+# endif /* defined(HASENVDC) */
+
+# if defined(HASSYSDC)
+/*
+ * If HASSYSDC is defined, record the path of the system-wide device
+ * cache file, unless the mode is write.
+ */
+ if (rw != 2)
+ DCpath[2] = HASSYSDC;
+ else
+ DCpath[2] = (char *)NULL;
+# endif /* defined(HASSYSDC) */
+
+# if defined(HASPERSDC)
+/*
+ * If HASPERSDC is defined, form a personal device cache path by
+ * interpreting the conversions specified in it.
+ *
+ * Get (HASPERSDCPATH) from the environment and add it to the home directory
+ * path, if possible.
+ */
+ for (cp1 = HASPERSDC, endf = i = 0; *cp1 && !endf; cp1++) {
+ if (*cp1 != '%') {
+
+ /*
+ * If the format character isn't a `%', copy it.
+ */
+ if (i < (int)sizeof(buf)) {
+ buf[i++] = *cp1;
+ continue;
+ } else {
+ ierr = 2;
+ break;
+ }
+ }
+ /*
+ * `%' starts a conversion; the next character specifies
+ * the conversion type.
+ */
+ cp1++;
+ switch (*cp1) {
+
+ /*
+ * Two consecutive `%' characters convert to one `%'
+ * character in the output.
+ */
+
+ case '%':
+ if (i < (int)sizeof(buf))
+ buf[i++] = '%';
+ else
+ ierr = 2;
+ break;
+
+ /*
+ * ``%0'' defines a root boundary. If the effective
+ * (setuid-root) or real UID of the process is root, any
+ * path formed to this point is discarded and path formation
+ * begins with the next character.
+ *
+ * If neither the effective nor the real UID is root, path
+ * formation ends.
+ *
+ * This allows HASPERSDC to specify one path for non-root
+ * UIDs and another for the root (effective or real) UID.
+ */
+
+ case '0':
+ if (Setuidroot || !Myuid)
+ i = 0;
+ else
+ endf = 1;
+ break;
+
+ /*
+ * ``%h'' converts to the home directory.
+ */
+
+ case 'h':
+ if (!p && !(p = getpwuid(Myuid))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't get home dir for UID: %d\n",
+ Pn, (int)Myuid);
+ ierr = 1;
+ break;
+ }
+ if ((i + (l = strlen(p->pw_dir))) >= (int)sizeof(buf)) {
+ ierr = 2;
+ break;
+ }
+ (void) strcpy(&buf[i], p->pw_dir);
+ i += l;
+ if (i > 0 && buf[i - 1] == '/' && *(cp1 + 1)) {
+
+ /*
+ * If the home directory ends in a '/' and the next format
+ * character is a '/', delete the '/' at the end of the home
+ * directory.
+ */
+ i--;
+ buf[i] = '\0';
+ }
+ break;
+
+ /*
+ * ``%l'' converts to the full host name.
+ *
+ * ``%L'' converts to the first component (characters up
+ * to the first `.') of the host name.
+ */
+
+ case 'l':
+ case 'L':
+ if (gethostname(hn, sizeof(hn) - 1) < 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: no gethostname for %%l or %%L: %s\n",
+ Pn, strerror(errno));
+ ierr = 1;
+ break;
+ }
+ hn[sizeof(hn) - 1] = '\0';
+ if (*cp1 == 'L' && (cp2 = strchr(hn, '.')) && cp2 > hn)
+ *cp2 = '\0';
+ j = strlen(hn);
+ if ((j + i) < (int)sizeof(buf)) {
+ (void) strcpy(&buf[i], hn);
+ i += j;
+ } else
+ ierr = 2;
+ break;
+
+ /*
+ * ``%p'' converts to the contents of LSOFPERSDCPATH, followed
+ * by a '/'.
+ *
+ * It is ignored when:
+ *
+ * The lsof process is setuid-root;
+ * The real UID of the lsof process is 0;
+ * The mode is write and the process is setgid.
+ */
+
+ case 'p':
+
+# if defined(HASPERSDCPATH)
+ if ((cp2 = getenv(HASPERSDCPATH))
+ && (l = strlen(cp2)) > 0
+ && !Setuidroot
+ && Myuid
+ && (rw == 1 || !Setgid))
+ {
+ if (i && buf[i - 1] == '/' && *cp2 == '/') {
+ cp2++;
+ l--;
+ }
+ if ((i + l) < ((int)sizeof(buf) - 1)) {
+ (void) strcpy(&buf[i], cp2);
+ i += l;
+ if (buf[i - 1] != '/') {
+ if (i < ((int)sizeof(buf) - 2)) {
+ buf[i++] = '/';
+ buf[i] = '\0';
+ } else
+ ierr = 2;
+ }
+ } else
+ ierr = 2;
+ } else {
+ if (cp2 && l > 0) {
+ if (!Fwarn && wpp) {
+ (void) fprintf(stderr,
+ "%s: WARNING: ignoring environment: %s",
+ Pn, HASPERSDCPATH);
+ safestrprt(cp2, stderr, 1);
+ }
+ wpp = 0;
+ }
+ }
+# else /* !defined(HASPERSDCPATH) */
+ if (!Fwarn && wpp)
+ (void) fprintf(stderr,
+ "%s: WARNING: HASPERSDCPATH disabled: %s\n",
+ Pn, HASPERSDC);
+ ierr = 1;
+ wpp = 0;
+# endif /* defined(HASPERSDCPATH) */
+
+ break;
+
+ /*
+ * ``%u'' converts to the login name of the real UID of the
+ * lsof process.
+ */
+
+ case 'u':
+ if (!p && !(p = getpwuid(Myuid))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't get login name for UID: %d\n",
+ Pn, (int)Myuid);
+ ierr = 1;
+ break;
+ }
+ if ((i + (l = strlen(p->pw_name))) >= (int)sizeof(buf)) {
+ ierr = 2;
+ break;
+ }
+ (void) strcpy(&buf[i], p->pw_name);
+ i += l;
+ break;
+
+ /*
+ * ``%U'' converts to the real UID of the lsof process.
+ */
+
+ case 'U':
+ (void) snpf(hn, sizeof(hn), "%d", (int)Myuid);
+ if ((i + (l = strlen(hn))) >= (int)sizeof(buf))
+ ierr = 2;
+ else {
+ (void) strcpy(&buf[i], hn);
+ i += l;
+ }
+ break;
+ default:
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: bad conversion (%%%c): %s\n",
+ Pn, *cp1, HASPERSDC);
+ ierr = 1;
+ }
+ if (endf || ierr > 1)
+ break;
+ }
+ if (ierr) {
+
+ /*
+ * If there was an intermediate error of some type, handle it.
+ * A type 1 intermediate error has already been noted with a
+ * warning message. A type 2 intermediate error requires the
+ * issuing of a buffer overlow warning message.
+ */
+ if (ierr == 2 && !Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: device cache path too large: %s\n",
+ Pn, HASPERSDC);
+ i = 0;
+ }
+ buf[i] = '\0';
+/*
+ * If there is one, allocate space for the personal device cache path,
+ * copy buf[] to it, and store its pointer in DCpath[3].
+ */
+ if (i) {
+ if (!(cp1 = mkstrcpy(buf, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: no space for device cache path: ", Pn);
+ safestrprt(buf, stderr, 1);
+ merr = 1;
+ } else
+ DCpath[3] = cp1;
+ }
+# endif /* defined(HASPERSDC) */
+
+/*
+ * Quit if there was a malloc() error. The appropriate error message
+ * will have been issued to stderr.
+ */
+ if (merr)
+ Exit(1);
+/*
+ * Return the index of the first defined path. Since DCpath[] is arranged
+ * in priority order, searching it beginning to end follows priority.
+ * Return an error indication if the search discloses no path name.
+ */
+ for (i = 0; i < MAXDCPATH; i++) {
+ if (DCpath[i])
+ return(i);
+ }
+ if (!Fwarn && npw)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't form any device cache path\n", Pn);
+ return(-1);
+}
+
+
+/*
+ * open_dcache() - open device cache file
+ */
+
+int
+open_dcache(m, r, s)
+ int m; /* mode: 1 = read; 2 = write */
+ int r; /* create DCpath[] if 0, reuse if 1 */
+ struct stat *s; /* stat() receiver */
+{
+ char buf[128];
+ char *w = (char *)NULL;
+/*
+ * Get the device cache file paths.
+ */
+ if (!r) {
+ if ((DCpathX = dcpath(m, 1)) < 0)
+ return(1);
+ }
+/*
+ * Switch to the requested open() action.
+ */
+ switch (m) {
+ case 1:
+
+ /*
+ * Check for access permission.
+ */
+ if (!is_readable(DCpath[DCpathX], 0)) {
+ if (DCpathX == 2 && errno == ENOENT)
+ return(2);
+ if (!Fwarn)
+ (void) fprintf(stderr, ACCESSERRFMT,
+ Pn, DCpath[DCpathX], strerror(errno));
+ return(1);
+ }
+ /*
+ * Open for reading.
+ */
+ if ((DCfd = open(DCpath[DCpathX], O_RDONLY, 0)) < 0) {
+ if (DCstate == 3 && errno == ENOENT)
+ return(1);
+
+cant_open:
+ (void) fprintf(stderr,
+ "%s: WARNING: can't open %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ return(1);
+ }
+ if (stat(DCpath[DCpathX], s) != 0) {
+
+cant_stat:
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't stat(%s): %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+close_exit:
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+ if ((int)(s->st_mode & 07777) != ((DCpathX == 2) ? 0644 : 0600)) {
+ (void) snpf(buf, sizeof(buf), "doesn't have %04o modes",
+ (DCpathX == 2) ? 0644 : 0600);
+ w = buf;
+ } else if ((s->st_mode & S_IFMT) != S_IFREG)
+ w = "isn't a regular file";
+ else if (!s->st_size)
+ w = "is empty";
+ if (w) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: %s %s.\n", Pn, DCpath[DCpathX], w);
+ goto close_exit;
+ }
+ return(0);
+ case 2:
+
+ /*
+ * Open for writing: first unlink any previous version; then
+ * open exclusively, specifying it's an error if the file exists.
+ */
+ if (unlink(DCpath[DCpathX]) < 0) {
+ if (errno != ENOENT) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't unlink %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ return(1);
+ }
+ }
+ if ((DCfd = open(DCpath[DCpathX], O_RDWR|O_CREAT|O_EXCL, 0600)) < 0)
+ goto cant_open;
+ /*
+ * If the real user is not root, but the process is setuid-root,
+ * change the ownerships of the file to the real ones.
+ */
+ if (Myuid && Setuidroot) {
+
+# if defined(DVCH_CHOWN)
+ if (chown(DCpath[DCpathX], Myuid, getgid()) < 0)
+# else /* !defined(DVCH_CHOWN) */
+ if (fchown(DCfd, Myuid, getgid()) < 0)
+# endif /* defined(DVCH_CHOWN) */
+
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't change ownerships of %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ }
+ }
+ if (!Fwarn && DCstate != 1 && !DCunsafe)
+ (void) fprintf(stderr,
+ "%s: WARNING: created device cache file: %s\n",
+ Pn, DCpath[DCpathX]);
+ if (stat(DCpath[DCpathX], s) != 0) {
+ (void) unlink(DCpath[DCpathX]);
+ goto cant_stat;
+ }
+ return(0);
+ default:
+
+ /*
+ * Oops!
+ */
+ (void) fprintf(stderr, "%s: internal error: open_dcache=%d\n",
+ Pn, m);
+ Exit(1);
+ }
+ return(1);
+}
+
+
+/*
+ * read_dcache() - read device cache file
+ */
+
+int
+read_dcache()
+{
+ char buf[MAXPATHLEN*2], cbuf[64], *cp;
+ int i, len, ov;
+ struct stat sb, devsb;
+/*
+ * Open the device cache file.
+ *
+ * If the open at HASSYSDC fails because the file doesn't exist, and
+ * the real UID of this process is not zero, try to open a device cache
+ * file at HASPERSDC.
+ */
+ if ((ov = open_dcache(1, 0, &sb)) != 0) {
+ if (DCpathX == 2) {
+ if (ov == 2 && DCpath[3]) {
+ DCpathX = 3;
+ if (open_dcache(1, 1, &sb) != 0)
+ return(1);
+ } else
+ return(1);
+ } else
+ return(1);
+ }
+/*
+ * If the open device cache file's last mtime/ctime isn't greater than
+ * DVCH_DEVPATH's mtime/ctime, ignore it, unless -Dr was specified.
+ */
+ if (stat(DVCH_DEVPATH, &devsb) != 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't stat(%s): %s\n",
+ Pn, DVCH_DEVPATH, strerror(errno));
+ } else {
+ if (sb.st_mtime <= devsb.st_mtime || sb.st_ctime <= devsb.st_ctime)
+ DCunsafe = 1;
+ }
+ if (!(DCfs = fdopen(DCfd, "r"))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't fdopen(%s)\n", Pn, DCpath[DCpathX]);
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+/*
+ * Read the section count line; initialize the CRC table;
+ * validate the section count line.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+
+cant_read:
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't fread %s: %s\n", Pn, DCpath[DCpathX],
+ strerror(errno));
+read_close:
+ (void) fclose(DCfs);
+ DCfd = -1;
+ DCfs = (FILE *)NULL;
+ (void) clr_devtab();
+
+# if defined(DCACHE_CLR)
+ (void) DCACHE_CLR();
+# endif /* defined(DCACHE_CLR) */
+
+ return(1);
+ }
+ (void) crcbld();
+ DCcksum = 0;
+ (void) crc(buf, strlen(buf), &DCcksum);
+ i = 1;
+ cp = "";
+
+# if defined(HASBLKDEV)
+ i++;
+ cp = "s";
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_PSEUDO) */
+
+ (void) snpf(cbuf, sizeof(cbuf), "%d section%s", i, cp);
+ len = strlen(cbuf);
+ (void) snpf(&cbuf[len], sizeof(cbuf) - len, ", dev=%lx\n",
+ (long)DevDev);
+ if (!strncmp(buf, cbuf, len) && (buf[len] == '\n')) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: no /dev device in %s: line ", Pn,
+ DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ if (strcmp(buf, cbuf)) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad section count line in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Read device section header and validate it.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs))
+ goto cant_read;
+ (void) crc(buf, strlen(buf), &DCcksum);
+ len = strlen("device section: ");
+ if (strncmp(buf, "device section: ", len) != 0) {
+
+read_dhdr:
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad device section header in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Compute the device count; allocate Sdev[] and Devtp[] space.
+ */
+ if ((Ndev = atoi(&buf[len])) < 1)
+ goto read_dhdr;
+ alloc_dcache();
+/*
+ * Read the device lines and store their information in Devtp[].
+ * Construct the Sdev[] pointers to Devtp[].
+ */
+ for (i = 0; i < Ndev; i++) {
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read device %d from %s\n",
+ Pn, i + 1, DCpath[DCpathX]);
+ goto read_close;
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ /*
+ * Convert hexadecimal device number.
+ */
+ if (!(cp = x2dev(buf, &Devtp[i].rdev)) || *cp != ' ') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: device %d: bad device in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ /*
+ * Convert inode number.
+ */
+ for (cp++, Devtp[i].inode = (INODETYPE)0; *cp != ' '; cp++) {
+ if (*cp < '0' || *cp > '9') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: device %d: bad inode # in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ Devtp[i].inode = (INODETYPE)((Devtp[i].inode * 10)
+ + (int)(*cp - '0'));
+ }
+ /*
+ * Get path name; allocate space for it; copy it; store the
+ * pointer in Devtp[]; clear verify status; construct the Sdev[]
+ * pointer to Devtp[].
+ */
+ if ((len = strlen(++cp)) < 2 || *(cp + len - 1) != '\n') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: device %d: bad path in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ *(cp + len - 1) = '\0';
+ if (!(Devtp[i].name = mkstrcpy(cp, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: device %d: no space for path: line ", Pn, i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ Exit(1);
+ }
+ Devtp[i].v = 0;
+ Sdev[i] = &Devtp[i];
+ }
+
+# if defined(HASBLKDEV)
+/*
+ * Read block device section header and validate it.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs))
+ goto cant_read;
+ (void) crc(buf, strlen(buf), &DCcksum);
+ len = strlen("block device section: ");
+ if (strncmp(buf, "block device section: ", len) != 0) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad block device section header in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Compute the block device count; allocate BSdev[] and BDevtp[] space.
+ */
+ if ((BNdev = atoi(&buf[len])) > 0) {
+ alloc_bdcache();
+ /*
+ * Read the block device lines and store their information in BDevtp[].
+ * Construct the BSdev[] pointers to BDevtp[].
+ */
+ for (i = 0; i < BNdev; i++) {
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read block device %d from %s\n",
+ Pn, i + 1, DCpath[DCpathX]);
+ goto read_close;
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ /*
+ * Convert hexadecimal device number.
+ */
+ if (!(cp = x2dev(buf, &BDevtp[i].rdev)) || *cp != ' ') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: block dev %d: bad device in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ /*
+ * Convert inode number.
+ */
+ for (cp++, BDevtp[i].inode = (INODETYPE)0; *cp != ' '; cp++) {
+ if (*cp < '0' || *cp > '9') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: block dev %d: bad inode # in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ BDevtp[i].inode = (INODETYPE)((BDevtp[i].inode * 10)
+ + (int)(*cp - '0'));
+ }
+ /*
+ * Get path name; allocate space for it; copy it; store the
+ * pointer in BDevtp[]; clear verify status; construct the BSdev[]
+ * pointer to BDevtp[].
+ */
+ if ((len = strlen(++cp)) < 2 || *(cp + len - 1) != '\n') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: block dev %d: bad path in %s: line",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ *(cp + len - 1) = '\0';
+ if (!(BDevtp[i].name = mkstrcpy(cp, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: block dev %d: no space for path: line", Pn, i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ Exit(1);
+ }
+ BDevtp[i].v = 0;
+ BSdev[i] = &BDevtp[i];
+ }
+ }
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+/*
+ * Read the clone section.
+ */
+ if (DCACHE_CLONE(1))
+ goto read_close;
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+/*
+ * Read the pseudo section.
+ */
+ if (DCACHE_PSEUDO(1))
+ goto read_close;
+# endif /* defined(DCACHE_PSEUDO) */
+
+/*
+ * Read and check the CRC section; it must be the last thing in the file.
+ */
+ (void) snpf(cbuf, sizeof(cbuf), "CRC section: %x\n", DCcksum);
+ if (!fgets(buf, sizeof(buf), DCfs) || strcmp(buf, cbuf) != 0) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad CRC section in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ if (fgets(buf, sizeof(buf), DCfs)) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: data follows CRC section in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Check one device entry at random -- the randomness based on our
+ * PID.
+ */
+ i = (int)(Mypid % Ndev);
+ if (stat(Devtp[i].name, &sb) != 0
+
+# if defined(DVCH_EXPDEV)
+ || expdev(sb.st_rdev) != Devtp[i].rdev
+# else /* !defined(DVCH_EXPDEV) */
+ || sb.st_rdev != Devtp[i].rdev
+# endif /* defined(DVCH_EXPDEV) */
+
+ || sb.st_ino != Devtp[i].inode) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: device cache mismatch: %s\n",
+ Pn, Devtp[i].name);
+ goto read_close;
+ }
+/*
+ * Close the device cache file and return OK.
+ */
+ (void) fclose(DCfs);
+ DCfd = -1;
+ DCfs = (FILE *)NULL;
+ return(0);
+}
+
+
+# if defined(DCACHE_CLONE_LOCAL)
+/*
+ * rw_clone_sect() - read/write the device cache file clone section
+ */
+
+static int
+rw_clone_sect(m)
+ int m; /* mode: 1 = read; 2 = write */
+{
+ char buf[MAXPATHLEN*2], *cp, *cp1;
+ struct clone *c;
+ struct l_dev *dp;
+ int i, j, len, n;
+
+ if (m == 1) {
+
+ /*
+ * Read the clone section header and validate it.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+
+bad_clone_sect:
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: bad clone section header in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ return(1);
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ len = strlen("clone section: ");
+ if (strncmp(buf, "clone section: ", len) != 0)
+ goto bad_clone_sect;
+ if ((n = atoi(&buf[len])) < 0)
+ goto bad_clone_sect;
+ /*
+ * Read the clone section lines and create the Clone list.
+ */
+ for (i = 0; i < n; i++) {
+ if (fgets(buf, sizeof(buf), DCfs) == NULL) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: no %d clone line in %s: line ", Pn, i + 1,
+ DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ return(1);
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ /*
+ * Assemble Devtp[] index and make sure it's correct.
+ */
+ for (cp = buf, j = 0; *cp != ' '; cp++) {
+ if (*cp < '0' || *cp > '9') {
+
+bad_clone_index:
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: clone %d: bad cached device index: line ",
+ Pn, i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ return(1);
+ }
+ j = (j * 10) + (int)(*cp - '0');
+ }
+ if (j < 0 || j >= Ndev || (cp1 = strchr(++cp, '\n')) == NULL)
+ goto bad_clone_index;
+ if (strncmp(cp, Devtp[j].name, (cp1 - cp)) != 0)
+ goto bad_clone_index;
+ /*
+ * Allocate and complete a clone structure.
+ */
+ if (!(c = (struct clone *)malloc(sizeof(struct clone)))) {
+ (void) fprintf(stderr,
+ "%s: clone %d: no space for cached clone: line ", Pn,
+ i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ Exit(1);
+ }
+ c->dx = j;
+ c->next = Clone;
+ Clone = c;
+ }
+ return(0);
+ } else if (m == 2) {
+
+ /*
+ * Write the clone section header.
+ */
+ for (c = Clone, n = 0; c; c = c->next, n++)
+ ;
+ (void) snpf(buf, sizeof(buf), "clone section: %d\n", n);
+ if (wr2DCfd(buf, &DCcksum))
+ return(1);
+ /*
+ * Write the clone section lines.
+ */
+ for (c = Clone; c; c = c->next) {
+ for (dp = &Devtp[c->dx], j = 0; j < Ndev; j++) {
+ if (dp == Sdev[j])
+ break;
+ }
+ if (j >= Ndev) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: can't make index for clone: ", Pn);
+ safestrprt(dp->name, stderr, 1);
+ }
+ (void) unlink(DCpath[DCpathX]);
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+ (void) snpf(buf, sizeof(buf), "%d %s\n", j, dp->name);
+ if (wr2DCfd(buf, &DCcksum))
+ return(1);
+ }
+ return(0);
+ }
+/*
+ * A shouldn't-happen case: mode neither 1 nor 2.
+ */
+ (void) fprintf(stderr, "%s: internal rw_clone_sect error: %d\n",
+ Pn, m);
+ Exit(1);
+ return(1); /* This useless return(1) keeps some
+ * compilers happy. */
+}
+# endif /* defined(DCACHE_CLONE_LOCAL) */
+
+
+/*
+ * write_dcache() - write device cache file
+ */
+
+void
+write_dcache()
+{
+ char buf[MAXPATHLEN*2], *cp;
+ struct l_dev *dp;
+ int i;
+ struct stat sb;
+/*
+ * Open the cache file; set up the CRC table; write the section count.
+ */
+ if (open_dcache(2, 0, &sb))
+ return;
+ i = 1;
+ cp = "";
+
+# if defined(HASBLKDEV)
+ i++;
+ cp = "s";
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_PSEUDO) */
+
+ (void) snpf(buf, sizeof(buf), "%d section%s, dev=%lx\n", i, cp,
+ (long)DevDev);
+ (void) crcbld();
+ DCcksum = 0;
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+/*
+ * Write the device section from the contents of Sdev[] and Devtp[].
+ */
+ (void) snpf(buf, sizeof(buf), "device section: %d\n", Ndev);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ for (i = 0; i < Ndev; i++) {
+ dp = Sdev[i];
+ (void) snpf(buf, sizeof(buf), "%lx %ld %s\n", (long)dp->rdev,
+ (long)dp->inode, dp->name);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ }
+
+# if defined(HASBLKDEV)
+/*
+ * Write the block device section from the contents of BSdev[] and BDevtp[].
+ */
+ (void) snpf(buf, sizeof(buf), "block device section: %d\n", BNdev);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ if (BNdev) {
+ for (i = 0; i < BNdev; i++) {
+ dp = BSdev[i];
+ (void) snpf(buf, sizeof(buf), "%lx %ld %s\n", (long)dp->rdev,
+ (long)dp->inode, dp->name);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ }
+ }
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+/*
+ * Write the clone section.
+ */
+ if (DCACHE_CLONE(2))
+ return;
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+/*
+ * Write the pseudo section.
+ */
+ if (DCACHE_PSEUDO(2))
+ return;
+# endif /* defined(DCACHE_PSEUDO) */
+
+/*
+ * Write the CRC section and close the file.
+ */
+ (void) snpf(buf, sizeof(buf), "CRC section: %x\n", DCcksum);
+ if (wr2DCfd(buf, (unsigned *)NULL))
+ return;
+ if (close(DCfd) != 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't close %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ (void) unlink(DCpath[DCpathX]);
+ DCfd = -1;
+ }
+ DCfd = -1;
+/*
+ * If the previous reading of the previous device cache file marked it
+ * "unsafe," drop that marking and record that the device cache file was
+ * rebuilt.
+ */
+ if (DCunsafe) {
+ DCunsafe = 0;
+ DCrebuilt = 1;
+ }
+}
+
+
+/*
+ * wr2DCfd() - write to the DCfd file descriptor
+ */
+
+int
+wr2DCfd(b, c)
+ char *b; /* buffer */
+ unsigned *c; /* checksum receiver */
+{
+ int bl, bw;
+
+ bl = strlen(b);
+ if (c)
+ (void) crc(b, bl, c);
+ while (bl > 0) {
+ if ((bw = write(DCfd, b, bl)) < 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't write to %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ (void) unlink(DCpath[DCpathX]);
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+ b += bw;
+ bl -= bw;
+ }
+ return(0);
+}
+#else /* !defined(HASDCACHE) */
+char dvch_d1[] = "d"; char *dvch_d2 = dvch_d1;
+#endif /* defined(HASDCACHE) */
diff --git a/lib/fino.c b/lib/fino.c
new file mode 100644
index 0000000..6beff86
--- /dev/null
+++ b/lib/fino.c
@@ -0,0 +1,148 @@
+/*
+ * fino.c -- find inode functions for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+/*
+ * fino.c -- find block (optional) and character device file inode numbers
+ *
+ * The caller must define:
+ *
+ * HASBLKDEV to activate the block device inode lookup
+ */
+
+
+#include "../machine.h"
+
+#if defined(HASBLKDEV) || defined(USE_LIB_FIND_CH_INO)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: fino.c,v 1.5 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+#else /* !defined(HASBLKDEV) && !defined(USE_LIB_FIND_CH_INO) */
+char fino_d1[] = "d"; char *fino_d2 = fino_d1;
+#endif /* defined(HASBLKDEV) || defined(USE_LIB_FIND_CH_INO) */
+
+
+#if defined(HASBLKDEV)
+/*
+ * find_bl_ino() - find the inode number for a block device file
+ */
+
+void
+find_bl_ino()
+{
+ dev_t ldev, tdev;
+ int low, hi, mid;
+
+ readdev(0);
+
+# if defined(HASDCACHE)
+find_bl_ino_again:
+# endif /* defined(HASDCACHE) */
+
+ low = mid = 0;
+ hi = BNdev - 1;
+ if (!Lf->dev_def || (Lf->dev != DevDev) || !Lf->rdev_def)
+ return;
+ ldev = Lf->rdev;
+ while (low <= hi) {
+ mid = (low + hi) / 2;
+ tdev = BSdev[mid]->rdev;
+ if (ldev < tdev)
+ hi = mid - 1;
+ else if (ldev > tdev)
+ low = mid + 1;
+ else {
+
+# if defined(HASDCACHE)
+ if (DCunsafe && !BSdev[mid]->v && !vfy_dev(BSdev[mid]))
+ goto find_bl_ino_again;
+# endif /* defined(HASDCACHE) */
+
+ Lf->inode = BSdev[mid]->inode;
+ if (Lf->inp_ty == 0)
+ Lf->inp_ty = 1;
+ return;
+ }
+ }
+}
+#endif /* defined(HASBLKDEV) */
+
+
+#if defined(USE_LIB_FIND_CH_INO)
+/*
+ * find_ch_ino() - find the inode number for a character device file
+ */
+
+void
+find_ch_ino()
+{
+ dev_t ldev, tdev;
+ int low, hi, mid;
+
+ readdev(0);
+
+# if defined(HASDCACHE)
+find_ch_ino_again:
+# endif /* defined(HASDCACHE) */
+
+ low = mid = 0;
+ hi = Ndev - 1;
+ if (!Lf->dev_def || (Lf->dev != DevDev) || !Lf->rdev_def)
+ return;
+ ldev = Lf->rdev;
+ while (low <= hi) {
+ mid = (low + hi) / 2;
+ tdev = Sdev[mid]->rdev;
+ if (ldev < tdev)
+ hi = mid - 1;
+ else if (ldev > tdev)
+ low = mid + 1;
+ else {
+
+# if defined(HASDCACHE)
+ if (DCunsafe && !Sdev[mid]->v && !vfy_dev(Sdev[mid]))
+ goto find_ch_ino_again;
+# endif /* defined(HASDCACHE) */
+
+ Lf->inode = Sdev[mid]->inode;
+ if (Lf->inp_ty == 0)
+ Lf->inp_ty = 1;
+ return;
+ }
+ }
+}
+#endif /* defined(USE_LIB_FIND_CH_INO) */
diff --git a/lib/isfn.c b/lib/isfn.c
new file mode 100644
index 0000000..b23846d
--- /dev/null
+++ b/lib/isfn.c
@@ -0,0 +1,418 @@
+/*
+ * isfn.c -- is_file_named() function for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+/*
+ * To use this source file:
+ *
+ * 1. Define USE_LIB_IS_FILE_NAMED.
+ *
+ * 2. If clone support is required:
+ *
+ * a. Define HAVECLONEMAJ to be the name of the variable that
+ * contains the status of the clone major device -- e.g.,
+ *
+ * #define HAVECLONEMAJ HaveCloneMaj
+ *
+ * b. Define CLONEMAJ to be the name of the constant or
+ * variable that defines the clone major device -- e.g.,
+ *
+ * #define CLONEMAJ CloneMaj
+ *
+ * c. Make sure that clone devices are identified by an lfile
+ * element is_stream value of 1.
+ *
+ * d. Accept clone searching by device number only.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_IS_FILE_NAMED)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: isfn.c,v 1.10 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+/*
+ * Local structures
+ */
+
+struct hsfile {
+ struct sfile *s; /* the Sfile table address */
+ struct hsfile *next; /* the next hash bucket entry */
+};
+
+/*
+ * Local static variables
+ */
+
+# if defined(HAVECLONEMAJ)
+static struct hsfile *HbyCd = /* hash by clone buckets */
+ (struct hsfile *)NULL;
+static int HbyCdCt = 0; /* HbyCd entry count */
+# endif /* defined(HAVECLONEMAJ) */
+
+static struct hsfile *HbyFdi = /* hash by file (dev,ino) buckets */
+ (struct hsfile *)NULL;
+static int HbyFdiCt = 0; /* HbyFdi entry count */
+static struct hsfile *HbyFrd = /* hash by file raw device buckets */
+ (struct hsfile *)NULL;
+static int HbyFrdCt = 0; /* HbyFrd entry count */
+static struct hsfile *HbyFsd = /* hash by file system buckets */
+ (struct hsfile *)NULL;
+static int HbyFsdCt = 0; /* HbyFsd entry count */
+static struct hsfile *HbyNm = /* hash by name buckets */
+ (struct hsfile *)NULL;
+static int HbyNmCt = 0; /* HbyNm entry count */
+
+
+/*
+ * Local definitions
+ */
+
+# if defined(HAVECLONEMAJ)
+#define SFCDHASH 1024 /* Sfile hash by clone device (power
+ * of 2!) */
+# endif /* defined(HAVECLONEMAJ) */
+
+#define SFDIHASH 4094 /* Sfile hash by (device,inode) number
+ * pair bucket count (power of 2!) */
+#define SFFSHASH 1024 /* Sfile hash by file system device
+ * number bucket count (power of 2!) */
+#define SFHASHDEVINO(maj, min, ino, mod) ((int)(((int)((((int)(maj+1))*((int)((min+1))))+ino)*31415)&(mod-1)))
+ /* hash for Sfile by major device,
+ * minor device, and inode, modulo mod
+ * (mod must be a power of 2) */
+#define SFRDHASH 1024 /* Sfile hash by raw device number
+ * bucket count (power of 2!) */
+#define SFHASHRDEVI(maj, min, rmaj, rmin, ino, mod) ((int)(((int)((((int)(maj+1))*((int)((min+1))))+((int)(rmaj+1)*(int)(rmin+1))+ino)*31415)&(mod-1)))
+ /* hash for Sfile by major device,
+ * minor device, major raw device,
+ * minor raw device, and inode, modulo
+ * mod (mod must be a power of 2) */
+#define SFNMHASH 4096 /* Sfile hash by name bucket count
+ * (must be a power of 2!) */
+
+
+
+/*
+ * hashSfile() - hash Sfile entries for use in is_file_named() searches
+ */
+
+void
+hashSfile()
+{
+ static int hs = 0;
+ int i;
+ int sfplm = 3;
+ struct sfile *s;
+ struct hsfile *sh, *sn;
+/*
+ * Do nothing if there are no file search arguments cached or if the
+ * hashes have already been constructed.
+ */
+ if (!Sfile || hs)
+ return;
+/*
+ * Allocate hash buckets by (device,inode), file system device, and file name.
+ */
+
+# if defined(HAVECLONEMAJ)
+ if (HAVECLONEMAJ) {
+ if (!(HbyCd = (struct hsfile *)calloc((MALLOC_S)SFCDHASH,
+ sizeof(struct hsfile))))
+ {
+ (void) fprintf(stderr,
+ "%s: can't allocate space for %d clone hash buckets\n",
+ Pn, SFCDHASH);
+ Exit(1);
+ }
+ sfplm++;
+ }
+# endif /* defined(HAVECLONEMAJ) */
+
+ if (!(HbyFdi = (struct hsfile *)calloc((MALLOC_S)SFDIHASH,
+ sizeof(struct hsfile))))
+ {
+ (void) fprintf(stderr,
+ "%s: can't allocate space for %d (dev,ino) hash buckets\n",
+ Pn, SFDIHASH);
+ Exit(1);
+ }
+ if (!(HbyFrd = (struct hsfile *)calloc((MALLOC_S)SFRDHASH,
+ sizeof(struct hsfile))))
+ {
+ (void) fprintf(stderr,
+ "%s: can't allocate space for %d rdev hash buckets\n",
+ Pn, SFRDHASH);
+ Exit(1);
+ }
+ if (!(HbyFsd = (struct hsfile *)calloc((MALLOC_S)SFFSHASH,
+ sizeof(struct hsfile))))
+ {
+ (void) fprintf(stderr,
+ "%s: can't allocate space for %d file sys hash buckets\n",
+ Pn, SFFSHASH);
+ Exit(1);
+ }
+ if (!(HbyNm = (struct hsfile *)calloc((MALLOC_S)SFNMHASH,
+ sizeof(struct hsfile))))
+ {
+ (void) fprintf(stderr,
+ "%s: can't allocate space for %d name hash buckets\n",
+ Pn, SFNMHASH);
+ Exit(1);
+ }
+ hs++;
+/*
+ * Scan the Sfile chain, building file, file system, raw device, and file
+ * name hash bucket chains.
+ */
+ for (s = Sfile; s; s = s->next) {
+ for (i = 0; i < sfplm; i++) {
+ if (i == 0) {
+ if (!s->aname)
+ continue;
+ sh = &HbyNm[hashbyname(s->aname, SFNMHASH)];
+ HbyNmCt++;
+ } else if (i == 1) {
+ if (s->type) {
+ sh = &HbyFdi[SFHASHDEVINO(GET_MAJ_DEV(s->dev),
+ GET_MIN_DEV(s->dev), s->i,
+ SFDIHASH)];
+ HbyFdiCt++;
+ } else {
+ sh = &HbyFsd[SFHASHDEVINO(GET_MAJ_DEV(s->dev),
+ GET_MIN_DEV(s->dev),
+ 0,
+ SFFSHASH)];
+ HbyFsdCt++;
+ }
+ } else if (i == 2) {
+ if ((s->mode == S_IFCHR) || (s->mode == S_IFBLK)) {
+ sh = &HbyFrd[SFHASHRDEVI(GET_MAJ_DEV(s->dev),
+ GET_MIN_DEV(s->dev),
+ GET_MAJ_DEV(s->rdev),
+ GET_MIN_DEV(s->rdev),
+ s->i,
+ SFRDHASH)];
+ HbyFrdCt++;
+ } else
+ continue;
+ }
+
+# if defined(HAVECLONEMAJ)
+ else {
+ if (!HAVECLONEMAJ || (GET_MAJ_DEV(s->rdev) != CLONEMAJ))
+ continue;
+ sh = &HbyCd[SFHASHDEVINO(0, GET_MIN_DEV(s->rdev), 0,
+ SFCDHASH)];
+ HbyCdCt++;
+ }
+# else /* ! defined(HAVECLONEMAJ) */
+ else
+ continue;
+# endif /* defined(HAVECLONEMAJ) */
+
+ if (!sh->s) {
+ sh->s = s;
+ sh->next = (struct hsfile *)NULL;
+ continue;
+ } else {
+ if (!(sn = (struct hsfile *)malloc(
+ (MALLOC_S)sizeof(struct hsfile))))
+ {
+ (void) fprintf(stderr,
+ "%s: can't allocate hsfile bucket for: %s\n",
+ Pn, s->aname);
+ Exit(1);
+ }
+ sn->s = s;
+ sn->next = sh->next;
+ sh->next = sn;
+ }
+ }
+ }
+}
+
+
+/*
+ * is_file_named() - is this file named?
+ */
+
+int
+is_file_named(p, cd)
+ char *p; /* path name; NULL = search by device
+ * and inode (from *Lf) */
+ int cd; /* character or block type file --
+ * VCHR or VBLK vnode, or S_IFCHR
+ * or S_IFBLK inode */
+{
+ char *ep;
+ int f = 0;
+ struct sfile *s = (struct sfile *)NULL;
+ struct hsfile *sh;
+ size_t sz;
+/*
+ * Check for a path name match, as requested.
+ */
+ if (p && HbyNmCt) {
+ for (sh = &HbyNm[hashbyname(p, SFNMHASH)]; sh; sh = sh->next) {
+ if ((s = sh->s) && strcmp(p, s->aname) == 0) {
+ f = 2;
+ break;
+ }
+ }
+ }
+
+# if defined(HAVECLONEMAJ)
+/*
+ * If this is a stream, check for a clone device match.
+ */
+ if (!f && HbyCdCt && Lf->is_stream && Lf->dev_def && Lf->rdev_def
+ && (Lf->dev == DevDev))
+ {
+ for (sh = &HbyCd[SFHASHDEVINO(0, GET_MAJ_DEV(Lf->rdev), 0,
+ SFCDHASH)];
+ sh;
+ sh = sh->next)
+ {
+ if ((s = sh->s) && (GET_MAJ_DEV(Lf->rdev)
+ == GET_MIN_DEV(s->rdev))) {
+ f = 3;
+ break;
+ }
+ }
+ }
+# endif /* defined(HAVECLONEMAJ) */
+
+/*
+ * Check for a regular file.
+ */
+ if (!f && HbyFdiCt && Lf->dev_def
+ && (Lf->inp_ty == 1 || Lf->inp_ty == 3))
+ {
+ for (sh = &HbyFdi[SFHASHDEVINO(GET_MAJ_DEV(Lf->dev),
+ GET_MIN_DEV(Lf->dev),
+ Lf->inode,
+ SFDIHASH)];
+ sh;
+ sh = sh->next)
+ {
+ if ((s = sh->s) && (Lf->dev == s->dev)
+ && (Lf->inode == s->i)) {
+ f = 1;
+ break;
+ }
+ }
+ }
+/*
+ * Check for a file system match.
+ */
+ if (!f && HbyFsdCt && Lf->dev_def) {
+ for (sh = &HbyFsd[SFHASHDEVINO(GET_MAJ_DEV(Lf->dev),
+ GET_MIN_DEV(Lf->dev), 0,
+ SFFSHASH)];
+ sh;
+ sh = sh->next)
+ {
+ if ((s = sh->s) && (s->dev == Lf->dev)) {
+ f = 1;
+ break;
+ }
+ }
+ }
+/*
+ * Check for a character or block device match.
+ */
+ if (!f && HbyFrdCt && cd
+ && Lf->dev_def && (Lf->dev == DevDev)
+ && Lf->rdev_def
+ && (Lf->inp_ty == 1 || Lf->inp_ty == 3))
+ {
+ for (sh = &HbyFrd[SFHASHRDEVI(GET_MAJ_DEV(Lf->dev),
+ GET_MIN_DEV(Lf->dev),
+ GET_MAJ_DEV(Lf->rdev),
+ GET_MIN_DEV(Lf->rdev),
+ Lf->inode, SFRDHASH)];
+ sh;
+ sh = sh->next)
+ {
+ if ((s = sh->s) && (s->dev == Lf->dev)
+ && (s->rdev == Lf->rdev) && (s->i == Lf->inode))
+ {
+ f = 1;
+ break;
+ }
+ }
+ }
+/*
+ * Convert the name if a match occurred.
+ */
+ switch (f) {
+ case 0:
+ return(0);
+ case 1:
+ if (s->type) {
+
+ /*
+ * If the search argument isn't a file system, propagate it
+ * to Namech[]; otherwise, let printname() compose the name.
+ */
+ (void) snpf(Namech, Namechl, "%s", s->name);
+ if (s->devnm) {
+ ep = endnm(&sz);
+ (void) snpf(ep, sz, " (%s)", s->devnm);
+ }
+ }
+ break;
+ case 2:
+ (void) strcpy(Namech, p);
+ break;
+
+# if defined(HAVECLONEMAJ)
+ /* case 3: do nothing for stream clone matches */
+# endif /* defined(HAVECLONEMAJ) */
+
+ }
+ if (s)
+ s->f = 1;
+ return(1);
+}
+#else /* !defined(USE_LIB_IS_FILE_NAMED) */
+char isfn_d1[] = "d"; char *isfn_d2 = isfn_d1;
+#endif /* defined(USE_LIB_IS_FILE_NAMED) */
diff --git a/lib/lkud.c b/lib/lkud.c
new file mode 100644
index 0000000..268af4f
--- /dev/null
+++ b/lib/lkud.c
@@ -0,0 +1,207 @@
+/*
+ * lkud.c -- device lookup functions for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+/*
+ * lkud.c -- lookup device
+ *
+ * The caller may define:
+ *
+ * HASBLKDEV to activate block device lookup
+ */
+
+
+#include "../machine.h"
+
+#if defined(HASBLKDEV) || defined(USE_LIB_LKUPDEV)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: lkud.c,v 1.7 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+#else /* !defined(HASBLKDEV) && !defined(USE_LIB_LKUPDEV) */
+char lkud_d1[] = "d"; char *lkud_d2 = lkud_d1;
+#endif /* defined(HASBLKDEV) || defined(USE_LIB_LKUPDEV) */
+
+
+
+#if defined(HASBLKDEV)
+/*
+ * lkupbdev() - look up a block device
+ */
+
+struct l_dev *
+lkupbdev(dev, rdev, i, r)
+ dev_t *dev; /* pointer to device number */
+ dev_t *rdev; /* pointer to raw device number */
+ int i; /* inode match status */
+ int r; /* if 1, rebuild the device cache with
+ * rereaddev() when no match is found
+ * and HASDCACHE is defined and
+ * DCunsafe is one */
+{
+ INODETYPE inode = (INODETYPE)0;
+ int low, hi, mid;
+ struct l_dev *dp;
+ int ty = 0;
+
+ if (*dev != DevDev)
+ return((struct l_dev *)NULL);
+ readdev(0);
+ if (i) {
+ inode = Lf->inode;
+ ty = Lf->inp_ty;
+ }
+/*
+ * Search block device table for match.
+ */
+
+# if defined(HASDCACHE)
+
+lkupbdev_again:
+
+# endif /* defined(HASDCACHE) */
+
+ low = mid = 0;
+ hi = BNdev - 1;
+ while (low <= hi) {
+ mid = (low + hi) / 2;
+ dp = BSdev[mid];
+ if (*rdev < dp->rdev)
+ hi = mid - 1;
+ else if (*rdev > dp->rdev)
+ low = mid + 1;
+ else {
+ if ((i == 0) || (ty != 1) || (inode == dp->inode)) {
+
+# if defined(HASDCACHE)
+ if (DCunsafe && !dp->v && !vfy_dev(dp))
+ goto lkupbdev_again;
+# endif /* defined(HASDCACHE) */
+
+ return(dp);
+ }
+ if (inode < dp->inode)
+ hi = mid - 1;
+ else
+ low = mid + 1;
+ }
+ }
+
+# if defined(HASDCACHE)
+ if (DCunsafe && r) {
+ (void) rereaddev();
+ goto lkupbdev_again;
+ }
+# endif /* defined(HASDCACHE) */
+
+ return((struct l_dev *)NULL);
+}
+#endif /* defined(HASBLKDEV) */
+
+
+#if defined(USE_LIB_LKUPDEV)
+/*
+ * lkupdev() - look up a character device
+ */
+
+struct l_dev *
+lkupdev(dev, rdev, i, r)
+ dev_t *dev; /* pointer to device number */
+ dev_t *rdev; /* pointer to raw device number */
+ int i; /* inode match status */
+ int r; /* if 1, rebuild the device cache with
+ * rereaddev() when no match is found
+ * and HASDCACHE is defined and
+ * DCunsafe is one */
+{
+ INODETYPE inode = (INODETYPE)0;
+ int low, hi, mid;
+ struct l_dev *dp;
+ int ty = 0;
+
+ if (*dev != DevDev)
+ return((struct l_dev *)NULL);
+ readdev(0);
+ if (i) {
+ inode = Lf->inode;
+ ty = Lf->inp_ty;
+ }
+/*
+ * Search device table for match.
+ */
+
+# if defined(HASDCACHE)
+
+lkupdev_again:
+
+# endif /* defined(HASDCACHE) */
+
+ low = mid = 0;
+ hi = Ndev - 1;
+ while (low <= hi) {
+ mid = (low + hi) / 2;
+ dp = Sdev[mid];
+ if (*rdev < dp->rdev)
+ hi = mid - 1;
+ else if (*rdev > dp->rdev)
+ low = mid + 1;
+ else {
+ if ((i == 0) || (ty != 1) || (inode == dp->inode)) {
+
+# if defined(HASDCACHE)
+ if (DCunsafe && !dp->v && !vfy_dev(dp))
+ goto lkupdev_again;
+# endif /* defined(HASDCACHE) */
+
+ return(dp);
+ }
+ if (inode < dp->inode)
+ hi = mid - 1;
+ else
+ low = mid + 1;
+ }
+ }
+
+# if defined(HASDCACHE)
+ if (DCunsafe && r) {
+ (void) rereaddev();
+ goto lkupdev_again;
+ }
+# endif /* defined(HASDCACHE) */
+
+ return((struct l_dev *)NULL);
+}
+#endif /* defined(USE_LIB_LKUPDEV) */
diff --git a/lib/pdvn.c b/lib/pdvn.c
new file mode 100644
index 0000000..ccd5c73
--- /dev/null
+++ b/lib/pdvn.c
@@ -0,0 +1,182 @@
+/*
+ * pdvn.c -- print device name functions for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_PRINTDEVNAME)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: pdvn.c,v 1.8 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+#else /* !defined(USE_LIB_PRINTDEVNAME) */
+char pdvn_d1[] = "d"; char *pdvn_d2 = pdvn_d1;
+#endif /* defined(USE_LIB_PRINTDEVNAME) */
+
+
+/*
+ * To use this source file:
+ *
+ * 1. Define USE_LIB_PRINTDEVNAME, or both.
+ *
+ * 2. Define HAS_STD_CLONE to enable standard clone searches in
+ * printdevname().
+ *
+ * 3. Define HASBLDKDEV to enable block device processing.
+ */
+
+
+/*
+ * Local definitions
+ */
+
+#define LIKE_BLK_SPEC "like block special"
+#define LIKE_CHR_SPEC "like character special"
+
+
+# if defined(USE_LIB_PRINTDEVNAME)
+/*
+ * printdevname() - print block or character device name
+ */
+
+int
+printdevname(dev, rdev, f, nty)
+ dev_t *dev; /* device */
+ dev_t *rdev; /* raw device */
+ int f; /* 1 = print trailing '\n' */
+ int nty; /* node type: N_BLK or N_CHR */
+{
+
+# if defined(HAS_STD_CLONE)
+ struct clone *c;
+# endif /* defined(HAS_STD_CLONE) */
+
+ struct l_dev *dp;
+ int r = 1;
+
+# if defined(HASDCACHE)
+
+printdevname_again:
+
+# endif /* defined(HASDCACHE) */
+
+# if defined(HAS_STD_CLONE)
+/*
+ * Search for clone if this is a character device on the same device as
+ * /dev (or /devices).
+ */
+ if ((nty == N_CHR) && Lf->is_stream && Clone && (*dev == DevDev)) {
+ r = 0; /* Don't let lkupdev() rebuild the device cache,
+ * because when it has been rebuilt we want to
+ * search again for clones. */
+ readdev(0);
+ for (c = Clone; c; c = c->next) {
+ if (GET_MAJ_DEV(*rdev) == GET_MIN_DEV(Devtp[c->dx].rdev)) {
+
+# if defined(HASDCACHE)
+ if (DCunsafe && !Devtp[c->dx].v && !vfy_dev(&Devtp[c->dx]))
+ goto printdevname_again;
+# endif /* defined(HASDCACHE) */
+
+ safestrprt(Devtp[c->dx].name, stdout, f);
+ return(1);
+ }
+ }
+ }
+# endif /* defined(HAS_STD_CLONE) */
+
+/*
+ * Search appropriate device table for a full match.
+ */
+
+# if defined(HASBLKDEV)
+ if (nty == N_BLK)
+ dp = lkupbdev(dev, rdev, 1, r);
+ else
+# endif /* defined(HASBLKDEV) */
+
+ dp = lkupdev(dev, rdev, 1, r);
+ if (dp) {
+ safestrprt(dp->name, stdout, f);
+ return(1);
+ }
+/*
+ * Search device table for a match without inode number and dev.
+ */
+
+# if defined(HASBLKDEV)
+ if (nty == N_BLK)
+ dp = lkupbdev(&DevDev, rdev, 0, r);
+ else
+# endif /* defined(HASBLKDEV) */
+
+ dp = lkupdev(&DevDev, rdev, 0, r);
+ if (dp) {
+ /*
+ * A match was found. Record it as a name column addition.
+ */
+ char *cp, *ttl;
+ int len;
+
+ ttl = (nty == N_BLK) ? LIKE_BLK_SPEC : LIKE_CHR_SPEC;
+ len = (int)(1 + strlen(ttl) + 1 + strlen(dp->name) + 1);
+ if (!(cp = (char *)malloc((MALLOC_S)(len + 1)))) {
+ (void) fprintf(stderr, "%s: no nma space for: (%s %s)\n",
+ Pn, ttl, dp->name);
+ Exit(1);
+ }
+ (void) snpf(cp, len + 1, "(%s %s)", ttl, dp->name);
+ (void) add_nma(cp, len);
+ (void) free((MALLOC_P *)cp);
+ return(0);
+ }
+
+# if defined(HASDCACHE)
+/*
+ * We haven't found a match.
+ *
+ * If rebuilding the device cache was suppressed and the device cache is
+ * "unsafe," rebuild it.
+ */
+ if (!r && DCunsafe) {
+ (void) rereaddev();
+ goto printdevname_again;
+ }
+# endif /* defined(HASDCACHE) */
+
+ return(0);
+}
+#endif /* defined(USE_LIB_PRINTDEVNAME) */
diff --git a/lib/prfp.c b/lib/prfp.c
new file mode 100644
index 0000000..2f8d841
--- /dev/null
+++ b/lib/prfp.c
@@ -0,0 +1,212 @@
+/*
+ * prfp.c -- process_file() function for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_PROCESS_FILE)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: prfp.c,v 1.14 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+/*
+ * process_file() - process file
+ */
+
+/*
+ * The caller may define:
+ *
+ * FILEPTR as the name of the location to store a pointer
+ * to the current file struct -- e.g.,
+ *
+ * struct file *foobar;
+ * #define FILEPTR foobar
+ */
+
+void
+process_file(fp)
+ KA_T fp; /* kernel file structure address */
+{
+ struct file f;
+ int flag;
+ char tbuf[32];
+
+#if defined(FILEPTR)
+/*
+ * Save file structure address for process_node().
+ */
+ FILEPTR = &f;
+#endif /* defined(FILEPTR) */
+
+/*
+ * Read file structure.
+ */
+ if (kread((KA_T)fp, (char *)&f, sizeof(f))) {
+ (void) snpf(Namech, Namechl, "can't read file struct from %s",
+ print_kptr(fp, (char *)NULL, 0));
+ enter_nm(Namech);
+ return;
+ }
+ Lf->off = (SZOFFTYPE)f.f_offset;
+ if (f.f_count) {
+
+ /*
+ * Construct access code.
+ */
+ if ((flag = (f.f_flag & (FREAD | FWRITE))) == FREAD)
+ Lf->access = 'r';
+ else if (flag == FWRITE)
+ Lf->access = 'w';
+ else if (flag == (FREAD | FWRITE))
+ Lf->access = 'u';
+
+#if defined(HASFSTRUCT)
+ /*
+ * Save file structure values.
+ */
+
+# if !defined(HASNOFSCOUNT)
+ if (Fsv & FSV_CT) {
+ Lf->fct = (long)f.f_count;
+ Lf->fsv |= FSV_CT;
+ }
+# endif /* !defined(HASNOFSCOUNT) */
+
+# if !defined(HASNOFSADDR)
+ if (Fsv & FSV_FA) {
+ Lf->fsa = fp;
+ Lf->fsv |= FSV_FA;
+ }
+# endif /* !defined(HASNOFSADDR) */
+
+# if !defined(HASNOFSFLAGS)
+ if (Fsv & FSV_FG) {
+ Lf->ffg = (long)f.f_flag;
+ Lf->fsv |= FSV_FG;
+ }
+# endif /* !defined(HASNOFSFLAGS) */
+
+# if !defined(HASNOFSNADDR)
+ if (Fsv & FSV_NI) {
+ Lf->fna = (KA_T)f.f_data;
+ Lf->fsv |= FSV_NI;
+ }
+# endif /* !defined(HASNOFSNADDR) */
+#endif /* defined(HASFSTRUCT) */
+
+ /*
+ * Process structure by its type.
+ */
+ switch (f.f_type) {
+
+
+#if defined(DTYPE_PIPE)
+ case DTYPE_PIPE:
+# if defined(HASPIPEFN)
+ if (!Selinet)
+ HASPIPEFN((KA_T)f.f_data);
+# endif /* defined(HASPIPEFN) */
+ return;
+#endif /* defined(DTYPE_PIPE) */
+
+#if defined(DTYPE_GNODE)
+ case DTYPE_GNODE:
+#endif /* defined(DTYPE_GNODE) */
+
+#if defined(DTYPE_INODE)
+ case DTYPE_INODE:
+#endif /* defined(DTYPE_INODE) */
+
+#if defined(DTYPE_PORT)
+ case DTYPE_PORT:
+#endif /* defined(DTYPE_PORT) */
+
+#if defined(DTYPE_VNODE)
+ case DTYPE_VNODE:
+#endif /* defined(DTYPE_VNODE) */
+
+#if defined(HASF_VNODE)
+ process_node((KA_T)f.f_vnode);
+#else /* !defined(HASF_VNODE) */
+ process_node((KA_T)f.f_data);
+#endif /* defined(HASF_VNODE) */
+
+ return;
+ case DTYPE_SOCKET:
+ process_socket((KA_T)f.f_data);
+ return;
+
+#if defined(HASKQUEUE)
+ case DTYPE_KQUEUE:
+ process_kqueue((KA_T)f.f_data);
+ return;
+#endif /* defined(HASKQUEUE) */
+
+#if defined(HASPSXSEM)
+ case DTYPE_PSXSEM:
+ process_psxsem((KA_T)f.f_data);
+ return;
+#endif /* defined(HASPSXSEM) */
+
+#if defined(HASPSXSHM)
+ case DTYPE_PSXSHM:
+ process_psxshm((KA_T)f.f_data);
+ return;
+#endif /* defined(HASPSXSHM) */
+
+#if defined(HASPRIVFILETYPE)
+ case PRIVFILETYPE:
+ HASPRIVFILETYPE((KA_T)f.f_data);
+ return;
+#endif /* defined(HASPRIVFILETYPE) */
+
+ default:
+ if (f.f_type || f.f_ops) {
+ (void) snpf(Namech, Namechl,
+ "%s file struct, ty=%#x, op=%s",
+ print_kptr(fp, tbuf, sizeof(tbuf)), (int)f.f_type,
+ print_kptr((KA_T)f.f_ops, (char *)NULL, 0));
+ enter_nm(Namech);
+ return;
+ }
+ }
+ }
+ enter_nm("no more information");
+}
+#else /* !defined(USE_LIB_PROCESS_FILE) */
+char prfp_d1[] = "d"; char *prfp_d2 = prfp_d1;
+#endif /* defined(USE_LIB_PROCESS_FILE) */
diff --git a/lib/ptti.c b/lib/ptti.c
new file mode 100644
index 0000000..e542454
--- /dev/null
+++ b/lib/ptti.c
@@ -0,0 +1,1370 @@
+/*
+ * ptti.c -- BSD style print_tcptpi() function for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_PRINT_TCPTPI)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: ptti.c,v 1.6 2008/10/21 16:13:23 abe Exp $";
+# endif /* !defined(lint) */
+
+#define TCPSTATES /* activate tcpstates[] */
+#include "../lsof.h"
+
+
+/*
+ * build_IPstates() -- build the TCP and UDP state tables
+ *
+ * Note: this module does not support a UDP state table.
+ */
+
+void
+build_IPstates()
+{
+
+/*
+ * Set the TcpNstates global variable.
+ */
+ TcpNstates = TCP_NSTATES;
+ TcpSt = (char **)&tcpstates;
+}
+
+
+/*
+ * print_tcptpi() - print TCP/TPI info
+ */
+
+void
+print_tcptpi(nl)
+ int nl; /* 1 == '\n' required */
+{
+ int ps = 0;
+ int s;
+
+ if ((Ftcptpi & TCPTPI_STATE) && Lf->lts.type == 0) {
+ if (Ffield)
+ (void) printf("%cST=", LSOF_FID_TCPTPI);
+ else
+ putchar('(');
+ if (!TcpNstates)
+ (void) build_IPstates();
+ if ((s = Lf->lts.state.i) < 0 || s >= TcpNstates)
+ (void) printf("UNKNOWN_TCP_STATE_%d", s);
+ else
+ (void) fputs(TcpSt[s], stdout);
+ ps++;
+ if (Ffield)
+ putchar(Terminator);
+ }
+
+#if defined(HASTCPTPIQ)
+ if (Ftcptpi & TCPTPI_QUEUES) {
+ if (Lf->lts.rqs) {
+ if (Ffield)
+ putchar(LSOF_FID_TCPTPI);
+ else {
+ if (ps)
+ putchar(' ');
+ else
+ putchar('(');
+ }
+ (void) printf("QR=%lu", Lf->lts.rq);
+ if (Ffield)
+ putchar(Terminator);
+ ps++;
+ }
+ if (Lf->lts.sqs) {
+ if (Ffield)
+ putchar(LSOF_FID_TCPTPI);
+ else {
+ if (ps)
+ putchar(' ');
+ else
+ putchar('(');
+ }
+ (void) printf("QS=%lu", Lf->lts.sq);
+ if (Ffield)
+ putchar(Terminator);
+ ps++;
+ }
+ }
+#endif /* defined(HASTCPTPIQ) */
+
+#if defined(HASSOOPT)
+ if (Ftcptpi & TCPTPI_FLAGS) {
+ int opt;
+
+ if ((opt = Lf->lts.opt)
+ || Lf->lts.pqlens || Lf->lts.qlens || Lf->lts.qlims
+ || Lf->lts.rbszs || Lf->lts.sbsz
+ ) {
+ char sep = ' ';
+
+ if (Ffield)
+ sep = LSOF_FID_TCPTPI;
+ else if (!ps)
+ sep = '(';
+ (void) printf("%cSO", sep);
+ ps++;
+ sep = '=';
+
+# if defined(SO_ACCEPTCONN)
+ if (opt & SO_ACCEPTCONN) {
+ (void) printf("%cACCEPTCONN", sep);
+ opt &= ~SO_ACCEPTCONN;
+ sep = ',';
+ }
+# endif /* defined(SO_ACCEPTCONN) */
+
+# if defined(SO_ACCEPTFILTER)
+ if (opt & SO_ACCEPTFILTER) {
+ (void) printf("%cACCEPTFILTER", sep);
+ opt &= ~SO_ACCEPTFILTER;
+ sep = ',';
+ }
+# endif /* defined(SO_ACCEPTFILTER) */
+
+# if defined(SO_AUDIT)
+ if (opt & SO_AUDIT) {
+ (void) printf("%cAUDIT", sep);
+ opt &= ~SO_AUDIT;
+ sep = ',';
+ }
+# endif /* defined(SO_AUDIT) */
+
+# if defined(SO_BINDANY)
+ if (opt & SO_BINDANY) {
+ (void) printf("%cBINDANY", sep);
+ opt &= ~SO_BINDANY;
+ sep = ',';
+ }
+# endif /* defined(SO_BINDANY) */
+
+# if defined(SO_BINTIME)
+ if (opt & SO_BINTIME) {
+ (void) printf("%cBINTIME", sep);
+ opt &= ~SO_BINTIME;
+ sep = ',';
+ }
+# endif /* defined(SO_BINTIME) */
+
+# if defined(SO_BROADCAST)
+ if (opt & SO_BROADCAST) {
+ (void) printf("%cBROADCAST", sep);
+ opt &= ~SO_BROADCAST;
+ sep = ',';
+ }
+# endif /* defined(SO_BROADCAST) */
+
+# if defined(SO_CKSUMRECV)
+ if (opt & SO_CKSUMRECV) {
+ (void) printf("%cCKSUMRECV", sep);
+ opt &= ~SO_CKSUMRECV;
+ sep = ',';
+ }
+# endif /* defined(SO_CKSUMRECV) */
+
+# if defined(SO_CLUA_IN_NOALIAS)
+ if (opt & SO_CLUA_IN_NOALIAS) {
+ (void) printf("%cCLUA_IN_NOALIAS", sep);
+ opt &= ~SO_CLUA_IN_NOALIAS;
+ sep = ',';
+ }
+# endif /* defined(SO_CLUA_IN_NOALIAS) */
+
+# if defined(SO_CLUA_IN_NOLOCAL)
+ if (opt & SO_CLUA_IN_NOLOCAL) {
+ (void) printf("%cCLUA_IN_NOLOCAL", sep);
+ opt &= ~SO_CLUA_IN_NOLOCAL;
+ sep = ',';
+ }
+# endif /* defined(SO_CLUA_IN_NOLOCAL) */
+
+# if defined(SO_DEBUG)
+ if (opt & SO_DEBUG) {
+ (void) printf("%cDEBUG", sep);
+ opt &= ~ SO_DEBUG;
+ sep = ',';
+ }
+# endif /* defined(SO_DEBUG) */
+
+# if defined(SO_DGRAM_ERRIND)
+ if (opt & SO_DGRAM_ERRIND) {
+ (void) printf("%cDGRAM_ERRIND", sep);
+ opt &= ~SO_DGRAM_ERRIND;
+ sep = ',';
+ }
+# endif /* defined(SO_DGRAM_ERRIND) */
+
+# if defined(SO_DONTROUTE)
+ if (opt & SO_DONTROUTE) {
+ (void) printf("%cDONTROUTE", sep);
+ opt &= ~SO_DONTROUTE;
+ sep = ',';
+ }
+# endif /* defined(SO_DONTROUTE) */
+
+# if defined(SO_DONTTRUNC)
+ if (opt & SO_DONTTRUNC) {
+ (void) printf("%cDONTTRUNC", sep);
+ opt &= ~SO_DONTTRUNC;
+ sep = ',';
+ }
+# endif /* defined(SO_DONTTRUNC) */
+
+# if defined(SO_EXPANDED_RIGHTS)
+ if (opt & SO_EXPANDED_RIGHTS) {
+ (void) printf("%cEXPANDED_RIGHTS", sep);
+ opt &= ~SO_EXPANDED_RIGHTS;
+ sep = ',';
+ }
+# endif /* defined(SO_EXPANDED_RIGHTS) */
+
+# if defined(SO_KEEPALIVE)
+ if (opt & SO_KEEPALIVE) {
+ (void) printf("%cKEEPALIVE", sep);
+ if (Lf->lts.kai)
+ (void) printf("=%d", Lf->lts.kai);
+ opt &= ~SO_KEEPALIVE;
+ sep = ',';
+ }
+# endif /* defined(SO_KEEPALIVE) */
+
+# if defined(SO_KERNACCEPT)
+ if (opt & SO_KERNACCEPT) {
+ (void) printf("%cKERNACCEPT", sep);
+ opt &= ~SO_KERNACCEPT;
+ sep = ',';
+ }
+# endif /* defined(SO_KERNACCEPT) */
+
+# if defined(SO_IMASOCKET)
+ if (opt & SO_IMASOCKET) {
+ (void) printf("%cIMASOCKET", sep);
+ opt &= ~SO_IMASOCKET;
+ sep = ',';
+ }
+# endif /* defined(SO_IMASOCKET) */
+
+# if defined(SO_LINGER)
+ if (opt & SO_LINGER) {
+ (void) printf("%cLINGER", sep);
+ if (Lf->lts.ltm)
+ (void) printf("=%d", Lf->lts.ltm);
+ opt &= ~SO_LINGER;
+ sep = ',';
+ }
+# endif /* defined(SO_LINGER) */
+
+# if defined(SO_LISTENING)
+ if (opt & SO_LISTENING) {
+ (void) printf("%cLISTENING", sep);
+ opt &= ~SO_LISTENING;
+ sep = ',';
+ }
+# endif /* defined(SO_LISTENING) */
+
+# if defined(SO_MGMT)
+ if (opt & SO_MGMT) {
+ (void) printf("%cMGMT", sep);
+ opt &= ~SO_MGMT;
+ sep = ',';
+ }
+# endif /* defined(SO_MGMT) */
+
+# if defined(SO_PAIRABLE)
+ if (opt & SO_PAIRABLE) {
+ (void) printf("%cPAIRABLE", sep);
+ opt &= ~SO_PAIRABLE;
+ sep = ',';
+ }
+# endif /* defined(SO_PAIRABLE) */
+
+# if defined(SO_RESVPORT)
+ if (opt & SO_RESVPORT) {
+ (void) printf("%cRESVPORT", sep);
+ opt &= ~SO_RESVPORT;
+ sep = ',';
+ }
+# endif /* defined(SO_RESVPORT) */
+
+# if defined(SO_NOREUSEADDR)
+ if (opt & SO_NOREUSEADDR) {
+ (void) printf("%cNOREUSEADDR", sep);
+ opt &= ~SO_NOREUSEADDR;
+ sep = ',';
+ }
+# endif /* defined(SO_NOREUSEADDR) */
+
+# if defined(SO_NOSIGPIPE)
+ if (opt & SO_NOSIGPIPE) {
+ (void) printf("%cNOSIGPIPE", sep);
+ opt &= ~SO_NOSIGPIPE;
+ sep = ',';
+ }
+# endif /* defined(SO_NOSIGPIPE) */
+
+# if defined(SO_OOBINLINE)
+ if (opt & SO_OOBINLINE) {
+ (void) printf("%cOOBINLINE", sep);
+ opt &= ~SO_OOBINLINE;
+ sep = ',';
+ }
+# endif /* defined(SO_OOBINLINE) */
+
+# if defined(SO_ORDREL)
+ if (opt & SO_ORDREL) {
+ (void) printf("%cORDREL", sep);
+ opt &= ~SO_ORDREL;
+ sep = ',';
+ }
+# endif /* defined(SO_ORDREL) */
+
+ if (Lf->lts.pqlens) {
+ (void) printf("%cPQLEN=%u", sep, Lf->lts.pqlen);
+ sep = ',';
+ }
+ if (Lf->lts.qlens) {
+ (void) printf("%cQLEN=%u", sep, Lf->lts.qlen);
+ sep = ',';
+ }
+ if (Lf->lts.qlims) {
+ (void) printf("%cQLIM=%u", sep, Lf->lts.qlim);
+ sep = ',';
+ }
+ if (Lf->lts.rbszs) {
+ (void) printf("%cRCVBUF=%lu", sep, Lf->lts.rbsz);
+ sep = ',';
+ }
+
+# if defined(SO_REUSEADDR)
+ if (opt & SO_REUSEADDR) {
+ (void) printf("%cREUSEADDR", sep);
+ opt &= ~SO_REUSEADDR;
+ sep = ',';
+ }
+# endif /* defined(SO_REUSEADDR) */
+
+# if defined(SO_REUSEALIASPORT)
+ if (opt & SO_REUSEALIASPORT) {
+ (void) printf("%cREUSEALIASPORT", sep);
+ opt &= ~SO_REUSEALIASPORT;
+ sep = ',';
+ }
+# endif /* defined(SO_REUSEALIASPORT) */
+
+# if defined(SO_REUSEPORT)
+ if (opt & SO_REUSEPORT) {
+ (void) printf("%cREUSEPORT", sep);
+ opt &= ~SO_REUSEPORT;
+ sep = ',';
+ }
+# endif /* defined(SO_REUSEPORT) */
+
+# if defined(SO_REUSERAD)
+ if (opt & SO_REUSERAD) {
+ (void) printf("%cREUSERAD", sep);
+ opt &= ~SO_REUSERAD;
+ sep = ',';
+ }
+# endif /* defined(SO_REUSERAD) */
+
+# if defined(SO_SECURITY_REQUEST)
+ if (opt & SO_SECURITY_REQUEST) {
+ (void) printf("%cSECURITY_REQUEST", sep);
+ opt &= ~SO_SECURITY_REQUEST;
+ sep = ',';
+ }
+# endif /* defined(SO_SECURITY_REQUEST) */
+
+ if (Lf->lts.sbszs) {
+ (void) printf("%cSNDBUF=%lu", sep, Lf->lts.sbsz);
+ sep = ',';
+ }
+
+# if defined(SO_TIMESTAMP)
+ if (opt & SO_TIMESTAMP) {
+ (void) printf("%cTIMESTAMP", sep);
+ opt &= ~SO_TIMESTAMP;
+ sep = ',';
+ }
+# endif /* defined(SO_TIMESTAMP) */
+
+# if defined(SO_UMC)
+ if (opt & SO_UMC) {
+ (void) printf("%cUMC", sep);
+ opt &= ~SO_UMC;
+ sep = ',';
+ }
+# endif /* defined(SO_UMC) */
+
+# if defined(SO_USE_IFBUFS)
+ if (opt & SO_USE_IFBUFS) {
+ (void) printf("%cUSE_IFBUFS", sep);
+ opt &= ~SO_USE_IFBUFS;
+ sep = ',';
+ }
+# endif /* defined(SO_USE_IFBUFS) */
+
+# if defined(SO_USELOOPBACK)
+ if (opt & SO_USELOOPBACK) {
+ (void) printf("%cUSELOOPBACK", sep);
+ opt &= ~SO_USELOOPBACK;
+ sep = ',';
+ }
+# endif /* defined(SO_USELOOPBACK) */
+
+# if defined(SO_WANTMORE)
+ if (opt & SO_WANTMORE) {
+ (void) printf("%cWANTMORE", sep);
+ opt &= ~SO_WANTMORE;
+ sep = ',';
+ }
+# endif /* defined(SO_WANTMORE) */
+
+# if defined(SO_WANTOOBFLAG)
+ if (opt & SO_WANTOOBFLAG) {
+ (void) printf("%cWANTOOBFLAG", sep);
+ opt &= ~SO_WANTOOBFLAG;
+ sep = ',';
+ }
+# endif /* defined(SO_WANTOOBFLAG) */
+
+ if (opt)
+ (void) printf("%cUNKNOWN=%#x", sep, opt);
+ if (Ffield)
+ putchar(Terminator);
+ }
+ }
+#endif /* defined(HASSOOPT) */
+
+#if defined(HASSOSTATE)
+ if (Ftcptpi & TCPTPI_FLAGS) {
+ unsigned int ss;
+
+ if ((ss = Lf->lts.ss)) {
+ char sep = ' ';
+
+ if (Ffield)
+ sep = LSOF_FID_TCPTPI;
+ else if (!ps)
+ sep = '(';
+ (void) printf("%cSS", sep);
+ ps++;
+ sep = '=';
+
+# if defined(SS_ASYNC)
+ if (ss & SS_ASYNC) {
+ (void) printf("%cASYNC", sep);
+ ss &= ~SS_ASYNC;
+ sep = ',';
+ }
+# endif /* defined(SS_ASYNC) */
+
+# if defined(SS_BOUND)
+ if (ss & SS_BOUND) {
+ (void) printf("%cBOUND", sep);
+ ss &= ~SS_BOUND;
+ sep = ',';
+ }
+# endif /* defined(SS_BOUND) */
+
+# if defined(HASSBSTATE)
+# if defined(SBS_CANTRCVMORE)
+ if (Lf->lts.sbs_rcv & SBS_CANTRCVMORE) {
+ (void) printf("%cCANTRCVMORE", sep);
+ Lf->lts.sbs_rcv &= ~SBS_CANTRCVMORE;
+ sep = ',';
+ }
+# endif /* defined(SBS_CANTRCVMORE) */
+
+# if defined(SBS_CANTSENDMORE)
+ if (Lf->lts.sbs_snd & SBS_CANTSENDMORE) {
+ (void) printf("%cCANTSENDMORE", sep);
+ Lf->lts.sbs_snd &= ~SBS_CANTSENDMORE;
+ sep = ',';
+ }
+# endif /* defined(SS_CANTSENDMORE) */
+# else /* !defined(HASSBSTATE) */
+
+# if defined(SS_CANTRCVMORE)
+ if (ss & SS_CANTRCVMORE) {
+ (void) printf("%cCANTRCVMORE", sep);
+ ss &= ~SS_CANTRCVMORE;
+ sep = ',';
+ }
+# endif /* defined(SS_CANTRCVMORE) */
+
+# if defined(SS_CANTSENDMORE)
+ if (ss & SS_CANTSENDMORE) {
+ (void) printf("%cCANTSENDMORE", sep);
+ ss &= ~SS_CANTSENDMORE;
+ sep = ',';
+ }
+# endif /* defined(SS_CANTSENDMORE) */
+# endif /* defined(HASSBSTATE) */
+
+# if defined(SS_COMP)
+ if (ss & SS_COMP) {
+ (void) printf("%cCOMP", sep);
+ ss &= ~SS_COMP;
+ sep = ',';
+ }
+# endif /* defined(SS_COMP) */
+
+# if defined(SS_CONNECTOUT)
+ if (ss & SS_CONNECTOUT) {
+ (void) printf("%cCONNECTOUT", sep);
+ ss &= ~SS_CONNECTOUT;
+ sep = ',';
+ }
+# endif /* defined(SS_CONNECTOUT) */
+
+# if defined(SS_HIPRI)
+ if (ss & SS_HIPRI) {
+ (void) printf("%cHIPRI", sep);
+ ss &= ~SS_HIPRI;
+ sep = ',';
+ }
+# endif /* defined(SS_HIPRI) */
+
+# if defined(SS_IGNERR)
+ if (ss & SS_IGNERR) {
+ (void) printf("%cIGNERR", sep);
+ ss &= ~SS_IGNERR;
+ sep = ',';
+ }
+# endif /* defined(SS_IGNERR) */
+
+# if defined(SS_INCOMP)
+ if (ss & SS_INCOMP) {
+ (void) printf("%cINCOMP", sep);
+ ss &= ~SS_INCOMP;
+ sep = ',';
+ }
+# endif /* defined(SS_INCOMP) */
+
+# if defined(SS_IOCWAIT)
+ if (ss & SS_IOCWAIT) {
+ (void) printf("%cIOCWAIT", sep);
+ ss &= ~SS_IOCWAIT;
+ sep = ',';
+ }
+# endif /* defined(SS_IOCWAIT) */
+
+# if defined(SS_ISCONFIRMING)
+ if (ss & SS_ISCONFIRMING) {
+ (void) printf("%cISCONFIRMING", sep);
+ ss &= ~SS_ISCONFIRMING;
+ sep = ',';
+ }
+# endif /* defined(SS_ISCONFIRMING) */
+
+# if defined(SS_ISCONNECTED)
+ if (ss & SS_ISCONNECTED) {
+ (void) printf("%cISCONNECTED", sep);
+ ss &= ~SS_ISCONNECTED;
+ sep = ',';
+ }
+# endif /* defined(SS_ISCONNECTED) */
+
+# if defined(SS_ISCONNECTING)
+ if (ss & SS_ISCONNECTING) {
+ (void) printf("%cISCONNECTING", sep);
+ ss &= ~SS_ISCONNECTING;
+ sep = ',';
+ }
+# endif /* defined(SS_ISCONNECTING) */
+
+# if defined(SS_ISDISCONNECTING)
+ if (ss & SS_ISDISCONNECTING) {
+ (void) printf("%cISDISCONNECTING", sep);
+ ss &= ~SS_ISDISCONNECTING;
+ sep = ',';
+ }
+# endif /* defined(SS_ISDISCONNECTING) */
+
+# if defined(SS_MORETOSEND)
+ if (ss & SS_MORETOSEND) {
+ (void) printf("%cMORETOSEND", sep);
+ ss &= ~SS_MORETOSEND;
+ sep = ',';
+ }
+# endif /* defined(SS_MORETOSEND) */
+
+# if defined(SS_NBIO)
+ if (ss & SS_NBIO) {
+ (void) printf("%cNBIO", sep);
+ ss &= ~SS_NBIO;
+ sep = ',';
+ }
+# endif /* defined(SS_NBIO) */
+
+# if defined(SS_NOCONN)
+ if (ss & SS_NOCONN) {
+ (void) printf("%cNOCONN", sep);
+ ss &= ~SS_NOCONN;
+ sep = ',';
+ }
+# endif /* defined(SS_NOCONN) */
+
+# if defined(SS_NODELETE)
+ if (ss & SS_NODELETE) {
+ (void) printf("%cNODELETE", sep);
+ ss &= ~SS_NODELETE;
+ sep = ',';
+ }
+# endif /* defined(SS_NODELETE) */
+
+# if defined(SS_NOFDREF)
+ if (ss & SS_NOFDREF) {
+ (void) printf("%cNOFDREF", sep);
+ ss &= ~SS_NOFDREF;
+ sep = ',';
+ }
+# endif /* defined(SS_NOFDREF) */
+
+# if defined(SS_NOGHOST)
+ if (ss & SS_NOGHOST) {
+ (void) printf("%cNOGHOST", sep);
+ ss &= ~SS_NOGHOST;
+ sep = ',';
+ }
+# endif /* defined(SS_NOGHOST) */
+
+# if defined(SS_NOINPUT)
+ if (ss & SS_NOINPUT) {
+ (void) printf("%cNOINPUT", sep);
+ ss &= ~SS_NOINPUT;
+ sep = ',';
+ }
+# endif /* defined(SS_NOINPUT) */
+
+# if defined(SS_PRIV)
+ if (ss & SS_PRIV) {
+ (void) printf("%cPRIV", sep);
+ ss &= ~SS_PRIV;
+ sep = ',';
+ }
+# endif /* defined(SS_PRIV) */
+
+# if defined(SS_QUEUE)
+ if (ss & SS_QUEUE) {
+ (void) printf("%cQUEUE", sep);
+ ss &= ~SS_QUEUE;
+ sep = ',';
+ }
+# endif /* defined(SS_QUEUE) */
+
+# if defined(HASSBSTATE)
+# if defined(SBS_RCVATMARK)
+ if (Lf->lts.sbs_rcv & SBS_RCVATMARK) {
+ (void) printf("%cRCVATMARK", sep);
+ Lf->lts.sbs_rcv &= ~SBS_RCVATMARK;
+ sep = ',';
+ }
+# endif /* defined(SBS_RCVATMARK) */
+
+# else /* !defined(HASSBSTATE) */
+# if defined(SS_RCVATMARK)
+ if (ss & SS_RCVATMARK) {
+ (void) printf("%cRCVATMARK", sep);
+ ss &= ~SS_RCVATMARK;
+ sep = ',';
+ }
+# endif /* defined(SS_RCVATMARK) */
+# endif /* defined(HASSBSTATE) */
+
+# if defined(SS_READWAIT)
+ if (ss & SS_READWAIT) {
+ (void) printf("%cREADWAIT", sep);
+ ss &= ~SS_READWAIT;
+ sep = ',';
+ }
+# endif /* defined(SS_READWAIT) */
+
+# if defined(SS_SETRCV)
+ if (ss & SS_SETRCV) {
+ (void) printf("%cSETRCV", sep);
+ ss &= ~SS_SETRCV;
+ sep = ',';
+ }
+# endif /* defined(SS_SETRCV) */
+
+# if defined(SS_SETSND)
+ if (ss & SS_SETSND) {
+ (void) printf("%cSETSND", sep);
+ ss &= ~SS_SETSND;
+ sep = ',';
+ }
+# endif /* defined(SS_SETSND) */
+
+# if defined(SS_SIGREAD)
+ if (ss & SS_SIGREAD) {
+ (void) printf("%cSIGREAD", sep);
+ ss &= ~SS_SIGREAD;
+ sep = ',';
+ }
+# endif /* defined(SS_SIGREAD) */
+
+# if defined(SS_SIGWRITE)
+ if (ss & SS_SIGWRITE) {
+ (void) printf("%cSIGWRITE", sep);
+ ss &= ~SS_SIGWRITE;
+ sep = ',';
+ }
+# endif /* defined(SS_SIGWRITE) */
+
+# if defined(SS_SPLICED)
+ if (ss & SS_SPLICED) {
+ (void) printf("%cSPLICED", sep);
+ ss &= ~SS_SPLICED;
+ sep = ',';
+ }
+# endif /* defined(SS_SPLICED) */
+
+# if defined(SS_WRITEWAIT)
+ if (ss & SS_WRITEWAIT) {
+ (void) printf("%cWRITEWAIT", sep);
+ ss &= ~SS_WRITEWAIT;
+ sep = ',';
+ }
+# endif /* defined(SS_WRITEWAIT) */
+
+# if defined(SS_ZOMBIE)
+ if (ss & SS_ZOMBIE) {
+ (void) printf("%cZOMBIE", sep);
+ ss &= ~SS_ZOMBIE;
+ sep = ',';
+ }
+# endif /* defined(SS_ZOMBIE) */
+
+ if (ss)
+ (void) printf("%cUNKNOWN=%#x", sep, ss);
+ if (Ffield)
+ putchar(Terminator);
+ }
+ }
+#endif /* defined(HASSOSTATE) */
+
+#if defined(HASTCPOPT)
+ if (Ftcptpi & TCPTPI_FLAGS) {
+ int topt;
+
+ if ((topt = Lf->lts.topt) || Lf->lts.msss) {
+ char sep = ' ';
+
+ if (Ffield)
+ sep = LSOF_FID_TCPTPI;
+ else if (!ps)
+ sep = '(';
+ (void) printf("%cTF", sep);
+ ps++;
+ sep = '=';
+
+# if defined(TF_ACKNOW)
+ if (topt & TF_ACKNOW) {
+ (void) printf("%cACKNOW", sep);
+ topt &= ~TF_ACKNOW;
+ sep = ',';
+ }
+# endif /* defined(TF_ACKNOW) */
+
+# if defined(TF_CANT_TXSACK)
+ if (topt & TF_CANT_TXSACK) {
+ (void) printf("%cCANT_TXSACK", sep);
+ topt &= ~TF_CANT_TXSACK;
+ sep = ',';
+ }
+# endif /* defined(TF_CANT_TXSACK) */
+
+# if defined(TF_DEAD)
+ if (topt & TF_DEAD) {
+ (void) printf("%cDEAD", sep);
+ topt &= ~TF_DEAD;
+ sep = ',';
+ }
+# endif /* defined(TF_DEAD) */
+
+# if defined(TF_DELACK)
+ if (topt & TF_DELACK) {
+ (void) printf("%cDELACK", sep);
+ topt &= ~TF_DELACK;
+ sep = ',';
+ }
+# endif /* defined(TF_DELACK) */
+
+# if defined(TF_DELAY_ACK)
+ if (topt & TF_DELAY_ACK) {
+ (void) printf("%cDELAY_ACK", sep);
+ topt &= ~TF_DELAY_ACK;
+ sep = ',';
+ }
+# endif /* defined(TF_DELAY_ACK) */
+
+# if defined(TF_DISABLE_ECN)
+ if (topt & TF_DISABLE_ECN) {
+ (void) printf("%cDISABLE_ECN", sep);
+ topt &= ~TF_DISABLE_ECN;
+ sep = ',';
+ }
+# endif /* defined(TF_DISABLE_ECN) */
+
+# if defined(TF_ECN)
+ if (topt & TF_ECN) {
+ (void) printf("%cECN", sep);
+ topt &= ~TF_ECN;
+ sep = ',';
+ }
+# endif /* defined(TF_ECN) */
+
+# if defined(TF_ECN_PERMIT)
+ if (topt & TF_ECN_PERMIT) {
+ (void) printf("%cECN_PERMIT", sep);
+ topt &= ~TF_ECN_PERMIT;
+ sep = ',';
+ }
+# endif /* defined(TF_ECN_PERMIT) */
+
+# if defined(TF_FASTRECOVERY)
+ if (topt & TF_FASTRECOVERY) {
+ (void) printf("%cFASTRECOVERY", sep);
+ topt &= ~TF_FASTRECOVERY;
+ sep = ',';
+ }
+# endif /* defined(TF_FASTRECOVERY) */
+
+# if defined(TF_FASTRXMT_PHASE)
+ if (topt & TF_FASTRXMT_PHASE) {
+ (void) printf("%cFASTRXMT_PHASE", sep);
+ topt &= ~TF_FASTRXMT_PHASE;
+ sep = ',';
+ }
+# endif /* defined(TF_FASTRXMT_PHASE) */
+
+# if defined(TF_HAVEACKED)
+ if (topt & TF_HAVEACKED) {
+ (void) printf("%cHAVEACKED", sep);
+ topt &= ~TF_HAVEACKED;
+ sep = ',';
+ }
+# endif /* defined(TF_HAVEACKED) */
+
+# if defined(TF_HAVECLOSED)
+ if (topt & TF_HAVECLOSED) {
+ (void) printf("%cHAVECLOSED", sep);
+ topt &= ~TF_HAVECLOSED;
+ sep = ',';
+ }
+# endif /* defined(TF_HAVECLOSED) */
+
+# if defined(TF_IGNR_RXSACK)
+ if (topt & TF_IGNR_RXSACK) {
+ (void) printf("%cIGNR_RXSACK", sep);
+ topt &= ~TF_IGNR_RXSACK;
+ sep = ',';
+ }
+# endif /* defined(TF_IGNR_RXSACK) */
+
+# if defined(TF_IOLOCK)
+ if (topt & TF_IOLOCK) {
+ (void) printf("%cIOLOCK", sep);
+ topt &= ~TF_IOLOCK;
+ sep = ',';
+ }
+# endif /* defined(TF_IOLOCK) */
+
+# if defined(TF_LARGESEND)
+ if (topt & TF_LARGESEND) {
+ (void) printf("%cLARGESEND", sep);
+ topt &= ~TF_LARGESEND;
+ sep = ',';
+ }
+# endif /* defined(TF_LARGESEND) */
+
+# if defined(TF_LASTIDLE)
+ if (topt & TF_LASTIDLE) {
+ (void) printf("%cLASTIDLE", sep);
+ topt &= ~TF_LASTIDLE;
+ sep = ',';
+ }
+# endif /* defined(TF_LASTIDLE) */
+
+# if defined(TF_LQ_OVERFLOW)
+ if (topt & TF_LQ_OVERFLOW) {
+ (void) printf("%cLQ_OVERFLOW", sep);
+ topt &= ~TF_LQ_OVERFLOW;
+ sep = ',';
+ }
+# endif /* defined(TF_LQ_OVERFLOW) */
+
+ if (Lf->lts.msss) {
+ (void) printf("%cMSS=%lu", sep, Lf->lts.mss);
+ sep = ',';
+ }
+
+# if defined(TF_MORETOCOME)
+ if (topt & TF_MORETOCOME) {
+ (void) printf("%cMORETOCOME", sep);
+ topt &= ~TF_MORETOCOME;
+ sep = ',';
+ }
+# endif /* defined(TF_MORETOCOME) */
+
+# if defined(TF_NEEDACK)
+ if (topt & TF_NEEDACK) {
+ (void) printf("%cNEEDACK", sep);
+ topt &= ~TF_NEEDACK;
+ sep = ',';
+ }
+# endif /* defined(TF_NEEDACK) */
+
+# if defined(TF_NEEDCLOSE)
+ if (topt & TF_NEEDCLOSE) {
+ (void) printf("%cNEEDCLOSE", sep);
+ topt &= ~TF_NEEDCLOSE;
+ sep = ',';
+ }
+# endif /* defined(TF_NEEDCLOSE) */
+
+# if defined(TF_NEEDFIN)
+ if (topt & TF_NEEDFIN) {
+ (void) printf("%cNEEDFIN", sep);
+ topt &= ~TF_NEEDFIN;
+ sep = ',';
+ }
+# endif /* defined(TF_NEEDFIN) */
+
+# if defined(TF_NEEDIN)
+ if (topt & TF_NEEDIN) {
+ (void) printf("%cNEEDIN", sep);
+ topt &= ~TF_NEEDIN;
+ sep = ',';
+ }
+# endif /* defined(TF_NEEDIN) */
+
+# if defined(TF_NEEDOUT)
+ if (topt & TF_NEEDOUT) {
+ (void) printf("%cNEEDOUT", sep);
+ topt &= ~TF_NEEDOUT;
+ sep = ',';
+ }
+# endif /* defined(TF_NEEDOUT) */
+
+# if defined(TF_NEEDSYN)
+ if (topt & TF_NEEDSYN) {
+ (void) printf("%cNEEDSYN", sep);
+ topt &= ~TF_NEEDSYN;
+ sep = ',';
+ }
+# endif /* defined(TF_NEEDSYN) */
+
+# if defined(TF_NEEDTIMER)
+ if (topt & TF_NEEDTIMER) {
+ (void) printf("%cNEEDTIMER", sep);
+ topt &= ~TF_NEEDTIMER;
+ sep = ',';
+ }
+# endif /* defined(TF_NEEDTIMER) */
+
+# if defined(TF_NEWRENO_RXMT)
+ if (topt & TF_NEWRENO_RXMT) {
+ (void) printf("%cNEWRENO_RXMT", sep);
+ topt &= ~TF_NEWRENO_RXMT;
+ sep = ',';
+ }
+# endif /* defined(TF_NEWRENO_RXMT) */
+
+# if defined(TF_NODELACK)
+ if (topt & TF_NODELACK) {
+ (void) printf("%cNODELACK", sep);
+ topt &= ~TF_NODELACK;
+ sep = ',';
+ }
+# endif /* defined(TF_NODELACK) */
+
+# if defined(TF_NODELAY)
+ if (topt & TF_NODELAY) {
+ (void) printf("%cNODELAY", sep);
+ topt &= ~TF_NODELAY;
+ sep = ',';
+ }
+# endif /* defined(TF_NODELAY) */
+
+# if defined(TF_NOOPT)
+ if (topt & TF_NOOPT) {
+ (void) printf("%cNOOPT", sep);
+ topt &= ~TF_NOOPT;
+ sep = ',';
+ }
+# endif /* defined(TF_NOOPT) */
+
+# if defined(TF_NOPUSH)
+ if (topt & TF_NOPUSH) {
+ (void) printf("%cNOPUSH", sep);
+ topt &= ~TF_NOPUSH;
+ sep = ',';
+ }
+# endif /* defined(TF_NOPUSH) */
+
+# if defined(TF_NO_PMTU)
+ if (topt & TF_NO_PMTU) {
+ (void) printf("%cNO_PMTU", sep);
+ topt &= ~TF_NO_PMTU;
+ sep = ',';
+ }
+# endif /* defined(TF_NO_PMTU) */
+
+# if defined(TF_RAW)
+ if (topt & TF_RAW) {
+ (void) printf("%cRAW", sep);
+ topt &= ~TF_RAW;
+ sep = ',';
+ }
+# endif /* defined(TF_RAW) */
+
+# if defined(TF_RCVD_CC)
+ if (topt & TF_RCVD_CC) {
+ (void) printf("%cRCVD_CC", sep);
+ topt &= ~TF_RCVD_CC;
+ sep = ',';
+ }
+# endif /* defined(TF_RCVD_CC) */
+
+# if defined(TF_RCVD_SCALE)
+ if (topt & TF_RCVD_SCALE) {
+ (void) printf("%cRCVD_SCALE", sep);
+ topt &= ~TF_RCVD_SCALE;
+ sep = ',';
+ }
+# endif /* defined(TF_RCVD_SCALE) */
+
+# if defined(TF_RCVD_CE)
+ if (topt & TF_RCVD_CE) {
+ (void) printf("%cRCVD_CE", sep);
+ topt &= ~TF_RCVD_CE;
+ sep = ',';
+ }
+# endif /* defined(TF_RCVD_CE) */
+
+# if defined(TF_RCVD_TS)
+ if (topt & TF_RCVD_TS) {
+ (void) printf("%cRCVD_TS", sep);
+ topt &= ~TF_RCVD_TS;
+ sep = ',';
+ }
+# endif /* defined(TF_RCVD_TS) */
+
+# if defined(TF_RCVD_TSTMP)
+ if (topt & TF_RCVD_TSTMP) {
+ (void) printf("%cRCVD_TSTMP", sep);
+ topt &= ~TF_RCVD_TSTMP;
+ sep = ',';
+ }
+# endif /* defined(TF_RCVD_TSTMP) */
+
+# if defined(TF_RCVD_WS)
+ if (topt & TF_RCVD_WS) {
+ (void) printf("%cRCVD_WS", sep);
+ topt &= ~TF_RCVD_WS;
+ sep = ',';
+ }
+# endif /* defined(TF_RCVD_WS) */
+
+# if defined(TF_REASSEMBLING)
+ if (topt & TF_REASSEMBLING) {
+ (void) printf("%cREASSEMBLING", sep);
+ topt &= ~TF_REASSEMBLING;
+ sep = ',';
+ }
+# endif /* defined(TF_REASSEMBLING) */
+
+# if defined(TF_REQ_CC)
+ if (topt & TF_REQ_CC) {
+ (void) printf("%cREQ_CC", sep);
+ topt &= ~TF_REQ_CC;
+ sep = ',';
+ }
+# endif /* defined(TF_REQ_CC) */
+
+# if defined(TF_REQ_SCALE)
+ if (topt & TF_REQ_SCALE) {
+ (void) printf("%cREQ_SCALE", sep);
+ topt &= ~TF_REQ_SCALE;
+ sep = ',';
+ }
+# endif /* defined(TF_REQ_SCALE) */
+
+# if defined(TF_REQ_TSTMP)
+ if (topt & TF_REQ_TSTMP) {
+ (void) printf("%cREQ_TSTMP", sep);
+ topt &= ~TF_REQ_TSTMP;
+ sep = ',';
+ }
+# endif /* defined(TF_REQ_TSTMP) */
+
+# if defined(TF_RFC1323)
+ if (topt & TF_RFC1323) {
+ (void) printf("%cRFC1323", sep);
+ topt &= ~TF_RFC1323;
+ sep = ',';
+ }
+# endif /* defined(TF_RFC1323) */
+
+# if defined(TF_RXWIN0SENT)
+ if (topt & TF_RXWIN0SENT) {
+ (void) printf("%cRXWIN0SENT", sep);
+ topt &= ~TF_RXWIN0SENT;
+ sep = ',';
+ }
+# endif /* defined(TF_RXWIN0SENT) */
+
+# if defined(TF_SACK_GENERATE)
+ if (topt & TF_SACK_GENERATE) {
+ (void) printf("%cSACK_GENERATE", sep);
+ topt &= ~TF_SACK_GENERATE;
+ sep = ',';
+ }
+# endif /* defined(TF_SACK_GENERATE) */
+
+# if defined(TF_SACK_PERMIT)
+ if (topt & TF_SACK_PERMIT) {
+ (void) printf("%cSACK_PERMIT", sep);
+ topt &= ~TF_SACK_PERMIT;
+ sep = ',';
+ }
+# endif /* defined(TF_SACK_PERMIT) */
+
+# if defined(TF_SACK_PROCESS)
+ if (topt & TF_SACK_PROCESS) {
+ (void) printf("%cSACK_PROCESS", sep);
+ topt &= ~TF_SACK_PROCESS;
+ sep = ',';
+ }
+# endif /* defined(TF_SACK_PROCESS) */
+
+# if defined(TF_SEND)
+ if (topt & TF_SEND) {
+ (void) printf("%cSEND", sep);
+ topt &= ~TF_SEND;
+ sep = ',';
+ }
+# endif /* defined(TF_SEND) */
+
+# if defined(TF_SEND_AND_DISCONNECT)
+ if (topt & TF_SEND_AND_DISCONNECT) {
+ (void) printf("%cSEND_AND_DISCONNECT", sep);
+ topt &= ~TF_SEND_AND_DISCONNECT;
+ sep = ',';
+ }
+# endif /* defined(TF_SEND_AND_DISCONNECT) */
+
+# if defined(TF_SENDCCNEW)
+ if (topt & TF_SENDCCNEW) {
+ (void) printf("%cSENDCCNEW", sep);
+ topt &= ~TF_SENDCCNEW;
+ sep = ',';
+ }
+# endif /* defined(TF_SENDCCNEW) */
+
+# if defined(TF_SEND_CWR)
+ if (topt & TF_SEND_CWR) {
+ (void) printf("%cSEND_CWR", sep);
+ topt &= ~TF_SEND_CWR;
+ sep = ',';
+ }
+# endif /* defined(TF_SEND_CWR) */
+
+# if defined(TF_SEND_ECHO)
+ if (topt & TF_SEND_ECHO) {
+ (void) printf("%cSEND_ECHO", sep);
+ topt &= ~TF_SEND_ECHO;
+ sep = ',';
+ }
+# endif /* defined(TF_SEND_ECHO) */
+
+# if defined(TF_SEND_TSTMP)
+ if (topt & TF_SEND_TSTMP) {
+ (void) printf("%cSEND_TSTMP", sep);
+ topt &= ~TF_SEND_TSTMP;
+ sep = ',';
+ }
+# endif /* defined(TF_SEND_TSTMP) */
+
+# if defined(TF_SENTFIN)
+ if (topt & TF_SENTFIN) {
+ (void) printf("%cSENTFIN", sep);
+ topt &= ~TF_SENTFIN;
+ sep = ',';
+ }
+# endif /* defined(TF_SENTFIN) */
+
+# if defined(TF_SENT_TS)
+ if (topt & TF_SENT_TS) {
+ (void) printf("%cSENT_TS", sep);
+ topt &= ~TF_SENT_TS;
+ sep = ',';
+ }
+# endif /* defined(TF_SENT_TS) */
+
+# if defined(TF_SENT_WS)
+ if (topt & TF_SENT_WS) {
+ (void) printf("%cSENT_WS", sep);
+ topt &= ~TF_SENT_WS;
+ sep = ',';
+ }
+# endif /* defined(TF_SENT_WS) */
+
+# if defined(TF_SIGNATURE)
+ if (topt & TF_SIGNATURE) {
+ (void) printf("%cSIGNATURE", sep);
+ topt &= ~TF_SIGNATURE;
+ sep = ',';
+ }
+# endif /* defined(TF_SIGNATURE) */
+
+# if defined(TF_SLOWLINK)
+ if (topt & TF_SLOWLINK) {
+ (void) printf("%cSLOWLINK", sep);
+ topt &= ~TF_SLOWLINK;
+ sep = ',';
+ }
+# endif /* defined(TF_SLOWLINK) */
+
+# if defined(TF_STDURG)
+ if (topt & TF_STDURG) {
+ (void) printf("%cSTDURG", sep);
+ topt &= ~TF_STDURG;
+ sep = ',';
+ }
+# endif /* defined(TF_STDURG) */
+
+# if defined(TF_SYN_REXMT)
+ if (topt & TF_SYN_REXMT) {
+ (void) printf("%cSYN_REXMT", sep);
+ topt &= ~TF_SYN_REXMT;
+ sep = ',';
+ }
+# endif /* defined(TF_SYN_REXMT) */
+
+# if defined(TF_UIOMOVED)
+ if (topt & TF_UIOMOVED) {
+ (void) printf("%cUIOMOVED", sep);
+ topt &= ~TF_UIOMOVED;
+ sep = ',';
+ }
+# endif /* defined(TF_UIOMOVED) */
+
+# if defined(TF_USE_SCALE)
+ if (topt & TF_USE_SCALE) {
+ (void) printf("%cUSE_SCALE", sep);
+ topt &= ~TF_USE_SCALE;
+ sep = ',';
+ }
+# endif /* defined(TF_USE_SCALE) */
+
+# if defined(TF_WASIDLE)
+ if (topt & TF_WASIDLE) {
+ (void) printf("%cWASIDLE", sep);
+ topt &= ~TF_WASIDLE;
+ sep = ',';
+ }
+# endif /* defined(TF_WASIDLE) */
+
+# if defined(TF_WASFRECOVERY)
+ if (topt & TF_WASFRECOVERY) {
+ (void) printf("%cWASFRECOVERY", sep);
+ topt &= ~TF_WASFRECOVERY;
+ sep = ',';
+ }
+# endif /* defined(TF_WASFRECOVERY) */
+
+# if defined(TF_WILL_SACK)
+ if (topt & TF_WILL_SACK) {
+ (void) printf("%cWILL_SACK", sep);
+ topt &= ~TF_WILL_SACK;
+ sep = ',';
+ }
+# endif /* defined(TF_WILL_SACK) */
+
+ if (topt)
+ (void) printf("%cUNKNOWN=%#x", sep, topt);
+ if (Ffield)
+ putchar(Terminator);
+ }
+ }
+#endif /* defined(HASTCPOPT) */
+
+#if defined(HASTCPTPIW)
+ if (Ftcptpi & TCPTPI_WINDOWS) {
+ if (Lf->lts.rws) {
+ if (Ffield)
+ putchar(LSOF_FID_TCPTPI);
+ else {
+ if (ps)
+ putchar(' ');
+ else
+ putchar('(');
+ }
+ (void) printf("WR=%lu", Lf->lts.rw);
+ if (Ffield)
+ putchar(Terminator);
+ ps++;
+ }
+ if (Lf->lts.wws) {
+ if (Ffield)
+ putchar(LSOF_FID_TCPTPI);
+ else {
+ if (ps)
+ putchar(' ');
+ else
+ putchar('(');
+ }
+ (void) printf("WW=%lu", Lf->lts.ww);
+ if (Ffield)
+ putchar(Terminator);
+ ps++;
+ }
+ }
+#endif /* defined(HASTCPTPIW) */
+
+ if (ps && !Ffield)
+ putchar(')');
+ if (nl)
+ putchar('\n');
+}
+#else /* !defined(USE_LIB_PRINT_TCPTPI) */
+char ptti_d1[] = "d"; char *ptti_d2 = ptti_d1;
+#endif /* defined(USE_LIB_PRINT_TCPTPI) */
diff --git a/lib/rdev.c b/lib/rdev.c
new file mode 100644
index 0000000..9c5b6f4
--- /dev/null
+++ b/lib/rdev.c
@@ -0,0 +1,524 @@
+/*
+ * rdev.c -- readdev() function for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_READDEV)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: rdev.c,v 1.12 2008/10/21 16:13:23 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+_PROTOTYPE(static int rmdupdev,(struct l_dev ***dp, int n, char *nm));
+
+
+/*
+ * To use this source file:
+ *
+ * 1. Define DIRTYPE as:
+ *
+ * #define DIRTYPE direct
+ * or #define DIRTYPE dirent
+ *
+ * 2. Define HASDNAMLEN if struct DIRTYPE has a d_namlen element, giving
+ * the length of d_name.
+ *
+ * 3. Define the RDEV_EXPDEV macro to apply special handling to device
+ * numbers, as required. For example, for EP/IX 2.1.1:
+ *
+ * #define RDEV_EXPDEV(n) expdev(n)
+ *
+ * to use the expdev() function to expand device numbers. If
+ * no RDEV_EXPDEV macro is defined, it defaults to:
+ *
+ * #define RDEV_EXPDEV(n) (n)
+ *
+ * 4. Define HASBLKDEV to request that information on S_IFBLK devices be
+ * recorded in BDevtp[].
+ *
+ * Define NOWARNBLKDEV to suppress the issuance of a warning when no
+ * block devices are found.
+ *
+ * 5. Define RDEV_STATFN to be a stat function other than stat() or lstat()
+ * -- e.g.,
+ *
+ * #define RDEV_STATFN private_stat
+ *
+ * 6. Define HAS_STD_CLONE to request that clone device information be stored
+ * in standard clone structures (defined in lsof.h and addressed via
+ * Clone). If HAS_STD_CLONE is defined, these must also be defined:
+ *
+ * a. Define CLONEMAJ to be the name of the constant or
+ * variable that defines the clone major device -- e.g.,
+ *
+ * #define CLONEMAJ CloneMaj
+ *
+ * b. Define HAVECLONEMAJ to be the name of the variable that
+ * contains the status of the clone major device -- e.g.,
+ *
+ * #define HAVECLONEMAJ HaveCloneMaj
+ *
+ * Define HAS_STD_CLONE to be 1 if readdev() is expected to build the
+ * clone table, the clone table is cached (if HASDCACHE is defined), and
+ * there is a function to clear the cache table when the device table must
+ * be reloaded. (See dvch.c for naming the clone cache build and clear
+ * functions.)
+ */
+
+
+# if !defined(RDEV_EXPDEV)
+#define RDEV_EXPDEV(n) (n)
+# endif /* !defined(RDEV_EXPDEV) */
+
+# if !defined(RDEV_STATFN)
+# if defined(USE_STAT)
+#define RDEV_STATFN stat
+# else /* !defined(USE_STAT) */
+#define RDEV_STATFN lstat
+# endif /* defined(USE_STAT) */
+# endif /* !defined(RDEV_STATFN) */
+
+
+/*
+ * readdev() - read device names, modes and types
+ */
+
+void
+readdev(skip)
+ int skip; /* skip device cache read if 1 */
+{
+
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+ struct clone *c;
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+
+# if defined(HASDCACHE)
+ int dcrd;
+# endif /* defined(HASDCACHE) */
+
+ DIR *dfp;
+ int dnamlen;
+ struct DIRTYPE *dp;
+ char *fp = (char *)NULL;
+ int i = 0;
+
+# if defined(HASBLKDEV)
+ int j = 0;
+# endif /* defined(HASBLKDEV) */
+
+ char *path = (char *)NULL;
+ MALLOC_S pl;
+ struct stat sb;
+
+ if (Sdev)
+ return;
+
+# if defined(HASDCACHE)
+/*
+ * Read device cache, as directed.
+ */
+ if (!skip) {
+ if (DCstate == 2 || DCstate == 3) {
+ if ((dcrd = read_dcache()) == 0)
+ return;
+ }
+ } else
+ dcrd = 1;
+# endif /* defined(HASDCACHE) */
+
+ Dstkn = Dstkx = 0;
+ Dstk = (char **)NULL;
+ (void) stkdir("/dev");
+/*
+ * Unstack the next /dev or /dev/<subdirectory> directory.
+ */
+ while (--Dstkx >= 0) {
+ if (!(dfp = OpenDir(Dstk[Dstkx]))) {
+
+# if defined(WARNDEVACCESS)
+ if (!Fwarn) {
+ (void) fprintf(stderr, "%s: WARNING: can't open: ", Pn);
+ safestrprt(Dstk[Dstkx], stderr, 1);
+ }
+# endif /* defined(WARNDEVACCESS) */
+
+ (void) free((FREE_P *)Dstk[Dstkx]);
+ Dstk[Dstkx] = (char *)NULL;
+ continue;
+ }
+ if (path) {
+ (void) free((FREE_P *)path);
+ path = (char *)NULL;
+ }
+ if (!(path = mkstrcat(Dstk[Dstkx], -1, "/", 1, (char *)NULL, -1,
+ &pl)))
+ {
+ (void) fprintf(stderr, "%s: no space for: ", Pn);
+ safestrprt(Dstk[Dstkx], stderr, 1);
+ Exit(1);
+ }
+ (void) free((FREE_P *)Dstk[Dstkx]);
+ Dstk[Dstkx] = (char *)NULL;
+ /*
+ * Scan the directory.
+ */
+ for (dp = ReadDir(dfp); dp; dp = ReadDir(dfp)) {
+ if (dp->d_ino == 0 || dp->d_name[0] == '.')
+ continue;
+ /*
+ * Form the full path name and get its status.
+ */
+
+# if defined(HASDNAMLEN)
+ dnamlen = (int)dp->d_namlen;
+# else /* !defined(HASDNAMLEN) */
+ dnamlen = (int)strlen(dp->d_name);
+# endif /* defined(HASDNAMLEN) */
+
+ if (fp) {
+ (void) free((FREE_P *)fp);
+ fp = (char *)NULL;
+ }
+ if (!(fp = mkstrcat(path, pl, dp->d_name, dnamlen,
+ (char *)NULL, -1, (MALLOC_S *)NULL)))
+ {
+ (void) fprintf(stderr, "%s: no space for: ", Pn);
+ safestrprt(path, stderr, 0);
+ safestrprtn(dp->d_name, dnamlen, stderr, 1);
+ Exit(1);
+ }
+ if (RDEV_STATFN(fp, &sb) != 0) {
+ if (errno == ENOENT) /* a sym link to nowhere? */
+ continue;
+
+# if defined(WARNDEVACCESS)
+ if (!Fwarn) {
+ int errno_save = errno;
+
+ (void) fprintf(stderr, "%s: can't stat ", Pn);
+ safestrprt(fp, stderr, 0);
+ (void) fprintf(stderr, ": %s\n", strerror(errno_save));
+ }
+# endif /* defined(WARNDEVACCESS) */
+
+ continue;
+ }
+ /*
+ * If it's a subdirectory, stack its name for later
+ * processing.
+ */
+ if ((sb.st_mode & S_IFMT) == S_IFDIR) {
+ (void) stkdir(fp);
+ continue;
+ }
+ if ((sb.st_mode & S_IFMT) == S_IFCHR) {
+
+ /*
+ * Save character device information in Devtp[].
+ */
+ if (i >= Ndev) {
+ Ndev += DEVINCR;
+ if (!Devtp)
+ Devtp = (struct l_dev *)malloc(
+ (MALLOC_S)(sizeof(struct l_dev)*Ndev));
+ else
+ Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp,
+ (MALLOC_S)(sizeof(struct l_dev)*Ndev));
+ if (!Devtp) {
+ (void) fprintf(stderr,
+ "%s: no space for character device\n", Pn);
+ Exit(1);
+ }
+ }
+ Devtp[i].rdev = RDEV_EXPDEV(sb.st_rdev);
+ Devtp[i].inode = (INODETYPE)sb.st_ino;
+ if (!(Devtp[i].name = mkstrcpy(fp, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: no space for device name: ", Pn);
+ safestrprt(fp, stderr, 1);
+ Exit(1);
+ }
+ Devtp[i].v = 0;
+
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+ if (HAVECLONEMAJ && GET_MAJ_DEV(Devtp[i].rdev) == CLONEMAJ)
+ {
+
+ /*
+ * Record clone device information.
+ */
+ if (!(c = (struct clone *)malloc(sizeof(struct clone))))
+ {
+ (void) fprintf(stderr,
+ "%s: no space for clone device: ", Pn);
+ safestrprt(fp, stderr, 1);
+ Exit(1);
+ }
+ c->dx = i;
+ c->next = Clone;
+ Clone = c;
+ }
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+
+ i++;
+ }
+
+# if defined(HASBLKDEV)
+ if ((sb.st_mode & S_IFMT) == S_IFBLK) {
+
+ /*
+ * Save block device information in BDevtp[].
+ */
+ if (j >= BNdev) {
+ BNdev += DEVINCR;
+ if (!BDevtp)
+ BDevtp = (struct l_dev *)malloc(
+ (MALLOC_S)(sizeof(struct l_dev)*BNdev));
+ else
+ BDevtp = (struct l_dev *)realloc((MALLOC_P *)BDevtp,
+ (MALLOC_S)(sizeof(struct l_dev)*BNdev));
+ if (!BDevtp) {
+ (void) fprintf(stderr,
+ "%s: no space for block device\n", Pn);
+ Exit(1);
+ }
+ }
+ BDevtp[j].name = fp;
+ fp = (char *)NULL;
+ BDevtp[j].inode = (INODETYPE)sb.st_ino;
+ BDevtp[j].rdev = RDEV_EXPDEV(sb.st_rdev);
+ BDevtp[j].v = 0;
+ j++;
+ }
+# endif /* defined(HASBLKDEV) */
+
+ }
+ (void) CloseDir(dfp);
+ }
+/*
+ * Free any allocated space.
+ */
+ if (!Dstk) {
+ (void) free((FREE_P *)Dstk);
+ Dstk = (char **)NULL;
+ }
+ if (fp)
+ (void) free((FREE_P *)fp);
+ if (path)
+ (void) free((FREE_P *)path);
+
+# if defined(HASBLKDEV)
+/*
+ * Reduce the BDevtp[] (optional) and Devtp[] tables to their minimum
+ * sizes; allocate and build sort pointer lists; and sort the tables by
+ * device number.
+ */
+ if (BNdev) {
+ if (BNdev > j) {
+ BNdev = j;
+ BDevtp = (struct l_dev *)realloc((MALLOC_P *)BDevtp,
+ (MALLOC_S)(sizeof(struct l_dev) * BNdev));
+ }
+ if (!(BSdev = (struct l_dev **)malloc(
+ (MALLOC_S)(sizeof(struct l_dev *) * BNdev))))
+ {
+ (void) fprintf(stderr,
+ "%s: no space for block device sort pointers\n", Pn);
+ Exit(1);
+ }
+ for (j = 0; j < BNdev; j++) {
+ BSdev[j] = &BDevtp[j];
+ }
+ (void) qsort((QSORT_P *)BSdev, (size_t)BNdev,
+ (size_t)sizeof(struct l_dev *), compdev);
+ BNdev = rmdupdev(&BSdev, BNdev, "block");
+ }
+
+# if !defined(NOWARNBLKDEV)
+ else {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: no block devices found\n", Pn);
+ }
+# endif /* !defined(NOWARNBLKDEV) */
+# endif /* defined(HASBLKDEV) */
+
+ if (Ndev) {
+ if (Ndev > i) {
+ Ndev = i;
+ Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp,
+ (MALLOC_S)(sizeof(struct l_dev) * Ndev));
+ }
+ if (!(Sdev = (struct l_dev **)malloc(
+ (MALLOC_S)(sizeof(struct l_dev *) * Ndev))))
+ {
+ (void) fprintf(stderr,
+ "%s: no space for character device sort pointers\n", Pn);
+ Exit(1);
+ }
+ for (i = 0; i < Ndev; i++) {
+ Sdev[i] = &Devtp[i];
+ }
+ (void) qsort((QSORT_P *)Sdev, (size_t)Ndev,
+ (size_t)sizeof(struct l_dev *), compdev);
+ Ndev = rmdupdev(&Sdev, Ndev, "char");
+ } else {
+ (void) fprintf(stderr, "%s: no character devices found\n", Pn);
+ Exit(1);
+ }
+
+# if defined(HASDCACHE)
+/*
+ * Write device cache file, as required.
+ */
+ if (DCstate == 1 || (DCstate == 3 && dcrd))
+ write_dcache();
+# endif /* defined(HASDCACHE) */
+
+}
+
+
+# if defined(HASDCACHE)
+/*
+ * rereaddev() - reread device names, modes and types
+ */
+
+void
+rereaddev()
+{
+ (void) clr_devtab();
+
+# if defined(DCACHE_CLR)
+ (void) DCACHE_CLR();
+# endif /* defined(DCACHE_CLR) */
+
+ readdev(1);
+ DCunsafe = 0;
+}
+#endif /* defined(HASDCACHE) */
+
+
+/*
+ * rmdupdev() - remove duplicate (major/minor/inode) devices
+ */
+
+static int
+rmdupdev(dp, n, nm)
+ struct l_dev ***dp; /* device table pointers address */
+ int n; /* number of pointers */
+ char *nm; /* device table name for error message */
+{
+
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+ struct clone *c, *cp;
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+
+ int i, j, k;
+ struct l_dev **p;
+
+ for (i = j = 0, p = *dp; i < n ;) {
+ for (k = i + 1; k < n; k++) {
+ if (p[i]->rdev != p[k]->rdev || p[i]->inode != p[k]->inode)
+ break;
+
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+ /*
+ * See if we're deleting a duplicate clone device. If so,
+ * delete its clone table entry.
+ */
+ for (c = Clone, cp = (struct clone *)NULL;
+ c;
+ cp = c, c = c->next)
+ {
+ if (&Devtp[c->dx] != p[k])
+ continue;
+ if (!cp)
+ Clone = c->next;
+ else
+ cp->next = c->next;
+ (void) free((FREE_P *)c);
+ break;
+ }
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+
+ }
+ if (i != j)
+ p[j] = p[i];
+ j++;
+ i = k;
+ }
+ if (n == j)
+ return(n);
+ if (!(*dp = (struct l_dev **)realloc((MALLOC_P *)*dp,
+ (MALLOC_S)(j * sizeof(struct l_dev *)))))
+ {
+ (void) fprintf(stderr, "%s: can't realloc %s device pointers\n",
+ Pn, nm);
+ Exit(1);
+ }
+ return(j);
+}
+
+
+# if defined(HASDCACHE)
+/*
+ * vfy_dev() - verify a device table entry (usually when DCunsafe == 1)
+ *
+ * Note: rereads entire device table when an entry can't be verified.
+ */
+
+int
+vfy_dev(dp)
+ struct l_dev *dp; /* device table pointer */
+{
+ struct stat sb;
+
+ if (!DCunsafe || dp->v)
+ return(1);
+ if (RDEV_STATFN(dp->name, &sb) != 0
+ || dp->rdev != RDEV_EXPDEV(sb.st_rdev)
+ || dp->inode != sb.st_ino) {
+ (void) rereaddev();
+ return(0);
+ }
+ dp->v = 1;
+ return(1);
+}
+# endif /* defined(HASDCACHE) */
+#else /* !defined(USE_LIB_READDEV) */
+char rdev_d1[] = "d"; char *rdev_d2 = rdev_d1;
+#endif /* defined(USE_LIB_READDEV) */
diff --git a/lib/regex.c b/lib/regex.c
new file mode 100644
index 0000000..88e959d
--- /dev/null
+++ b/lib/regex.c
@@ -0,0 +1,6328 @@
+/*
+ * regex.c -- POSIX-conformant regular expression function set for the lsof
+ * library
+ *
+ * This file is used when the UNIX dialect does not have a POSIX-conformant
+ * regular expression function set. In that case USE_LIB_REGEX is defined.
+ *
+ * V. Abell <abe@purdue.edu>
+ * Purdue University Computing Center
+ */
+
+
+/*
+ * Copyright 2000 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.
+ *
+ * This software has been adapted from snprintf.c in sendmail 8.9.3. It
+ * is subject to the sendmail copyright statements listed below, and the
+ * sendmail licensing terms stated in the sendmail LICENSE file comment
+ * section of this file.
+ *
+ * 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.
+ */
+
+
+#include "../machine.h"
+
+#ifdef USE_LIB_REGEX
+/*
+ * This file comes from GLIBC 2.2. It is used when the UNIX dialect does not
+ * have a POSIX-conformant regular expression function set. In that case
+ * USE_LIB_REGEX is defined.
+ */
+
+/* Extended regular expression matching and search library,
+ version 0.12.
+ (Implements POSIX draft P1003.2/D11.2, except for some of the
+ internationalization features.)
+ Copyright (C) 1993-1999, 2000 Free Software Foundation, Inc.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined _AIX && !defined REGEX_MALLOC
+ #pragma alloca
+#endif
+
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef PARAMS
+# if defined __GNUC__ || (defined __STDC__ && __STDC__)
+# define PARAMS(args) args
+# else
+# define PARAMS(args) ()
+# endif /* GCC. */
+#endif /* Not PARAMS. */
+
+#if defined STDC_HEADERS && !defined emacs
+# include <stddef.h>
+#else
+/* We need this for `regex.h', and perhaps for the Emacs include files. */
+# include <sys/types.h>
+#endif
+
+#define WIDE_CHAR_SUPPORT (HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_BTOWC)
+
+/* For platform which support the ISO C amendement 1 functionality we
+ support user defined character classes. */
+#if defined _LIBC || WIDE_CHAR_SUPPORT
+/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */
+# include <wchar.h>
+# include <wctype.h>
+#endif
+
+#ifdef _LIBC
+/* We have to keep the namespace clean. */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+ __regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+ __re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+ __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+ __re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+ __re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+ __re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+ __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+# define btowc __btowc
+
+/* We are also using some library internals. */
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <langinfo.h>
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) __dcgettext ("libc", msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+/* The `emacs' switch turns on certain matching commands
+ that make sense only in Emacs. */
+#ifdef emacs
+
+# include "lisp.h"
+# include "buffer.h"
+# include "syntax.h"
+
+#else /* not emacs */
+
+/* If we are not linking with Emacs proper,
+ we can't use the relocating allocator
+ even if config.h says that we can. */
+# undef REL_ALLOC
+
+# if defined STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# else
+char *malloc ();
+char *realloc ();
+# endif
+
+/* When used in Emacs's lib-src, we need to get bzero and bcopy somehow.
+ If nothing else has been done, use the method below. */
+# ifdef INHIBIT_STRING_HEADER
+# if !(defined HAVE_BZERO && defined HAVE_BCOPY)
+# if !defined bzero && !defined bcopy
+# undef INHIBIT_STRING_HEADER
+# endif
+# endif
+# endif
+
+/* This is the normal way of making sure we have a bcopy and a bzero.
+ This is used in most programs--a few other programs avoid this
+ by defining INHIBIT_STRING_HEADER. */
+# ifndef INHIBIT_STRING_HEADER
+# if defined HAVE_STRING_H || defined STDC_HEADERS || defined _LIBC
+# include <string.h>
+# ifndef bzero
+# ifndef _LIBC
+# define bzero(s, n) (memset (s, '\0', n), (s))
+# else
+# define bzero(s, n) __bzero (s, n)
+# endif
+# endif
+# else
+# include <strings.h>
+# ifndef memcmp
+# define memcmp(s1, s2, n) bcmp (s1, s2, n)
+# endif
+# ifndef memcpy
+# define memcpy(d, s, n) (bcopy (s, d, n), (d))
+# endif
+# endif
+# endif
+
+/* Define the syntax stuff for \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match_2. */
+# ifndef Sword
+# define Sword 1
+# endif
+
+# ifdef SWITCH_ENUM_BUG
+# define SWITCH_ENUM_CAST(x) ((int)(x))
+# else
+# define SWITCH_ENUM_CAST(x) (x)
+# endif
+
+#endif /* not emacs */
+
+#if defined _LIBC || HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#ifndef MB_LEN_MAX
+# define MB_LEN_MAX 1
+#endif
+
+/* Get the interface, including the syntax bits. */
+/* Disabled by V. Abell on January 29, 2001: #include <regex.h> */
+#include "../regex.h"
+
+/* isalpha etc. are used for the character classes. */
+#include <ctype.h>
+
+/* Jim Meyering writes:
+
+ "... Some ctype macros are valid only for character codes that
+ isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+ using /bin/cc or gcc but without giving an ansi option). So, all
+ ctype uses should be through macros like ISPRINT... If
+ STDC_HEADERS is defined, then autoconf has verified that the ctype
+ macros don't need to be guarded with references to isascii. ...
+ Defining isascii to 1 should let any compiler worth its salt
+ eliminate the && through constant folding."
+ Solaris defines some of these symbols so we must undefine them first. */
+
+#undef ISASCII
+#if defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII)
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII (c) && isblank (c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
+#else
+# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
+#endif
+
+#undef ISPRINT
+#define ISPRINT(c) (ISASCII (c) && isprint (c))
+#define ISDIGIT(c) (ISASCII (c) && isdigit (c))
+#define ISALNUM(c) (ISASCII (c) && isalnum (c))
+#define ISALPHA(c) (ISASCII (c) && isalpha (c))
+#define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
+#define ISLOWER(c) (ISASCII (c) && islower (c))
+#define ISPUNCT(c) (ISASCII (c) && ispunct (c))
+#define ISSPACE(c) (ISASCII (c) && isspace (c))
+#define ISUPPER(c) (ISASCII (c) && isupper (c))
+#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
+
+#ifdef _tolower
+# define TOLOWER(c) _tolower(c)
+#else
+# define TOLOWER(c) tolower(c)
+#endif
+
+#ifndef NULL
+# define NULL (void *)0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+ since ours (we hope) works properly with all combinations of
+ machines, compilers, `char' and `unsigned char' argument types.
+ (Per Bothner suggested the basic approach.) */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+# define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+# define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+
+#ifndef emacs
+/* How many characters in the character set. */
+# define CHAR_SET_SIZE 256
+
+# ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+# else /* not SYNTAX_TABLE */
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+ bzero (re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 0; c < CHAR_SET_SIZE; ++c)
+ if (ISALNUM (c))
+ re_syntax_table[c] = Sword;
+
+ re_syntax_table['_'] = Sword;
+
+ done = 1;
+}
+
+# endif /* not SYNTAX_TABLE */
+
+# define SYNTAX(c) re_syntax_table[(unsigned char) (c)]
+
+#endif /* emacs */
+
+/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
+ use `alloca' instead of `malloc'. This is because using malloc in
+ re_search* or re_match* could cause memory leaks when C-g is used in
+ Emacs; also, malloc is slower and causes storage fragmentation. On
+ the other hand, malloc is more portable, and easier to debug.
+
+ Because we sometimes use alloca, some routines have to be macros,
+ not functions -- `alloca'-allocated space disappears at the end of the
+ function it is called in. */
+
+#ifdef REGEX_MALLOC
+
+# define REGEX_ALLOCATE malloc
+# define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+# define REGEX_FREE free
+
+#else /* not REGEX_MALLOC */
+
+/* Emacs already defines alloca, sometimes. */
+# ifndef alloca
+
+/* Make alloca work the best possible way. */
+# ifdef __GNUC__
+# define alloca __builtin_alloca
+# else /* not __GNUC__ */
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# endif /* HAVE_ALLOCA_H */
+# endif /* not __GNUC__ */
+
+# endif /* not alloca */
+
+# define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable. */
+# define REGEX_REALLOCATE(source, osize, nsize) \
+ (destination = (char *) alloca (nsize), \
+ memcpy (destination, source, osize))
+
+/* No need to do anything to free, after alloca. */
+# define REGEX_FREE(arg) ((void)0) /* Do nothing! But inhibit gcc warning. */
+
+#endif /* not REGEX_MALLOC */
+
+/* Define how to allocate the failure stack. */
+
+#if defined REL_ALLOC && defined REGEX_MALLOC
+
+# define REGEX_ALLOCATE_STACK(size) \
+ r_alloc (&failure_stack_ptr, (size))
+# define REGEX_REALLOCATE_STACK(source, osize, nsize) \
+ r_re_alloc (&failure_stack_ptr, (nsize))
+# define REGEX_FREE_STACK(ptr) \
+ r_alloc_free (&failure_stack_ptr)
+
+#else /* not using relocating allocator */
+
+# ifdef REGEX_MALLOC
+
+# define REGEX_ALLOCATE_STACK malloc
+# define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize)
+# define REGEX_FREE_STACK free
+
+# else /* not REGEX_MALLOC */
+
+# define REGEX_ALLOCATE_STACK alloca
+
+# define REGEX_REALLOCATE_STACK(source, osize, nsize) \
+ REGEX_REALLOCATE (source, osize, nsize)
+/* No need to explicitly free anything. */
+# define REGEX_FREE_STACK(arg)
+
+# endif /* not REGEX_MALLOC */
+#endif /* not using relocating allocator */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+ `string1' or just past its end. This works if PTR is NULL, which is
+ a good thing. */
+#define FIRST_STRING_P(ptr) \
+ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail. */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define RETALLOC_IF(addr, n, t) \
+ if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t)
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits. */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+static int re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp,
+ const char *string1, int size1,
+ const char *string2, int size2,
+ int pos,
+ struct re_registers *regs,
+ int stop));
+
+/* These are the command codes that appear in compiled regular
+ expressions. Some opcodes are followed by argument bytes. A
+ command code can specify any interpretation whatsoever for its
+ arguments. Zero bytes may appear in the compiled regular expression. */
+
+typedef enum
+{
+ no_op = 0,
+
+ /* Succeed right away--no more backtracking. */
+ succeed,
+
+ /* Followed by one byte giving n, then by n literal bytes. */
+ exactn,
+
+ /* Matches any (more or less) character. */
+ anychar,
+
+ /* Matches any one char belonging to specified set. First
+ following byte is number of bitmap bytes. Then come bytes
+ for a bitmap saying which chars are in. Bits in each byte
+ are ordered low-bit-first. A character is in the set if its
+ bit is 1. A character too large to have a bit in the map is
+ automatically not in the set. */
+ charset,
+
+ /* Same parameters as charset, but match any character that is
+ not one of those specified. */
+ charset_not,
+
+ /* Start remembering the text that is matched, for storing in a
+ register. Followed by one byte with the register number, in
+ the range 0 to one less than the pattern buffer's re_nsub
+ field. Then followed by one byte with the number of groups
+ inner to this one. (This last has to be part of the
+ start_memory only because we need it in the on_failure_jump
+ of re_match_2.) */
+ start_memory,
+
+ /* Stop remembering the text that is matched and store it in a
+ memory register. Followed by one byte with the register
+ number, in the range 0 to one less than `re_nsub' in the
+ pattern buffer, and one byte with the number of inner groups,
+ just like `start_memory'. (We need the number of inner
+ groups here because we don't have any easy way of finding the
+ corresponding start_memory when we're at a stop_memory.) */
+ stop_memory,
+
+ /* Match a duplicate of something remembered. Followed by one
+ byte containing the register number. */
+ duplicate,
+
+ /* Fail unless at beginning of line. */
+ begline,
+
+ /* Fail unless at end of line. */
+ endline,
+
+ /* Succeeds if at beginning of buffer (if emacs) or at beginning
+ of string to be matched (if not). */
+ begbuf,
+
+ /* Analogously, for end of buffer/string. */
+ endbuf,
+
+ /* Followed by two byte relative address to which to jump. */
+ jump,
+
+ /* Same as jump, but marks the end of an alternative. */
+ jump_past_alt,
+
+ /* Followed by two-byte relative address of place to resume at
+ in case of failure. */
+ on_failure_jump,
+
+ /* Like on_failure_jump, but pushes a placeholder instead of the
+ current string position when executed. */
+ on_failure_keep_string_jump,
+
+ /* Throw away latest failure point and then jump to following
+ two-byte relative address. */
+ pop_failure_jump,
+
+ /* Change to pop_failure_jump if know won't have to backtrack to
+ match; otherwise change to jump. This is used to jump
+ back to the beginning of a repeat. If what follows this jump
+ clearly won't match what the repeat does, such that we can be
+ sure that there is no use backtracking out of repetitions
+ already matched, then we change it to a pop_failure_jump.
+ Followed by two-byte address. */
+ maybe_pop_jump,
+
+ /* Jump to following two-byte address, and push a dummy failure
+ point. This failure point will be thrown away if an attempt
+ is made to use it for a failure. A `+' construct makes this
+ before the first repeat. Also used as an intermediary kind
+ of jump when compiling an alternative. */
+ dummy_failure_jump,
+
+ /* Push a dummy failure point and continue. Used at the end of
+ alternatives. */
+ push_dummy_failure,
+
+ /* Followed by two-byte relative address and two-byte number n.
+ After matching N times, jump to the address upon failure. */
+ succeed_n,
+
+ /* Followed by two-byte relative address, and two-byte number n.
+ Jump to the address N times, then fail. */
+ jump_n,
+
+ /* Set the following two-byte relative address to the
+ subsequent two-byte number. The address *includes* the two
+ bytes of number. */
+ set_number_at,
+
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound /* Succeeds if not at a word boundary. */
+
+#ifdef emacs
+ ,before_dot, /* Succeeds if before point. */
+ at_dot, /* Succeeds if at point. */
+ after_dot, /* Succeeds if after point. */
+
+ /* Matches any character whose syntax is specified. Followed by
+ a byte which contains a syntax code, e.g., Sword. */
+ syntaxspec,
+
+ /* Matches any character whose syntax is not that specified. */
+ notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+
+/* Common operations on the compiled pattern. */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
+
+#define STORE_NUMBER(destination, number) \
+ do { \
+ (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; \
+ } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+ the byte after where the number is stored. Therefore, DESTINATION
+ must be an lvalue. */
+
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ do { \
+ STORE_NUMBER (destination, number); \
+ (destination) += 2; \
+ } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+ at SOURCE. */
+
+#define EXTRACT_NUMBER(destination, source) \
+ do { \
+ (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
+ } while (0)
+
+#ifdef DEBUG
+static void extract_number _RE_ARGS ((int *dest, unsigned char *source));
+static void
+extract_number (dest, source)
+ int *dest;
+ unsigned char *source;
+{
+ int temp = SIGN_EXTEND_CHAR (*(source + 1));
+ *dest = *source & 0377;
+ *dest += temp << 8;
+}
+
+# ifndef EXTRACT_MACROS /* To debug the macros. */
+# undef EXTRACT_NUMBER
+# define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+# endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+ SOURCE must be an lvalue. */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ do { \
+ EXTRACT_NUMBER (destination, source); \
+ (source) += 2; \
+ } while (0)
+
+#ifdef DEBUG
+static void extract_number_and_incr _RE_ARGS ((int *destination,
+ unsigned char **source));
+static void
+extract_number_and_incr (destination, source)
+ int *destination;
+ unsigned char **source;
+{
+ extract_number (destination, *source);
+ *source += 2;
+}
+
+# ifndef EXTRACT_MACROS
+# undef EXTRACT_NUMBER_AND_INCR
+# define EXTRACT_NUMBER_AND_INCR(dest, src) \
+ extract_number_and_incr (&dest, &src)
+# endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+ it is doing (if the variable `debug' is nonzero). If linked with the
+ main program in `iregex.c', you can enter patterns and strings
+ interactively. And if linked with the main program in `main.c' and
+ the other test files, you can run the already-written tests. */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging. */
+# include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging. */
+# include <assert.h>
+
+static int debug;
+
+# define DEBUG_STATEMENT(e) e
+# define DEBUG_PRINT1(x) if (debug) printf (x)
+# define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+# define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+# define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \
+ if (debug) print_partial_compiled_pattern (s, e)
+# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \
+ if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+/* Print the fastmap in human-readable form. */
+
+void
+print_fastmap (fastmap)
+ char *fastmap;
+{
+ unsigned was_a_range = 0;
+ unsigned i = 0;
+
+ while (i < (1 << BYTEWIDTH))
+ {
+ if (fastmap[i++])
+ {
+ was_a_range = 0;
+ putchar (i - 1);
+ while (i < (1 << BYTEWIDTH) && fastmap[i])
+ {
+ was_a_range = 1;
+ i++;
+ }
+ if (was_a_range)
+ {
+ printf ("-");
+ putchar (i - 1);
+ }
+ }
+ }
+ putchar ('\n');
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+ the START pointer into it and ending just before the pointer END. */
+
+void
+print_partial_compiled_pattern (start, end)
+ unsigned char *start;
+ unsigned char *end;
+{
+ int mcnt, mcnt2;
+ unsigned char *p1;
+ unsigned char *p = start;
+ unsigned char *pend = end;
+
+ if (start == NULL)
+ {
+ printf ("(null)\n");
+ return;
+ }
+
+ /* Loop over pattern commands. */
+ while (p < pend)
+ {
+#ifdef _LIBC
+ printf ("%t:\t", p - start);
+#else
+ printf ("%ld:\t", (long int) (p - start));
+#endif
+
+ switch ((re_opcode_t) *p++)
+ {
+ case no_op:
+ printf ("/no_op");
+ break;
+
+ case exactn:
+ mcnt = *p++;
+ printf ("/exactn/%d", mcnt);
+ do
+ {
+ putchar ('/');
+ putchar (*p++);
+ }
+ while (--mcnt);
+ break;
+
+ case start_memory:
+ mcnt = *p++;
+ printf ("/start_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case stop_memory:
+ mcnt = *p++;
+ printf ("/stop_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case duplicate:
+ printf ("/duplicate/%d", *p++);
+ break;
+
+ case anychar:
+ printf ("/anychar");
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ register int c, last = -100;
+ register int in_range = 0;
+
+ printf ("/charset [%s",
+ (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
+
+ assert (p + *p < pend);
+
+ for (c = 0; c < 256; c++)
+ if (c / 8 < *p
+ && (p[1 + (c/8)] & (1 << (c % 8))))
+ {
+ /* Are we starting a range? */
+ if (last + 1 == c && ! in_range)
+ {
+ putchar ('-');
+ in_range = 1;
+ }
+ /* Have we broken a range? */
+ else if (last + 1 != c && in_range)
+ {
+ putchar (last);
+ in_range = 0;
+ }
+
+ if (! in_range)
+ putchar (c);
+
+ last = c;
+ }
+
+ if (in_range)
+ putchar (last);
+
+ putchar (']');
+
+ p += 1 + *p;
+ }
+ break;
+
+ case begline:
+ printf ("/begline");
+ break;
+
+ case endline:
+ printf ("/endline");
+ break;
+
+ case on_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+#ifdef _LIBC
+ printf ("/on_failure_jump to %t", p + mcnt - start);
+#else
+ printf ("/on_failure_jump to %ld", (long int) (p + mcnt - start));
+#endif
+ break;
+
+ case on_failure_keep_string_jump:
+ extract_number_and_incr (&mcnt, &p);
+#ifdef _LIBC
+ printf ("/on_failure_keep_string_jump to %t", p + mcnt - start);
+#else
+ printf ("/on_failure_keep_string_jump to %ld",
+ (long int) (p + mcnt - start));
+#endif
+ break;
+
+ case dummy_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+#ifdef _LIBC
+ printf ("/dummy_failure_jump to %t", p + mcnt - start);
+#else
+ printf ("/dummy_failure_jump to %ld", (long int) (p + mcnt - start));
+#endif
+ break;
+
+ case push_dummy_failure:
+ printf ("/push_dummy_failure");
+ break;
+
+ case maybe_pop_jump:
+ extract_number_and_incr (&mcnt, &p);
+#ifdef _LIBC
+ printf ("/maybe_pop_jump to %t", p + mcnt - start);
+#else
+ printf ("/maybe_pop_jump to %ld", (long int) (p + mcnt - start));
+#endif
+ break;
+
+ case pop_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+#ifdef _LIBC
+ printf ("/pop_failure_jump to %t", p + mcnt - start);
+#else
+ printf ("/pop_failure_jump to %ld", (long int) (p + mcnt - start));
+#endif
+ break;
+
+ case jump_past_alt:
+ extract_number_and_incr (&mcnt, &p);
+#ifdef _LIBC
+ printf ("/jump_past_alt to %t", p + mcnt - start);
+#else
+ printf ("/jump_past_alt to %ld", (long int) (p + mcnt - start));
+#endif
+ break;
+
+ case jump:
+ extract_number_and_incr (&mcnt, &p);
+#ifdef _LIBC
+ printf ("/jump to %t", p + mcnt - start);
+#else
+ printf ("/jump to %ld", (long int) (p + mcnt - start));
+#endif
+ break;
+
+ case succeed_n:
+ extract_number_and_incr (&mcnt, &p);
+ p1 = p + mcnt;
+ extract_number_and_incr (&mcnt2, &p);
+#ifdef _LIBC
+ printf ("/succeed_n to %t, %d times", p1 - start, mcnt2);
+#else
+ printf ("/succeed_n to %ld, %d times",
+ (long int) (p1 - start), mcnt2);
+#endif
+ break;
+
+ case jump_n:
+ extract_number_and_incr (&mcnt, &p);
+ p1 = p + mcnt;
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/jump_n to %d, %d times", p1 - start, mcnt2);
+ break;
+
+ case set_number_at:
+ extract_number_and_incr (&mcnt, &p);
+ p1 = p + mcnt;
+ extract_number_and_incr (&mcnt2, &p);
+#ifdef _LIBC
+ printf ("/set_number_at location %t to %d", p1 - start, mcnt2);
+#else
+ printf ("/set_number_at location %ld to %d",
+ (long int) (p1 - start), mcnt2);
+#endif
+ break;
+
+ case wordbound:
+ printf ("/wordbound");
+ break;
+
+ case notwordbound:
+ printf ("/notwordbound");
+ break;
+
+ case wordbeg:
+ printf ("/wordbeg");
+ break;
+
+ case wordend:
+ printf ("/wordend");
+
+# ifdef emacs
+ case before_dot:
+ printf ("/before_dot");
+ break;
+
+ case at_dot:
+ printf ("/at_dot");
+ break;
+
+ case after_dot:
+ printf ("/after_dot");
+ break;
+
+ case syntaxspec:
+ printf ("/syntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+
+ case notsyntaxspec:
+ printf ("/notsyntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+# endif /* emacs */
+
+ case wordchar:
+ printf ("/wordchar");
+ break;
+
+ case notwordchar:
+ printf ("/notwordchar");
+ break;
+
+ case begbuf:
+ printf ("/begbuf");
+ break;
+
+ case endbuf:
+ printf ("/endbuf");
+ break;
+
+ default:
+ printf ("?%d", *(p-1));
+ }
+
+ putchar ('\n');
+ }
+
+#ifdef _LIBC
+ printf ("%t:\tend of pattern.\n", p - start);
+#else
+ printf ("%ld:\tend of pattern.\n", (long int) (p - start));
+#endif
+}
+
+
+void
+print_compiled_pattern (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *buffer = bufp->buffer;
+
+ print_partial_compiled_pattern (buffer, buffer + bufp->used);
+ printf ("%ld bytes used/%ld bytes allocated.\n",
+ bufp->used, bufp->allocated);
+
+ if (bufp->fastmap_accurate && bufp->fastmap)
+ {
+ printf ("fastmap: ");
+ print_fastmap (bufp->fastmap);
+ }
+
+#ifdef _LIBC
+ printf ("re_nsub: %Zd\t", bufp->re_nsub);
+#else
+ printf ("re_nsub: %ld\t", (long int) bufp->re_nsub);
+#endif
+ printf ("regs_alloc: %d\t", bufp->regs_allocated);
+ printf ("can_be_null: %d\t", bufp->can_be_null);
+ printf ("newline_anchor: %d\n", bufp->newline_anchor);
+ printf ("no_sub: %d\t", bufp->no_sub);
+ printf ("not_bol: %d\t", bufp->not_bol);
+ printf ("not_eol: %d\t", bufp->not_eol);
+ printf ("syntax: %lx\n", bufp->syntax);
+ /* Perhaps we should print the translate table? */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+ const char *where;
+ const char *string1;
+ const char *string2;
+ int size1;
+ int size2;
+{
+ int this_char;
+
+ if (where == NULL)
+ printf ("(null)");
+ else
+ {
+ if (FIRST_STRING_P (where))
+ {
+ for (this_char = where - string1; this_char < size1; this_char++)
+ putchar (string1[this_char]);
+
+ where = string2;
+ }
+
+ for (this_char = where - string2; this_char < size2; this_char++)
+ putchar (string2[this_char]);
+ }
+}
+
+void
+printchar (c)
+ int c;
+{
+ putc (c, stderr);
+}
+
+#else /* not DEBUG */
+
+# undef assert
+# define assert(e)
+
+# define DEBUG_STATEMENT(e)
+# define DEBUG_PRINT1(x)
+# define DEBUG_PRINT2(x1, x2)
+# define DEBUG_PRINT3(x1, x2, x3)
+# define DEBUG_PRINT4(x1, x2, x3, x4)
+# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+/* This has no initializer because initialized variables in Emacs
+ become read-only after dumping. */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+#ifdef DEBUG
+ if (syntax & RE_DEBUG)
+ debug = 1;
+ else if (debug) /* was on but now is not */
+ debug = 0;
+#endif /* DEBUG */
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there.
+ POSIX doesn't require that we do anything for REG_NOERROR,
+ but why not be nice? */
+
+static const char re_error_msgid[] =
+ {
+#define REG_NOERROR_IDX 0
+ gettext_noop ("Success") /* REG_NOERROR */
+ "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+ gettext_noop ("No match") /* REG_NOMATCH */
+ "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+ gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+ "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+ gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+ "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+ gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+ "\0"
+#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+ gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+ "\0"
+#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+ gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+ "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+ gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+ "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+ gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+ "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+ gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+ "\0"
+#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+ gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+ "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+ gettext_noop ("Invalid range end") /* REG_ERANGE */
+ "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+ gettext_noop ("Memory exhausted") /* REG_ESPACE */
+ "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+ gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+ "\0"
+#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+ gettext_noop ("Premature end of regular expression") /* REG_EEND */
+ "\0"
+#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
+ gettext_noop ("Regular expression too big") /* REG_ESIZE */
+ "\0"
+#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
+ gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+ };
+
+static const size_t re_error_msgid_idx[] =
+ {
+ REG_NOERROR_IDX,
+ REG_NOMATCH_IDX,
+ REG_BADPAT_IDX,
+ REG_ECOLLATE_IDX,
+ REG_ECTYPE_IDX,
+ REG_EESCAPE_IDX,
+ REG_ESUBREG_IDX,
+ REG_EBRACK_IDX,
+ REG_EPAREN_IDX,
+ REG_EBRACE_IDX,
+ REG_BADBR_IDX,
+ REG_ERANGE_IDX,
+ REG_ESPACE_IDX,
+ REG_BADRPT_IDX,
+ REG_EEND_IDX,
+ REG_ESIZE_IDX,
+ REG_ERPAREN_IDX
+ };
+
+/* Avoiding alloca during matching, to placate r_alloc. */
+
+/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the
+ searching and matching functions should not call alloca. On some
+ systems, alloca is implemented in terms of malloc, and if we're
+ using the relocating allocator routines, then malloc could cause a
+ relocation, which might (if the strings being searched are in the
+ ralloc heap) shift the data out from underneath the regexp
+ routines.
+
+ Here's another reason to avoid allocation: Emacs
+ processes input from X in a signal handler; processing X input may
+ call malloc; if input arrives while a matching routine is calling
+ malloc, then we're scrod. But Emacs can't just block input while
+ calling matching routines; then we don't notice interrupts when
+ they come in. So, Emacs blocks input around all regexp calls
+ except the matching calls, which it leaves unprotected, in the
+ faith that they will not malloc. */
+
+/* Normally, this is fine. */
+#define MATCH_MAY_ALLOCATE
+
+/* When using GNU C, we are not REALLY using the C alloca, no matter
+ what config.h may say. So don't take precautions for it. */
+#ifdef __GNUC__
+# undef C_ALLOCA
+#endif
+
+/* The match routines may not allocate if (1) they would do it with malloc
+ and (2) it's not safe for them to use malloc.
+ Note that if REL_ALLOC is defined, matching would not use malloc for the
+ failure stack, but we would still use it for the register vectors;
+ so REL_ALLOC should not affect this. */
+#if (defined C_ALLOCA || defined REGEX_MALLOC) && defined emacs
+# undef MATCH_MAY_ALLOCATE
+#endif
+
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+ re_match_2 use a failure stack. These have to be macros because of
+ REGEX_ALLOCATE_STACK. */
+
+
+/* Number of failure points for which to initially allocate space
+ when matching. If this number is exceeded, we allocate more
+ space, so it is not a hard limit. */
+#ifndef INIT_FAILURE_ALLOC
+# define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always used MAX_FAILURE_ITEMS items each time we failed.
+ This is a variable only so users of regex can assign to it; we never
+ change it ourselves. */
+
+#ifdef INT_IS_16BIT
+
+# if defined MATCH_MAY_ALLOCATE
+/* 4400 was enough to cause a crash on Alpha OSF/1,
+ whose default stack limit is 2mb. */
+long int re_max_failures = 4000;
+# else
+long int re_max_failures = 2000;
+# endif
+
+union fail_stack_elt
+{
+ unsigned char *pointer;
+ long int integer;
+};
+
+typedef union fail_stack_elt fail_stack_elt_t;
+
+typedef struct
+{
+ fail_stack_elt_t *stack;
+ unsigned long int size;
+ unsigned long int avail; /* Offset of next open position. */
+} fail_stack_type;
+
+#else /* not INT_IS_16BIT */
+
+# if defined MATCH_MAY_ALLOCATE
+/* 4400 was enough to cause a crash on Alpha OSF/1,
+ whose default stack limit is 2mb. */
+int re_max_failures = 4000;
+# else
+int re_max_failures = 2000;
+# endif
+
+union fail_stack_elt
+{
+ unsigned char *pointer;
+ int integer;
+};
+
+typedef union fail_stack_elt fail_stack_elt_t;
+
+typedef struct
+{
+ fail_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} fail_stack_type;
+
+#endif /* INT_IS_16BIT */
+
+#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
+
+
+/* Define macros to initialize and free the failure stack.
+ Do `return -2' if the alloc fails. */
+
+#ifdef MATCH_MAY_ALLOCATE
+# define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.stack = (fail_stack_elt_t *) \
+ REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+ \
+ if (fail_stack.stack == NULL) \
+ return -2; \
+ \
+ fail_stack.size = INIT_FAILURE_ALLOC; \
+ fail_stack.avail = 0; \
+ } while (0)
+
+# define RESET_FAIL_STACK() REGEX_FREE_STACK (fail_stack.stack)
+#else
+# define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.avail = 0; \
+ } while (0)
+
+# define RESET_FAIL_STACK()
+#endif
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+ Return 1 if succeeds, and 0 if either ran out of memory
+ allocating space for it or it was already too large.
+
+ REGEX_REALLOCATE_STACK requires `destination' be declared. */
+
+#define DOUBLE_FAIL_STACK(fail_stack) \
+ ((fail_stack).size > (unsigned) (re_max_failures * MAX_FAILURE_ITEMS) \
+ ? 0 \
+ : ((fail_stack).stack = (fail_stack_elt_t *) \
+ REGEX_REALLOCATE_STACK ((fail_stack).stack, \
+ (fail_stack).size * sizeof (fail_stack_elt_t), \
+ ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
+ \
+ (fail_stack).stack == NULL \
+ ? 0 \
+ : ((fail_stack).size <<= 1, \
+ 1)))
+
+
+/* Push pointer POINTER on FAIL_STACK.
+ Return 1 if was able to do so and 0 if ran out of memory allocating
+ space to do so. */
+#define PUSH_PATTERN_OP(POINTER, FAIL_STACK) \
+ ((FAIL_STACK_FULL () \
+ && !DOUBLE_FAIL_STACK (FAIL_STACK)) \
+ ? 0 \
+ : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER, \
+ 1))
+
+/* Push a pointer value onto the failure stack.
+ Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_POINTER(item) \
+ fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item)
+
+/* This pushes an integer-valued item onto the failure stack.
+ Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_INT(item) \
+ fail_stack.stack[fail_stack.avail++].integer = (item)
+
+/* Push a fail_stack_elt_t value onto the failure stack.
+ Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_ELT(item) \
+ fail_stack.stack[fail_stack.avail++] = (item)
+
+/* These three POP... operations complement the three PUSH... operations.
+ All assume that `fail_stack' is nonempty. */
+#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer
+#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer
+#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging. */
+#ifdef DEBUG
+# define DEBUG_PUSH PUSH_FAILURE_INT
+# define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT ()
+#else
+# define DEBUG_PUSH(item)
+# define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+ if we ever fail back to it.
+
+ Requires variables fail_stack, regstart, regend, reg_info, and
+ num_regs_pushed be declared. DOUBLE_FAIL_STACK requires `destination'
+ be declared.
+
+ Does `return FAILURE_CODE' if runs out of memory. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
+ do { \
+ char *destination; \
+ /* Must be int, so when we don't save any registers, the arithmetic \
+ of 0 + -1 isn't done as unsigned. */ \
+ /* Can't be int, since there is not a shred of a guarantee that int \
+ is wide enough to hold a value of something to which pointer can \
+ be assigned */ \
+ active_reg_t this_reg; \
+ \
+ DEBUG_STATEMENT (failure_id++); \
+ DEBUG_STATEMENT (nfailure_points_pushed++); \
+ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
+ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
+ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
+ \
+ DEBUG_PRINT2 (" slots needed: %ld\n", NUM_FAILURE_ITEMS); \
+ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
+ \
+ /* Ensure we have enough space allocated for what we will push. */ \
+ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
+ { \
+ if (!DOUBLE_FAIL_STACK (fail_stack)) \
+ return failure_code; \
+ \
+ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
+ (fail_stack).size); \
+ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+ } \
+ \
+ /* Push the info, starting with the registers. */ \
+ DEBUG_PRINT1 ("\n"); \
+ \
+ if (1) \
+ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+ this_reg++) \
+ { \
+ DEBUG_PRINT2 (" Pushing reg: %lu\n", this_reg); \
+ DEBUG_STATEMENT (num_regs_pushed++); \
+ \
+ DEBUG_PRINT2 (" start: %p\n", regstart[this_reg]); \
+ PUSH_FAILURE_POINTER (regstart[this_reg]); \
+ \
+ DEBUG_PRINT2 (" end: %p\n", regend[this_reg]); \
+ PUSH_FAILURE_POINTER (regend[this_reg]); \
+ \
+ DEBUG_PRINT2 (" info: %p\n ", \
+ reg_info[this_reg].word.pointer); \
+ DEBUG_PRINT2 (" match_null=%d", \
+ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" matched_something=%d", \
+ MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" ever_matched=%d", \
+ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT1 ("\n"); \
+ PUSH_FAILURE_ELT (reg_info[this_reg].word); \
+ } \
+ \
+ DEBUG_PRINT2 (" Pushing low active reg: %ld\n", lowest_active_reg);\
+ PUSH_FAILURE_INT (lowest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing high active reg: %ld\n", highest_active_reg);\
+ PUSH_FAILURE_INT (highest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing pattern %p:\n", pattern_place); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
+ PUSH_FAILURE_POINTER (pattern_place); \
+ \
+ DEBUG_PRINT2 (" Pushing string %p: `", string_place); \
+ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
+ size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ PUSH_FAILURE_POINTER (string_place); \
+ \
+ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
+ DEBUG_PUSH (failure_id); \
+ } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+ for each register. */
+#define NUM_REG_ITEMS 3
+
+/* Individual items aside from the registers. */
+#ifdef DEBUG
+# define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
+#else
+# define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack. */
+/* We used to use (num_regs - 1), which is the number of registers
+ this regexp will save; but that was changed to 5
+ to avoid stack overflow for a regexp with lots of parens. */
+#define MAX_FAILURE_ITEMS (5 * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items. */
+#define NUM_FAILURE_ITEMS \
+ (((0 \
+ ? 0 : highest_active_reg - lowest_active_reg + 1) \
+ * NUM_REG_ITEMS) \
+ + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it. */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+ We restore into the parameters, all of which should be lvalues:
+ STR -- the saved data position.
+ PAT -- the saved pattern position.
+ LOW_REG, HIGH_REG -- the highest and lowest active registers.
+ REGSTART, REGEND -- arrays of string positions.
+ REG_INFO -- array of information about each subexpression.
+
+ Also assumes the variables `fail_stack' and (if debugging), `bufp',
+ `pend', `string1', `size1', `string2', and `size2'. */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{ \
+ DEBUG_STATEMENT (unsigned failure_id;) \
+ active_reg_t this_reg; \
+ const unsigned char *string_temp; \
+ \
+ assert (!FAIL_STACK_EMPTY ()); \
+ \
+ /* Remove failure points and point to how many regs pushed. */ \
+ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
+ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
+ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
+ \
+ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
+ \
+ DEBUG_POP (&failure_id); \
+ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
+ \
+ /* If the saved string location is NULL, it came from an \
+ on_failure_keep_string_jump opcode, and we want to throw away the \
+ saved NULL, thus retaining our current position in the string. */ \
+ string_temp = POP_FAILURE_POINTER (); \
+ if (string_temp != NULL) \
+ str = (const char *) string_temp; \
+ \
+ DEBUG_PRINT2 (" Popping string %p: `", str); \
+ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ \
+ pat = (unsigned char *) POP_FAILURE_POINTER (); \
+ DEBUG_PRINT2 (" Popping pattern %p:\n", pat); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
+ \
+ /* Restore register info. */ \
+ high_reg = (active_reg_t) POP_FAILURE_INT (); \
+ DEBUG_PRINT2 (" Popping high active reg: %ld\n", high_reg); \
+ \
+ low_reg = (active_reg_t) POP_FAILURE_INT (); \
+ DEBUG_PRINT2 (" Popping low active reg: %ld\n", low_reg); \
+ \
+ if (1) \
+ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
+ { \
+ DEBUG_PRINT2 (" Popping reg: %ld\n", this_reg); \
+ \
+ reg_info[this_reg].word = POP_FAILURE_ELT (); \
+ DEBUG_PRINT2 (" info: %p\n", \
+ reg_info[this_reg].word.pointer); \
+ \
+ regend[this_reg] = (const char *) POP_FAILURE_POINTER (); \
+ DEBUG_PRINT2 (" end: %p\n", regend[this_reg]); \
+ \
+ regstart[this_reg] = (const char *) POP_FAILURE_POINTER (); \
+ DEBUG_PRINT2 (" start: %p\n", regstart[this_reg]); \
+ } \
+ else \
+ { \
+ for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \
+ { \
+ reg_info[this_reg].word.integer = 0; \
+ regend[this_reg] = 0; \
+ regstart[this_reg] = 0; \
+ } \
+ highest_active_reg = high_reg; \
+ } \
+ \
+ set_regs_matched_done = 0; \
+ DEBUG_STATEMENT (nfailure_points_popped++); \
+} /* POP_FAILURE_POINT */
+
+
+
+/* Structure for per-register (a.k.a. per-group) information.
+ Other register information, such as the
+ starting and ending positions (which are addresses), and the list of
+ inner groups (which is a bits list) are maintained in separate
+ variables.
+
+ We are making a (strictly speaking) nonportable assumption here: that
+ the compiler will pack our bit fields into something that fits into
+ the type of `word', i.e., is something that fits into one item on the
+ failure stack. */
+
+
+/* Declarations and macros for re_match_2. */
+
+typedef union
+{
+ fail_stack_elt_t word;
+ struct
+ {
+ /* This field is one if this group can match the empty string,
+ zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
+#define MATCH_NULL_UNSET_VALUE 3
+ unsigned match_null_string_p : 2;
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ unsigned ever_matched_something : 1;
+ } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+ for the subexpressions which we are currently inside. Also records
+ that those subexprs have matched. */
+#define SET_REGS_MATCHED() \
+ do \
+ { \
+ if (!set_regs_matched_done) \
+ { \
+ active_reg_t r; \
+ set_regs_matched_done = 1; \
+ for (r = lowest_active_reg; r <= highest_active_reg; r++) \
+ { \
+ MATCHED_SOMETHING (reg_info[r]) \
+ = EVER_MATCHED_SOMETHING (reg_info[r]) \
+ = 1; \
+ } \
+ } \
+ } \
+ while (0)
+
+/* Registers are set to a sentinel when they haven't yet matched. */
+static char reg_unset_dummy;
+#define REG_UNSET_VALUE (&reg_unset_dummy)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+/* Subroutine declarations and macros for regex_compile. */
+
+static reg_errcode_t regex_compile _RE_ARGS ((const char *pattern, size_t size,
+ reg_syntax_t syntax,
+ struct re_pattern_buffer *bufp));
+static void store_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc, int arg));
+static void store_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc,
+ int arg1, int arg2));
+static void insert_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc,
+ int arg, unsigned char *end));
+static void insert_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc,
+ int arg1, int arg2, unsigned char *end));
+static boolean at_begline_loc_p _RE_ARGS ((const char *pattern, const char *p,
+ reg_syntax_t syntax));
+static boolean at_endline_loc_p _RE_ARGS ((const char *p, const char *pend,
+ reg_syntax_t syntax));
+static reg_errcode_t compile_range _RE_ARGS ((unsigned int range_start,
+ const char **p_ptr,
+ const char *pend,
+ char *translate,
+ reg_syntax_t syntax,
+ unsigned char *b));
+
+/* Fetch the next character in the uncompiled pattern---translating it
+ if necessary. Also cast from a signed character in the constant
+ string passed to us by the user to an unsigned char that we can use
+ as an array index (in, e.g., `translate'). */
+#ifndef PATFETCH
+# define PATFETCH(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ if (translate) c = (unsigned char) translate[c]; \
+ } while (0)
+#endif
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D. We
+ cast the subscript to translate because some data is declared as
+ `char *', to avoid warnings when a string constant is passed. But
+ when we use a character as a subscript we must make it unsigned. */
+#ifndef TRANSLATE
+# define TRANSLATE(d) \
+ (translate ? (char) translate[(unsigned char) (d)] : (d))
+#endif
+
+
+/* Macros for outputting the compiled pattern into `buffer'. */
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 32
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ while ((unsigned long) (b - bufp->buffer + (n)) > bufp->allocated) \
+ EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it. */
+#define BUF_PUSH(c) \
+ do { \
+ GET_BUFFER_SPACE (1); \
+ *b++ = (unsigned char) (c); \
+ } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
+#define BUF_PUSH_2(c1, c2) \
+ do { \
+ GET_BUFFER_SPACE (2); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes. */
+#define BUF_PUSH_3(c1, c2, c3) \
+ do { \
+ GET_BUFFER_SPACE (3); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ *b++ = (unsigned char) (c3); \
+ } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO. We store a
+ relative address offset by the three bytes the jump itself occupies. */
+#define STORE_JUMP(op, loc, to) \
+ store_op1 (op, loc, (int) ((to) - (loc) - 3))
+
+/* Likewise, for a two-argument jump. */
+#define STORE_JUMP2(op, loc, to, arg) \
+ store_op2 (op, loc, (int) ((to) - (loc) - 3), arg)
+
+/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP(op, loc, to) \
+ insert_op1 (op, loc, (int) ((to) - (loc) - 3), b)
+
+/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP2(op, loc, to, arg) \
+ insert_op2 (op, loc, (int) ((to) - (loc) - 3), arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+ into the pattern are two bytes long. So if 2^16 bytes turns out to
+ be too small, many things would have to change. */
+/* Any other compiler which, like MSC, has allocation limit below 2^16
+ bytes will have to use approach similar to what was done below for
+ MSC and drop MAX_BUF_SIZE a bit. Otherwise you may end up
+ reallocating to 0 bytes. Such thing is not going to work too well.
+ You have been warned!! */
+#if defined _MSC_VER && !defined WIN32
+/* Microsoft C 16-bit versions limit malloc to approx 65512 bytes.
+ The REALLOC define eliminates a flurry of conversion warnings,
+ but is not required. */
+# define MAX_BUF_SIZE 65500L
+# define REALLOC(p,s) realloc ((p), (size_t) (s))
+#else
+# define MAX_BUF_SIZE (1L << 16)
+# define REALLOC(p,s) realloc ((p), (s))
+#endif
+
+/* Extend the buffer by twice its current size via realloc and
+ reset the pointers that pointed into the old block to point to the
+ correct places in the new one. If extending the buffer results in it
+ being larger than MAX_BUF_SIZE, then flag memory exhausted. */
+#if __BOUNDED_POINTERS__
+# define SET_HIGH_BOUND(P) (__ptrhigh (P) = __ptrlow (P) + bufp->allocated)
+# define MOVE_BUFFER_POINTER(P) \
+ (__ptrlow (P) += incr, SET_HIGH_BOUND (P), __ptrvalue (P) += incr)
+# define ELSE_EXTEND_BUFFER_HIGH_BOUND \
+ else \
+ { \
+ SET_HIGH_BOUND (b); \
+ SET_HIGH_BOUND (begalt); \
+ if (fixup_alt_jump) \
+ SET_HIGH_BOUND (fixup_alt_jump); \
+ if (laststart) \
+ SET_HIGH_BOUND (laststart); \
+ if (pending_exact) \
+ SET_HIGH_BOUND (pending_exact); \
+ }
+#else
+# define MOVE_BUFFER_POINTER(P) (P) += incr
+# define ELSE_EXTEND_BUFFER_HIGH_BOUND
+#endif
+#define EXTEND_BUFFER() \
+ do { \
+ unsigned char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == MAX_BUF_SIZE) \
+ return REG_ESIZE; \
+ bufp->allocated <<= 1; \
+ if (bufp->allocated > MAX_BUF_SIZE) \
+ bufp->allocated = MAX_BUF_SIZE; \
+ bufp->buffer = (unsigned char *) REALLOC (bufp->buffer, bufp->allocated);\
+ if (bufp->buffer == NULL) \
+ return REG_ESPACE; \
+ /* If the buffer moved, move all the pointers into it. */ \
+ if (old_buffer != bufp->buffer) \
+ { \
+ int incr = bufp->buffer - old_buffer; \
+ MOVE_BUFFER_POINTER (b); \
+ MOVE_BUFFER_POINTER (begalt); \
+ if (fixup_alt_jump) \
+ MOVE_BUFFER_POINTER (fixup_alt_jump); \
+ if (laststart) \
+ MOVE_BUFFER_POINTER (laststart); \
+ if (pending_exact) \
+ MOVE_BUFFER_POINTER (pending_exact); \
+ } \
+ ELSE_EXTEND_BUFFER_HIGH_BOUND \
+ } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+ {start,stop}_memory, the maximum number of groups we can report
+ things about is what fits in that byte. */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers. We just
+ ignore the excess. */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack. */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+ be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
+/* int may be not enough when sizeof(int) == 2. */
+typedef long pattern_offset_t;
+
+typedef struct
+{
+ pattern_offset_t begalt_offset;
+ pattern_offset_t fixup_alt_jump;
+ pattern_offset_t inner_group_offset;
+ pattern_offset_t laststart_offset;
+ regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+ compile_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
+
+/* The next available element. */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list. */
+#define SET_LIST_BIT(c) \
+ (b[((unsigned char) (c)) / BYTEWIDTH] \
+ |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while ('0' <= c && c <= '9') \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+#if defined _LIBC || WIDE_CHAR_SUPPORT
+/* The GNU C library provides support for user-defined character classes
+ and the functions from ISO C amendement 1. */
+# ifdef CHARCLASS_NAME_MAX
+# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
+# else
+/* This shouldn't happen but some implementation might still have this
+ problem. Use a reasonable default value. */
+# define CHAR_CLASS_MAX_LENGTH 256
+# endif
+
+# ifdef _LIBC
+# define IS_CHAR_CLASS(string) __wctype (string)
+# else
+# define IS_CHAR_CLASS(string) wctype (string)
+# endif
+#else
+# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
+
+# define IS_CHAR_CLASS(string) \
+ (STREQ (string, "alpha") || STREQ (string, "upper") \
+ || STREQ (string, "lower") || STREQ (string, "digit") \
+ || STREQ (string, "alnum") || STREQ (string, "xdigit") \
+ || STREQ (string, "space") || STREQ (string, "print") \
+ || STREQ (string, "punct") || STREQ (string, "graph") \
+ || STREQ (string, "cntrl") || STREQ (string, "blank"))
+#endif
+
+#ifndef MATCH_MAY_ALLOCATE
+
+/* If we cannot allocate large objects within re_match_2_internal,
+ we make the fail stack and register vectors global.
+ The fail stack, we grow to the maximum size when a regexp
+ is compiled.
+ The register vectors, we adjust in size each time we
+ compile a regexp, according to the number of registers it needs. */
+
+static fail_stack_type fail_stack;
+
+/* Size with which the following vectors are currently allocated.
+ That is so we can make them bigger as needed,
+ but never make them smaller. */
+static int regs_allocated_size;
+
+static const char ** regstart, ** regend;
+static const char ** old_regstart, ** old_regend;
+static const char **best_regstart, **best_regend;
+static register_info_type *reg_info;
+static const char **reg_dummy;
+static register_info_type *reg_info_dummy;
+
+/* Make the register vectors big enough for NUM_REGS registers,
+ but don't make them smaller. */
+
+static
+regex_grow_registers (num_regs)
+ int num_regs;
+{
+ if (num_regs > regs_allocated_size)
+ {
+ RETALLOC_IF (regstart, num_regs, const char *);
+ RETALLOC_IF (regend, num_regs, const char *);
+ RETALLOC_IF (old_regstart, num_regs, const char *);
+ RETALLOC_IF (old_regend, num_regs, const char *);
+ RETALLOC_IF (best_regstart, num_regs, const char *);
+ RETALLOC_IF (best_regend, num_regs, const char *);
+ RETALLOC_IF (reg_info, num_regs, register_info_type);
+ RETALLOC_IF (reg_dummy, num_regs, const char *);
+ RETALLOC_IF (reg_info_dummy, num_regs, register_info_type);
+
+ regs_allocated_size = num_regs;
+ }
+}
+
+#endif /* not MATCH_MAY_ALLOCATE */
+
+static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type
+ compile_stack,
+ regnum_t regnum));
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+ Returns one of error codes defined in `regex.h', or zero for success.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate'
+ fields are set in BUFP on entry.
+
+ If it succeeds, results are put in BUFP (if it returns an error, the
+ contents of BUFP are undefined):
+ `buffer' is the compiled pattern;
+ `syntax' is set to SYNTAX;
+ `used' is set to the length of the compiled pattern;
+ `fastmap_accurate' is zero;
+ `re_nsub' is the number of subexpressions in PATTERN;
+ `not_bol' and `not_eol' are zero;
+
+ The `fastmap' and `newline_anchor' fields are neither
+ examined nor set. */
+
+/* Return, freeing storage we allocated. */
+#define FREE_STACK_RETURN(value) \
+ return (free (compile_stack.stack), value)
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+ const char *pattern;
+ size_t size;
+ reg_syntax_t syntax;
+ struct re_pattern_buffer *bufp;
+{
+ /* We fetch characters from PATTERN here. Even though PATTERN is
+ `char *' (i.e., signed), we declare these variables as unsigned, so
+ they can be reliably used as array indices. */
+ register unsigned char c, c1;
+
+ /* A random temporary spot in PATTERN. */
+ const char *p1;
+
+ /* Points to the end of the buffer, where we should append. */
+ register unsigned char *b;
+
+ /* Keeps track of unclosed groups. */
+ compile_stack_type compile_stack;
+
+ /* Points to the current (ending) position in the pattern. */
+ const char *p = pattern;
+ const char *pend = pattern + size;
+
+ /* How to translate the characters in the pattern. */
+ RE_TRANSLATE_TYPE translate = bufp->translate;
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell if a new exact-match
+ character can be added to that command or if the character requires
+ a new `exactn' command. */
+ unsigned char *pending_exact = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells, e.g., postfix * where to find the start of its
+ operand. Reset at the beginning of groups and alternatives. */
+ unsigned char *laststart = 0;
+
+ /* Address of beginning of regexp, or inside of last group. */
+ unsigned char *begalt;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ which to go back if the interval is invalid. */
+ const char *beg_interval;
+
+ /* Address of the place where a forward jump should go to the end of
+ the containing expression. Each alternative of an `or' -- except the
+ last -- ends with a forward jump of this sort. */
+ unsigned char *fixup_alt_jump = 0;
+
+ /* Counts open-groups as they are encountered. Remembered for the
+ matching close-group on the compile stack, so the same register
+ number is put in the stop_memory as the start_memory. */
+ regnum_t regnum = 0;
+
+#ifdef DEBUG
+ DEBUG_PRINT1 ("\nCompiling pattern: ");
+ if (debug)
+ {
+ unsigned debug_count;
+
+ for (debug_count = 0; debug_count < size; debug_count++)
+ putchar (pattern[debug_count]);
+ putchar ('\n');
+ }
+#endif /* DEBUG */
+
+ /* Initialize the compile stack. */
+ compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+ if (compile_stack.stack == NULL)
+ return REG_ESPACE;
+
+ compile_stack.size = INIT_COMPILE_STACK_SIZE;
+ compile_stack.avail = 0;
+
+ /* Initialize the pattern buffer. */
+ bufp->syntax = syntax;
+ bufp->fastmap_accurate = 0;
+ bufp->not_bol = bufp->not_eol = 0;
+
+ /* Set `used' to zero, so that if we return an error, the pattern
+ printer (for debugging) will think there's no pattern. We reset it
+ at the end. */
+ bufp->used = 0;
+
+ /* Always count groups, whether or not bufp->no_sub is set. */
+ bufp->re_nsub = 0;
+
+#if !defined emacs && !defined SYNTAX_TABLE
+ /* Initialize the syntax table. */
+ init_syntax_once ();
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ if (bufp->buffer)
+ { /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. */
+ RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+ }
+ else
+ { /* Caller did not allocate a buffer. Do it for them. */
+ bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+ }
+ if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE);
+
+ bufp->allocated = INIT_BUF_SIZE;
+ }
+
+ begalt = b = bufp->buffer;
+
+ /* Loop through the uncompiled pattern until we're at the end. */
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '^':
+ {
+ if ( /* If at start of pattern, it's an operator. */
+ p == pattern + 1
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's come before. */
+ || at_begline_loc_p (pattern, p, syntax))
+ BUF_PUSH (begline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '$':
+ {
+ if ( /* If at end of pattern, it's an operator. */
+ p == pend
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's next. */
+ || at_endline_loc_p (p, pend, syntax))
+ BUF_PUSH (endline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '+':
+ case '?':
+ if ((syntax & RE_BK_PLUS_QM)
+ || (syntax & RE_LIMITED_OPS))
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern... */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ FREE_STACK_RETURN (REG_BADRPT);
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+
+ {
+ /* Are we optimizing this jump? */
+ boolean keep_string_p = false;
+
+ /* 1 means zero (many) matches is allowed. */
+ char zero_times_ok = 0, many_times_ok = 0;
+
+ /* If there is a sequence of repetition chars, collapse it
+ down to just one (the right one). We can't combine
+ interval operators with these because of, e.g., `a{2}*',
+ which should only match an even number of `a's. */
+
+ for (;;)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+
+ if (p == pend)
+ break;
+
+ PATFETCH (c);
+
+ if (c == '*'
+ || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+ ;
+
+ else if (syntax & RE_BK_PLUS_QM && c == '\\')
+ {
+ if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+
+ /* If we get here, we found another repeat character. */
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok)
+ { /* More than one repetition is allowed, so put in at the
+ end a backward relative jump from `b' to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump).
+
+ But if we are at the `*' in the exact sequence `.*\n',
+ insert an unconditional jump backwards to the .,
+ instead of the beginning of the loop. This way we only
+ push a failure point once, instead of every time
+ through the loop. */
+ assert (p - 1 > pattern);
+
+ /* Allocate the space for the jump. */
+ GET_BUFFER_SPACE (3);
+
+ /* We know we are not at the first character of the pattern,
+ because laststart was nonzero. And we've already
+ incremented `p', by the way, to be the character after
+ the `*'. Do we have to do something analogous here
+ for null bytes, because of RE_DOT_NOT_NULL? */
+ if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+ && zero_times_ok
+ && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+ && !(syntax & RE_DOT_NEWLINE))
+ { /* We have .*\n. */
+ STORE_JUMP (jump, b, laststart);
+ keep_string_p = true;
+ }
+ else
+ /* Anything else. */
+ STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+ /* We've added more stuff to the buffer. */
+ b += 3;
+ }
+
+ /* On failure, jump from laststart to b + 3, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+ : on_failure_jump,
+ laststart, b + 3);
+ pending_exact = 0;
+ b += 3;
+
+ if (!zero_times_ok)
+ {
+ /* At least one repetition is required, so insert a
+ `dummy_failure_jump' before the initial
+ `on_failure_jump' instruction of the loop. This
+ effects a skip over that instruction the first time
+ we hit that loop. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+ b += 3;
+ }
+ }
+ break;
+
+
+ case '.':
+ laststart = b;
+ BUF_PUSH (anychar);
+ break;
+
+
+ case '[':
+ {
+ boolean had_char_class = false;
+ unsigned int range_start = 0xffffffff;
+
+ if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+ /* Ensure that we have enough space to push a charset: the
+ opcode, the length count, and the bitset; 34 bytes in all. */
+ GET_BUFFER_SPACE (34);
+
+ laststart = b;
+
+ /* We test `*p == '^' twice, instead of using an if
+ statement, so we only need one BUF_PUSH. */
+ BUF_PUSH (*p == '^' ? charset_not : charset);
+ if (*p == '^')
+ p++;
+
+ /* Remember the first position in the bracket expression. */
+ p1 = p;
+
+ /* Push the number of bytes in the bitmap. */
+ BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* Clear the whole map. */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* charset_not matches newline according to a syntax bit. */
+ if ((re_opcode_t) b[-2] == charset_not
+ && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+ SET_LIST_BIT ('\n');
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;)
+ {
+ if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+ PATFETCH (c);
+
+ /* \ might escape characters inside [...] and [^...]. */
+ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+ {
+ if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+ PATFETCH (c1);
+ SET_LIST_BIT (c1);
+ range_start = c1;
+ continue;
+ }
+
+ /* Could be the end of the bracket expression. If it's
+ not (i.e., when the bracket expression is `[]' so
+ far), the ']' character bit gets set way below. */
+ if (c == ']' && p != p1 + 1)
+ break;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (had_char_class && c == '-' && *p != ']')
+ FREE_STACK_RETURN (REG_ERANGE);
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character: if this is a hyphen not at the
+ beginning or the end of a list, then it's the range
+ operator. */
+ if (c == '-'
+ && !(p - 2 >= pattern && p[-2] == '[')
+ && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+ && *p != ']')
+ {
+ reg_errcode_t ret
+ = compile_range (range_start, &p, pend, translate,
+ syntax, b);
+ if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
+ range_start = 0xffffffff;
+ }
+
+ else if (p[0] == '-' && p[1] != ']')
+ { /* This handles ranges made up of characters only. */
+ reg_errcode_t ret;
+
+ /* Move past the `-'. */
+ PATFETCH (c1);
+
+ ret = compile_range (c, &p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
+ range_start = 0xffffffff;
+ }
+
+ /* See if we're at the beginning of a possible character
+ class. */
+
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+ { /* Leave room for the null. */
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if ((c == ':' && *p == ']') || p == pend)
+ break;
+ if (c1 < CHAR_CLASS_MAX_LENGTH)
+ str[c1++] = c;
+ else
+ /* This is in any case an invalid class name. */
+ str[0] = '\0';
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and `:]':
+ undo the ending character, the letters, and leave
+ the leading `:' and `[' (but set bits for them). */
+ if (c == ':' && *p == ']')
+ {
+#if defined _LIBC || WIDE_CHAR_SUPPORT
+ boolean is_lower = STREQ (str, "lower");
+ boolean is_upper = STREQ (str, "upper");
+ wctype_t wt;
+ int ch;
+
+ wt = IS_CHAR_CLASS (str);
+ if (wt == 0)
+ FREE_STACK_RETURN (REG_ECTYPE);
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ++ch)
+ {
+# ifdef _LIBC
+ if (__iswctype (__btowc (ch), wt))
+ SET_LIST_BIT (ch);
+# else
+ if (iswctype (btowc (ch), wt))
+ SET_LIST_BIT (ch);
+# endif
+
+ if (translate && (is_upper || is_lower)
+ && (ISUPPER (ch) || ISLOWER (ch)))
+ SET_LIST_BIT (ch);
+ }
+
+ had_char_class = true;
+#else
+ int ch;
+ boolean is_alnum = STREQ (str, "alnum");
+ boolean is_alpha = STREQ (str, "alpha");
+ boolean is_blank = STREQ (str, "blank");
+ boolean is_cntrl = STREQ (str, "cntrl");
+ boolean is_digit = STREQ (str, "digit");
+ boolean is_graph = STREQ (str, "graph");
+ boolean is_lower = STREQ (str, "lower");
+ boolean is_print = STREQ (str, "print");
+ boolean is_punct = STREQ (str, "punct");
+ boolean is_space = STREQ (str, "space");
+ boolean is_upper = STREQ (str, "upper");
+ boolean is_xdigit = STREQ (str, "xdigit");
+
+ if (!IS_CHAR_CLASS (str))
+ FREE_STACK_RETURN (REG_ECTYPE);
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+ {
+ /* This was split into 3 if's to
+ avoid an arbitrary limit in some compiler. */
+ if ( (is_alnum && ISALNUM (ch))
+ || (is_alpha && ISALPHA (ch))
+ || (is_blank && ISBLANK (ch))
+ || (is_cntrl && ISCNTRL (ch)))
+ SET_LIST_BIT (ch);
+ if ( (is_digit && ISDIGIT (ch))
+ || (is_graph && ISGRAPH (ch))
+ || (is_lower && ISLOWER (ch))
+ || (is_print && ISPRINT (ch)))
+ SET_LIST_BIT (ch);
+ if ( (is_punct && ISPUNCT (ch))
+ || (is_space && ISSPACE (ch))
+ || (is_upper && ISUPPER (ch))
+ || (is_xdigit && ISXDIGIT (ch)))
+ SET_LIST_BIT (ch);
+ if ( translate && (is_upper || is_lower)
+ && (ISUPPER (ch) || ISLOWER (ch)))
+ SET_LIST_BIT (ch);
+ }
+ had_char_class = true;
+#endif /* libc || wctype.h */
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+ range_start = ':';
+ had_char_class = false;
+ }
+ }
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == '=')
+ {
+ unsigned char str[MB_LEN_MAX + 1];
+#ifdef _LIBC
+ uint32_t nrules =
+ _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+#endif
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[='. */
+ if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if ((c == '=' && *p == ']') || p == pend)
+ break;
+ if (c1 < MB_LEN_MAX)
+ str[c1++] = c;
+ else
+ /* This is in any case an invalid class name. */
+ str[0] = '\0';
+ }
+ str[c1] = '\0';
+
+ if (c == '=' && *p == ']' && str[0] != '\0')
+ {
+ /* If we have no collation data we use the default
+ collation in which each character is in a class
+ by itself. It also means that ASCII is the
+ character set and therefore we cannot have character
+ with more than one byte in the multibyte
+ representation. */
+#ifdef _LIBC
+ if (nrules == 0)
+#endif
+ {
+ if (c1 != 1)
+ FREE_STACK_RETURN (REG_ECOLLATE);
+
+ /* Throw away the ] at the end of the equivalence
+ class. */
+ PATFETCH (c);
+
+ /* Set the bit for the character. */
+ SET_LIST_BIT (str[0]);
+ }
+#ifdef _LIBC
+ else
+ {
+ /* Try to match the byte sequence in `str' against
+ those known to the collate implementation.
+ First find out whether the bytes in `str' are
+ actually from exactly one character. */
+ const int32_t *table;
+ const unsigned char *weights;
+ const unsigned char *extra;
+ const int32_t *indirect;
+ int32_t idx;
+ const unsigned char *cp = str;
+ int ch;
+
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+
+ table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+
+ idx = findidx (&cp);
+ if (idx == 0 || cp < str + c1)
+ /* This is no valid character. */
+ FREE_STACK_RETURN (REG_ECOLLATE);
+
+ /* Throw away the ] at the end of the equivalence
+ class. */
+ PATFETCH (c);
+
+ /* Now we have to go throught the whole table
+ and find all characters which have the same
+ first level weight.
+
+ XXX Note that this is not entirely correct.
+ we would have to match multibyte sequences
+ but this is not possible with the current
+ implementation. */
+ for (ch = 1; ch < 256; ++ch)
+ /* XXX This test would have to be changed if we
+ would allow matching multibyte sequences. */
+ if (table[ch] > 0)
+ {
+ int32_t idx2 = table[ch];
+ size_t len = weights[idx2];
+
+ /* Test whether the lenghts match. */
+ if (weights[idx] == len)
+ {
+ /* They do. New compare the bytes of
+ the weight. */
+ size_t cnt = 0;
+
+ while (cnt < len
+ && (weights[idx + 1 + cnt]
+ == weights[idx2 + 1 + cnt]))
+ ++len;
+
+ if (cnt == len)
+ /* They match. Mark the character as
+ acceptable. */
+ SET_LIST_BIT (ch);
+ }
+ }
+ }
+#endif
+ had_char_class = true;
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT ('=');
+ range_start = '=';
+ had_char_class = false;
+ }
+ }
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == '.')
+ {
+ unsigned char str[128]; /* Should be large enough. */
+#ifdef _LIBC
+ uint32_t nrules =
+ _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+#endif
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[='. */
+ if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if ((c == '.' && *p == ']') || p == pend)
+ break;
+ if (c1 < sizeof (str))
+ str[c1++] = c;
+ else
+ /* This is in any case an invalid class name. */
+ str[0] = '\0';
+ }
+ str[c1] = '\0';
+
+ if (c == '.' && *p == ']' && str[0] != '\0')
+ {
+ /* If we have no collation data we use the default
+ collation in which each character is the name
+ for its own class which contains only the one
+ character. It also means that ASCII is the
+ character set and therefore we cannot have character
+ with more than one byte in the multibyte
+ representation. */
+#ifdef _LIBC
+ if (nrules == 0)
+#endif
+ {
+ if (c1 != 1)
+ FREE_STACK_RETURN (REG_ECOLLATE);
+
+ /* Throw away the ] at the end of the equivalence
+ class. */
+ PATFETCH (c);
+
+ /* Set the bit for the character. */
+ SET_LIST_BIT (str[0]);
+ range_start = ((const unsigned char *) str)[0];
+ }
+#ifdef _LIBC
+ else
+ {
+ /* Try to match the byte sequence in `str' against
+ those known to the collate implementation.
+ First find out whether the bytes in `str' are
+ actually from exactly one character. */
+ int32_t table_size;
+ const int32_t *symb_table;
+ const unsigned char *extra;
+ int32_t idx;
+ int32_t elem;
+ int32_t second;
+ int32_t hash;
+
+ table_size =
+ _NL_CURRENT_WORD (LC_COLLATE,
+ _NL_COLLATE_SYMB_HASH_SIZEMB);
+ symb_table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_TABLEMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_EXTRAMB);
+
+ /* Locate the character in the hashing table. */
+ hash = elem_hash (str, c1);
+
+ idx = 0;
+ elem = hash % table_size;
+ second = hash % (table_size - 2);
+ while (symb_table[2 * elem] != 0)
+ {
+ /* First compare the hashing value. */
+ if (symb_table[2 * elem] == hash
+ && c1 == extra[symb_table[2 * elem + 1]]
+ && memcmp (str,
+ &extra[symb_table[2 * elem + 1]
+ + 1],
+ c1) == 0)
+ {
+ /* Yep, this is the entry. */
+ idx = symb_table[2 * elem + 1];
+ idx += 1 + extra[idx];
+ break;
+ }
+
+ /* Next entry. */
+ elem += second;
+ }
+
+ if (symb_table[2 * elem] == 0)
+ /* This is no valid character. */
+ FREE_STACK_RETURN (REG_ECOLLATE);
+
+ /* Throw away the ] at the end of the equivalence
+ class. */
+ PATFETCH (c);
+
+ /* Now add the multibyte character(s) we found
+ to the accept list.
+
+ XXX Note that this is not entirely correct.
+ we would have to match multibyte sequences
+ but this is not possible with the current
+ implementation. Also, we have to match
+ collating symbols, which expand to more than
+ one file, as a whole and not allow the
+ individual bytes. */
+ c1 = extra[idx++];
+ if (c1 == 1)
+ range_start = extra[idx];
+ while (c1-- > 0)
+ {
+ SET_LIST_BIT (extra[idx]);
+ ++idx;
+ }
+ }
+#endif
+ had_char_class = false;
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT ('.');
+ range_start = '.';
+ had_char_class = false;
+ }
+ }
+ else
+ {
+ had_char_class = false;
+ SET_LIST_BIT (c);
+ range_start = c;
+ }
+ }
+
+ /* Discard any (non)matching list bytes that are all 0 at the
+ end of the map. Decrease the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ }
+ break;
+
+
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_open;
+ else
+ goto normal_char;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_close;
+ else
+ goto normal_char;
+
+
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '|':
+ if (syntax & RE_NO_BK_VBAR)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '{':
+ if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+ goto handle_interval;
+ else
+ goto normal_char;
+
+
+ case '\\':
+ if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW (c);
+
+ switch (c)
+ {
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto normal_backslash;
+
+ handle_open:
+ bufp->re_nsub++;
+ regnum++;
+
+ if (COMPILE_STACK_FULL)
+ {
+ RETALLOC (compile_stack.stack, compile_stack.size << 1,
+ compile_stack_elt_t);
+ if (compile_stack.stack == NULL) return REG_ESPACE;
+
+ compile_stack.size <<= 1;
+ }
+
+ /* These are the values to restore when we hit end of this
+ group. They are all relative offsets, so that if the
+ whole pattern moves because of realloc, they will still
+ be valid. */
+ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+ COMPILE_STACK_TOP.fixup_alt_jump
+ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+ COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+ COMPILE_STACK_TOP.regnum = regnum;
+
+ /* We will eventually replace the 0 with the number of
+ groups inner to this one. But do not push a
+ start_memory for groups beyond the last one we can
+ represent in the compiled pattern. */
+ if (regnum <= MAX_REGNUM)
+ {
+ COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+ BUF_PUSH_3 (start_memory, regnum, 0);
+ }
+
+ compile_stack.avail++;
+
+ fixup_alt_jump = 0;
+ laststart = 0;
+ begalt = b;
+ /* If we've reached MAX_REGNUM groups, then this open
+ won't actually generate any code, so we'll have to
+ clear pending_exact explicitly. */
+ pending_exact = 0;
+ break;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+ if (COMPILE_STACK_EMPTY)
+ {
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_backslash;
+ else
+ FREE_STACK_RETURN (REG_ERPAREN);
+ }
+
+ handle_close:
+ if (fixup_alt_jump)
+ { /* Push a dummy failure point at the end of the
+ alternative for a possible future
+ `pop_failure_jump' to pop. See comments at
+ `push_dummy_failure' in `re_match_2'. */
+ BUF_PUSH (push_dummy_failure);
+
+ /* We allocated space for this jump when we assigned
+ to `fixup_alt_jump', in the `handle_alt' case below. */
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+ }
+
+ /* See similar code for backslashed left paren above. */
+ if (COMPILE_STACK_EMPTY)
+ {
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ else
+ FREE_STACK_RETURN (REG_ERPAREN);
+ }
+
+ /* Since we just checked for an empty stack above, this
+ ``can't happen''. */
+ assert (compile_stack.avail != 0);
+ {
+ /* We don't just want to restore into `regnum', because
+ later groups should continue to be numbered higher,
+ as in `(ab)c(de)' -- the second group is #2. */
+ regnum_t this_group_regnum;
+
+ compile_stack.avail--;
+ begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+ fixup_alt_jump
+ = COMPILE_STACK_TOP.fixup_alt_jump
+ ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
+ : 0;
+ laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+ this_group_regnum = COMPILE_STACK_TOP.regnum;
+ /* If we've reached MAX_REGNUM groups, then this open
+ won't actually generate any code, so we'll have to
+ clear pending_exact explicitly. */
+ pending_exact = 0;
+
+ /* We're at the end of the group, so now we know how many
+ groups were inside this one. */
+ if (this_group_regnum <= MAX_REGNUM)
+ {
+ unsigned char *inner_group_loc
+ = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+
+ *inner_group_loc = regnum - this_group_regnum;
+ BUF_PUSH_3 (stop_memory, this_group_regnum,
+ regnum - this_group_regnum);
+ }
+ }
+ break;
+
+
+ case '|': /* `\|'. */
+ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+ goto normal_backslash;
+ handle_alt:
+ if (syntax & RE_LIMITED_OPS)
+ goto normal_char;
+
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (on_failure_jump, begalt, b + 6);
+ pending_exact = 0;
+ b += 3;
+
+ /* The alternative before this one has a jump after it
+ which gets executed if it gets matched. Adjust that
+ jump so it will jump to this alternative's analogous
+ jump (put in below, which in turn will jump to the next
+ (if any) alternative's such jump, etc.). The last such
+ jump jumps to the correct final destination. A picture:
+ _____ _____
+ | | | |
+ | v | v
+ a | b | c
+
+ If we are at `b', then fixup_alt_jump right now points to a
+ three-byte space after `a'. We'll put in the jump, set
+ fixup_alt_jump to right after `b', and leave behind three
+ bytes which we'll fill in when we get to after `c'. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ /* Mark and leave space for a jump after this alternative,
+ to be filled in later either by next alternative or
+ when know we're at the end of a series of alternatives. */
+ fixup_alt_jump = b;
+ GET_BUFFER_SPACE (3);
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case '{':
+ /* If \{ is a literal. */
+ if (!(syntax & RE_INTERVALS)
+ /* If we're at `\{' and it's not the open-interval
+ operator. */
+ || (syntax & RE_NO_BK_BRACES))
+ goto normal_backslash;
+
+ handle_interval:
+ {
+ /* If got here, then the syntax allows intervals. */
+
+ /* At least (most) this many matches must be made. */
+ int lower_bound = -1, upper_bound = -1;
+
+ beg_interval = p - 1;
+
+ if (p == pend)
+ {
+ if (!(syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ goto unfetch_interval;
+ else
+ FREE_STACK_RETURN (REG_EBRACE);
+ }
+
+ GET_UNSIGNED_NUMBER (lower_bound);
+
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if ((!(syntax & RE_NO_BK_BRACES) && c != '\\')
+ || ((syntax & RE_NO_BK_BRACES) && c != '}'))
+ FREE_STACK_RETURN (REG_BADBR);
+
+ if (upper_bound < 0)
+ upper_bound = RE_DUP_MAX;
+ }
+ else
+ /* Interval such as `{1}' => match exactly once. */
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound)
+ {
+ if (!(syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ goto unfetch_interval;
+ else
+ FREE_STACK_RETURN (REG_BADBR);
+ }
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (c != '\\') FREE_STACK_RETURN (REG_EBRACE);
+
+ PATFETCH (c);
+ }
+
+ if (c != '}')
+ {
+ if (!(syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ goto unfetch_interval;
+ else
+ FREE_STACK_RETURN (REG_BADBR);
+ }
+
+ /* We just parsed a valid interval. */
+
+ /* If it's invalid to have no preceding re. */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ FREE_STACK_RETURN (REG_BADRPT);
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ laststart = b;
+ else
+ goto unfetch_interval;
+ }
+
+ /* If the upper bound is zero, don't want to succeed at
+ all; jump from `laststart' to `b + 3', which will be
+ the end of the buffer after we insert the jump. */
+ if (upper_bound == 0)
+ {
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (jump, laststart, b + 3);
+ b += 3;
+ }
+
+ /* Otherwise, we have a nontrivial interval. When
+ we're all done, the pattern will look like:
+ set_number_at <jump count> <upper bound>
+ set_number_at <succeed_n count> <lower bound>
+ succeed_n <after jump addr> <succeed_n count>
+ <body of loop>
+ jump_n <succeed_n addr> <jump count>
+ (The upper bound and `jump_n' are omitted if
+ `upper_bound' is 1, though.) */
+ else
+ { /* If the upper bound is > 1, we need to insert
+ more at the end of the loop. */
+ unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+ GET_BUFFER_SPACE (nbytes);
+
+ /* Initialize lower bound of the `succeed_n', even
+ though it will be set during matching by its
+ attendant `set_number_at' (inserted next),
+ because `re_compile_fastmap' needs to know.
+ Jump to the `jump_n' we might insert below. */
+ INSERT_JUMP2 (succeed_n, laststart,
+ b + 5 + (upper_bound > 1) * 5,
+ lower_bound);
+ b += 5;
+
+ /* Code to initialize the lower bound. Insert
+ before the `succeed_n'. The `5' is the last two
+ bytes of this `set_number_at', plus 3 bytes of
+ the following `succeed_n'. */
+ insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+ b += 5;
+
+ if (upper_bound > 1)
+ { /* More than one repetition is allowed, so
+ append a backward jump to the `succeed_n'
+ that starts this interval.
+
+ When we've reached this during matching,
+ we'll have matched the interval once, so
+ jump back only `upper_bound - 1' times. */
+ STORE_JUMP2 (jump_n, b, laststart + 5,
+ upper_bound - 1);
+ b += 5;
+
+ /* The location we want to set is the second
+ parameter of the `jump_n'; that is `b-2' as
+ an absolute address. `laststart' will be
+ the `set_number_at' we're about to insert;
+ `laststart+3' the number to set, the source
+ for the relative address. But we are
+ inserting into the middle of the pattern --
+ so everything is getting moved up by 5.
+ Conclusion: (b - 2) - (laststart + 3) + 5,
+ i.e., b - laststart.
+
+ We insert this at the beginning of the loop
+ so that if we fail during matching, we'll
+ reinitialize the bounds. */
+ insert_op2 (set_number_at, laststart, b - laststart,
+ upper_bound - 1, b);
+ b += 5;
+ }
+ }
+ pending_exact = 0;
+ beg_interval = NULL;
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ assert (beg_interval);
+ p = beg_interval;
+ beg_interval = NULL;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH (c);
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (p > pattern && p[-1] == '\\')
+ goto normal_backslash;
+ }
+ goto normal_char;
+
+#ifdef emacs
+ /* There is no way to specify the before_dot and after_dot
+ operators. rms says this is ok. --karl */
+ case '=':
+ BUF_PUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+
+ case 'w':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ laststart = b;
+ BUF_PUSH (wordchar);
+ break;
+
+
+ case 'W':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ laststart = b;
+ BUF_PUSH (notwordchar);
+ break;
+
+
+ case '<':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (wordbeg);
+ break;
+
+ case '>':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (wordend);
+ break;
+
+ case 'b':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (wordbound);
+ break;
+
+ case 'B':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (notwordbound);
+ break;
+
+ case '`':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (begbuf);
+ break;
+
+ case '\'':
+ if (syntax & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (endbuf);
+ break;
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (syntax & RE_NO_BK_REFS)
+ goto normal_char;
+
+ c1 = c - '0';
+
+ if (c1 > regnum)
+ FREE_STACK_RETURN (REG_ESUBREG);
+
+ /* Can't back reference to a subexpression if inside of it. */
+ if (group_in_compile_stack (compile_stack, (regnum_t) c1))
+ goto normal_char;
+
+ laststart = b;
+ BUF_PUSH_2 (duplicate, c1);
+ break;
+
+
+ case '+':
+ case '?':
+ if (syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backslash;
+
+ default:
+ normal_backslash:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ c = TRANSLATE (c);
+ goto normal_char;
+ }
+ break;
+
+
+ default:
+ /* Expects the character in `c'. */
+ normal_char:
+ /* If no exactn currently being built. */
+ if (!pending_exact
+
+ /* If last exactn not at current position. */
+ || pending_exact + *pending_exact + 1 != b
+
+ /* We have only one byte following the exactn for the count. */
+ || *pending_exact == (1 << BYTEWIDTH) - 1
+
+ /* If followed by a repetition operator. */
+ || *p == '*' || *p == '^'
+ || ((syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?'))
+ || ((syntax & RE_INTERVALS)
+ && ((syntax & RE_NO_BK_BRACES)
+ ? *p == '{'
+ : (p[0] == '\\' && p[1] == '{'))))
+ {
+ /* Start building a new exactn. */
+
+ laststart = b;
+
+ BUF_PUSH_2 (exactn, 0);
+ pending_exact = b - 1;
+ }
+
+ BUF_PUSH (c);
+ (*pending_exact)++;
+ break;
+ } /* switch (c) */
+ } /* while p != pend */
+
+
+ /* Through the pattern now. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ if (!COMPILE_STACK_EMPTY)
+ FREE_STACK_RETURN (REG_EPAREN);
+
+ /* If we don't want backtracking, force success
+ the first time we reach the end of the compiled pattern. */
+ if (syntax & RE_NO_POSIX_BACKTRACKING)
+ BUF_PUSH (succeed);
+
+ free (compile_stack.stack);
+
+ /* We have succeeded; set the length of the buffer. */
+ bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+ if (debug)
+ {
+ DEBUG_PRINT1 ("\nCompiled pattern: \n");
+ print_compiled_pattern (bufp);
+ }
+#endif /* DEBUG */
+
+#ifndef MATCH_MAY_ALLOCATE
+ /* Initialize the failure stack to the largest possible stack. This
+ isn't necessary unless we're trying to avoid calling alloca in
+ the search and match routines. */
+ {
+ int num_regs = bufp->re_nsub + 1;
+
+ /* Since DOUBLE_FAIL_STACK refuses to double only if the current size
+ is strictly greater than re_max_failures, the largest possible stack
+ is 2 * re_max_failures failure points. */
+ if (fail_stack.size < (2 * re_max_failures * MAX_FAILURE_ITEMS))
+ {
+ fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS);
+
+# ifdef emacs
+ if (! fail_stack.stack)
+ fail_stack.stack
+ = (fail_stack_elt_t *) xmalloc (fail_stack.size
+ * sizeof (fail_stack_elt_t));
+ else
+ fail_stack.stack
+ = (fail_stack_elt_t *) xrealloc (fail_stack.stack,
+ (fail_stack.size
+ * sizeof (fail_stack_elt_t)));
+# else /* not emacs */
+ if (! fail_stack.stack)
+ fail_stack.stack
+ = (fail_stack_elt_t *) malloc (fail_stack.size
+ * sizeof (fail_stack_elt_t));
+ else
+ fail_stack.stack
+ = (fail_stack_elt_t *) realloc (fail_stack.stack,
+ (fail_stack.size
+ * sizeof (fail_stack_elt_t)));
+# endif /* not emacs */
+ }
+
+ regex_grow_registers (num_regs);
+ }
+#endif /* not MATCH_MAY_ALLOCATE */
+
+ return REG_NOERROR;
+} /* regex_compile */
+
+/* Subroutines for `regex_compile'. */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG. */
+
+static void
+store_op1 (op, loc, arg)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg1);
+ STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+ for OP followed by two-byte integer parameter ARG. */
+
+static void
+insert_op1 (op, loc, arg, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 3;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 5;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN. Return true if that ^ comes
+ after an alternative or a begin-subexpression. We assume there is at
+ least one character before the ^. */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+ const char *pattern, *p;
+ reg_syntax_t syntax;
+{
+ const char *prev = p - 2;
+ boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+
+ return
+ /* After a subexpression? */
+ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+ /* After an alternative? */
+ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p. This one is for $. We assume there is
+ at least one character after the $, i.e., `P < PEND'. */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+ const char *p, *pend;
+ reg_syntax_t syntax;
+{
+ const char *next = p;
+ boolean next_backslash = *next == '\\';
+ const char *next_next = p + 1 < pend ? p + 1 : 0;
+
+ return
+ /* Before a subexpression? */
+ (syntax & RE_NO_BK_PARENS ? *next == ')'
+ : next_backslash && next_next && *next_next == ')')
+ /* Before an alternative? */
+ || (syntax & RE_NO_BK_VBAR ? *next == '|'
+ : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+ false if it's not. */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+ compile_stack_type compile_stack;
+ regnum_t regnum;
+{
+ int this_element;
+
+ for (this_element = compile_stack.avail - 1;
+ this_element >= 0;
+ this_element--)
+ if (compile_stack.stack[this_element].regnum == regnum)
+ return true;
+
+ return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+ uncompiled pattern *P_PTR (which ends at PEND). We assume the
+ starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
+ Then we set the translation of all bits between the starting and
+ ending characters (inclusive) in the compiled pattern B.
+
+ Return an error code.
+
+ We use these short variable names so we can use the same macros as
+ `regex_compile' itself. */
+
+static reg_errcode_t
+compile_range (range_start_char, p_ptr, pend, translate, syntax, b)
+ unsigned int range_start_char;
+ const char **p_ptr, *pend;
+ RE_TRANSLATE_TYPE translate;
+ reg_syntax_t syntax;
+ unsigned char *b;
+{
+ unsigned this_char;
+ const char *p = *p_ptr;
+ reg_errcode_t ret;
+#if _LIBC
+ const unsigned char *collseq;
+ unsigned int start_colseq;
+ unsigned int end_colseq;
+#else
+ unsigned end_char;
+#endif
+
+ if (p == pend)
+ return REG_ERANGE;
+
+ /* Have to increment the pointer into the pattern string, so the
+ caller isn't still at the ending character. */
+ (*p_ptr)++;
+
+ /* Report an error if the range is empty and the syntax prohibits this. */
+ ret = syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+#if _LIBC
+ collseq = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_COLLSEQMB);
+
+ start_colseq = collseq[(unsigned char) TRANSLATE (range_start_char)];
+ end_colseq = collseq[(unsigned char) TRANSLATE (p[0])];
+ for (this_char = 0; this_char <= (unsigned char) -1; ++this_char)
+ {
+ unsigned int this_colseq = collseq[(unsigned char) TRANSLATE (this_char)];
+
+ if (start_colseq <= this_colseq && this_colseq <= end_colseq)
+ {
+ SET_LIST_BIT (TRANSLATE (this_char));
+ ret = REG_NOERROR;
+ }
+ }
+#else
+ /* Here we see why `this_char' has to be larger than an `unsigned
+ char' -- we would otherwise go into an infinite loop, since all
+ characters <= 0xff. */
+ range_start_char = TRANSLATE (range_start_char);
+ end_char = TRANSLATE (p[0]);
+ for (this_char = range_start_char; this_char <= end_char; ++this_char)
+ {
+ SET_LIST_BIT (TRANSLATE (this_char));
+ ret = REG_NOERROR;
+ }
+#endif
+
+ return ret;
+}
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+ BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
+ characters can start a string that matches the pattern. This fastmap
+ is used by re_search to skip quickly over impossible starting points.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as BUFP->fastmap.
+
+ We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+ the pattern buffer.
+
+ Returns 0 if we succeed, -2 if an internal error. */
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int j, k;
+#ifdef MATCH_MAY_ALLOCATE
+ fail_stack_type fail_stack;
+#endif
+#ifndef REGEX_MALLOC
+ char *destination;
+#endif
+
+ register char *fastmap = bufp->fastmap;
+ unsigned char *pattern = bufp->buffer;
+ unsigned char *p = pattern;
+ register unsigned char *pend = pattern + bufp->used;
+
+#ifdef REL_ALLOC
+ /* This holds the pointer to the failure stack, when
+ it is allocated relocatably. */
+ fail_stack_elt_t *failure_stack_ptr;
+#endif
+
+ /* Assume that each path through the pattern can be null until
+ proven otherwise. We set this false at the bottom of switch
+ statement, to which we get only if a particular path doesn't
+ match the empty string. */
+ boolean path_can_be_null = true;
+
+ /* We aren't doing a `succeed_n' to begin with. */
+ boolean succeed_n_p = false;
+
+ assert (fastmap != NULL && p != NULL);
+
+ INIT_FAIL_STACK ();
+ bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
+ bufp->fastmap_accurate = 1; /* It will be when we're done. */
+ bufp->can_be_null = 0;
+
+ while (1)
+ {
+ if (p == pend || *p == succeed)
+ {
+ /* We have reached the (effective) end of pattern. */
+ if (!FAIL_STACK_EMPTY ())
+ {
+ bufp->can_be_null |= path_can_be_null;
+
+ /* Reset for next path. */
+ path_can_be_null = true;
+
+ p = fail_stack.stack[--fail_stack.avail].pointer;
+
+ continue;
+ }
+ else
+ break;
+ }
+
+ /* We should never be about to go beyond the end of the pattern. */
+ assert (p < pend);
+
+ switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
+ {
+
+ /* I guess the idea here is to simply not bother with a fastmap
+ if a backreference is used, since it's too hard to figure out
+ the fastmap for the corresponding group. Setting
+ `can_be_null' stops `re_search_2' from using the fastmap, so
+ that is all we do. */
+ case duplicate:
+ bufp->can_be_null = 1;
+ goto done;
+
+
+ /* Following are the cases which match a character. These end
+ with `break'. */
+
+ case exactn:
+ fastmap[p[1]] = 1;
+ break;
+
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ fastmap[j] = 1;
+ break;
+
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed. */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ fastmap[j] = 1;
+ break;
+
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case anychar:
+ {
+ int fastmap_newline = fastmap['\n'];
+
+ /* `.' matches anything ... */
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ /* ... except perhaps newline. */
+ if (!(bufp->syntax & RE_DOT_NEWLINE))
+ fastmap['\n'] = fastmap_newline;
+
+ /* Return if we have already set `can_be_null'; if we have,
+ then the fastmap is irrelevant. Something's wrong here. */
+ else if (bufp->can_be_null)
+ goto done;
+
+ /* Otherwise, have to check alternative paths. */
+ break;
+ }
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ /* All cases after this match the empty string. These end with
+ `continue'. */
+
+
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ continue;
+#endif /* emacs */
+
+
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ case push_dummy_failure:
+ continue;
+
+
+ case jump_n:
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case jump_past_alt:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (j > 0)
+ continue;
+
+ /* Jump backward implies we just went through the body of a
+ loop and matched nothing. Opcode jumped to should be
+ `on_failure_jump' or `succeed_n'. Just treat it like an
+ ordinary jump. For a * loop, it has pushed its failure
+ point already; if so, discard that as redundant. */
+ if ((re_opcode_t) *p != on_failure_jump
+ && (re_opcode_t) *p != succeed_n)
+ continue;
+
+ p++;
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+
+ /* If what's on the stack is where we are now, pop it. */
+ if (!FAIL_STACK_EMPTY ()
+ && fail_stack.stack[fail_stack.avail - 1].pointer == p)
+ fail_stack.avail--;
+
+ continue;
+
+
+ case on_failure_jump:
+ case on_failure_keep_string_jump:
+ handle_on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+
+ /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+ end of the pattern. We don't want to push such a point,
+ since when we restore it above, entering the switch will
+ increment `p' past the end of the pattern. We don't need
+ to push such a point since we obviously won't find any more
+ fastmap entries beyond `pend'. Such a pattern can match
+ the null string, though. */
+ if (p + j < pend)
+ {
+ if (!PUSH_PATTERN_OP (p + j, fail_stack))
+ {
+ RESET_FAIL_STACK ();
+ return -2;
+ }
+ }
+ else
+ bufp->can_be_null = 1;
+
+ if (succeed_n_p)
+ {
+ EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
+ succeed_n_p = false;
+ }
+
+ continue;
+
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p += 2;
+
+ /* Increment p past the n for when k != 0. */
+ EXTRACT_NUMBER_AND_INCR (k, p);
+ if (k == 0)
+ {
+ p -= 4;
+ succeed_n_p = true; /* Spaghetti code alert. */
+ goto handle_on_failure_jump;
+ }
+ continue;
+
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ continue;
+
+
+ default:
+ abort (); /* We have listed all the cases. */
+ } /* switch *p++ */
+
+ /* Getting here means we have found the possible starting
+ characters for one path of the pattern -- and that the empty
+ string does not match. We need not follow this path further.
+ Instead, look at the next alternative (remembered on the
+ stack), or quit if no more. The test at the top of the loop
+ does these things. */
+ path_can_be_null = false;
+ p = pend;
+ } /* while p */
+
+ /* Set `can_be_null' for the last path (also the first path, if the
+ pattern is empty). */
+ bufp->can_be_null |= path_can_be_null;
+
+ done:
+ RESET_FAIL_STACK ();
+ return 0;
+} /* re_compile_fastmap */
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t *) 0;
+ }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Searching routines. */
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
+ regs, size);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+ virtual concatenation of STRING1 and STRING2, starting first at index
+ STARTPOS, then at STARTPOS + 1, and so on.
+
+ STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+ RANGE is how far to scan while trying to match. RANGE = 0 means try
+ only at STARTPOS; in general, the last start tried is STARTPOS +
+ RANGE.
+
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire BUFP->buffer and its contained
+ subexpressions.
+
+ Do not consider matching one past the index STOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ We return either the position in the strings at which the match was
+ found, -1 if no match, or -2 if error (such as failure
+ stack overflow). */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ int range;
+ struct re_registers *regs;
+ int stop;
+{
+ int val;
+ register char *fastmap = bufp->fastmap;
+ register RE_TRANSLATE_TYPE translate = bufp->translate;
+ int total_size = size1 + size2;
+ int endpos = startpos + range;
+
+ /* Check for out-of-range STARTPOS. */
+ if (startpos < 0 || startpos > total_size)
+ return -1;
+
+ /* Fix up RANGE if it might eventually take us outside
+ the virtual concatenation of STRING1 and STRING2.
+ Make sure we won't move STARTPOS below 0 or above TOTAL_SIZE. */
+ if (endpos < 0)
+ range = 0 - startpos;
+ else if (endpos > total_size)
+ range = total_size - startpos;
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ search for a pattern that must be anchored. */
+ if (bufp->used > 0 && range > 0
+ && ((re_opcode_t) bufp->buffer[0] == begbuf
+ /* `begline' is like `begbuf' if it cannot match at newlines. */
+ || ((re_opcode_t) bufp->buffer[0] == begline
+ && !bufp->newline_anchor)))
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+#ifdef emacs
+ /* In a forward search for something that starts with \=.
+ don't keep searching past point. */
+ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0)
+ {
+ range = PT - startpos;
+ if (range <= 0)
+ return -1;
+ }
+#endif /* emacs */
+
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !bufp->fastmap_accurate)
+ if (re_compile_fastmap (bufp) == -2)
+ return -2;
+
+ /* Loop through the string, looking for a place to start matching. */
+ for (;;)
+ {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot be the start of a match. If the pattern can match the
+ null string, however, we don't need to skip characters; we want
+ the first null string. */
+ if (fastmap && startpos < total_size && !bufp->can_be_null)
+ {
+ if (range > 0) /* Searching forwards. */
+ {
+ register const char *d;
+ register int lim = 0;
+ int irange = range;
+
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+
+ /* Written out as an if-else to avoid testing `translate'
+ inside the loop. */
+ if (translate)
+ while (range > lim
+ && !fastmap[(unsigned char)
+ translate[(unsigned char) *d++]])
+ range--;
+ else
+ while (range > lim && !fastmap[(unsigned char) *d++])
+ range--;
+
+ startpos += irange - range;
+ }
+ else /* Searching backwards. */
+ {
+ register char c = (size1 == 0 || startpos >= size1
+ ? string2[startpos - size1]
+ : string1[startpos]);
+
+ if (!fastmap[(unsigned char) TRANSLATE (c)])
+ goto advance;
+ }
+ }
+
+ /* If can't match the null string, and that's all we have left, fail. */
+ if (range >= 0 && startpos == total_size && fastmap
+ && !bufp->can_be_null)
+ return -1;
+
+ val = re_match_2_internal (bufp, string1, size1, string2, size2,
+ startpos, regs, stop);
+#ifndef REGEX_MALLOC
+# ifdef C_ALLOCA
+ alloca (0);
+# endif
+#endif
+
+ if (val >= 0)
+ return startpos;
+
+ if (val == -2)
+ return -2;
+
+ advance:
+ if (!range)
+ break;
+ else if (range > 0)
+ {
+ range--;
+ startpos++;
+ }
+ else
+ {
+ range++;
+ startpos--;
+ }
+ }
+ return -1;
+} /* re_search_2 */
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+ and `string2' into an offset from the beginning of that string. */
+#define POINTER_TO_OFFSET(ptr) \
+ (FIRST_STRING_P (ptr) \
+ ? ((regoff_t) ((ptr) - string1)) \
+ : ((regoff_t) ((ptr) - string2 + size1)))
+
+/* Macros for dealing with the split strings in re_match_2. */
+
+#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
+
+/* Call before fetching a character with *d. This switches over to
+ string2 if necessary. */
+#define PREFETCH() \
+ while (d == dend) \
+ { \
+ /* End of string2 => fail. */ \
+ if (dend == end_match_2) \
+ goto fail; \
+ /* End of string1 => advance to string2. */ \
+ d = string2; \
+ dend = end_match_2; \
+ }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+ of `string1' and `string2'. If only one string, it's `string2'. */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)
+
+
+/* Test if D points to a character which is word-constituent. We have
+ two special cases to check for: if past the end of string1, look at
+ the first character in string2; and if before the beginning of
+ string2, look at the last character in string1. */
+#define WORDCHAR_P(d) \
+ (SYNTAX ((d) == end1 ? *string2 \
+ : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
+ == Sword)
+
+/* Disabled due to a compiler bug -- see comment at case wordbound */
+#if 0
+/* Test if the character before D and the one at D differ with respect
+ to being word-constituent. */
+#define AT_WORD_BOUNDARY(d) \
+ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
+ || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+#endif
+
+/* Free everything we malloc. */
+#ifdef MATCH_MAY_ALLOCATE
+# define FREE_VAR(var) if (var) REGEX_FREE (var); var = NULL
+# define FREE_VARIABLES() \
+ do { \
+ REGEX_FREE_STACK (fail_stack.stack); \
+ FREE_VAR (regstart); \
+ FREE_VAR (regend); \
+ FREE_VAR (old_regstart); \
+ FREE_VAR (old_regend); \
+ FREE_VAR (best_regstart); \
+ FREE_VAR (best_regend); \
+ FREE_VAR (reg_info); \
+ FREE_VAR (reg_dummy); \
+ FREE_VAR (reg_info_dummy); \
+ } while (0)
+#else
+# define FREE_VARIABLES() ((void)0) /* Do nothing! But inhibit gcc warning. */
+#endif /* not MATCH_MAY_ALLOCATE */
+
+/* These values must meet several constraints. They must not be valid
+ register values; since we have a limit of 255 registers (because
+ we use only one byte in the pattern for the register number), we can
+ use numbers larger than 255. They must differ by 1, because of
+ NUM_FAILURE_ITEMS above. And the value for the lowest register must
+ be larger than the value for the highest register, so we do not try
+ to actually save any registers when none are active. */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+/* Matching routines. */
+
+#ifndef emacs /* Emacs never uses this. */
+/* re_match is like re_match_2 except it takes only a single string. */
+
+int
+re_match (bufp, string, size, pos, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, pos;
+ struct re_registers *regs;
+{
+ int result = re_match_2_internal (bufp, NULL, 0, string, size,
+ pos, regs, size);
+# ifndef REGEX_MALLOC
+# ifdef C_ALLOCA
+ alloca (0);
+# endif
+# endif
+ return result;
+}
+# ifdef _LIBC
+weak_alias (__re_match, re_match)
+# endif
+#endif /* not emacs */
+
+static boolean group_match_null_string_p _RE_ARGS ((unsigned char **p,
+ unsigned char *end,
+ register_info_type *reg_info));
+static boolean alt_match_null_string_p _RE_ARGS ((unsigned char *p,
+ unsigned char *end,
+ register_info_type *reg_info));
+static boolean common_op_match_null_string_p _RE_ARGS ((unsigned char **p,
+ unsigned char *end,
+ register_info_type *reg_info));
+static int bcmp_translate _RE_ARGS ((const char *s1, const char *s2,
+ int len, char *translate));
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+ the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+ and SIZE2, respectively). We start matching at POS, and stop
+ matching at STOP.
+
+ If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+ store offsets for the substring each group matched in REGS. See the
+ documentation for exactly how many groups we fill.
+
+ We return -1 if no match, -2 if an internal error (such as the
+ failure stack overflowing). Otherwise, we return the length of the
+ matched substring. */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int stop;
+{
+ int result = re_match_2_internal (bufp, string1, size1, string2, size2,
+ pos, regs, stop);
+#ifndef REGEX_MALLOC
+# ifdef C_ALLOCA
+ alloca (0);
+# endif
+#endif
+ return result;
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+/* This is a separate function so that we can force an alloca cleanup
+ afterwards. */
+static int
+re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int stop;
+{
+ /* General temporaries. */
+ int mcnt;
+ unsigned char *p1;
+
+ /* Just past the end of the corresponding string. */
+ const char *end1, *end2;
+
+ /* Pointers into string1 and string2, just past the last characters in
+ each to consider matching. */
+ const char *end_match_1, *end_match_2;
+
+ /* Where we are in the data, and the end of the current string. */
+ const char *d, *dend;
+
+ /* Where we are in the pattern, and the end of the pattern. */
+ unsigned char *p = bufp->buffer;
+ register unsigned char *pend = p + bufp->used;
+
+ /* Mark the opcode just after a start_memory, so we can test for an
+ empty subpattern when we get to the stop_memory. */
+ unsigned char *just_past_start_mem = 0;
+
+ /* We use this to map every character in the string. */
+ RE_TRANSLATE_TYPE translate = bufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to
+ the subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where
+ to resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is
+ a ``dummy''; if a failure happens and the failure point is a dummy,
+ it gets discarded and the next next one is tried. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */
+ fail_stack_type fail_stack;
+#endif
+#ifdef DEBUG
+ static unsigned failure_id;
+ unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+#ifdef REL_ALLOC
+ /* This holds the pointer to the failure stack, when
+ it is allocated relocatably. */
+ fail_stack_elt_t *failure_stack_ptr;
+#endif
+
+ /* We fill all the registers internally, independent of what we
+ return, for use in backreferences. The number here includes
+ an element for register zero. */
+ size_t num_regs = bufp->re_nsub + 1;
+
+ /* The currently active registers. */
+ active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
+ const char **regstart, **regend;
+#endif
+
+ /* If a group that's operated upon by a repetition operator fails to
+ match anything, then the register for its start will need to be
+ restored because it will have been set to wherever in the string we
+ are when we last see its open-group operator. Similarly for a
+ register's end. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
+ const char **old_regstart, **old_regend;
+#endif
+
+ /* The is_active field of reg_info helps us keep track of which (possibly
+ nested) subexpressions we are currently in. The matched_something
+ field of reg_info[reg_num] helps us tell whether or not we have
+ matched any of the pattern so far this time through the reg_num-th
+ subexpression. These two fields get reset each time through any
+ loop their register is in. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */
+ register_info_type *reg_info;
+#endif
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+ unsigned best_regs_set = false;
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
+ const char **best_regstart, **best_regend;
+#endif
+
+ /* Logically, this is `best_regend[0]'. But we don't want to have to
+ allocate space for that if we're not allocating space for anything
+ else (see below). Also, we never need info about register 0 for
+ any of the other register vectors, and it seems rather a kludge to
+ treat `best_regend' differently than the rest. So we keep track of
+ the end of the best match so far in a separate variable. We
+ initialize this to NULL so that when we backtrack the first time
+ and need to test it, it's not garbage. */
+ const char *match_end = NULL;
+
+ /* This helps SET_REGS_MATCHED avoid doing redundant work. */
+ int set_regs_matched_done = 0;
+
+ /* Used when we pop values we don't care about. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
+ const char **reg_dummy;
+ register_info_type *reg_info_dummy;
+#endif
+
+#ifdef DEBUG
+ /* Counts the total number of registers pushed. */
+ unsigned num_regs_pushed = 0;
+#endif
+
+ DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+
+ INIT_FAIL_STACK ();
+
+#ifdef MATCH_MAY_ALLOCATE
+ /* Do not bother to initialize all the register variables if there are
+ no groups in the pattern, as it takes a fair amount of time. If
+ there are groups, we include space for register 0 (the whole
+ pattern), even though we never use it, since it simplifies the
+ array indexing. We should fix this. */
+ if (bufp->re_nsub)
+ {
+ regstart = REGEX_TALLOC (num_regs, const char *);
+ regend = REGEX_TALLOC (num_regs, const char *);
+ old_regstart = REGEX_TALLOC (num_regs, const char *);
+ old_regend = REGEX_TALLOC (num_regs, const char *);
+ best_regstart = REGEX_TALLOC (num_regs, const char *);
+ best_regend = REGEX_TALLOC (num_regs, const char *);
+ reg_info = REGEX_TALLOC (num_regs, register_info_type);
+ reg_dummy = REGEX_TALLOC (num_regs, const char *);
+ reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+ if (!(regstart && regend && old_regstart && old_regend && reg_info
+ && best_regstart && best_regend && reg_dummy && reg_info_dummy))
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+ }
+ else
+ {
+ /* We must initialize all our variables to NULL, so that
+ `FREE_VARIABLES' doesn't try to free them. */
+ regstart = regend = old_regstart = old_regend = best_regstart
+ = best_regend = reg_dummy = NULL;
+ reg_info = reg_info_dummy = (register_info_type *) NULL;
+ }
+#endif /* MATCH_MAY_ALLOCATE */
+
+ /* The starting position is bogus. */
+ if (pos < 0 || pos > size1 + size2)
+ {
+ FREE_VARIABLES ();
+ return -1;
+ }
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ start_memory/stop_memory has been seen for. Also initialize the
+ register information struct. */
+ for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = regend[mcnt]
+ = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+
+ REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ /* We move `string1' into `string2' if the latter's empty -- but not if
+ `string1' is null. */
+ if (size2 == 0 && string1 != NULL)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings. */
+ if (stop <= size1)
+ {
+ end_match_1 = string1 + stop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + stop - size1;
+ }
+
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within. `d'
+ is advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal `string2'. */
+ if (size1 > 0 && pos <= size1)
+ {
+ d = string1 + pos;
+ dend = end_match_1;
+ }
+ else
+ {
+ d = string2 + pos - size1;
+ dend = end_match_2;
+ }
+
+ DEBUG_PRINT1 ("The compiled pattern is:\n");
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+ DEBUG_PRINT1 ("The string to match is: `");
+ DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+ DEBUG_PRINT1 ("'\n");
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if the match is complete, or it drops through if the match
+ fails at this starting point in the input data. */
+ for (;;)
+ {
+#ifdef _LIBC
+ DEBUG_PRINT2 ("\n%p: ", p);
+#else
+ DEBUG_PRINT2 ("\n0x%x: ", p);
+#endif
+
+ if (p == pend)
+ { /* End of pattern means we might have succeeded. */
+ DEBUG_PRINT1 ("end of pattern ... ");
+
+ /* If we haven't matched the entire string, and we want the
+ longest match, try backtracking. */
+ if (d != end_match_2)
+ {
+ /* 1 if this match ends in the same string (string1 or string2)
+ as the best previous match. */
+ boolean same_str_p = (FIRST_STRING_P (match_end)
+ == MATCHING_IN_FIRST_STRING);
+ /* 1 if this match is the best seen so far. */
+ boolean best_match_p;
+
+ /* AIX compiler got confused when this was combined
+ with the previous declaration. */
+ if (same_str_p)
+ best_match_p = d > match_end;
+ else
+ best_match_p = !MATCHING_IN_FIRST_STRING;
+
+ DEBUG_PRINT1 ("backtracking.\n");
+
+ if (!FAIL_STACK_EMPTY ())
+ { /* More failure points to try. */
+
+ /* If exceeds best match so far, save it. */
+ if (!best_regs_set || best_match_p)
+ {
+ best_regs_set = true;
+ match_end = d;
+
+ DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+
+ for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
+ {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+
+ /* If no failure points, don't restore garbage. And if
+ last match is real best match, don't restore second
+ best one. */
+ else if (best_regs_set && !best_match_p)
+ {
+ restore_best_regs:
+ /* Restore best match. It may happen that `dend ==
+ end_match_1' while the restored d is in string2.
+ For example, the pattern `x.*y.*z' against the
+ strings `x-' and `y-z-', if the two strings are
+ not consecutive in memory. */
+ DEBUG_PRINT1 ("Restoring best registers.\n");
+
+ d = match_end;
+ dend = ((d >= string1 && d <= end1)
+ ? end_match_1 : end_match_2);
+
+ for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
+ }
+ }
+ } /* d != end_match_2 */
+
+ succeed_label:
+ DEBUG_PRINT1 ("Accepting match.\n");
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !bufp->no_sub)
+ {
+ /* Have the register data arrays been allocated? */
+ if (bufp->regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. We need one
+ extra element beyond `num_regs' for the `-1' marker
+ GNU code uses. */
+ regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+ regs->start = TALLOC (regs->num_regs, regoff_t);
+ regs->end = TALLOC (regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+ bufp->regs_allocated = REGS_REALLOCATE;
+ }
+ else if (bufp->regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (regs->num_regs < num_regs + 1)
+ {
+ regs->num_regs = num_regs + 1;
+ RETALLOC (regs->start, regs->num_regs, regoff_t);
+ RETALLOC (regs->end, regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+ }
+ }
+ else
+ {
+ /* These braces fend off a "empty body in an else-statement"
+ warning under GCC when assert expands to nothing. */
+ assert (bufp->regs_allocated == REGS_FIXED);
+ }
+
+ /* Convert the pointer data in `regstart' and `regend' to
+ indices. Register zero has to be set differently,
+ since we haven't kept track of any info for it. */
+ if (regs->num_regs > 0)
+ {
+ regs->start[0] = pos;
+ regs->end[0] = (MATCHING_IN_FIRST_STRING
+ ? ((regoff_t) (d - string1))
+ : ((regoff_t) (d - string2 + size1)));
+ }
+
+ /* Go through the first `min (num_regs, regs->num_regs)'
+ registers, since that is all we initialized. */
+ for (mcnt = 1; (unsigned) mcnt < MIN (num_regs, regs->num_regs);
+ mcnt++)
+ {
+ if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ else
+ {
+ regs->start[mcnt]
+ = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]);
+ regs->end[mcnt]
+ = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]);
+ }
+ }
+
+ /* If the regs structure we return has more elements than
+ were in the pattern, set the extra elements to -1. If
+ we (re)allocated the registers, this is the case,
+ because we always allocate enough to have at least one
+ -1 at the end. */
+ for (mcnt = num_regs; (unsigned) mcnt < regs->num_regs; mcnt++)
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ } /* regs && !bufp->no_sub */
+
+ DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+ nfailure_points_pushed, nfailure_points_popped,
+ nfailure_points_pushed - nfailure_points_popped);
+ DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+ mcnt = d - pos - (MATCHING_IN_FIRST_STRING
+ ? string1
+ : string2 - size1);
+
+ DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+ FREE_VARIABLES ();
+ return mcnt;
+ }
+
+ /* Otherwise match next pattern command. */
+ switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
+ {
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case no_op:
+ DEBUG_PRINT1 ("EXECUTING no_op.\n");
+ break;
+
+ case succeed:
+ DEBUG_PRINT1 ("EXECUTING succeed.\n");
+ goto succeed_label;
+
+ /* Match the next n pattern characters exactly. The following
+ byte in the pattern defines n, and the n bytes after that
+ are the characters to match. */
+ case exactn:
+ mcnt = *p++;
+ DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (translate)
+ {
+ do
+ {
+ PREFETCH ();
+ if ((unsigned char) translate[(unsigned char) *d++]
+ != (unsigned char) *p++)
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH ();
+ if (*d++ != (char) *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED ();
+ break;
+
+
+ /* Match any character except possibly a newline or a null. */
+ case anychar:
+ DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+ PREFETCH ();
+
+ if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+ || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+ goto fail;
+
+ SET_REGS_MATCHED ();
+ DEBUG_PRINT2 (" Matched `%d'.\n", *d);
+ d++;
+ break;
+
+
+ case charset:
+ case charset_not:
+ {
+ register unsigned char c;
+ boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+ DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+ PREFETCH ();
+ c = TRANSLATE (*d); /* The character to match. */
+
+ /* Cast to `unsigned' instead of `unsigned char' in case the
+ bit list is a full 32 bytes long. */
+ if (c < (unsigned) (*p * BYTEWIDTH)
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+ }
+
+
+ /* The beginning of a group is represented by start_memory.
+ The arguments are the register number in the next byte, and the
+ number of groups inner to this one in the next. The text
+ matched within the group is recorded (in the internal
+ registers data structure) under the register number. */
+ case start_memory:
+ DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+ /* Find out if this group can match the empty string. */
+ p1 = p; /* To send to group_match_null_string_p. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[*p])
+ = group_match_null_string_p (&p1, pend, reg_info);
+
+ /* Save the position in the string where we were the last time
+ we were at this open-group operator in case the group is
+ operated upon by a repetition operator, e.g., with `(a*)*b'
+ against `ab'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+ : regstart[*p];
+ DEBUG_PRINT2 (" old_regstart: %d\n",
+ POINTER_TO_OFFSET (old_regstart[*p]));
+
+ regstart[*p] = d;
+ DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+ IS_ACTIVE (reg_info[*p]) = 1;
+ MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* Clear this whenever we change the register activity status. */
+ set_regs_matched_done = 0;
+
+ /* This is the new highest active register. */
+ highest_active_reg = *p;
+
+ /* If nothing was active before, this is the new lowest active
+ register. */
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *p;
+
+ /* Move past the register number and inner group count. */
+ p += 2;
+ just_past_start_mem = p;
+
+ break;
+
+
+ /* The stop_memory opcode represents the end of a group. Its
+ arguments are the same as start_memory's: the register
+ number, and the number of inner groups. */
+ case stop_memory:
+ DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+
+ /* We need to save the string position the last time we were at
+ this close-group operator in case the group is operated
+ upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+ against `aba'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regend[*p]) ? d : regend[*p]
+ : regend[*p];
+ DEBUG_PRINT2 (" old_regend: %d\n",
+ POINTER_TO_OFFSET (old_regend[*p]));
+
+ regend[*p] = d;
+ DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+ /* This register isn't active anymore. */
+ IS_ACTIVE (reg_info[*p]) = 0;
+
+ /* Clear this whenever we change the register activity status. */
+ set_regs_matched_done = 0;
+
+ /* If this was the only register active, nothing is active
+ anymore. */
+ if (lowest_active_reg == highest_active_reg)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ { /* We must scan for the new highest active register, since
+ it isn't necessarily one less than now: consider
+ (a(b)c(d(e)f)g). When group 3 ends, after the f), the
+ new highest active register is 1. */
+ unsigned char r = *p - 1;
+ while (r > 0 && !IS_ACTIVE (reg_info[r]))
+ r--;
+
+ /* If we end up at register zero, that means that we saved
+ the registers as the result of an `on_failure_jump', not
+ a `start_memory', and we jumped to past the innermost
+ `stop_memory'. For example, in ((.)*) we save
+ registers 1 and 2 as a result of the *, but when we pop
+ back to the second ), we are at the stop_memory 1.
+ Thus, nothing is active. */
+ if (r == 0)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ highest_active_reg = r;
+ }
+
+ /* If just failed to match something this time around with a
+ group that's operated on by a repetition operator, try to
+ force exit from the ``loop'', and restore the register
+ information for this group that we had before trying this
+ last match. */
+ if ((!MATCHED_SOMETHING (reg_info[*p])
+ || just_past_start_mem == p - 1)
+ && (p + 2) < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ p1 = p + 2;
+ mcnt = 0;
+ switch ((re_opcode_t) *p1++)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (is_a_jump_n)
+ p1 += 2;
+ break;
+
+ default:
+ /* do nothing */ ;
+ }
+ p1 += mcnt;
+
+ /* If the next operation is a jump backwards in the pattern
+ to an on_failure_jump right before the start_memory
+ corresponding to this stop_memory, exit from the loop
+ by forcing a failure after pushing on the stack the
+ on_failure_jump's jump in the pattern, and d. */
+ if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+ && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+ {
+ /* If this group ever matched anything, then restore
+ what its registers were before trying this last
+ failed match, e.g., with `(a*)*b' against `ab' for
+ regstart[1], and, e.g., with `((a*)*(b*)*)*'
+ against `aba' for regend[3].
+
+ Also restore the registers for inner groups for,
+ e.g., `((a*)(b*))*' against `aba' (register 3 would
+ otherwise get trashed). */
+
+ if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+ {
+ unsigned r;
+
+ EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* Restore this and inner groups' (if any) registers. */
+ for (r = *p; r < (unsigned) *p + (unsigned) *(p + 1);
+ r++)
+ {
+ regstart[r] = old_regstart[r];
+
+ /* xx why this test? */
+ if (old_regend[r] >= regstart[r])
+ regend[r] = old_regend[r];
+ }
+ }
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+ goto fail;
+ }
+ }
+
+ /* Move past the register number and the inner group count. */
+ p += 2;
+ break;
+
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ register const char *d2, *dend2;
+ int regno = *p++; /* Get which register to match against. */
+ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+ /* Can't back reference a group which we've never matched. */
+ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+ goto fail;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = ((FIRST_STRING_P (regstart[regno])
+ == FIRST_STRING_P (regend[regno]))
+ ? regend[regno] : end_match_1);
+ for (;;)
+ {
+ /* If necessary, advance to next segment in register
+ contents. */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+
+ /* End of string1 => advance to string2. */
+ d2 = string2;
+ dend2 = regend[regno];
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* If necessary, advance to next segment in data. */
+ PREFETCH ();
+
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if (translate
+ ? bcmp_translate (d, d2, mcnt, translate)
+ : memcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+
+ /* Do this because we've match some characters. */
+ SET_REGS_MATCHED ();
+ }
+ }
+ break;
+
+
+ /* begline matches the empty string at the beginning of the string
+ (unless `not_bol' is set in `bufp'), and, if
+ `newline_anchor' is set, after newlines. */
+ case begline:
+ DEBUG_PRINT1 ("EXECUTING begline.\n");
+
+ if (AT_STRINGS_BEG (d))
+ {
+ if (!bufp->not_bol) break;
+ }
+ else if (d[-1] == '\n' && bufp->newline_anchor)
+ {
+ break;
+ }
+ /* In all other cases, we fail. */
+ goto fail;
+
+
+ /* endline is the dual of begline. */
+ case endline:
+ DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+ if (AT_STRINGS_END (d))
+ {
+ if (!bufp->not_eol) break;
+ }
+
+ /* We have to ``prefetch'' the next character. */
+ else if ((d == end1 ? *string2 : *d) == '\n'
+ && bufp->newline_anchor)
+ {
+ break;
+ }
+ goto fail;
+
+
+ /* Match at the very beginning of the data. */
+ case begbuf:
+ DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+ if (AT_STRINGS_BEG (d))
+ break;
+ goto fail;
+
+
+ /* Match at the very end of the data. */
+ case endbuf:
+ DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+ if (AT_STRINGS_END (d))
+ break;
+ goto fail;
+
+
+ /* on_failure_keep_string_jump is used to optimize `.*\n'. It
+ pushes NULL as the value for the string on the stack. Then
+ `pop_failure_point' will keep the current value for the
+ string, instead of restoring it. To see why, consider
+ matching `foo\nbar' against `.*\n'. The .* matches the foo;
+ then the . fails against the \n. But the next thing we want
+ to do is match the \n against the \n; if we restored the
+ string value, we would be back at the foo.
+
+ Because this is used only in specific cases, we don't need to
+ check all the things that `on_failure_jump' does, to make
+ sure the right things get saved on the stack. Hence we don't
+ share its code. The only reason to push anything on the
+ stack at all is that otherwise we would have to change
+ `anychar's code to do something besides goto fail in this
+ case; that seems worse than this. */
+ case on_failure_keep_string_jump:
+ DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+#ifdef _LIBC
+ DEBUG_PRINT3 (" %d (to %p):\n", mcnt, p + mcnt);
+#else
+ DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+#endif
+
+ PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+ break;
+
+
+ /* Uses of on_failure_jump:
+
+ Each alternative starts with an on_failure_jump that points
+ to the beginning of the next alternative. Each alternative
+ except the last ends with a jump that in effect jumps past
+ the rest of the alternatives. (They really jump to the
+ ending jump of the following alternative, because tensioning
+ these jumps is a hassle.)
+
+ Repeats start with an on_failure_jump that points past both
+ the repetition text and either the following jump or
+ pop_failure_jump back to this on_failure_jump. */
+ case on_failure_jump:
+ on_failure:
+ DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+#ifdef _LIBC
+ DEBUG_PRINT3 (" %d (to %p)", mcnt, p + mcnt);
+#else
+ DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+#endif
+
+ /* If this on_failure_jump comes right before a group (i.e.,
+ the original * applied to a group), save the information
+ for that group and all inner ones, so that if we fail back
+ to this point, the group's information will be correct.
+ For example, in \(a*\)*\1, we need the preceding group,
+ and in \(zz\(a*\)b*\)\2, we need the inner group. */
+
+ /* We can't use `p' to check ahead because we push
+ a failure point to `p + mcnt' after we do this. */
+ p1 = p;
+
+ /* We need to skip no_op's before we look for the
+ start_memory in case this on_failure_jump is happening as
+ the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+ against aba. */
+ while (p1 < pend && (re_opcode_t) *p1 == no_op)
+ p1++;
+
+ if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+ {
+ /* We have a new highest active register now. This will
+ get reset at the start_memory we are about to get to,
+ but we will have saved all the registers relevant to
+ this repetition op, as described above. */
+ highest_active_reg = *(p1 + 1) + *(p1 + 2);
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *(p1 + 1);
+ }
+
+ DEBUG_PRINT1 (":\n");
+ PUSH_FAILURE_POINT (p + mcnt, d, -2);
+ break;
+
+
+ /* A smart repeat ends with `maybe_pop_jump'.
+ We change it to either `pop_failure_jump' or `jump'. */
+ case maybe_pop_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+ {
+ register unsigned char *p2 = p;
+
+ /* Compare the beginning of the repeat with what in the
+ pattern follows its end. If we can establish that there
+ is nothing that they would both match, i.e., that we
+ would have to backtrack because of (as in, e.g., `a*a')
+ then we can change to pop_failure_jump, because we'll
+ never have to backtrack.
+
+ This is not true in the case of alternatives: in
+ `(a|ab)*' we do need to backtrack to the `ab' alternative
+ (e.g., if the string was `ab'). But instead of trying to
+ detect that here, the alternative has put on a dummy
+ failure point which is what we will end up popping. */
+
+ /* Skip over open/close-group commands.
+ If what follows this loop is a ...+ construct,
+ look at what begins its body, since we will have to
+ match at least one of that. */
+ while (1)
+ {
+ if (p2 + 2 < pend
+ && ((re_opcode_t) *p2 == stop_memory
+ || (re_opcode_t) *p2 == start_memory))
+ p2 += 3;
+ else if (p2 + 6 < pend
+ && (re_opcode_t) *p2 == dummy_failure_jump)
+ p2 += 6;
+ else
+ break;
+ }
+
+ p1 = p + mcnt;
+ /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+ to the `maybe_finalize_jump' of this case. Examine what
+ follows. */
+
+ /* If we're at the end of the pattern, we can change. */
+ if (p2 == pend)
+ {
+ /* Consider what happens when matching ":\(.*\)"
+ against ":/". I don't really understand this code
+ yet. */
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1
+ (" End of pattern: change to `pop_failure_jump'.\n");
+ }
+
+ else if ((re_opcode_t) *p2 == exactn
+ || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+ {
+ register unsigned char c
+ = *p2 == (unsigned char) endline ? '\n' : p2[2];
+
+ if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
+ c, p1[5]);
+ }
+
+ else if ((re_opcode_t) p1[3] == charset
+ || (re_opcode_t) p1[3] == charset_not)
+ {
+ int not = (re_opcode_t) p1[3] == charset_not;
+
+ if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ /* `not' is equal to 1 if c would match, which means
+ that we can't change to pop_failure_jump. */
+ if (!not)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ }
+ else if ((re_opcode_t) *p2 == charset)
+ {
+ /* We win if the first character of the loop is not part
+ of the charset. */
+ if ((re_opcode_t) p1[3] == exactn
+ && ! ((int) p2[1] * BYTEWIDTH > (int) p1[5]
+ && (p2[2 + p1[5] / BYTEWIDTH]
+ & (1 << (p1[5] % BYTEWIDTH)))))
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+
+ else if ((re_opcode_t) p1[3] == charset_not)
+ {
+ int idx;
+ /* We win if the charset_not inside the loop
+ lists every character listed in the charset after. */
+ for (idx = 0; idx < (int) p2[1]; idx++)
+ if (! (p2[2 + idx] == 0
+ || (idx < (int) p1[4]
+ && ((p2[2 + idx] & ~ p1[5 + idx]) == 0))))
+ break;
+
+ if (idx == p2[1])
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ else if ((re_opcode_t) p1[3] == charset)
+ {
+ int idx;
+ /* We win if the charset inside the loop
+ has no overlap with the one after the loop. */
+ for (idx = 0;
+ idx < (int) p2[1] && idx < (int) p1[4];
+ idx++)
+ if ((p2[2 + idx] & p1[5 + idx]) != 0)
+ break;
+
+ if (idx == p2[1] || idx == p1[4])
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ }
+ }
+ p -= 2; /* Point at relative address again. */
+ if ((re_opcode_t) p[-1] != pop_failure_jump)
+ {
+ p[-1] = (unsigned char) jump;
+ DEBUG_PRINT1 (" Match => jump.\n");
+ goto unconditional_jump;
+ }
+ /* Note fall through. */
+
+
+ /* The end of a simple repeat has a pop_failure_jump back to
+ its matching on_failure_jump, where the latter will push a
+ failure point. The pop_failure_jump takes off failure
+ points put on by this pop_failure_jump's matching
+ on_failure_jump; we got through the pattern to here from the
+ matching on_failure_jump, so didn't fail. */
+ case pop_failure_jump:
+ {
+ /* We need to pass separate storage for the lowest and
+ highest registers, even though we don't care about the
+ actual values. Otherwise, we will restore only one
+ register from the stack, since lowest will == highest in
+ `pop_failure_point'. */
+ active_reg_t dummy_low_reg, dummy_high_reg;
+ unsigned char *pdummy;
+ const char *sdummy;
+
+ DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+ POP_FAILURE_POINT (sdummy, pdummy,
+ dummy_low_reg, dummy_high_reg,
+ reg_dummy, reg_dummy, reg_info_dummy);
+ }
+ /* Note fall through. */
+
+ unconditional_jump:
+#ifdef _LIBC
+ DEBUG_PRINT2 ("\n%p: ", p);
+#else
+ DEBUG_PRINT2 ("\n0x%x: ", p);
+#endif
+ /* Note fall through. */
+
+ /* Unconditionally jump (without popping any failure points). */
+ case jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
+ DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+ p += mcnt; /* Do the jump. */
+#ifdef _LIBC
+ DEBUG_PRINT2 ("(to %p).\n", p);
+#else
+ DEBUG_PRINT2 ("(to 0x%x).\n", p);
+#endif
+ break;
+
+
+ /* We need this opcode so we can detect where alternatives end
+ in `group_match_null_string_p' et al. */
+ case jump_past_alt:
+ DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+ goto unconditional_jump;
+
+
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at pop_failure_jump. We will end up at
+ pop_failure_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for pop_failure_jump to pop. */
+ case dummy_failure_jump:
+ DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+ /* It doesn't matter what we push for the string here. What
+ the code at `fail' tests is the value for the pattern. */
+ PUSH_FAILURE_POINT (NULL, NULL, -2);
+ goto unconditional_jump;
+
+
+ /* At the end of an alternative, we need to push a dummy failure
+ point in case we are followed by a `pop_failure_jump', because
+ we don't want the failure point for the alternative to be
+ popped. For example, matching `(a|ab)*' against `aab'
+ requires that we match the `ab' alternative. */
+ case push_dummy_failure:
+ DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+ /* See comments just above at `dummy_failure_jump' about the
+ two zeroes. */
+ PUSH_FAILURE_POINT (NULL, NULL, -2);
+ break;
+
+ /* Have to succeed matching what follows at least n times.
+ After that, handle like `on_failure_jump'. */
+ case succeed_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+ assert (mcnt >= 0);
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt > 0)
+ {
+ mcnt--;
+ p += 2;
+ STORE_NUMBER_AND_INCR (p, mcnt);
+#ifdef _LIBC
+ DEBUG_PRINT3 (" Setting %p to %d.\n", p - 2, mcnt);
+#else
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p - 2, mcnt);
+#endif
+ }
+ else if (mcnt == 0)
+ {
+#ifdef _LIBC
+ DEBUG_PRINT2 (" Setting two bytes from %p to no_op.\n", p+2);
+#else
+ DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
+#endif
+ p[2] = (unsigned char) no_op;
+ p[3] = (unsigned char) no_op;
+ goto on_failure;
+ }
+ break;
+
+ case jump_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt)
+ {
+ mcnt--;
+ STORE_NUMBER (p + 2, mcnt);
+#ifdef _LIBC
+ DEBUG_PRINT3 (" Setting %p to %d.\n", p + 2, mcnt);
+#else
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p + 2, mcnt);
+#endif
+ goto unconditional_jump;
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ break;
+
+ case set_number_at:
+ {
+ DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+#ifdef _LIBC
+ DEBUG_PRINT3 (" Setting %p to %d.\n", p1, mcnt);
+#else
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt);
+#endif
+ STORE_NUMBER (p1, mcnt);
+ break;
+ }
+
+#if 0
+ /* The DEC Alpha C compiler 3.x generates incorrect code for the
+ test WORDCHAR_P (d - 1) != WORDCHAR_P (d) in the expansion of
+ AT_WORD_BOUNDARY, so this code is disabled. Expanding the
+ macro and introducing temporary variables works around the bug. */
+
+ case wordbound:
+ DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ break;
+ goto fail;
+
+ case notwordbound:
+ DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ goto fail;
+ break;
+#else
+ case wordbound:
+ {
+ boolean prevchar, thischar;
+
+ DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+ if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d))
+ break;
+
+ prevchar = WORDCHAR_P (d - 1);
+ thischar = WORDCHAR_P (d);
+ if (prevchar != thischar)
+ break;
+ goto fail;
+ }
+
+ case notwordbound:
+ {
+ boolean prevchar, thischar;
+
+ DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+ if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d))
+ goto fail;
+
+ prevchar = WORDCHAR_P (d - 1);
+ thischar = WORDCHAR_P (d);
+ if (prevchar != thischar)
+ goto fail;
+ break;
+ }
+#endif
+
+ case wordbeg:
+ DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+ if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+ break;
+ goto fail;
+
+ case wordend:
+ DEBUG_PRINT1 ("EXECUTING wordend.\n");
+ if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+ && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+ break;
+ goto fail;
+
+#ifdef emacs
+ case before_dot:
+ DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) != point)
+ goto fail;
+ break;
+
+ case after_dot:
+ DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+ goto fail;
+ break;
+
+ case syntaxspec:
+ DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchsyntax;
+
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+ mcnt = (int) Sword;
+ matchsyntax:
+ PREFETCH ();
+ /* Can't use *d++ here; SYNTAX may be an unsafe macro. */
+ d++;
+ if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+ case notsyntaxspec:
+ DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchnotsyntax;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+ mcnt = (int) Sword;
+ matchnotsyntax:
+ PREFETCH ();
+ /* Can't use *d++ here; SYNTAX may be an unsafe macro. */
+ d++;
+ if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+#else /* not emacs */
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+ PREFETCH ();
+ if (!WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+ PREFETCH ();
+ if (WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+#endif /* not emacs */
+
+ default:
+ abort ();
+ }
+ continue; /* Successfully executed one pattern command; keep going. */
+
+
+ /* We goto here if a matching operation fails. */
+ fail:
+ if (!FAIL_STACK_EMPTY ())
+ { /* A restart point is known. Restore to that state. */
+ DEBUG_PRINT1 ("\nFAIL:\n");
+ POP_FAILURE_POINT (d, p,
+ lowest_active_reg, highest_active_reg,
+ regstart, regend, reg_info);
+
+ /* If this failure point is a dummy, try the next one. */
+ if (!p)
+ goto fail;
+
+ /* If we failed to the end of the pattern, don't examine *p. */
+ assert (p <= pend);
+ if (p < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ /* If failed to a backwards jump that's part of a repetition
+ loop, need to pop this failure point and use the next one. */
+ switch ((re_opcode_t) *p)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case maybe_pop_jump:
+ case pop_failure_jump:
+ case jump:
+ p1 = p + 1;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+
+ if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+ || (!is_a_jump_n
+ && (re_opcode_t) *p1 == on_failure_jump))
+ goto fail;
+ break;
+ default:
+ /* do nothing */ ;
+ }
+ }
+
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ } /* for (;;) */
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_VARIABLES ();
+
+ return -1; /* Failure to match. */
+} /* re_match_2 */
+
+/* Subroutine definitions for re_match_2. */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+
+ Return true if the pattern up to the corresponding stop_memory can
+ match the empty string, and false otherwise.
+
+ If we find the matching stop_memory, sets P to point to one past its number.
+ Otherwise, sets P to an undefined byte less than or equal to END.
+
+ We don't handle duplicates properly (yet). */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ /* Point to after the args to the start_memory. */
+ unsigned char *p1 = *p + 2;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and return true or
+ false, as appropriate, when we get to one that can't, or to the
+ matching stop_memory. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* Could be either a loop or a series of alternatives. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ /* If the next operation is not a jump backwards in the
+ pattern. */
+
+ if (mcnt >= 0)
+ {
+ /* Go through the on_failure_jumps of the alternatives,
+ seeing if any of the alternatives cannot match nothing.
+ The last alternative starts with only a jump,
+ whereas the rest start with on_failure_jump and end
+ with a jump, e.g., here is the pattern for `a|b|c':
+
+ /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+ /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+ /exactn/1/c
+
+ So, we have to first go through the first (n-1)
+ alternatives and then deal with the last one separately. */
+
+
+ /* Deal with the first (n-1) alternatives, which start
+ with an on_failure_jump (see above) that jumps to right
+ past a jump_past_alt. */
+
+ while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+ {
+ /* `mcnt' holds how many bytes long the alternative
+ is, including the ending `jump_past_alt' and
+ its number. */
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
+ reg_info))
+ return false;
+
+ /* Move to right after this alternative, including the
+ jump_past_alt. */
+ p1 += mcnt;
+
+ /* Break if it's the beginning of an n-th alternative
+ that doesn't begin with an on_failure_jump. */
+ if ((re_opcode_t) *p1 != on_failure_jump)
+ break;
+
+ /* Still have to check that it's not an n-th
+ alternative that starts with an on_failure_jump. */
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+ {
+ /* Get to the beginning of the n-th alternative. */
+ p1 -= 3;
+ break;
+ }
+ }
+
+ /* Deal with the last alternative: go back and get number
+ of the `jump_past_alt' just before it. `mcnt' contains
+ the length of the alternative. */
+ EXTRACT_NUMBER (mcnt, p1 - 2);
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+ return false;
+
+ p1 += mcnt; /* Get past the n-th alternative. */
+ } /* if mcnt > 0 */
+ break;
+
+
+ case stop_memory:
+ assert (p1[1] == **p);
+ *p = p1 + 2;
+ return true;
+
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+ It expects P to be the first byte of a single alternative and END one
+ byte past the last. The alternative can contain groups. */
+
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+ unsigned char *p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ unsigned char *p1 = p;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and break when we get
+ to one that can't. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* It's a loop. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ break;
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+ alt_match_null_string_p.
+
+ Sets P to one after the op and its arguments, if any. */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ boolean ret;
+ int reg_no;
+ unsigned char *p1 = *p;
+
+ switch ((re_opcode_t) *p1++)
+ {
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbeg:
+ case wordend:
+ case wordbound:
+ case notwordbound:
+#ifdef emacs
+ case before_dot:
+ case at_dot:
+ case after_dot:
+#endif
+ break;
+
+ case start_memory:
+ reg_no = *p1;
+ assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+ ret = group_match_null_string_p (&p1, end, reg_info);
+
+ /* Have to set this here in case we're checking a group which
+ contains a group and a back reference to it. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+ if (!ret)
+ return false;
+ break;
+
+ /* If this is an optimized succeed_n for zero times, make the jump. */
+ case jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (mcnt >= 0)
+ p1 += mcnt;
+ else
+ return false;
+ break;
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p1 += 2;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ if (mcnt == 0)
+ {
+ p1 -= 4;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ }
+ else
+ return false;
+ break;
+
+ case duplicate:
+ if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+ return false;
+ break;
+
+ case set_number_at:
+ p1 += 4;
+
+ default:
+ /* All other opcodes mean we cannot match the empty string. */
+ return false;
+ }
+
+ *p = p1;
+ return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+ bytes; nonzero otherwise. */
+
+static int
+bcmp_translate (s1, s2, len, translate)
+ const char *s1, *s2;
+ register int len;
+ RE_TRANSLATE_TYPE translate;
+{
+ register const unsigned char *p1 = (const unsigned char *) s1;
+ register const unsigned char *p2 = (const unsigned char *) s2;
+ while (len)
+ {
+ if (translate[*p1++] != translate[*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length SIZE) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry.
+
+ We call regex_compile to do the actual compilation. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ size_t length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume at least RE_NREGS registers will be set
+ (and at least one extra will be -1). */
+ bufp->regs_allocated = REGS_UNALLOCATED;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+ if (!ret)
+ return NULL;
+ return gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+#ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+ these names if they don't use our functions, and still use
+ regcomp/regexec below without link errors. */
+weak_function
+#endif
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return gettext ("No previous regular expression");
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ re_comp_buf.buffer = (unsigned char *) malloc (200);
+ if (re_comp_buf.buffer == NULL)
+ return (char *) gettext (re_error_msgid
+ + re_error_msgid_idx[(int) REG_ESPACE]);
+ re_comp_buf.allocated = 200;
+
+ re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+ if (re_comp_buf.fastmap == NULL)
+ return (char *) gettext (re_error_msgid
+ + re_error_msgid_idx[(int) REG_ESPACE]);
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+
+ if (!ret)
+ return NULL;
+
+ /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
+ return (char *) gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
+}
+
+
+int
+#ifdef _LIBC
+weak_function
+#endif
+re_exec (s)
+ const char *s;
+{
+ const int len = strlen (s);
+ return
+ 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+
+#endif /* _REGEX_RE_COMP */
+
+/* POSIX.2 functions. Don't define these for Emacs. */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' to an allocated space for the fastmap;
+ `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *preg;
+ const char *pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ reg_syntax_t syntax
+ = (cflags & REG_EXTENDED) ?
+ RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+ /* regex_compile will allocate the space for the compiled pattern. */
+ preg->buffer = 0;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Try to allocate space for the fastmap. */
+ preg->fastmap = (char *) malloc (1 << BYTEWIDTH);
+
+ if (cflags & REG_ICASE)
+ {
+ unsigned i;
+
+ preg->translate
+ = (RE_TRANSLATE_TYPE) malloc (CHAR_SET_SIZE
+ * sizeof (*(RE_TRANSLATE_TYPE)0));
+ if (preg->translate == NULL)
+ return (int) REG_ESPACE;
+
+ /* Map uppercase characters to corresponding lowercase ones. */
+ for (i = 0; i < CHAR_SET_SIZE; i++)
+ preg->translate[i] = ISUPPER (i) ? TOLOWER (i) : i;
+ }
+ else
+ preg->translate = NULL;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+
+ preg->no_sub = !!(cflags & REG_NOSUB);
+
+ /* POSIX says a null character in the pattern terminates it, so we
+ can use strlen here in compiling the pattern. */
+ ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+ if (ret == REG_NOERROR && preg->fastmap)
+ {
+ /* Compute the fastmap now, since regexec cannot modify the pattern
+ buffer. */
+ if (re_compile_fastmap (preg) == -2)
+ {
+ /* Some error occurred while computing the fastmap, just forget
+ about it. */
+ free (preg->fastmap);
+ preg->fastmap = NULL;
+ }
+ }
+
+ return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ int ret;
+ struct re_registers regs;
+ regex_t private_preg;
+ int len = strlen (string);
+ boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+ private_preg = *preg;
+
+ private_preg.not_bol = !!(eflags & REG_NOTBOL);
+ private_preg.not_eol = !!(eflags & REG_NOTEOL);
+
+ /* The user has told us exactly how many registers to return
+ information about, via `nmatch'. We have to pass that on to the
+ matching routines. */
+ private_preg.regs_allocated = REGS_FIXED;
+
+ if (want_reg_info)
+ {
+ regs.num_regs = nmatch;
+ regs.start = TALLOC (nmatch * 2, regoff_t);
+ if (regs.start == NULL)
+ return (int) REG_NOMATCH;
+ regs.end = regs.start + nmatch;
+ }
+
+ /* Perform the searching operation. */
+ ret = re_search (&private_preg, string, len,
+ /* start: */ 0, /* range: */ len,
+ want_reg_info ? &regs : (struct re_registers *) 0);
+
+ /* Copy the register information to the POSIX structure. */
+ if (want_reg_info)
+ {
+ if (ret >= 0)
+ {
+ unsigned r;
+
+ for (r = 0; r < nmatch; r++)
+ {
+ pmatch[r].rm_so = regs.start[r];
+ pmatch[r].rm_eo = regs.end[r];
+ }
+ }
+
+ /* If we needed the temporary register info, free the space now. */
+ free (regs.start);
+ }
+
+ /* We want zero return to mean success, unlike `re_search'. */
+ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+#ifdef _LIBC
+weak_alias (__regexec, regexec)
+#endif
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (errcode < 0
+ || errcode >= (int) (sizeof (re_error_msgid_idx)
+ / sizeof (re_error_msgid_idx[0])))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = gettext (re_error_msgid + re_error_msgid_idx[errcode]);
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (errbuf_size != 0)
+ {
+ if (msg_size > errbuf_size)
+ {
+#if defined HAVE_MEMPCPY || defined _LIBC
+ *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
+#else
+ memcpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+#endif
+ }
+ else
+ memcpy (errbuf, msg, msg_size);
+ }
+
+ return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ if (preg->buffer != NULL)
+ free (preg->buffer);
+ preg->buffer = NULL;
+
+ preg->allocated = 0;
+ preg->used = 0;
+
+ if (preg->fastmap != NULL)
+ free (preg->fastmap);
+ preg->fastmap = NULL;
+ preg->fastmap_accurate = 0;
+
+ if (preg->translate != NULL)
+ free (preg->translate);
+ preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+#endif /* not emacs */
+#else /* !defined(USE_LIB_REGEX) */
+char regex_d1[] = "d"; char *regex_d2 = regex_d1;
+#endif /* defined(USE_LIB_REGEX) */
diff --git a/lib/rmnt.c b/lib/rmnt.c
new file mode 100644
index 0000000..0e1470f
--- /dev/null
+++ b/lib/rmnt.c
@@ -0,0 +1,243 @@
+/*
+ * rmnt.c -- readmnt() function for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(USE_LIB_READMNT)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: rmnt.c,v 1.12 2008/10/21 16:13:23 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+
+/*
+ * The caller may define:
+ *
+ * 1. An RMNT_EXPDEV macro to expand (ala EP/IX) device numbers;
+ *
+ * EP/IX, for example, uses:
+ *
+ * #define RMNT_EXPDEV(n) expdev(n)
+ *
+ * 2. A custom macro, MNTSKIP, for making decisions to skip entries
+ * -- e.g., ones whose mnt_type is MNTTYPE_IGNORE.
+ *
+ * 3. RMNT_FSTYPE to specify the member name of the character string of the
+ * mntent structure containing the file system type, and MOUNTS_FSTYPE to
+ * specify the member name of the character string pointer of the local
+ * mounts structure where RMNT_FSTYPE is to be copied.
+ *
+ * 4. RMNT_STAT_FSTYPE to specify the member name of the stat structure
+ * containing an integer file system type, and MOUNTS_STAT_FSTYPE to
+ * specify the member name of the integer in the local mounts structure
+ * where RMNT_STAT_FSTYPE is to be copied.
+ *
+ */
+
+#if !defined(RMNT_EXPDEV)
+#define RMNT_EXPDEV(n) n
+#endif /* !defined(RMNT_EXPDEV) */
+
+
+/*
+ * Local static definitions
+ */
+
+static struct mounts *Lmi = (struct mounts *)NULL; /* local mount info */
+static int Lmist = 0; /* Lmi status */
+
+
+/*
+ * readmnt() - read mount table
+ */
+
+struct mounts *
+readmnt()
+{
+ char *dn = (char *)NULL;
+ char *ln;
+ FILE *mfp;
+ struct mntent *mp;
+ struct mounts *mtp;
+ char *opt, *opte;
+ struct stat sb;
+
+ if (Lmi || Lmist)
+ return(Lmi);
+/*
+ * Open access to the mount table.
+ */
+ if (!(mfp = setmntent(MOUNTED, "r"))) {
+ (void) fprintf(stderr, "%s: can't access %s\n", Pn, MOUNTED);
+ Exit(1);
+ }
+/*
+ * Read mount table entries.
+ */
+ while ((mp = getmntent(mfp))) {
+
+#if defined(MNTSKIP)
+ /*
+ * Specfy in the MNTSKIP macro the decisions needed to determine
+ * that this entry should be skipped.
+ *
+ * Typically entries whose mnt_type is MNTTYPE_IGNORE are skipped.
+ *
+ * The MNTSKIP macro allows the caller to use other tests.
+ */
+ MNTSKIP
+#endif /* MNTSKIP */
+
+ /*
+ * Interpolate a possible symbolic directory link.
+ */
+ if (dn)
+ (void) free((FREE_P *)dn);
+ if (!(dn = mkstrcpy(mp->mnt_dir, (MALLOC_S *)NULL)))
+ goto no_space_for_mount;
+ if (!(ln = Readlink(dn))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ " Output information may be incomplete.\n");
+ continue;
+ }
+ if (ln != dn) {
+ (void) free((FREE_P *)dn);
+ dn = ln;
+ }
+ if (*dn != '/')
+ continue;
+ /*
+ * Stat() the directory.
+ */
+ if (statsafely(dn, &sb)) {
+ if (!Fwarn) {
+ (void) fprintf(stderr, "%s: WARNING: can't stat() ", Pn);
+ safestrprt(mp->mnt_type, stderr, 0);
+ (void) fprintf(stderr, " file system ");
+ safestrprt(mp->mnt_dir, stderr, 1);
+ (void) fprintf(stderr,
+ " Output information may be incomplete.\n");
+ }
+ if ((opt = strstr(mp->mnt_opts, "dev="))) {
+ (void) zeromem(&sb, sizeof(sb));
+ if ((opte = x2dev(opt + 4, (dev_t *)&sb.st_dev))) {
+ sb.st_mode = S_IFDIR | 0777;
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ " assuming \"%.*s\" from %s\n",
+ (int)(opte - opt), opt, MOUNTED);
+ } else
+ opt = (char *)NULL;
+ }
+ if (!opt)
+ continue;
+ }
+ /*
+ * Allocate and fill a local mounts structure with the directory
+ * (mounted) information.
+ */
+ if (!(mtp = (struct mounts *)malloc(sizeof(struct mounts)))) {
+
+no_space_for_mount:
+
+ (void) fprintf(stderr, "%s: no space for mount at ", Pn);
+ safestrprt(mp->mnt_fsname, stderr, 0);
+ (void) fprintf(stderr, " (");
+ safestrprt(mp->mnt_dir, stderr, 0);
+ (void) fprintf(stderr, ")\n");
+ Exit(1);
+ }
+ mtp->dir = dn;
+ dn = (char *)NULL;
+ mtp->next = Lmi;
+ mtp->dev = RMNT_EXPDEV(sb.st_dev);
+ mtp->rdev = RMNT_EXPDEV(sb.st_rdev);
+ mtp->inode = (INODETYPE)sb.st_ino;
+ mtp->mode = sb.st_mode;
+
+# if defined(RMNT_FSTYPE) && defined(MOUNTS_FSTYPE)
+ /*
+ * Make a copy of RMNT_FSTYPE in MOUNTS_FSTYPE.
+ */
+ if (!(mtp->MOUNTS_FSTYPE = mkstrcpy(mp->RMNT_FSTYPE,
+ (MALLOC_S *)NULL)))
+ {
+ (void) fprintf(stderr, "%s: no space for fstype (%s): %s\n",
+ Pn, mtp->dir, mp->RMNT_FSTYPE);
+ Exit(1);
+ }
+ (void) strcpy(mtp->MOUNTS_FSTYPE, mp->RMNT_FSTYPE);
+# endif /* defined(RMNT_FSTYP) && defined(MOUNTS_FSTYP) */
+
+# if defined(RMNT_STAT_FSTYPE) && defined(MOUNTS_STAT_FSTYPE)
+ /*
+ * Make a copy of RMNT_STAT_FSTYPE in MOUNTS_STAT_FSTYPE.
+ */
+ mtp->MOUNTS_STAT_FSTYPE = (int)sb.RMNT_STAT_FSTYPE;
+# endif /* defined(RMNT_STAT_FSTYP) && defined(MOUNTS_STAT_FSTYP) */
+
+ /*
+ * Interpolate a possible file system (mounted-on device) name link.
+ */
+ if (!(dn = mkstrcpy(mp->mnt_fsname, (MALLOC_S *)NULL)))
+ goto no_space_for_mount;
+ mtp->fsname = dn;
+ ln = Readlink(dn);
+ dn = (char *)NULL;
+ /*
+ * Stat() the file system (mounted-on) name and add file system
+ * information to the local mounts structure.
+ */
+ if (!ln || statsafely(ln, &sb))
+ sb.st_mode = 0;
+ mtp->fsnmres = ln;
+ mtp->fs_mode = sb.st_mode;
+ Lmi = mtp;
+ }
+ (void) endmntent(mfp);
+/*
+ * Clean up and return the local nount info table address.
+ */
+ if (dn)
+ (void) free((FREE_P *)dn);
+ Lmist = 1;
+ return(Lmi);
+}
+#else /* !defined(USE_LIB_READMNT) */
+char rmnt_d1[] = "d"; char *rmnt_d2 = rmnt_d1;
+#endif /* defined(USE_LIB_READMNT) */
diff --git a/lib/rnam.c b/lib/rnam.c
new file mode 100644
index 0000000..c7ed582
--- /dev/null
+++ b/lib/rnam.c
@@ -0,0 +1,669 @@
+/*
+ * rnam.c -- BSD format name cache functions for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(HASNCACHE) && defined(USE_LIB_RNAM)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: rnam.c,v 1.11 2008/10/21 16:13:23 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+/*
+ * rnam.c - read BSD format (struct namecache or nch) name cache
+ *
+ * This code is effective only when HASNCACHE is defined.
+ */
+
+/*
+ * The caller must:
+ *
+ * #include the relevant header file -- e.g., <sys/namei.h>.
+ *
+ * Define X_NCACHE as the nickname for the kernel cache address.
+ *
+ * Define X_NCSIZE as the nickname for the size of the kernel cache.
+ *
+ * Define NCACHE_NXT if the kernel's name cache is a linked list, starting
+ * at the X_NCACHE address, rather than a table, starting at that address.
+ *
+ * Define NCACHE_NO_ROOT if the calling dialect doesn't support
+ * the locating of the root node of a file system.
+ *
+ * Define the name of the name cache structure -- e.g.,
+ *
+ * #define NCACHE <structure name>
+ *
+ * Define the following casts, if they differ from the defaults:
+ *
+ * NCACHE_SZ_CAST cast for X_NCSIZE (default int)
+ *
+ * e.g.,
+ * #define NCACHE_SZ_CAST unsigned long
+ *
+ * Define the names of these elements of struct NCACHE:
+ *
+ * must #define NCACHE_NM <name>
+ * must #define NCACHE_NMLEN <name length
+ * optional #define NCACHE_NXT <link to next entry>
+ * must #define NCACHE_NODEADDR <node address>
+ * must #define NCACHE_NODEID <node capability ID)
+ * optional #define NCACHE_PARADDR <parent node address>
+ * optional #define NCACHE_PARID <parent node capability ID)
+ *
+ * The caller may need to:
+ *
+ * Define NCHNAMLEN as the length of the name element of NCACHE, if it's
+ * not defined in the header file that defines the NCACHE structure.
+ *
+ * Define this prototype for ncache_load():
+ *
+ * _PROTOTYPE(static void ncache_load,(void));
+ */
+
+
+/*
+ * Local static values
+ */
+
+static int Mch; /* name cache hash mask */
+
+# if !defined(NCACHE_NC_CAST)
+#define NCACHE_SZ_CAST int
+# endif /* !defined(NCACHE_NC_CAST) */
+
+static NCACHE_SZ_CAST Nc = 0; /* size of name cache */
+static int Nch = 0; /* size of name cache hash pointer
+ * table */
+struct l_nch {
+ KA_T na; /* node address */
+
+# if defined(NCACHE_NODEID)
+ unsigned long id; /* capability ID */
+# endif /* defined(NCACHE_NODEID) */
+
+# if defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
+ KA_T pa; /* parent node address */
+ struct l_nch *pla; /* parent local node address */
+ unsigned long did; /* parent capability ID */
+# endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
+
+ char nm[NCHNAMLEN+1]; /* name */
+ int nl; /* name length */
+};
+
+static struct l_nch *Ncache = (struct l_nch*)NULL;
+ /* the local name cache */
+static struct l_nch **Nchash = (struct l_nch **)NULL;
+ /* Ncache hash pointers */
+static int Ncfirst = 1; /* first-call status */
+
+# if defined(NCACHE_NODEID)
+#define ncachehash(i,n) Nchash+(((((int)(n)>>2)+((int)(i)))*31415)&Mch)
+_PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long i, KA_T na));
+# else /* !defined(NCACHE_NODEID) */
+#define ncachehash(n) Nchash+((((int)(n)>>2)*31415)&Mch)
+_PROTOTYPE(static struct l_nch *ncache_addr,(KA_T na));
+# endif /* defined(NCACHE_NODEID) */
+
+#define DEFNCACHESZ 1024 /* local size if X_NCSIZE kernel value < 1 */
+#define LNCHINCRSZ 64 /* local size increment */
+
+# if !defined(NCACHE_NO_ROOT)
+_PROTOTYPE(static int ncache_isroot,(KA_T na, char *cp));
+# endif /* !defined(NCACHE_NO_ROOT) */
+
+
+/*
+ * ncache_addr() - look up a node's local ncache address
+ */
+
+static struct l_nch *
+
+# if defined(NCACHE_NODEID)
+ncache_addr(i, na)
+ unsigned long i; /* node's capability ID */
+# else /* !defined(NCACHE_NODEID) */
+ncache_addr(na)
+# endif /* defined(NCACHE_NODEID) */
+
+ KA_T na; /* node's address */
+{
+ struct l_nch **hp;
+
+# if defined(NCACHE_NODEID)
+ for (hp = ncachehash(i, na); *hp; hp++)
+# else /* !defined(NCACHE_NODEID) */
+ for (hp = ncachehash(na); *hp; hp++)
+# endif /* defined(NCACHE_NODEID) */
+
+ {
+
+# if defined(NCACHE_NODEID)
+ if ((*hp)->id == i && (*hp)->na == na)
+# else /* !defined(NCACHE_NODEID) */
+ if ((*hp)->na == na)
+# endif /* defined(NCACHE_NODEID) */
+
+ return(*hp);
+ }
+ return((struct l_nch *)NULL);
+}
+
+
+# if !defined(NCACHE_NO_ROOT)
+/*
+ * ncache_isroot() - is head of name cache path a file system root?
+ */
+
+static int
+ncache_isroot(na, cp)
+ KA_T na; /* kernel node address */
+ char *cp; /* partial path */
+{
+ char buf[MAXPATHLEN];
+ int i;
+ MALLOC_S len;
+ struct mounts *mtp;
+ static int nca = 0;
+ static int ncn = 0;
+ static KA_T *nc = (KA_T *)NULL;
+ struct stat sb;
+ struct vnode v;
+
+ if (!na)
+ return(0);
+/*
+ * Search the root vnode cache.
+ */
+ for (i = 0; i < ncn; i++) {
+ if (na == nc[i])
+ return(1);
+ }
+/*
+ * Read the vnode and see if it's a VDIR node with the VROOT flag set. If
+ * it is, then the path is complete.
+ *
+ * If it isn't, and if the file has an inode number, search the mount table
+ * and see if the file system's inode number is known. If it is, form the
+ * possible full path, safely stat() it, and see if it's inode number matches
+ * the one we have for this file. If it does, then the path is complete.
+ */
+ if (kread((KA_T)na, (char *)&v, sizeof(v))
+ || v.v_type != VDIR || !(v.v_flag & VROOT)) {
+
+ /*
+ * The vnode tests failed. Try the inode tests.
+ */
+ if (Lf->inp_ty != 1 || !Lf->inode
+ || !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1)
+ return(0);
+ if ((len + 1 + strlen(cp) + 1) > sizeof(buf))
+ return(0);
+ for (mtp = readmnt(); mtp; mtp = mtp->next) {
+ if (!mtp->dir || !mtp->inode)
+ continue;
+ if (strcmp(Lf->fsdir, mtp->dir) == 0)
+ break;
+ }
+ if (!mtp)
+ return(0);
+ (void) strcpy(buf, Lf->fsdir);
+ if (buf[len - 1] != '/')
+ buf[len++] = '/';
+ (void) strcpy(&buf[len], cp);
+ if (statsafely(buf, &sb) != 0
+ || (unsigned long)sb.st_ino != Lf->inode)
+ return(0);
+ }
+/*
+ * Add the node address to the root node cache.
+ */
+ if (ncn >= nca) {
+ if (!nca) {
+ len = (MALLOC_S)(10 * sizeof(KA_T));
+ nc = (KA_T *)malloc(len);
+ } else {
+ len = (MALLOC_S)((nca + 10) * sizeof(KA_T));
+ nc = (KA_T *)realloc(nc, len);
+ }
+ if (!nc) {
+ (void) fprintf(stderr, "%s: no space for root node table\n",
+ Pn);
+ Exit(1);
+ }
+ nca += 10;
+ }
+ nc[ncn++] = na;
+ return(1);
+}
+# endif /* !defined(NCACHE_NO_ROOT) */
+
+
+/*
+ * ncache_load() - load the kernel's name cache
+ */
+
+void
+ncache_load()
+{
+ struct l_nch **hp, *lc;
+ int i, len, n;
+ static int iNc = 0;
+ struct NCACHE *kc;
+ static KA_T kp = (KA_T)NULL;
+ KA_T v;
+
+# if defined(NCACHE_NXT)
+ static KA_T kf;
+ struct NCACHE nc;
+# else /* !defined NCACHE_NXT) */
+ static struct NCACHE *kca = (struct NCACHE *)NULL;
+# endif /* defined(NCACHE_NXT) */
+
+ if (!Fncache)
+ return;
+ if (Ncfirst) {
+
+ /*
+ * Do startup (first-time) functions.
+ */
+ Ncfirst = 0;
+ /*
+ * Establish kernel cache size.
+ */
+ v = (KA_T)0;
+ if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0
+ || !v
+ || kread((KA_T)v, (char *)&Nc, sizeof(Nc)))
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read name cache size: %s\n",
+ Pn, print_kptr(v, (char *)NULL, 0));
+ iNc = Nc = 0;
+ return;
+ }
+ iNc = Nc;
+ if (Nc < 1) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: kernel name cache size: %d\n", Pn, Nc);
+ (void) fprintf(stderr,
+ " Cache size assumed to be: %d\n", DEFNCACHESZ);
+ }
+ iNc = Nc = DEFNCACHESZ;
+ }
+ /*
+ * Establish kernel cache address.
+ */
+ v = (KA_T)0;
+ if (get_Nl_value(X_NCACHE, (struct drive_Nl *)NULL, &v) < 0
+ || !v
+ || kread((KA_T)v, (char *)&kp, sizeof(kp))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read name cache address: %s\n",
+ Pn, print_kptr(v, (char *)NULL, 0));
+ iNc = Nc = 0;
+ return;
+ }
+
+# if defined(NCACHE_NXT)
+ kf = kp;
+
+# else /* !defined(NCACHE_NXT) */
+ /*
+ * Allocate space for a local copy of the kernel's cache.
+ */
+ len = Nc * sizeof(struct NCACHE);
+ if (!(kca = (struct NCACHE *)malloc((MALLOC_S)len))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: can't allocate name cache space: %d\n", Pn, len);
+ Exit(1);
+ }
+# endif /* defined(NCACHE_NXT) */
+
+ /*
+ * Allocate space for the local cache.
+ */
+ len = Nc * sizeof(struct l_nch);
+ if (!(Ncache = (struct l_nch *)malloc((MALLOC_S)len))) {
+
+no_local_space:
+
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: no space for %d byte local name cache\n", Pn, len);
+ Exit(1);
+ }
+ } else {
+
+ /*
+ * Do setup for repeat calls.
+ */
+ if ((Nc = iNc) == 0)
+ return;
+ if (Nchash) {
+ (void) free((FREE_P *)Nchash);
+ Nchash = (struct l_nch **)NULL;
+ }
+
+# if defined(NCACHE_NXT)
+ kp = kf;
+# endif /* defined(NCACHE_NXT) */
+
+ }
+
+# if !defined(NCACHE_NXT)
+
+/*
+ * Read the kernel's name cache.
+ */
+ if (kread(kp, (char *)kca, (Nc * sizeof(struct NCACHE)))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read kernel's name cache: %s\n",
+ Pn, print_kptr(kp, (char *)NULL, 0));
+ Nc = 0;
+ return;
+ }
+# endif /* !defined(NCACHE_NXT) */
+
+/*
+ * Build a local copy of the kernel name cache.
+ */
+
+# if defined(NCACHE_NXT)
+ for (i = iNc * 16, kc = &nc, lc = Ncache, n = 0; kp; )
+# else /* !defined(NCACHE_NXT) */
+ for (i = n = 0, kc = kca, lc = Ncache; i < Nc; i++, kc++)
+# endif /* defined(NCACHE_NXT) */
+
+ {
+
+# if defined(NCACHE_NXT)
+ if (kread(kp, (char *)kc, sizeof(nc)))
+ break;
+ if ((kp = (KA_T)kc->NCACHE_NXT) == kf)
+ kp = (KA_T)NULL;
+# endif /* defined(NCACHE_NXT) */
+
+ if (!kc->NCACHE_NODEADDR)
+ continue;
+ if ((len = kc->NCACHE_NMLEN) < 1 || len > NCHNAMLEN)
+ continue;
+ if (len < 3 && kc->NCACHE_NM[0] == '.') {
+ if (len == 1 || (len == 2 && kc->NCACHE_NM[1] == '.'))
+ continue;
+ }
+
+# if defined(NCACHE_NXT)
+ if (n >= Nc) {
+ Nc += LNCHINCRSZ;
+ if (!(Ncache = (struct l_nch *)realloc(Ncache,
+ (MALLOC_S)(Nc * sizeof(struct l_nch)))))
+ {
+ (void) fprintf(stderr,
+ "%s: no more space for %d entry local name cache\n",
+ Pn, Nc);
+ Exit(1);
+ }
+ lc = &Ncache[n];
+ }
+# endif /* defined(NCACHE_NXT) */
+
+# if defined(NCACHE_NODEID)
+ lc->na = (KA_T)kc->NCACHE_NODEADDR;
+ lc->id = kc->NCACHE_NODEID;
+# endif /* defined(NCACHE_NODEID) */
+
+# if defined(NCACHE_PARADDR)
+ lc->pa = (KA_T)kc->NCACHE_PARADDR;
+ lc->pla = (struct l_nch *)NULL;
+# endif /* defined(NCACHE_PARADDR) */
+
+# if defined(NCACHE_PARID)
+ lc->did = kc->NCACHE_PARID;
+# endif /* defined(NCACHE_PARID) */
+
+ (void) strncpy(lc->nm, kc->NCACHE_NM, len);
+ lc->nm[len] = '\0';
+ lc->nl = strlen(lc->nm);
+ n++;
+ lc++;
+
+# if defined(NCACHE_NXT)
+ if (n >= i) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: name cache truncated at %d entries\n",
+ Pn, n);
+ break;
+ }
+# endif /* defined(NCACHE_NXT) */
+
+ }
+/*
+ * Reduce memory usage, as required.
+ */
+
+# if !defined(NCACHE_NXT)
+ if (!RptTm)
+ (void) free((FREE_P *)kca);
+# endif /* !defined(NCACHE_NXT) */
+
+ if (n < 1) {
+ Nc = 0;
+ if (!RptTm) {
+ (void) free((FREE_P *)Ncache);
+ Ncache = (struct l_nch *)NULL;
+ }
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: unusable name cache size: %d\n", Pn, n);
+ return;
+ }
+ if (n < Nc) {
+ Nc = n;
+ if (!RptTm) {
+ len = Nc * sizeof(struct l_nch);
+ if (!(Ncache = (struct l_nch *)realloc(Ncache, len)))
+ goto no_local_space;
+ }
+ }
+/*
+ * Build a hash table to locate Ncache entries.
+ */
+ for (Nch = 1; Nch < Nc; Nch <<= 1)
+ ;
+ Nch <<= 1;
+ Mch = Nch - 1;
+ if (!(Nchash = (struct l_nch **)calloc(Nch+Nc, sizeof(struct l_nch *))))
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: no space for %d name cache hash pointers\n",
+ Pn, Nch + Nc);
+ Exit(1);
+ }
+ for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
+
+# if defined(NCACHE_NODEID)
+ for (hp = ncachehash(lc->id, lc->na), n = 1; *hp; hp++)
+# else /* defined(NCACHE_NODEID) */
+ for (hp = ncachehash(lc->na), n = 1; *hp; hp++)
+# endif /* defined(NCACHE_NODEID) */
+
+ {
+
+# if defined(NCACHE_NODEID)
+ if ((*hp)->na == lc->na && (*hp)->id == lc->id
+# else /* defined(NCACHE_NODEID) */
+ if ((*hp)->na == lc->na
+# endif /* defined(NCACHE_NODEID) */
+
+ && strcmp((*hp)->nm, lc->nm) == 0
+
+# if defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
+ && (*hp)->pa == lc->pa && (*hp)->did == lc->did
+# endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
+
+ ) {
+ n = 0;
+ break;
+ }
+ }
+ if (n)
+ *hp = lc;
+ }
+
+# if defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
+/*
+ * Make a final pass through the local cache and convert parent node
+ * addresses to local name cache pointers.
+ */
+ for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
+ if (!lc->pa)
+ continue;
+ lc->pla = ncache_addr(lc->did, lc->pa);
+ }
+# endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
+}
+
+
+/*
+ * ncache_lookup() - look up a node's name in the kernel's name cache
+ */
+
+char *
+ncache_lookup(buf, blen, fp)
+ char *buf; /* receiving name buffer */
+ int blen; /* receiving buffer length */
+ int *fp; /* full path reply */
+{
+ char *cp = buf;
+ struct l_nch *lc;
+ struct mounts *mtp;
+ int nl, rlen;
+
+ *cp = '\0';
+ *fp = 0;
+
+# if defined(HASFSINO)
+/*
+ * If the entry has an inode number that matches the inode number of the
+ * file system mount point, return an empty path reply. That tells the
+ * caller to print the file system mount point name only.
+ */
+ if ((Lf->inp_ty == 1) && Lf->fs_ino && (Lf->inode == Lf->fs_ino))
+ return(cp);
+# endif /* defined(HASFSINO) */
+
+/*
+ * Look up the name cache entry for the node address.
+ */
+
+# if defined(NCACHE_NODEID)
+ if (Nc == 0 || !(lc = ncache_addr(Lf->id, Lf->na)))
+# else /* defined(NCACHE_NODEID) */
+ if (Nc == 0 || !(lc = ncache_addr(Lf->na)))
+# endif /* defined(NCACHE_NODEID) */
+
+
+ {
+
+ /*
+ * If the node has no cache entry, see if it's the mount
+ * point of a known file system.
+ */
+ if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1)
+ return((char *)NULL);
+ for (mtp = readmnt(); mtp; mtp = mtp->next) {
+ if (!mtp->dir || !mtp->inode)
+ continue;
+ if (Lf->dev == mtp->dev
+ && mtp->inode == Lf->inode
+ && strcmp(mtp->dir, Lf->fsdir) == 0)
+ return(cp);
+ }
+ return((char *)NULL);
+ }
+/*
+ * Start the path assembly.
+ */
+ if ((nl = lc->nl) > (blen - 1))
+ return((char *)NULL);
+ cp = buf + blen - nl - 1;
+ rlen = blen - nl - 1;
+ (void) strcpy(cp, lc->nm);
+
+# if defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
+/*
+ * Look up the name cache entries that are parents of the node address.
+ * Quit when:
+ *
+ * there's no parent;
+ * the name length is too large to fit in the receiving buffer.
+ */
+ for (;;) {
+ if (!lc->pla) {
+
+# if !defined(NCACHE_NO_ROOT)
+ if (ncache_isroot(lc->pa, cp))
+ *fp = 1;
+# endif /* !defined(NCACHE_NO_ROOT) */
+
+ break;
+ }
+ lc = lc->pla;
+ if (((nl = lc->nl) + 1) > rlen)
+ break;
+ *(cp - 1) = '/';
+ cp--;
+ rlen--;
+ (void) strncpy((cp - nl), lc->nm, nl);
+ cp -= nl;
+ rlen -= nl;
+ }
+# endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
+ return(cp);
+}
+#else /* !defined(HASNCACHE) || !defined(USE_LIB_RNAM) */
+char rnam_d1[] = "d"; char *rnam_d2 = rnam_d1;
+#endif /* defined(HASNCACHE) && defined(USE_LIB_RNAM) */
diff --git a/lib/rnch.c b/lib/rnch.c
new file mode 100644
index 0000000..0bdb7db
--- /dev/null
+++ b/lib/rnch.c
@@ -0,0 +1,811 @@
+/*
+ * rnch.c -- Sun format name cache functions for lsof library
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(HASNCACHE) && defined(USE_LIB_RNCH)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: rnch.c,v 1.11 2008/10/21 16:13:23 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+/*
+ * rnch.c - read Sun format (struct ncache) name cache
+ *
+ * This code is effective only when HASNCACHE is defined.
+ */
+
+/*
+ * The caller must:
+ *
+ * #include the relevant header file -- e.g., <sys/dnlc.h>.
+ *
+ * Define X_NCSIZE as the nickname for the kernel cache size variable,
+ * or, if X_NCSIZE is undefined, define FIXED_NCSIZE as the size of the
+ * kernel cache.
+ *
+ * Define X_NCACHE as the nickname for the kernel cache address and
+ * define ADDR_NCACHE if the address is the address of the cache,
+ * rather than the address of a pointer to it.
+ *
+ * Define NCACHE_NXT if the kernel's name cache is a linked list, starting
+ * at the X_NCACHE address, rather than a table, starting at that address.
+ *
+ * Define any of the following casts that differ from their defaults:
+ *
+ * NCACHE_SZ_CAST cast for X_NCACHE (default int)
+ *
+ * The caller may:
+ *
+ * Define NCACHE_DP as the name of the element in the
+ * ncache structure that contains the
+ * parent vnode pointer.
+ *
+ * Default: dp
+ *
+ * Define NCACHE_NAME as the name of the element in the
+ * ncache structure that contains the
+ * name.
+ *
+ * Default: name
+ *
+ * Define NCACHE_NAMLEN as the name of the element in the
+ * ncache structure that contains the
+ * name length.
+ *
+ * Deafult: namlen
+ *
+ * Define NCACHE_NEGVN as the name of the name list element
+ * whose value is a vnode address to
+ * ignore when loading the kernel name
+ * cache.
+ *
+ * Define NCACHE_NODEID as the name of the element in the
+ * ncache structure that contains the
+ * vnode's capability ID.
+ *
+ * Define NCACHE_PARID as the name of the element in the
+ * ncache structure that contains the
+ * parent vnode's capability ID.
+ *
+ * Define NCACHE_VP as the name of the element in the
+ * ncache structure that contains the
+ * vnode pointer.
+ *
+ * Default: vp
+ *
+ * Note: if NCACHE_NODEID is defined, then NCACHE_PARID must be defined.
+ *
+ *
+ * The caller must:
+ *
+ * Define this prototype for ncache_load():
+ *
+ * _PROTOTYPE(void ncache_load,(void));
+ */
+
+
+/*
+ * Local static values
+ */
+
+static int Mch; /* name cache hash mask */
+
+# if !defined(NCACHE_NC_CAST)
+#define NCACHE_SZ_CAST int
+# endif /* !defined(NCACHE_NC_CAST) */
+
+static NCACHE_SZ_CAST Nc = 0; /* size of name cache */
+static int Nch = 0; /* size of name cache hash pointer
+ * table */
+struct l_nch {
+ KA_T vp; /* vnode address */
+ KA_T dp; /* parent vnode address */
+ struct l_nch *pa; /* parent Ncache address */
+
+# if defined(NCACHE_NODEID)
+ unsigned long id; /* node's capability ID */
+ unsigned long did; /* parent node's capability ID */
+# endif /* defined(NCACHE_NODEID) */
+
+ char *nm; /* name */
+ int nl; /* name length */
+};
+
+static struct l_nch *Ncache = (struct l_nch *)NULL;
+ /* the local name cache */
+static struct l_nch **Nchash = (struct l_nch **)NULL;
+ /* Ncache hash pointers */
+static int Ncfirst = 1; /* first-call status */
+
+# if defined(NCACHE_NEGVN)
+static KA_T NegVN = (KA_T)NULL; /* negative vnode address */
+static int NegVNSt = 0; /* NegVN status: 0 = not loaded */
+# endif /* defined(NCACHE_NEGVN) */
+
+# if defined(NCACHE_NODEID)
+_PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long i, KA_T v));
+#define ncachehash(i,v) Nchash+(((((int)(v)>>2)+((int)(i)))*31415)&Mch)
+# else /* !defined(NCACHE_NODEID) */
+_PROTOTYPE(static struct l_nch *ncache_addr,(KA_T v));
+#define ncachehash(v) Nchash+((((int)(v)>>2)*31415)&Mch)
+# endif /* defined(NCACHE_NODEID) */
+
+_PROTOTYPE(static int ncache_isroot,(KA_T va, char *cp));
+
+#define DEFNCACHESZ 1024 /* local size if X_NCSIZE kernel value < 1 */
+#define LNCHINCRSZ 64 /* local size increment */
+
+# if !defined(NCACHE_DP)
+#define NCACHE_DP dp
+# endif /* !defined(NCACHE_DP) */
+
+# if !defined(NCACHE_NAME)
+#define NCACHE_NAME name
+# endif /* !defined(NCACHE_NAME) */
+
+# if !defined(NCACHE_NAMLEN)
+#define NCACHE_NAMLEN namlen
+# endif /* !defined(NCACHE_NAMLEN) */
+
+# if !defined(NCACHE_VP)
+#define NCACHE_VP vp
+# endif /* !defined(NCACHE_VP) */
+
+
+/*
+ * ncache_addr() - look up a node's local ncache address
+ */
+
+static struct l_nch *
+
+# if defined(NCACHE_NODEID)
+ncache_addr(i, v)
+# else /* !defined(NCACHE_NODEID) */
+ncache_addr(v)
+# endif /* defined(NCACHE_NODEID) */
+
+# if defined(NCACHE_NODEID)
+ unsigned long i; /* capability ID */
+# endif /* defined(NCACHE_NODEID) */
+
+ KA_T v; /* vnode's address */
+{
+ struct l_nch **hp;
+
+# if defined(NCACHE_NODEID)
+ for (hp = ncachehash(i, v); *hp; hp++)
+# else /* !defined(NCACHE_NODEID) */
+ for (hp = ncachehash(v); *hp; hp++)
+# endif /* defined(NCACHE_NODEID) */
+
+ {
+
+# if defined(NCACHE_NODEID)
+ if ((*hp)->vp == v && (*hp)->id == i)
+# else /* !defined(NCACHE_NODEID) */
+ if ((*hp)->vp == v)
+# endif /* defined(NCACHE_NODEID) */
+
+ return(*hp);
+ }
+ return((struct l_nch *)NULL);
+}
+
+
+/*
+ * ncache_isroot() - is head of name cache path a file system root?
+ */
+
+static int
+ncache_isroot(va, cp)
+ KA_T va; /* kernel vnode address */
+ char *cp; /* partial path */
+{
+ char buf[MAXPATHLEN];
+ int i;
+ MALLOC_S len;
+ struct mounts *mtp;
+ struct stat sb;
+ struct vnode v;
+ static int vca = 0;
+ static int vcn = 0;
+ static KA_T *vc = (KA_T *)NULL;
+
+ if (!va)
+ return(0);
+/*
+ * Search the root vnode cache.
+ */
+ for (i = 0; i < vcn; i++) {
+ if (va == vc[i])
+ return(1);
+ }
+/*
+ * Read the vnode and see if it's a VDIR node with the VROOT flag set. If
+ * it is, then the path is complete.
+ *
+ * If it isn't, and if the file has an inode number, search the mount table
+ * and see if the file system's inode number is known. If it is, form the
+ * possible full path, safely stat() it, and see if it's inode number matches
+ * the one we have for this file. If it does, then the path is complete.
+ */
+ if (kread((KA_T)va, (char *)&v, sizeof(v))
+ || v.v_type != VDIR || !(v.v_flag & VROOT)) {
+
+ /*
+ * The vnode tests failed. Try the inode tests.
+ */
+ if (Lf->inp_ty != 1 || !Lf->inode
+ || !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1)
+ return(0);
+ if ((len + 1 + strlen(cp) + 1) > sizeof(buf))
+ return(0);
+ for (mtp = readmnt(); mtp; mtp = mtp->next) {
+ if (!mtp->dir || !mtp->inode)
+ continue;
+ if (strcmp(Lf->fsdir, mtp->dir) == 0)
+ break;
+ }
+ if (!mtp)
+ return(0);
+ (void) strcpy(buf, Lf->fsdir);
+ if (buf[len - 1] != '/')
+ buf[len++] = '/';
+ (void) strcpy(&buf[len], cp);
+ if (statsafely(buf, &sb) != 0
+ || (unsigned long)sb.st_ino != Lf->inode)
+ return(0);
+ }
+/*
+ * Add the vnode address to the root vnode cache.
+ */
+ if (vcn >= vca) {
+ vca += 10;
+ len = (MALLOC_S)(vca * sizeof(KA_T));
+ if (!vc)
+ vc = (KA_T *)malloc(len);
+ else
+ vc = (KA_T *)realloc(vc, len);
+ if (!vc) {
+ (void) fprintf(stderr, "%s: no space for root vnode table\n",
+ Pn);
+ Exit(1);
+ }
+ }
+ vc[vcn++] = va;
+ return(1);
+}
+
+
+/*
+ * ncache_load() - load the kernel's name cache
+ */
+
+void
+ncache_load()
+{
+ char *cp, *np;
+ struct l_nch **hp, *lc;
+ int i, len, n;
+ static int iNc = 0;
+ struct ncache *kc;
+ static KA_T kp = (KA_T)NULL;
+ KA_T v;
+
+# if defined(HASDNLCPTR)
+ static int na = 0;
+ static char *nb = (char *)NULL;
+# endif /* defined(HASDNLCPTR) */
+
+# if defined(NCACHE_NXT)
+ static KA_T kf;
+ struct ncache nc;
+# else /* !defined(NCACHE_NXT) */
+ static struct ncache *kca = (struct ncache *)NULL;
+# endif /* defined(NCACHE_NXT) */
+
+ if (!Fncache)
+ return;
+ if (Ncfirst) {
+
+ /*
+ * Do startup (first-time) functions.
+ */
+ Ncfirst = 0;
+ /*
+ * Establish kernel cache size.
+ */
+
+# if defined(X_NCSIZE)
+ v = (KA_T)0;
+ if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0
+ || !v
+ || kread((KA_T)v, (char *)&Nc, sizeof(Nc)))
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read name cache size: %s\n",
+ Pn, print_kptr(v, (char *)NULL, 0));
+ iNc = Nc = 0;
+ return;
+ }
+ iNc = Nc;
+# else /* !defined(X_NCSIZE) */
+ iNc = Nc = FIXED_NCSIZE;
+# endif /* defined(X_NCSIZE) */
+
+ if (Nc < 1) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: kernel name cache size: %d\n", Pn, Nc);
+ (void) fprintf(stderr,
+ " Cache size assumed to be: %d\n", DEFNCACHESZ);
+ }
+ iNc = Nc = DEFNCACHESZ;
+ }
+
+# if defined(NCACHE_NEGVN)
+ /*
+ * Get negative vnode address.
+ */
+ if (!NegVNSt) {
+ if (get_Nl_value(NCACHE_NEGVN, (struct drive_Nl *)NULL, &NegVN)
+ < 0)
+ NegVN = (KA_T)NULL;
+ NegVNSt = 1;
+ }
+# endif /* defined(NCACHE_NEGVN) */
+
+ /*
+ * Establish kernel cache address.
+ */
+
+# if defined(ADDR_NCACHE)
+ kp = (KA_T)0;
+ if (get_Nl_value(X_NCACHE,(struct drive_Nl *)NULL,(KA_T *)&kp) < 0
+ || !kp) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: no name cache address\n", Pn);
+ iNc = Nc = 0;
+ return;
+ }
+# else /* !defined(ADDR_NCACHE) */
+ v = (KA_T)0;
+ if (get_Nl_value(X_NCACHE, (struct drive_Nl *)NULL, &v) < 0
+ || !v
+ || kread((KA_T)v, (char *)&kp, sizeof(kp))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read name cache ptr: %s\n",
+ Pn, print_kptr(v, (char *)NULL, 0));
+ iNc = Nc = 0;
+ return;
+ }
+# endif /* defined(ADDR_NCACHE) */
+
+ /*
+ * Allocate space for a local copy of the kernel's cache.
+ */
+
+# if !defined(NCACHE_NXT)
+ len = Nc * sizeof(struct ncache);
+ if (!(kca = (struct ncache *)malloc((MALLOC_S)len))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: can't allocate name cache space: %d\n", Pn, len);
+ Exit(1);
+ }
+# endif /* !defined(NCACHE_NXT) */
+
+ /*
+ * Allocate space for the local cache.
+ */
+ len = Nc * sizeof(struct l_nch);
+ if (!(Ncache = (struct l_nch *)calloc(Nc, sizeof(struct l_nch)))) {
+
+no_local_space:
+
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: no space for %d byte local name cache\n", Pn, len);
+ Exit(1);
+ }
+ } else {
+
+ /*
+ * Do setup for repeat calls.
+ */
+ if (!iNc)
+ return;
+ if (Nchash) {
+ (void) free((FREE_P *)Nchash);
+ Nchash = (struct l_nch **)NULL;
+ }
+ if (Ncache) {
+
+ /*
+ * Free space malloc'd to names in local name cache.
+ */
+ for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
+ if (lc->nm) {
+ (void) free((FREE_P *)lc->nm);
+ lc->nm = (char *)NULL;
+ }
+ }
+ }
+ Nc = iNc;
+
+# if defined(NCACHE_NXT)
+ kp = kf;
+# endif /* defined(NCACHE_NXT) */
+
+ }
+
+# if !defined(NCACHE_NXT)
+
+/*
+ * Read the kernel's name cache.
+ */
+ if (kread(kp, (char *)kca, (Nc * sizeof(struct ncache)))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read kernel's name cache: %s\n",
+ Pn, print_kptr(kp, (char *)NULL, 0));
+ Nc = 0;
+ return;
+ }
+# endif /* !defined(NCACHE_NXT) */
+
+/*
+ * Build a local copy of the kernel name cache.
+ */
+
+# if defined(NCACHE_NXT)
+ for (i = iNc * 16, kc = &nc, kf = kp, lc = Ncache, n = 0; kp; )
+# else /* !defined(NCACHE_NXT) */
+ for (i = n = 0, kc = kca, lc = Ncache; i < Nc; i++, kc++)
+# endif /* defined(NCACHE_NXT) */
+
+ {
+
+# if defined(NCACHE_NXT)
+ if (kread(kp, (char *)kc, sizeof(nc)))
+ break;
+ if ((kp = (KA_T)kc->NCACHE_NXT) == kf)
+ kp = (KA_T)NULL;
+# endif /* defined(NCACHE_NXT) */
+
+ if (!kc->NCACHE_VP || (len = kc->NCACHE_NAMLEN) < 1)
+ continue;
+
+# if defined(NCACHE_NEGVN)
+ if (NegVN && ((KA_T)kc->NCACHE_VP == NegVN))
+ continue;
+# endif /* defined(NCACHE_NEGVN) */
+
+# if defined(HASDNLCPTR)
+ /*
+ * Read name from kernel to a temporary buffer.
+ */
+ if (len > na) {
+ na = len;
+ if (!nb)
+ nb = (char *)malloc(na);
+ else
+ nb = (char *)realloc((MALLOC_P *)nb, na);
+ if (!nb) {
+ (void) fprintf(stderr,
+ "%s: can't allocate %d byte temporary name buffer\n",
+ Pn, na);
+ Exit(1);
+ }
+ }
+ if (!kc->NCACHE_NAME || kread((KA_T)kc->NCACHE_NAME, nb, len))
+ continue;
+ np = nb;
+# else /* !defined(HASDNLCPTR) */
+ /*
+ * Use name that is in the kernel cache entry.
+ */
+ if (len > NC_NAMLEN)
+ continue;
+ np = kc->NCACHE_NAME;
+# endif /* defined(HASDNLCPTR) */
+
+ if (len < 3 && *np == '.') {
+ if (len == 1 || (len == 2 && np[1] == '.'))
+ continue;
+ }
+ /*
+ * Allocate space for name in local cache entry.
+ */
+ if (!(cp = (char *)malloc(len + 1))) {
+ (void) fprintf(stderr,
+ "%s: can't allocate %d bytes for name cache name: %s\n",
+ Pn, len + 1, np);
+ Exit(1);
+ }
+ (void) strncpy(cp, np, len);
+ cp[len] = '\0';
+
+# if defined(NCACHE_NXT)
+ if (n >= Nc) {
+
+ /*
+ * Allocate more local space to receive the kernel's linked
+ * entries.
+ */
+ Nc += LNCHINCRSZ;
+ if (!(Ncache = (struct l_nch *)realloc(Ncache,
+ (MALLOC_S)(Nc * sizeof(struct l_nch)))))
+ {
+ (void) fprintf(stderr,
+ "%s: no more space for %d entry local name cache\n",
+ Pn, Nc);
+ Exit(1);
+ }
+ lc = &Ncache[n];
+ iNc = Nc;
+ }
+# endif /* defined(NCACHE_NXT) */
+
+ /*
+ * Complete the local cache entry.
+ */
+ lc->vp = (KA_T)kc->NCACHE_VP;
+ lc->dp = (KA_T)kc->NCACHE_DP;
+ lc->pa = (struct l_nch *)NULL;
+ lc->nm = cp;
+ lc->nl = len;
+
+# if defined(NCACHE_NODEID)
+ lc->id = (unsigned long)kc->NCACHE_NODEID;
+ lc->did = (unsigned long)kc->NCACHE_PARID;
+# endif /* defined(NCACHE_NODEID) */
+
+ n++;
+ lc++;
+
+# if defined(NCACHE_NXT)
+ if (n >= i) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: name cache truncated at %d entries\n",
+ Pn, n);
+ break;
+ }
+# endif /* defined(NCACHE_NXT) */
+
+ }
+/*
+ * Reduce memory usage, as required.
+ */
+
+# if !defined(NCACHE_NXT)
+ if (!RptTm)
+ (void) free((FREE_P *)kca);
+# endif /* !defined(NCACHE_NXT) */
+
+ if (n < 1) {
+ if (!RptTm && Ncache) {
+
+ /*
+ * If not in repeat mode, free the space that has been malloc'd
+ * to the local name cache.
+ */
+ for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
+ if (lc->nm) {
+ (void) free((FREE_P *)lc->nm);
+ lc->nm = (char *)NULL;
+ }
+ }
+ (void) free((FREE_P *)Ncache);
+ Ncache = (struct l_nch *)NULL;
+ Nc = 0;
+ }
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: unusable name cache size: %d\n", Pn, n);
+ return;
+ }
+ if (n < Nc) {
+ Nc = n;
+ if (!RptTm) {
+ len = Nc * sizeof(struct l_nch);
+ if (!(Ncache = (struct l_nch *)realloc(Ncache, len)))
+ goto no_local_space;
+ }
+ }
+/*
+ * Build a hash table to locate Ncache entries.
+ */
+ for (Nch = 1; Nch < Nc; Nch <<= 1)
+ ;
+ Nch <<= 1;
+ Mch = Nch - 1;
+ if (!(Nchash = (struct l_nch **)calloc(Nch+Nc, sizeof(struct l_nch *))))
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: no space for %d name cache hash pointers\n",
+ Pn, Nch + Nc);
+ Exit(1);
+ }
+ for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
+
+# if defined(NCACHE_NODEID)
+ for (hp = ncachehash(lc->id, lc->vp), n = 1; *hp; hp++)
+# else /* !defined(NCACHE_NODEID) */
+ for (hp = ncachehash(lc->vp), n = 1; *hp; hp++)
+# endif /* defined(NCACHE_NODEID) */
+
+ {
+ if ((*hp)->vp == lc->vp && strcmp((*hp)->nm, lc->nm) == 0
+ && (*hp)->dp == lc->dp
+
+# if defined(NCACHE_NODEID)
+ && (*hp)->id == lc->id && (*hp)->did == lc->did
+# endif /* defined(NCACHE_NODEID) */
+
+ ) {
+ n = 0;
+ break;
+ }
+ }
+ if (n)
+ *hp = lc;
+ }
+/*
+ * Make a final pass through the local cache and convert parent vnode
+ * addresses to local name cache pointers.
+ */
+ for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
+ if (!lc->dp)
+ continue;
+
+# if defined(NCACHE_NEGVN)
+ if (NegVN && (lc->dp == NegVN)) {
+ lc->pa = (struct l_nch *)NULL;
+ continue;
+ }
+# endif /* defined(NCACHE_NEGVN) */
+
+# if defined(NCACHE_NODEID)
+ lc->pa = ncache_addr(lc->did, lc->dp);
+# else /* !defined(NCACHE_NODEID) */
+ lc->pa = ncache_addr(lc->dp);
+# endif /* defined(NCACHE_NODEID) */
+
+ }
+}
+
+
+/*
+ * ncache_lookup() - look up a node's name in the kernel's name cache
+ */
+
+char *
+ncache_lookup(buf, blen, fp)
+ char *buf; /* receiving name buffer */
+ int blen; /* receiving buffer length */
+ int *fp; /* full path reply */
+{
+ char *cp = buf;
+ struct l_nch *lc;
+ struct mounts *mtp;
+ int nl, rlen;
+
+ *cp = '\0';
+ *fp = 0;
+
+# if defined(HASFSINO)
+/*
+ * If the entry has an inode number that matches the inode number of the
+ * file system mount point, return an empty path reply. That tells the
+ * caller to print the file system mount point name only.
+ */
+ if ((Lf->inp_ty == 1) && Lf->fs_ino && (Lf->inode == Lf->fs_ino))
+ return(cp);
+# endif /* defined(HASFSINO) */
+
+/*
+ * Look up the name cache entry for the node address.
+ */
+ if (!Nc
+
+# if defined(NCACHE_NODEID)
+ || !(lc = ncache_addr(Lf->id, Lf->na))
+# else /* !defined(NCACHE_NODEID) */
+ || !(lc = ncache_addr(Lf->na))
+# endif /* defined(NCACHE_NODEID) */
+
+ ) {
+
+ /*
+ * If the node has no cache entry, see if it's the mount
+ * point of a known file system.
+ */
+ if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1)
+ return((char *)NULL);
+ for (mtp = readmnt(); mtp; mtp = mtp->next) {
+ if (!mtp->dir || !mtp->inode)
+ continue;
+ if (Lf->dev == mtp->dev
+ && mtp->inode == Lf->inode
+ && strcmp(mtp->dir, Lf->fsdir) == 0)
+ return(cp);
+ }
+ return((char *)NULL);
+ }
+/*
+ * Begin the path assembly.
+ */
+ if ((nl = lc->nl) > (blen - 1))
+ return((char *)NULL);
+ cp = buf + blen - nl - 1;
+ rlen = blen - nl - 1;
+ (void) strcpy(cp, lc->nm);
+/*
+ * Look up the name cache entries that are parents of the node address.
+ * Quit when:
+ *
+ * there's no parent;
+ * the name is too large to fit in the receiving buffer.
+ */
+ for (;;) {
+ if (!lc->pa) {
+ if (ncache_isroot(lc->dp, cp))
+ *fp = 1;
+ break;
+ }
+ lc = lc->pa;
+ if (((nl = lc->nl) + 1) > rlen)
+ break;
+ *(cp - 1) = '/';
+ cp--;
+ rlen--;
+ (void) strncpy((cp - nl), lc->nm, nl);
+ cp -= nl;
+ rlen -= nl;
+ }
+ return(cp);
+}
+#else /* !defined(HASNCACHE) || !defined(USE_LIB_RNCH) */
+char rnch_d1[] = "d"; char *rnch_d2 = rnch_d1;
+#endif /* defined(HASNCACHE) && defined(USE_LIB_RNCH) */
diff --git a/lib/rnmh.c b/lib/rnmh.c
new file mode 100644
index 0000000..9cf00a9
--- /dev/null
+++ b/lib/rnmh.c
@@ -0,0 +1,743 @@
+/*
+ * rnmh.c -- functions to read BSD format name cache information from a
+ * kernel hash table
+ */
+
+
+/*
+ * Copyright 1997 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.
+ */
+
+
+#include "../machine.h"
+
+#if defined(HASNCACHE) && defined(USE_LIB_RNMH)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: rnmh.c,v 1.13 2008/10/21 16:13:23 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+
+/*
+ * rnmh.c - read BSD format hashed kernel name cache
+ */
+
+/*
+ * The caller must:
+ *
+ * #include the relevant header file -- e.g., <sys/namei.h>.
+ *
+ * Define X_NCACHE as the nickname for the kernel cache hash tables
+ * address.
+ *
+ * Define X_NCSIZE as the nickname for the size of the kernel cache has
+ * table length.
+ *
+ * Define NCACHE_NO_ROOT if the calling dialect doesn't support
+ * the locating of the root node of a file system.
+ *
+ * Define the name of the name cache structure -- e.g.,
+ *
+ * #define NCACHE <structure name>
+ *
+ *
+ * Define the following casts, if they differ from the defaults:
+ *
+ * NCACHE_SZ_CAST case for X_NCSIZE (default unsigned long)
+ *
+ * Define the names of these elements of struct NCACHE:
+ *
+ * #define NCACHE_NM <name>
+ * #define NCACHE_NXT <link to next entry>
+ * #define NCACHE_NODEADDR <node address>
+ * #define NCACHE_PARADDR <parent node address>
+ *
+ * Optionally define:
+ *
+ * #define NCACHE_NMLEN <name length>
+ *
+ * Optionally define *both*:
+ *
+ * #define NCACHE_NODEID <node capability ID>
+ * #define NCACHE_PARID <parent node capability ID>
+ *
+ * The caller may need to:
+ *
+ * Define this prototype for ncache_load():
+ *
+ * _PROTOTYPE(static void ncache_load,(void));
+ *
+ * Define NCACHE_VROOT to be the value of the flag that signifies that
+ * the vnode is the root of its file system.
+ *
+ * E.g., for BSDI >= 5:
+ *
+ * #define NCACHE_VROOT VV_ROOT
+ *
+ * If not defined, NCACHE_VROOT is defined as "VROOT".
+ *
+ * Define VNODE_VFLAG if the vnode's flag member's name isn't v_flag.
+ *
+ * Note: if NCHNAMLEN is defined, the name is assumed to be in
+ * NCACHE_NM[NCHNAMLEN]; if it isn't defined, the name is assumed to be in an
+ * extension that begins at NCACHE_NM[0].
+ *
+ * Note: if NCACHE_NMLEN is not defined, then NCACHE_NM must be a pointer to
+ * a kernel allocated, NUL-terminated, string buffer.
+ */
+
+
+/*
+ * Casts
+ */
+
+# if !defined(NCACHE_NC_CAST)
+#define NCACHE_SZ_CAST unsigned long
+# endif /* !defined(NCACHE_NC_CAST) */
+
+
+/*
+ * Flags
+ */
+
+# if !defined(NCACHE_NMLEN)
+#undef NCHNAMLEN
+# endif /* !defined(NCACHE_NMLEN) */
+
+# if !defined(NCACHE_VROOT)
+#define NCACHE_VROOT VROOT /* vnode is root of its file system */
+# endif /* !defined(NCACHE_VROOT) */
+
+# if !defined(VNODE_VFLAG)
+#define VNODE_VFLAG v_flag
+# endif /* !defined(VNODE_VFLAG) */
+
+
+/*
+ * Local static values
+ */
+
+static int Mch; /* name cache hash mask */
+
+struct l_nch {
+ KA_T na; /* node address */
+ KA_T pa; /* parent node address */
+ struct l_nch *pla; /* parent local node address */
+ int nl; /* name length */
+ struct l_nch *next; /* next entry */
+
+# if defined(NCACHE_NODEID)
+ unsigned long id; /* capability ID */
+ unsigned long did; /* parent capability ID */
+# endif /* defined(NCACHE_NODEID) */
+
+# if defined(NCHNAMLEN)
+ char nm[NCHNAMLEN + 1]; /* name */
+# else /* !defined(NCHNAMLEN) */
+ char nm[1]; /* variable length name */
+# endif /* defined(NCHNAMLEN) */
+
+};
+
+static struct l_nch *Ncache = (struct l_nch *)NULL;
+ /* the head of the local name cache */
+static struct l_nch **Nchash = (struct l_nch **)NULL;
+ /* Ncache hash pointers */
+
+# if defined(NCACHE_NODEID)
+#define ncachehash(i,n) Nchash+(((((int)(n)>>2)+((int)(i)))*31415)&Mch)
+_PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long i, KA_T na));
+# else /* !defined(NCACHE_NODEID) */
+#define ncachehash(n) Nchash+((((int)(n)>>2)*31415)&Mch)
+_PROTOTYPE(static struct l_nch *ncache_addr,(KA_T na));
+# endif /* defined(NCACHE_NODEID) */
+
+# if !defined(NCACHE_NO_ROOT)
+_PROTOTYPE(static int ncache_isroot,(KA_T na, char *cp));
+# endif /* !defined(NCACHE_NO_ROOT) */
+
+
+/*
+ * ncache_addr() - look up a node's local ncache address
+ */
+
+static struct l_nch *
+
+# if defined(NCACHE_NODEID)
+ncache_addr(i, na)
+ unsigned long i; /* node's capability ID */
+# else /* !defined(NCACHE_NODEID) */
+ncache_addr(na)
+# endif /* defined(NCACHE_NODEID) */
+
+ KA_T na; /* node's address */
+{
+ struct l_nch **hp;
+
+# if defined(NCACHE_NODEID)
+ for (hp = ncachehash(i, na); *hp; hp++)
+# else /* !defined(NCACHE_NODEID) */
+ for (hp = ncachehash(na); *hp; hp++)
+# endif /* defined(NCACHE_NODEID) */
+
+ {
+
+# if defined(NCACHE_NODEID)
+ if ((*hp)->id == i && (*hp)->na == na)
+# else /* !defined(NCACHE_NODEID) */
+ if ((*hp)->na == na)
+# endif /* defined(NCACHE_NODEID) */
+
+ return(*hp);
+ }
+ return((struct l_nch *)NULL);
+}
+
+
+# if !defined(NCACHE_NO_ROOT)
+/*
+ * ncache_isroot() - is head of name cache path a file system root?
+ */
+
+static int
+ncache_isroot(na, cp)
+ KA_T na; /* kernel node address */
+ char *cp; /* partial path */
+{
+ char buf[MAXPATHLEN];
+ int i;
+ MALLOC_S len;
+ struct mounts *mtp;
+ static int nca = 0;
+ static int ncn = 0;
+ static KA_T *nc = (KA_T *)NULL;
+ struct stat sb;
+ struct vnode v;
+
+ if (!na)
+ return(0);
+/*
+ * Search the root vnode cache.
+ */
+ for (i = 0; i < ncn; i++) {
+ if (na == nc[i])
+ return(1);
+ }
+/*
+ * Read the vnode and see if it's a VDIR node with the NCACHE_VROOT flag set.
+ * If it is, then the path is complete.
+ *
+ * If it isn't, and if the file has an inode number, search the mount table
+ * and see if the file system's inode number is known. If it is, form the
+ * possible full path, safely stat() it, and see if it's inode number matches
+ * the one we have for this file. If it does, then the path is complete.
+ */
+ if (kread((KA_T)na, (char *)&v, sizeof(v))
+ || v.v_type != VDIR || !(v.VNODE_VFLAG & NCACHE_VROOT)) {
+
+ /*
+ * The vnode tests failed. Try the inode tests.
+ */
+ if (Lf->inp_ty != 1 || !Lf->inode
+ || !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1)
+ return(0);
+ if ((len + 1 + strlen(cp) + 1) > sizeof(buf))
+ return(0);
+ for (mtp = readmnt(); mtp; mtp = mtp->next) {
+ if (!mtp->dir || !mtp->inode)
+ continue;
+ if (strcmp(Lf->fsdir, mtp->dir) == 0)
+ break;
+ }
+ if (!mtp)
+ return(0);
+ (void) strcpy(buf, Lf->fsdir);
+ if (buf[len - 1] != '/')
+ buf[len++] = '/';
+ (void) strcpy(&buf[len], cp);
+ if (statsafely(buf, &sb) != 0
+ || (unsigned long)sb.st_ino != Lf->inode)
+ return(0);
+ }
+/*
+ * Add the node address to the root node cache.
+ */
+ if (ncn >= nca) {
+ if (!nca) {
+ len = (MALLOC_S)(10 * sizeof(KA_T));
+ nc = (KA_T *)malloc(len);
+ } else {
+ len = (MALLOC_S)((nca + 10) * sizeof(KA_T));
+ nc = (KA_T *)realloc(nc, len);
+ }
+ if (!nc) {
+ (void) fprintf(stderr, "%s: no space for root node table\n",
+ Pn);
+ Exit(1);
+ }
+ nca += 10;
+ }
+ nc[ncn++] = na;
+ return(1);
+}
+# endif /* !defined(NCACHE_NO_ROOT) */
+
+
+/*
+ * ncache_load() - load the kernel's name cache
+ */
+
+void
+ncache_load()
+{
+ struct NCACHE c;
+ struct l_nch **hp, *ln;
+ KA_T ka, knx;
+ static struct NCACHE **khp = (struct namecache **)NULL;
+ static int khpl = 0;
+ NCACHE_SZ_CAST khsz;
+ unsigned long kx;
+ static struct l_nch *lc = (struct l_nch *)NULL;
+ static int lcl = 0;
+ int len, lim, n, nch, nchl, nlcl;
+ char tbuf[32];
+ KA_T v;
+
+# if !defined(NCHNAMLEN)
+ int cin = sizeof(c.NCACHE_NM);
+ KA_T nmo = (KA_T)offsetof(struct NCACHE, NCACHE_NM);
+# endif /* !defined(NCHNAMLEN) */
+
+# if !defined(NCACHE_NMLEN)
+ char nbf[MAXPATHLEN + 1];
+ int nbfl = (int)(sizeof(nbf) - 1);
+ KA_T nk;
+ char *np;
+ int rl;
+
+ nbf[nbfl] = '\0';
+# endif /* !defined(NCACHE_NMLEN) */
+
+ if (!Fncache)
+ return;
+/*
+ * Free previously allocated space.
+ */
+ for (lc = Ncache; lc; lc = ln) {
+ ln = lc->next;
+ (void) free((FREE_P *)lc);
+ }
+ Ncache = (struct l_nch *)NULL;
+ if (Nchash)
+ (void) free((FREE_P *)Nchash);
+ Nchash = (struct l_nch **)NULL;
+/*
+ * Get kernel cache hash table size
+ */
+ v = (KA_T)0;
+ if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0
+ || !v
+ || kread((KA_T)v, (char *)&khsz, sizeof(khsz)))
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read name cache hash size: %s\n",
+ Pn, print_kptr(v, (char *)NULL, 0));
+ return;
+ }
+ if (khsz < 1) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: name cache hash size length error: %#lx\n",
+ Pn, khsz);
+ return;
+ }
+/*
+ * Get kernel cache hash table address.
+ */
+ ka = (KA_T)0;
+ v = (KA_T)0;
+ if (get_Nl_value(X_NCACHE, (struct drive_Nl *)NULL, &v) < 0
+ || !v
+ || kread((KA_T)v, (char *)&ka, sizeof(ka))
+ || !ka)
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: unusable name cache hash pointer: (%s)=%s\n",
+ Pn, print_kptr(v, tbuf, sizeof(tbuf)),
+ print_kptr(ka, (char *)NULL, 0));
+ return;
+ }
+/*
+ * Allocate space for the hash table pointers and read them.
+ */
+ len = (MALLOC_S)(khsz * sizeof(struct NCACHE *));
+ if (len > khpl) {
+ if (khp)
+ khp = (struct NCACHE **)realloc((MALLOC_P *)khp, len);
+ else
+ khp = (struct NCACHE **)malloc(len);
+ if (!khp) {
+ (void) fprintf(stderr,
+ "%s: can't allocate %d bytes for name cache hash table\n",
+ Pn, len);
+ Exit(1);
+ }
+ khpl = len;
+ }
+ if (kread((KA_T)ka, (char *)khp, len)) {
+ (void) fprintf(stderr,
+ "%s: can't read name cache hash pointers from: %s\n",
+ Pn, print_kptr(ka, (char *)NULL, 0));
+ return;
+ }
+/*
+ * Process the kernel's name cache hash table buckets.
+ */
+ lim = khsz * 10;
+ for (kx = nch = 0; kx < khsz; kx++) {
+
+ /*
+ * Loop through the entries for a hash bucket.
+ */
+ for (ka = (KA_T)khp[kx], n = 0; ka; ka = knx, n++) {
+ if (n > lim) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: name cache hash chain too long\n",
+ Pn);
+ break;
+ }
+ if (kread(ka, (char *)&c, sizeof(c)))
+ break;
+ knx = (KA_T)c.NCACHE_NXT;
+ if (!c.NCACHE_NODEADDR)
+ continue;
+
+# if defined(NCACHE_NMLEN)
+ if ((len = c.NCACHE_NMLEN) < 1)
+ continue;
+# else /* !defined(NCACHE_NMLEN) */
+ /*
+ * If it's possible to read the first four characters of the name,
+ * do so and check for "." and "..".
+ */
+ if (!c.NCACHE_NM
+ || kread((KA_T)c.NCACHE_NM, nbf, 4))
+ continue;
+ if (nbf[0] == '.') {
+ if (!nbf[1]
+ || ((nbf[1] == '.') && !nbf[2]))
+ continue;
+ }
+ /*
+ * Read the rest of the name, 32 characters at a time, until a NUL
+ * character has been read or nbfl characters have been read.
+ */
+ nbf[4] = '\0';
+ if ((len = (int)strlen(nbf)) < 4) {
+ if (!len)
+ continue;
+ } else {
+ for (np = &nbf[4]; len < nbfl; np += rl) {
+ if ((rl = nbfl - len) > 32) {
+ rl = 32;
+ nbf[len + rl] = '\0';
+ }
+ nk = (KA_T)((char *)c.NCACHE_NM + len);
+ if (kread(nk, np, rl)) {
+ rl = -1;
+ break;
+ }
+ rl = (int)strlen(np);
+ len += rl;
+ if (rl < 32)
+ break;
+ }
+ if (rl < 0)
+ continue;
+ }
+# endif /* defined(NCACHE_NMLEN) */
+
+ /*
+ * Allocate a cache entry long enough to contain the name and
+ * move the name to it.
+ */
+
+# if defined(NCHNAMLEN)
+ if (len > NCHNAMLEN)
+ continue;
+ if (len < 3 && c.NCACHE_NM[0] == '.') {
+ if (len == 1 || (len == 2 && c.NCACHE_NM[1] == '.'))
+ continue;
+ }
+ if ((nlcl = sizeof(struct l_nch)) > lcl)
+# else /* !defined(NCHNAMLEN) */
+ if ((nlcl = sizeof(struct l_nch) + len) > lcl)
+# endif /* defined(NCHNAMLEN) */
+
+ {
+ if (lc)
+ lc = (struct l_nch *)realloc(lc, nlcl);
+ else
+ lc = (struct l_nch *)malloc(nlcl);
+ if (!lc) {
+ (void) fprintf(stderr,
+ "%s: can't allocate %d local name cache bytes\n",
+ Pn, nlcl);
+ Exit(1);
+ }
+ lcl = nlcl;
+ }
+
+# if defined(NCHNAMLEN)
+ (void) strncpy(lc->nm, c.NCACHE_NM, len);
+# else /* !defined(NCHNAMLEN) */
+# if defined(NCACHE_NMLEN)
+ if ((len < 3) && (cin > 1)) {
+
+ /*
+ * If this is a one or two character name, and if NCACHE_NM[]
+ * in c has room for at least two characters, check for "."
+ * and ".." first, ignoring this entry if the name is either.
+ */
+ if (len < 3 && c.NCACHE_NM[0] == '.') {
+ if (len == 1 || (len == 2 && c.NCACHE_NM[1] == '.'))
+ continue;
+ }
+ }
+ if (len > cin) {
+
+ /*
+ * If not all (possibly not any, depending on the value in
+ * cin) of the name has yet been read to lc->nm[], read it
+ * or the rest of it. If it wasn't possible before to check
+ * for "." or "..", do that. too.
+ */
+ if (cin > 0)
+ (void) strncpy(lc->nm, c.NCACHE_NM, cin);
+ if (kread(ka + (KA_T)(nmo + cin), &lc->nm[cin], len - cin))
+ continue;
+ if ((cin < 2) && (len < 3) && (lc->nm[0] == '.')) {
+ if (len == 1 || (len == 2 && lc->nm[1] == '.'))
+ continue;
+ }
+ } else
+ (void) strncpy(lc->nm, c.NCACHE_NM, len);
+# else /* !defined(NCACHE_NMLEN) */
+ (void) strncpy(lc->nm, nbf, len);
+# endif /* defined(NCACHE_NMLEN) */
+
+# endif /* defined(NCHNAMLEN) */
+ lc->nm[len] = '\0';
+ /*
+ * Complete the new local cache entry and link it to the previous
+ * local cache chain.
+ */
+ lc->next = Ncache;
+ Ncache = lc;
+ lc->na = (KA_T)c.NCACHE_NODEADDR;
+ lc->nl = len;
+ lc->pa = (KA_T)c.NCACHE_PARADDR;
+ lc->pla = (struct l_nch *)NULL;
+
+# if defined(NCACHE_NODEID)
+ lc->id = c.NCACHE_NODEID;
+ lc->did = c.NCACHE_PARID;
+# endif /* defined(NCACHE_NODEID) */
+
+ lcl = 0;
+ lc = (struct l_nch *)NULL;
+ nch++;
+ }
+ }
+/*
+ * Reduce memory usage, as required.
+ */
+ if (!RptTm) {
+ (void) free((FREE_P *)khp);
+ khp = (struct NCACHE **)NULL;
+ khpl = 0;
+ }
+ if (nch < 1) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: unusable name cache size: %d\n", Pn, nch);
+ return;
+ }
+/*
+ * Build a hash table to locate Ncache entries.
+ */
+ for (nchl = 1; nchl < nch; nchl <<= 1)
+ ;
+ nchl <<= 1;
+ Mch = nchl - 1;
+ len = nchl + nch;
+ if (!(Nchash = (struct l_nch **)calloc(len, sizeof(struct l_nch *)))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: no space for %d local name cache hash pointers\n",
+ Pn, len);
+ Exit(1);
+ }
+ for (lc = Ncache; lc; lc = lc->next) {
+
+# if defined(NCACHE_NODEID)
+ for (hp = ncachehash(lc->id, lc->na),
+# else /* !defined(NCACHE_NODEID) */
+ for (hp = ncachehash(lc->na),
+# endif /* defined(NCACHE_NODEID) */
+
+ n = 1; *hp; hp++)
+ {
+ if ((*hp)->na == lc->na && strcmp((*hp)->nm, lc->nm) == 0) {
+ n = 0;
+ break;
+ }
+ }
+ if (n)
+ *hp = lc;
+ else
+ lc->pa = (KA_T)0;
+ }
+/*
+ * Make a final pass through the local cache and convert parent node
+ * addresses to local name cache pointers.
+ */
+ for (lc = Ncache; lc; lc = lc->next) {
+ if (!lc->pa)
+ continue;
+
+# if defined(NCACHE_NODEID)
+ lc->pla = ncache_addr(lc->did, lc->pa);
+# else /* !defined(NCACHE_NODEID) */
+ lc->pla = ncache_addr(lc->pa);
+# endif /* defined(NCACHE_NODEID) */
+
+ }
+}
+
+
+/*
+ * ncache_lookup() - look up a node's name in the kernel's name cache
+ */
+
+char *
+ncache_lookup(buf, blen, fp)
+ char *buf; /* receiving name buffer */
+ int blen; /* receiving buffer length */
+ int *fp; /* full path reply */
+{
+ char *cp = buf;
+ struct l_nch *lc;
+ struct mounts *mtp;
+ int nl, rlen;
+
+ *cp = '\0';
+ *fp = 0;
+
+# if defined(HASFSINO)
+/*
+ * If the entry has an inode number that matches the inode number of the
+ * file system mount point, return an empty path reply. That tells the
+ * caller to print the file system mount point name only.
+ */
+ if ((Lf->inp_ty == 1) && Lf->fs_ino && (Lf->inode == Lf->fs_ino))
+ return(cp);
+# endif /* defined(HASFSINO) */
+
+/*
+ * Look up the name cache entry for the node address.
+ */
+
+# if defined(NCACHE_NODEID)
+ if (!Nchash || !(lc = ncache_addr(Lf->id, Lf->na)))
+# else /* !defined(NCACHE_NODEID) */
+ if (!Nchash || !(lc = ncache_addr(Lf->na)))
+# endif /* defined(NCACHE_NODEID) */
+
+ {
+
+ /*
+ * If the node has no cache entry, see if it's the mount
+ * point of a known file system.
+ */
+ if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1)
+ return((char *)NULL);
+ for (mtp = readmnt(); mtp; mtp = mtp->next) {
+ if (!mtp->dir || !mtp->inode)
+ continue;
+ if (Lf->dev == mtp->dev
+ && mtp->inode == Lf->inode
+ && (strcmp(mtp->dir, Lf->fsdir) == 0))
+ return(cp);
+ }
+ return((char *)NULL);
+ }
+/*
+ * Start the path assembly.
+ */
+ if ((nl = lc->nl) > (blen - 1))
+ return((char *)NULL);
+ cp = buf + blen - nl - 1;
+ rlen = blen - nl - 1;
+ (void) strcpy(cp, lc->nm);
+/*
+ * Look up the name cache entries that are parents of the node address.
+ * Quit when:
+ *
+ * there's no parent;
+ * the name length is too large to fit in the receiving buffer.
+ */
+ for (;;) {
+ if (!lc->pla) {
+
+# if !defined(NCACHE_NO_ROOT)
+ if (ncache_isroot(lc->pa, cp))
+ *fp = 1;
+# endif /* !defined(NCACHE_NO_ROOT) */
+
+ break;
+ }
+ lc = lc->pla;
+ if (((nl = lc->nl) + 1) > rlen)
+ break;
+ *(cp - 1) = '/';
+ cp--;
+ rlen--;
+ (void) strncpy((cp - nl), lc->nm, nl);
+ cp -= nl;
+ rlen -= nl;
+ }
+ return(cp);
+}
+#else /* !defined(HASNCACHE) || !defined(USE_LIB_RNMH) */
+char rnmh_d1[] = "d"; char *rnmh_d2 = rnmh_d1;
+#endif /* defined(HASNCACHE) && defined(USE_LIB_RNMH) */
diff --git a/lib/snpf.c b/lib/snpf.c
new file mode 100644
index 0000000..c20b91f
--- /dev/null
+++ b/lib/snpf.c
@@ -0,0 +1,749 @@
+/*
+ * snpf.c -- snprintf() empulation functions for lsof library
+ *
+ * V. Abell
+ * Purdue University Computing Center
+ */
+
+/*
+ * Copyright 2000 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.
+ *
+ * This software has been adapted from snprintf.c in sendmail 8.9.3. It
+ * is subject to the sendmail copyright statements listed below, and the
+ * sendmail licensing terms stated in the sendmail LICENSE file comment
+ * section of this file.
+ *
+ * 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.
+ */
+
+#include "../machine.h"
+
+#ifdef USE_LIB_SNPF
+
+/*
+ * Sendmail copyright statements:
+ *
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * The LICENSE file may be found in the following comment section.
+ */
+
+
+/*
+ * Begin endmail LICENSE file.
+
+ SENDMAIL LICENSE
+
+The following license terms and conditions apply, unless a different
+license is obtained from Sendmail, Inc., 1401 Park Avenue, Emeryville, CA
+94608, or by electronic mail at license@sendmail.com.
+
+License Terms:
+
+Use, Modification and Redistribution (including distribution of any
+modified or derived work) in source and binary forms is permitted only if
+each of the following conditions is met:
+
+1. Redistributions qualify as "freeware" or "Open Source Software" under
+ one of the following terms:
+
+ (a) Redistributions are made at no charge beyond the reasonable cost of
+ materials and delivery.
+
+ (b) Redistributions are accompanied by a copy of the Source Code or by an
+ irrevocable offer to provide a copy of the Source Code for up to three
+ years at the cost of materials and delivery. Such redistributions
+ must allow further use, modification, and redistribution of the Source
+ Code under substantially the same terms as this license. For the
+ purposes of redistribution "Source Code" means the complete source
+ code of sendmail including all modifications.
+
+ Other forms of redistribution are allowed only under a separate royalty-
+ free agreement permitting such redistribution subject to standard
+ commercial terms and conditions. A copy of such agreement may be
+ obtained from Sendmail, Inc. at the above address.
+
+2. Redistributions of source code must retain the copyright notices as they
+ appear in each source code file, these license terms, and the
+ disclaimer/limitation of liability set forth as paragraph 6 below.
+
+3. Redistributions in binary form must reproduce the Copyright Notice,
+ these license terms, and the disclaimer/limitation of liability set
+ forth as paragraph 6 below, in the documentation and/or other materials
+ provided with the distribution. For the purposes of binary distribution
+ the "Copyright Notice" refers to the following language:
+ "Copyright (c) 1998 Sendmail, Inc. All rights reserved."
+
+4. Neither the name of Sendmail, Inc. nor the University of California nor
+ the names of their contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission. The name "sendmail" is a trademark of Sendmail, Inc.
+
+5. All redistributions must comply with the conditions imposed by the
+ University of California on certain embedded code, whose copyright
+ notice and conditions for redistribution are as follows:
+
+ (a) Copyright (c) 1988, 1993 The Regents of the University of
+ California. All rights reserved.
+
+ (b) Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ (i) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (ii) Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ (iii) All advertising materials mentioning features or use of this
+ software must display the following acknowledgement: "This
+ product includes software developed by the University of
+ California, Berkeley and its contributors."
+
+ (iv) Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
+ SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF
+ CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+(Version 8.6, last updated 6/24/1998)
+
+ * End endmail LICENSE file.
+ */
+
+
+/*
+ * If "ll" format support is not possible -- e.g., the long long type isn't
+ * supported -- define HAS_NO_LONG_LONG.
+ */
+
+# ifndef lint
+static char copyright[] =
+"@(#) Copyright 2000 Purdue Research Foundation.\nAll rights reserved.\n";
+# endif /* !defined(lint) */
+
+#include <varargs.h>
+
+#if defined(__STDC__)
+#define _PROTOTYPE(function, params) function params
+#else /* !defined(__STDC__) */
+#define _PROTOTYPE(function, params) function()
+#endif /* defined(__STDC__) */
+
+
+/*
+** SNPRINTF, VSNPRINT -- counted versions of printf
+**
+** These versions have been grabbed off the net. They have been
+** cleaned up to compile properly and support for .precision and
+** %lx has been added.
+*/
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ **************************************************************/
+
+/*static char _id[] = "$Id: snpf.c,v 1.5 2008/10/21 16:13:23 abe Exp $";*/
+
+
+/*
+ * Local function prototypes
+ */
+
+_PROTOTYPE(static void dopr,(char *bp, char *ep, char *fmt, va_list args));
+_PROTOTYPE(static void dopr_outch,(char **bp, char *ep, int c));
+_PROTOTYPE(static void dostr,(char **bp, char *ep, char *str, int));
+
+# if !defined(HAS_NO_LONG_LONG)
+_PROTOTYPE(static void fmtllnum,(char **bp, char *ep, long long value,
+ int base, int dosign, int ljust, int len,
+ int zpad));
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+_PROTOTYPE(static void fmtnum,(char **bp, char *ep, long value, int base,
+ int dosign, int ljust, int len, int zpad));
+_PROTOTYPE(static void fmtstr,(char **bp, char *ep, char *value, int ljust,
+ int len, int zpad,
+ int maxwidth));
+
+
+/*
+ * Local variables
+ */
+
+static int Length;
+
+
+/*
+ * snpf() -- count-controlled sprintf()
+ */
+
+int
+snpf(va_alist)
+ va_dcl /* requires at least three arguments:
+ * bp = receiving buffer pointer
+ * ct = length of buffer
+ * fmt = format string
+ */
+{
+ va_list args;
+ char *bp, *fmt;
+ int ct, len;
+
+ va_start(args);
+ bp = va_arg(args, char *);
+ ct = va_arg(args, int);
+ fmt = va_arg(args, char *);
+ len = vsnpf(bp, ct, fmt, args);
+ va_end(args);
+ return(len);
+}
+
+
+/*
+ * vsnpf() -- count-controlled vsprintf()
+ */
+
+int
+vsnpf(str, count, fmt, args)
+ char *str; /* result buffer */
+ int count; /* size of buffer */
+ char *fmt; /* format */
+ va_list args; /* variable length argument list */
+{
+ char *ep = str + count - 1;
+
+ *str = '\0';
+ (void) dopr(str, ep, fmt, args);
+ if (count > 0)
+ *ep = '\0';
+ return(Length);
+}
+
+
+/*
+ * dopr() -- poor man's version of doprintf
+ */
+
+
+static void
+dopr(bp, ep, fmt, args)
+ char *bp; /* buffer start */
+ char *ep; /* buffer end (start + length - 1) */
+ char *fmt; /* format */
+ va_list args; /* variable length argument list */
+{
+ int ch;
+ char ebuf[64];
+ int ebufl = (int)(sizeof(ebuf) - 1);
+ long value;
+ int longflag = 0;
+ int longlongflag = 0;
+ int pointflag = 0;
+ int maxwidth = 0;
+ char *strvalue;
+ int ljust;
+ int len;
+ int zpad;
+ int zxflag = 0;
+
+# if !defined(HAS_NO_LONG_LONG)
+ long long llvalue;
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+ Length = 0;
+ while((ch = *fmt++)) {
+ switch (ch) {
+ case '%':
+ ljust = len = zpad = zxflag = maxwidth = 0;
+ longflag = longlongflag = pointflag = 0;
+
+nextch:
+
+ ch = *fmt++;
+ switch (ch) {
+ case '\0':
+ dostr(&bp, ep, "**end of format**" , 0);
+ return;
+ case '-':
+ ljust = 1;
+ goto nextch;
+ case '0': /* set zero padding if len not set */
+ if ((len == 0) && !pointflag)
+ zpad = '0';
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (pointflag)
+ maxwidth = (maxwidth * 10) + (int)(ch - '0');
+ else
+ len = (len * 10) + (int)(ch - '0');
+ goto nextch;
+ case '*':
+ if (pointflag)
+ maxwidth = va_arg(args, int);
+ else
+ len = va_arg(args, int);
+ goto nextch;
+ case '#':
+ zxflag = 1;
+ goto nextch;
+ case '.':
+ pointflag = 1;
+ goto nextch;
+ case 'l':
+ if (longflag) {
+ longflag = 0;
+ longlongflag = 1;
+ goto nextch;
+ }
+ longflag = 1;
+ goto nextch;
+ case 'u':
+ case 'U':
+ if (longlongflag) {
+
+# if !defined(HAS_NO_LONG_LONG)
+ llvalue = va_arg(args, long long);
+ (void) fmtllnum(&bp,ep,llvalue,10,0,ljust,len,zpad);
+# else /* defined(HAS_NO_LONG_LONG) */
+ (void) strncpy(ebuf, "ll is unsupported", ebufl);
+ ebuf[(int)ebufl] = '\0';
+ (void) dostr(&bp, ep, ebuf, 0);
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+ break;
+ }
+ if (longflag)
+ value = va_arg(args, long);
+ else
+ value = va_arg(args, int);
+ (void) fmtnum(&bp, ep, value, 10,0, ljust, len, zpad);
+ break;
+ case 'o':
+ case 'O':
+ if (longlongflag) {
+
+# if !defined(HAS_NO_LONG_LONG)
+ llvalue = va_arg(args, long long);
+ (void) fmtllnum(&bp,ep,llvalue,8,0,ljust,len,zpad);
+# else /* defined(HAS_NO_LONG_LONG) */
+ (void) strncpy(ebuf, "ll is unsupported", ebufl);
+ ebuf[(int)ebufl] = '\0';
+ (void) dostr(&bp, ep, ebuf, 0);
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+ break;
+ }
+ if (longflag)
+ value = va_arg(args, long);
+ else
+ value = va_arg(args, int);
+ (void) fmtnum(&bp, ep, value, 8,0, ljust, len, zpad);
+ break;
+ case 'd':
+ case 'D':
+ if (longlongflag) {
+
+# if !defined(HAS_NO_LONG_LONG)
+ llvalue = va_arg(args, long long);
+ (void) fmtllnum(&bp,ep,llvalue,10,1,ljust,len,zpad);
+# else /* defined(HAS_NO_LONG_LONG) */
+ (void) strncpy(ebuf, "ll is unsupported", ebufl);
+ ebuf[(int)ebufl] = '\0';
+ (void) dostr(&bp, ep, ebuf, 0);
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+ break;
+ }
+ if (longflag)
+ value = va_arg(args, long);
+ else
+ value = va_arg(args, int);
+ (void) fmtnum(&bp, ep, value, 10,1, ljust, len, zpad);
+ break;
+ case 'x':
+ if (longlongflag) {
+
+# if !defined(HAS_NO_LONG_LONG)
+ llvalue = va_arg(args, long long);
+ if (zxflag && llvalue) {
+ (void) dostr(&bp, ep, "0x", 0);
+ if (len >= 2)
+ len -= 2;
+ }
+ (void) fmtllnum(&bp,ep,llvalue,16,0,ljust,len,zpad);
+# else /* defined(HAS_NO_LONG_LONG) */
+ (void) strncpy(ebuf, "ll is unsupported", ebufl);
+ ebuf[(int)ebufl] = '\0';
+ (void) dostr(&bp, ep, ebuf, 0);
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+ break;
+ }
+ if (longflag)
+ value = va_arg(args, long);
+ else
+ value = va_arg(args, int);
+ if (zxflag && value) {
+ (void) dostr(&bp, ep, "0x", 0);
+ if (len >= 2)
+ len -= 2;
+ }
+ (void) fmtnum(&bp, ep, value, 16,0, ljust, len, zpad);
+ break;
+ case 'X':
+ if (longlongflag) {
+
+# if !defined(HAS_NO_LONG_LONG)
+ llvalue = va_arg(args, long long);
+ if (zxflag && llvalue) {
+ (void) dostr(&bp, ep, "0x", 0);
+ if (len >= 2)
+ len -= 2;
+ }
+ (void) fmtllnum(&bp,ep,llvalue,-16,0,ljust,len,zpad);
+# else /* defined(HAS_NO_LONG_LONG) */
+ (void) strncpy(ebuf, "ll is unsupported", ebufl);
+ ebuf[(int)ebufl] = '\0';
+ (void) dostr(&bp, ep, ebuf, 0);
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+ break;
+ }
+ if (longflag)
+ value = va_arg(args, long);
+ else
+ value = va_arg(args, int);
+ if (zxflag && value) {
+ (void) dostr(&bp, ep, "0x", 0);
+ if (len >= 2)
+ len -= 2;
+ }
+ (void) fmtnum(&bp, ep, value,-16,0, ljust, len, zpad);
+ break;
+ case 's':
+ strvalue = va_arg(args, char *);
+ if (maxwidth > 0 || !pointflag) {
+ if (pointflag && len > maxwidth)
+ len = maxwidth; /* Adjust padding */
+ (void) fmtstr(&bp, ep, strvalue, ljust, len, zpad,
+ maxwidth);
+ }
+ break;
+ case 'c':
+ ch = va_arg(args, int);
+ dopr_outch(&bp, ep, ch);
+ break;
+ case '%':
+ (void) dopr_outch(&bp, ep, ch);
+ continue;
+ default:
+ ebuf[0] = ch;
+ (void) strncpy(&ebuf[1], " is unsupported", ebufl);
+ ebuf[(int)ebufl] = '\0';
+ (void) dostr(&bp, ep, ebuf, 0);
+ }
+ break;
+ default:
+ (void) dopr_outch(&bp, ep, ch);
+ break;
+ }
+ }
+ *bp = '\0';
+}
+
+
+# if !defined(HAS_NO_LONG_LONG)
+/*
+ * fmtllnum() -- format long long number for output
+ */
+
+static void
+fmtllnum(bp, ep, value, base, dosign, ljust, len, zpad)
+ char **bp; /* current buffer pointer */
+ char *ep; /* end of buffer (-1) */
+ long long value; /* number to format */
+ int base; /* number base */
+ int dosign; /* sign request */
+ int ljust; /* left justfication request */
+ int len; /* length request */
+ int zpad; /* zero padding request */
+{
+ int signvalue = 0;
+ unsigned long long uvalue;
+ char convert[20];
+ int place = 0;
+ int padlen = 0; /* amount to pad */
+ int caps = 0;
+
+ uvalue = value;
+ if (dosign) {
+ if (value < 0) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ }
+ if (base < 0) {
+ caps = 1;
+ base = -base;
+ }
+ do {
+ convert[place++] =
+ (caps ? "0123456789ABCDEF" : "0123456789abcdef")
+ [uvalue % (unsigned)base];
+ uvalue = (uvalue / (unsigned)base);
+ } while (uvalue && (place < (int)(sizeof(convert) - 1)));
+ convert[place] = 0;
+ padlen = len - place;
+ if (padlen < 0)
+ padlen = 0;
+ if(ljust)
+ padlen = -padlen;
+ if (zpad && padlen > 0) {
+ if (signvalue) {
+ (void) dopr_outch(bp, ep, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ (void) dopr_outch(bp, ep, zpad);
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ (void) dopr_outch(bp, ep, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ (void) dopr_outch(bp, ep, signvalue);
+ while (place > 0)
+ (void) dopr_outch(bp, ep, convert[--place]);
+ while (padlen < 0) {
+ (void) dopr_outch(bp, ep, ' ');
+ ++padlen;
+ }
+}
+# endif /* !defined(HAS_NO_LONG_LONG) */
+
+
+/*
+ * fmtnum() -- format number for output
+ */
+
+static void
+fmtnum(bp, ep, value, base, dosign, ljust, len, zpad)
+ char **bp; /* current buffer pointer */
+ char *ep; /* end of buffer (-1) */
+ long value; /* number to format */
+ int base; /* number base */
+ int dosign; /* sign request */
+ int ljust; /* left justfication request */
+ int len; /* length request */
+ int zpad; /* zero padding request */
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int padlen = 0; /* amount to pad */
+ int caps = 0;
+
+ uvalue = value;
+ if (dosign) {
+ if (value < 0) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ }
+ if (base < 0) {
+ caps = 1;
+ base = -base;
+ }
+ do {
+ convert[place++] =
+ (caps ? "0123456789ABCDEF" : "0123456789abcdef")
+ [uvalue % (unsigned)base];
+ uvalue = (uvalue / (unsigned)base);
+ } while (uvalue && (place < (int)(sizeof(convert) - 1)));
+ convert[place] = 0;
+ padlen = len - place;
+ if (padlen < 0)
+ padlen = 0;
+ if(ljust)
+ padlen = -padlen;
+ if (zpad && padlen > 0) {
+ if (signvalue) {
+ (void) dopr_outch(bp, ep, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ (void) dopr_outch(bp, ep, zpad);
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ (void) dopr_outch(bp, ep, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ (void) dopr_outch(bp, ep, signvalue);
+ while (place > 0)
+ (void) dopr_outch(bp, ep, convert[--place]);
+ while (padlen < 0) {
+ (void) dopr_outch(bp, ep, ' ');
+ ++padlen;
+ }
+}
+
+
+/*
+ * fmtstr() -- format string for output
+ */
+
+static void
+fmtstr(bp, ep, value, ljust, len, zpad, maxwidth)
+ char **bp; /* current buffer pointer */
+ char *ep; /* end of buffer (-1) */
+ char *value; /* string to format */
+ int ljust; /* left justification request */
+ int len; /* length request */
+ int zpad; /* zero padding request */
+ int maxwidth; /* maximum width request */
+{
+ int padlen, strlen; /* amount to pad */
+
+ if (value == 0)
+ value = "<NULL>";
+ for (strlen = 0; value[strlen]; ++ strlen) /* strlen() */
+ ;
+ if ((strlen > maxwidth) && maxwidth)
+ strlen = maxwidth;
+ padlen = len - strlen;
+ if (padlen < 0)
+ padlen = 0;
+ if (ljust)
+ padlen = -padlen;
+ while (padlen > 0) {
+ (void) dopr_outch(bp, ep, ' ');
+ --padlen;
+ }
+ (void) dostr(bp, ep, value, maxwidth);
+ while (padlen < 0) {
+ (void) dopr_outch(bp, ep, ' ');
+ ++padlen;
+ }
+}
+
+
+/*
+ * dostr() -- do string output
+ */
+
+static void
+dostr(bp, ep, str, cut)
+ char **bp; /* current buffer pointer */
+ char *ep; /* end of buffer (-1) */
+ char *str; /* string to output */
+ int cut; /* limit on amount of string to output:
+ * 0 == no limit */
+{
+ int f;
+
+ f = cut ? 1 : 0;
+ while (*str) {
+ if (f) {
+ if (cut-- > 0)
+ (void) dopr_outch(bp, ep, *str);
+ } else
+ (void) dopr_outch(bp, ep, *str);
+ str++;
+ }
+}
+
+
+/*
+ * dopr_outch() -- output a character (or two)
+ */
+
+static void
+dopr_outch(bp, ep, c)
+ char **bp; /* current buffer pointer */
+ char *ep; /* end of buffer (-1) */
+ int c; /* character to output */
+{
+ register char *cp = *bp;
+
+ if (iscntrl(c) && c != '\n' && c != '\t') {
+ c = '@' + (c & 0x1F);
+ if (cp < ep)
+ *cp++ = '^';
+ Length++;
+ }
+ if (cp < ep)
+ *cp++ = c;
+ *bp = cp;
+ Length++;
+}
+
+#else /* !defined(USE_LIB_SNPF) */
+char snpf_d1[] = "d"; char *snpf_d2 = snpf_d1;
+#endif /* defined(USE_LIB_SNPF) */