diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.skel | 62 | ||||
-rw-r--r-- | lib/ckkv.c | 93 | ||||
-rw-r--r-- | lib/cvfs.c | 110 | ||||
-rw-r--r-- | lib/dvch.c | 1413 | ||||
-rw-r--r-- | lib/fino.c | 148 | ||||
-rw-r--r-- | lib/isfn.c | 418 | ||||
-rw-r--r-- | lib/lkud.c | 207 | ||||
-rw-r--r-- | lib/pdvn.c | 182 | ||||
-rw-r--r-- | lib/prfp.c | 212 | ||||
-rw-r--r-- | lib/ptti.c | 1370 | ||||
-rw-r--r-- | lib/rdev.c | 524 | ||||
-rw-r--r-- | lib/regex.c | 6328 | ||||
-rw-r--r-- | lib/rmnt.c | 243 | ||||
-rw-r--r-- | lib/rnam.c | 669 | ||||
-rw-r--r-- | lib/rnch.c | 811 | ||||
-rw-r--r-- | lib/rnmh.c | 743 | ||||
-rw-r--r-- | lib/snpf.c | 749 |
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 (®_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 ? ®s : (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) */ |