Index: head/usr.bin/grep/Makefile =================================================================== --- head/usr.bin/grep/Makefile (revision 226270) +++ head/usr.bin/grep/Makefile (revision 226271) @@ -1,70 +1,78 @@ # $NetBSD: Makefile,v 1.4 2011/02/16 01:31:33 joerg Exp $ # $FreeBSD$ # $OpenBSD: Makefile,v 1.6 2003/06/25 15:00:04 millert Exp $ .include .if ${MK_BSD_GREP} == "yes" PROG= grep .else PROG= bsdgrep CLEANFILES+= bsdgrep.1 bsdgrep.1: grep.1 cp ${.ALLSRC} ${.TARGET} .endif SRCS= file.c grep.c queue.c util.c # Extra files ported backported form some regex improvements .PATH: ${.CURDIR}/regex SRCS+= fastmatch.c hashtable.c tre-compile.c tre-fastmatch.c xmalloc.c CFLAGS+=-I${.CURDIR}/regex .if ${MK_BSD_GREP} == "yes" LINKS= ${BINDIR}/grep ${BINDIR}/egrep \ ${BINDIR}/grep ${BINDIR}/fgrep \ ${BINDIR}/grep ${BINDIR}/zgrep \ ${BINDIR}/grep ${BINDIR}/zegrep \ ${BINDIR}/grep ${BINDIR}/zfgrep \ - ${BINDIR}/grep ${BINDIR}/bzgrep \ - ${BINDIR}/grep ${BINDIR}/bzegrep \ - ${BINDIR}/grep ${BINDIR}/bzfgrep \ ${BINDIR}/grep ${BINDIR}/xzgrep \ ${BINDIR}/grep ${BINDIR}/xzegrep \ ${BINDIR}/grep ${BINDIR}/xzfgrep \ ${BINDIR}/grep ${BINDIR}/lzgrep \ ${BINDIR}/grep ${BINDIR}/lzegrep \ ${BINDIR}/grep ${BINDIR}/lzfgrep MLINKS= grep.1 egrep.1 \ grep.1 fgrep.1 \ grep.1 zgrep.1 \ grep.1 zegrep.1 \ grep.1 zfgrep.1 \ - grep.1 bzgrep.1 \ - grep.1 bzegrep.1 \ - grep.1 bzfgrep.1 \ grep.1 xzgrep.1 \ grep.1 xzegrep.1 \ grep.1 xzfgrep.1 \ grep.1 lzgrep.1 \ grep.1 lzegrep.1 \ grep.1 lzfgrep.1 .endif -LDADD= -lz -lbz2 -llzma -DPADD= ${LIBZ} ${LIBBZ2} ${LIBLZMA} +LDADD= -lz -llzma +DPADD= ${LIBZ} ${LIBLZMA} + +.if !defined(WITHOUT_BZIP2) +LDADD+= -lbz2 +DPADD+= ${LIBBZ2} + +LINKS+= ${BINDIR}/grep ${BINDIR}/bzgrep \ + ${BINDIR}/grep ${BINDIR}/bzegrep \ + ${BINDIR}/grep ${BINDIR}/bzfgrep +MLINKS+= grep.1 bzgrep.1 \ + grep.1 bzegrep.1 \ + grep.1 bzfgrep.1 +.else +CFLAGS+= -DWITHOUT_BZIP2 +.endif .if !defined(WITHOUT_GNU_COMPAT) CFLAGS+= -I/usr/include/gnu LDADD+= -lgnuregex DPADD+= ${LIBGNUREGEX} .endif .if !defined(WITHOUT_NLS) .include "${.CURDIR}/nls/Makefile.inc" .else CFLAGS+= -DWITHOUT_NLS .endif .include Index: head/usr.bin/grep/file.c =================================================================== --- head/usr.bin/grep/file.c (revision 226270) +++ head/usr.bin/grep/file.c (revision 226271) @@ -1,315 +1,325 @@ /* $NetBSD: file.c,v 1.5 2011/02/16 18:35:39 joerg Exp $ */ /* $FreeBSD$ */ /* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */ /*- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav * Copyright (C) 2008-2010 Gabor Kovesdan * Copyright (C) 2010 Dimitry Andric * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include +#ifndef WITHOUT_BZIP2 +#include +#endif + #include "grep.h" #define MAXBUFSIZ (32 * 1024) #define LNBUFBUMP 80 static gzFile gzbufdesc; -static BZFILE* bzbufdesc; static lzma_stream lstrm = LZMA_STREAM_INIT; +#ifndef WITHOUT_BZIP2 +static BZFILE* bzbufdesc; +#endif static unsigned char *buffer; static unsigned char *bufpos; static size_t bufrem; static size_t fsiz; static unsigned char *lnbuf; static size_t lnbuflen; static inline int grep_refill(struct file *f) { ssize_t nr; - int bzerr; if (filebehave == FILE_MMAP) return (0); bufpos = buffer; bufrem = 0; - if (filebehave == FILE_GZIP) + if (filebehave == FILE_GZIP) { nr = gzread(gzbufdesc, buffer, MAXBUFSIZ); - else if (filebehave == FILE_BZIP && bzbufdesc != NULL) { +#ifndef WITHOUT_BZIP2 + } else if (filebehave == FILE_BZIP && bzbufdesc != NULL) { + int bzerr; + nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ); switch (bzerr) { case BZ_OK: case BZ_STREAM_END: /* No problem, nr will be okay */ break; case BZ_DATA_ERROR_MAGIC: /* * As opposed to gzread(), which simply returns the * plain file data, if it is not in the correct * compressed format, BZ2_bzRead() instead aborts. * * So, just restart at the beginning of the file again, * and use plain reads from now on. */ BZ2_bzReadClose(&bzerr, bzbufdesc); bzbufdesc = NULL; if (lseek(f->fd, 0, SEEK_SET) == -1) return (-1); nr = read(f->fd, buffer, MAXBUFSIZ); break; default: /* Make sure we exit with an error */ nr = -1; } +#endif } else if ((filebehave == FILE_XZ) || (filebehave == FILE_LZMA)) { lzma_action action = LZMA_RUN; uint8_t in_buf[MAXBUFSIZ]; lzma_ret ret; ret = (filebehave == FILE_XZ) ? lzma_stream_decoder(&lstrm, UINT64_MAX, LZMA_CONCATENATED) : lzma_alone_decoder(&lstrm, UINT64_MAX); if (ret != LZMA_OK) return (-1); lstrm.next_out = buffer; lstrm.avail_out = MAXBUFSIZ; lstrm.next_in = in_buf; nr = read(f->fd, in_buf, MAXBUFSIZ); if (nr < 0) return (-1); else if (nr == 0) action = LZMA_FINISH; lstrm.avail_in = nr; ret = lzma_code(&lstrm, action); if (ret != LZMA_OK && ret != LZMA_STREAM_END) return (-1); bufrem = MAXBUFSIZ - lstrm.avail_out; return (0); } else nr = read(f->fd, buffer, MAXBUFSIZ); if (nr < 0) return (-1); bufrem = nr; return (0); } static inline int grep_lnbufgrow(size_t newlen) { if (lnbuflen < newlen) { lnbuf = grep_realloc(lnbuf, newlen); lnbuflen = newlen; } return (0); } char * grep_fgetln(struct file *f, size_t *lenp) { unsigned char *p; char *ret; size_t len; size_t off; ptrdiff_t diff; /* Fill the buffer, if necessary */ if (bufrem == 0 && grep_refill(f) != 0) goto error; if (bufrem == 0) { /* Return zero length to indicate EOF */ *lenp = 0; return (bufpos); } /* Look for a newline in the remaining part of the buffer */ if ((p = memchr(bufpos, '\n', bufrem)) != NULL) { ++p; /* advance over newline */ ret = bufpos; len = p - bufpos; bufrem -= len; bufpos = p; *lenp = len; return (ret); } /* We have to copy the current buffered data to the line buffer */ for (len = bufrem, off = 0; ; len += bufrem) { /* Make sure there is room for more data */ if (grep_lnbufgrow(len + LNBUFBUMP)) goto error; memcpy(lnbuf + off, bufpos, len - off); off = len; if (grep_refill(f) != 0) goto error; if (bufrem == 0) /* EOF: return partial line */ break; if ((p = memchr(bufpos, '\n', bufrem)) == NULL) continue; /* got it: finish up the line (like code above) */ ++p; diff = p - bufpos; len += diff; if (grep_lnbufgrow(len)) goto error; memcpy(lnbuf + off, bufpos, diff); bufrem -= diff; bufpos = p; break; } *lenp = len; return (lnbuf); error: *lenp = 0; return (NULL); } /* * Opens a file for processing. */ struct file * grep_open(const char *path) { struct file *f; f = grep_malloc(sizeof *f); memset(f, 0, sizeof *f); if (path == NULL) { /* Processing stdin implies --line-buffered. */ lbflag = true; f->fd = STDIN_FILENO; } else if ((f->fd = open(path, O_RDONLY)) == -1) goto error1; if (filebehave == FILE_MMAP) { struct stat st; if ((fstat(f->fd, &st) == -1) || (st.st_size > OFF_MAX) || (!S_ISREG(st.st_mode))) filebehave = FILE_STDIO; else { int flags = MAP_PRIVATE | MAP_NOCORE | MAP_NOSYNC; #ifdef MAP_PREFAULT_READ flags |= MAP_PREFAULT_READ; #endif fsiz = st.st_size; buffer = mmap(NULL, fsiz, PROT_READ, flags, f->fd, (off_t)0); if (buffer == MAP_FAILED) filebehave = FILE_STDIO; else { bufrem = st.st_size; bufpos = buffer; madvise(buffer, st.st_size, MADV_SEQUENTIAL); } } } if ((buffer == NULL) || (buffer == MAP_FAILED)) buffer = grep_malloc(MAXBUFSIZ); if (filebehave == FILE_GZIP && (gzbufdesc = gzdopen(f->fd, "r")) == NULL) goto error2; +#ifndef WITHOUT_BZIP2 if (filebehave == FILE_BZIP && (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL) goto error2; +#endif /* Fill read buffer, also catches errors early */ if (bufrem == 0 && grep_refill(f) != 0) goto error2; /* Check for binary stuff, if necessary */ if (binbehave != BINFILE_TEXT && memchr(bufpos, '\0', bufrem) != NULL) f->binary = true; return (f); error2: close(f->fd); error1: free(f); return (NULL); } /* * Closes a file. */ void grep_close(struct file *f) { close(f->fd); /* Reset read buffer and line buffer */ if (filebehave == FILE_MMAP) { munmap(buffer, fsiz); buffer = NULL; } bufpos = buffer; bufrem = 0; free(lnbuf); lnbuf = NULL; lnbuflen = 0; } Index: head/usr.bin/grep/grep.c =================================================================== --- head/usr.bin/grep/grep.c (revision 226270) +++ head/usr.bin/grep/grep.c (revision 226271) @@ -1,729 +1,732 @@ /* $NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $ */ /* $FreeBSD$ */ /* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ /*- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav * Copyright (C) 2008-2009 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fastmatch.h" #include "grep.h" #ifndef WITHOUT_NLS #include nl_catd catalog; #endif /* * Default messags to use when NLS is disabled or no catalogue * is found. */ const char *errstr[] = { "", /* 1*/ "(standard input)", /* 2*/ "cannot read bzip2 compressed file", /* 3*/ "unknown %s option", /* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n", /* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", /* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", /* 7*/ "\t[--null] [pattern] [file ...]\n", /* 8*/ "Binary file %s matches\n", /* 9*/ "%s (BSD grep) %s\n", }; /* Flags passed to regcomp() and regexec() */ int cflags = REG_NOSUB; int eflags = REG_STARTEND; /* Shortcut for matching all cases like empty regex */ bool matchall; /* Searching patterns */ unsigned int patterns, pattern_sz; struct pat *pattern; regex_t *r_pattern; fastmatch_t *fg_pattern; /* Filename exclusion/inclusion patterns */ unsigned int fpatterns, fpattern_sz; unsigned int dpatterns, dpattern_sz; struct epat *dpattern, *fpattern; /* For regex errors */ char re_error[RE_ERROR_BUF + 1]; /* Command-line flags */ unsigned long long Aflag; /* -A x: print x lines trailing each match */ unsigned long long Bflag; /* -B x: print x lines leading each match */ bool Hflag; /* -H: always print file name */ bool Lflag; /* -L: only show names of files with no matches */ bool bflag; /* -b: show block numbers for each match */ bool cflag; /* -c: only show a count of matching lines */ bool hflag; /* -h: don't print filename headers */ bool iflag; /* -i: ignore case */ bool lflag; /* -l: only show names of files with matches */ bool mflag; /* -m x: stop reading the files after x matches */ long long mcount; /* count for -m */ bool nflag; /* -n: show line numbers in front of matching lines */ bool oflag; /* -o: print only matching part */ bool qflag; /* -q: quiet mode (don't output anything) */ bool sflag; /* -s: silent mode (ignore errors) */ bool vflag; /* -v: only show non-matching lines */ bool wflag; /* -w: pattern must start and end on word boundaries */ bool xflag; /* -x: pattern must match entire line */ bool lbflag; /* --line-buffered */ bool nullflag; /* --null */ char *label; /* --label */ const char *color; /* --color */ int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ int devbehave = DEV_READ; /* -D: handling of devices */ int dirbehave = DIR_READ; /* -dRr: handling of directories */ int linkbehave = LINK_READ; /* -OpS: handling of symlinks */ bool dexclude, dinclude; /* --exclude-dir and --include-dir */ bool fexclude, finclude; /* --exclude and --include */ enum { BIN_OPT = CHAR_MAX + 1, COLOR_OPT, HELP_OPT, MMAP_OPT, LINEBUF_OPT, LABEL_OPT, NULL_OPT, R_EXCLUDE_OPT, R_INCLUDE_OPT, R_DEXCLUDE_OPT, R_DINCLUDE_OPT }; static inline const char *init_color(const char *); /* Housekeeping */ bool first = true; /* flag whether we are processing the first match */ bool prev; /* flag whether or not the previous line matched */ int tail; /* lines left to print */ bool notfound; /* file not found */ -extern char *__progname; - /* * Prints usage information and returns 2. */ static void usage(void) { - fprintf(stderr, getstr(4), __progname); + fprintf(stderr, getstr(4), getprogname()); fprintf(stderr, "%s", getstr(5)); fprintf(stderr, "%s", getstr(5)); fprintf(stderr, "%s", getstr(6)); fprintf(stderr, "%s", getstr(7)); exit(2); } static const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; struct option long_options[] = { {"binary-files", required_argument, NULL, BIN_OPT}, {"help", no_argument, NULL, HELP_OPT}, {"mmap", no_argument, NULL, MMAP_OPT}, {"line-buffered", no_argument, NULL, LINEBUF_OPT}, {"label", required_argument, NULL, LABEL_OPT}, {"null", no_argument, NULL, NULL_OPT}, {"color", optional_argument, NULL, COLOR_OPT}, {"colour", optional_argument, NULL, COLOR_OPT}, {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, {"include", required_argument, NULL, R_INCLUDE_OPT}, {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, {"after-context", required_argument, NULL, 'A'}, {"text", no_argument, NULL, 'a'}, {"before-context", required_argument, NULL, 'B'}, {"byte-offset", no_argument, NULL, 'b'}, {"context", optional_argument, NULL, 'C'}, {"count", no_argument, NULL, 'c'}, {"devices", required_argument, NULL, 'D'}, {"directories", required_argument, NULL, 'd'}, {"extended-regexp", no_argument, NULL, 'E'}, {"regexp", required_argument, NULL, 'e'}, {"fixed-strings", no_argument, NULL, 'F'}, {"file", required_argument, NULL, 'f'}, {"basic-regexp", no_argument, NULL, 'G'}, {"no-filename", no_argument, NULL, 'h'}, {"with-filename", no_argument, NULL, 'H'}, {"ignore-case", no_argument, NULL, 'i'}, {"bz2decompress", no_argument, NULL, 'J'}, {"files-with-matches", no_argument, NULL, 'l'}, {"files-without-match", no_argument, NULL, 'L'}, {"max-count", required_argument, NULL, 'm'}, {"lzma", no_argument, NULL, 'M'}, {"line-number", no_argument, NULL, 'n'}, {"only-matching", no_argument, NULL, 'o'}, {"quiet", no_argument, NULL, 'q'}, {"silent", no_argument, NULL, 'q'}, {"recursive", no_argument, NULL, 'r'}, {"no-messages", no_argument, NULL, 's'}, {"binary", no_argument, NULL, 'U'}, {"unix-byte-offsets", no_argument, NULL, 'u'}, {"invert-match", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"word-regexp", no_argument, NULL, 'w'}, {"line-regexp", no_argument, NULL, 'x'}, {"xz", no_argument, NULL, 'X'}, {"decompress", no_argument, NULL, 'Z'}, {NULL, no_argument, NULL, 0} }; /* * Adds a searching pattern to the internal array. */ static void add_pattern(char *pat, size_t len) { /* Do not add further pattern is we already match everything */ if (matchall) return; /* Check if we can do a shortcut */ if (len == 0) { matchall = true; for (unsigned int i = 0; i < patterns; i++) { free(pattern[i].pat); } pattern = grep_realloc(pattern, sizeof(struct pat)); pattern[0].pat = NULL; pattern[0].len = 0; patterns = 1; return; } /* Increase size if necessary */ if (patterns == pattern_sz) { pattern_sz *= 2; pattern = grep_realloc(pattern, ++pattern_sz * sizeof(struct pat)); } if (len > 0 && pat[len - 1] == '\n') --len; /* pat may not be NUL-terminated */ pattern[patterns].pat = grep_malloc(len + 1); memcpy(pattern[patterns].pat, pat, len); pattern[patterns].len = len; pattern[patterns].pat[len] = '\0'; ++patterns; } /* * Adds a file include/exclude pattern to the internal array. */ static void add_fpattern(const char *pat, int mode) { /* Increase size if necessary */ if (fpatterns == fpattern_sz) { fpattern_sz *= 2; fpattern = grep_realloc(fpattern, ++fpattern_sz * sizeof(struct epat)); } fpattern[fpatterns].pat = grep_strdup(pat); fpattern[fpatterns].mode = mode; ++fpatterns; } /* * Adds a directory include/exclude pattern to the internal array. */ static void add_dpattern(const char *pat, int mode) { /* Increase size if necessary */ if (dpatterns == dpattern_sz) { dpattern_sz *= 2; dpattern = grep_realloc(dpattern, ++dpattern_sz * sizeof(struct epat)); } dpattern[dpatterns].pat = grep_strdup(pat); dpattern[dpatterns].mode = mode; ++dpatterns; } /* * Reads searching patterns from a file and adds them with add_pattern(). */ static void read_patterns(const char *fn) { struct stat st; FILE *f; char *line; size_t len; if ((f = fopen(fn, "r")) == NULL) err(2, "%s", fn); if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) { fclose(f); return; } while ((line = fgetln(f, &len)) != NULL) add_pattern(line, line[0] == '\n' ? 0 : len); if (ferror(f)) err(2, "%s", fn); fclose(f); } static inline const char * init_color(const char *d) { char *c; c = getenv("GREP_COLOR"); return (c != NULL && c[0] != '\0' ? c : d); } int main(int argc, char *argv[]) { char **aargv, **eargv, *eopts; - char *pn, *ep; + char *ep; + const char *pn; unsigned long long l; unsigned int aargc, eargc, i; int c, lastc, needpattern, newarg, prevoptind; setlocale(LC_ALL, ""); #ifndef WITHOUT_NLS catalog = catopen("grep", NL_CAT_LOCALE); #endif /* Check what is the program name of the binary. In this way we can have all the funcionalities in one binary without the need of scripting and using ugly hacks. */ - pn = __progname; + pn = getprogname(); if (pn[0] == 'b' && pn[1] == 'z') { filebehave = FILE_BZIP; pn += 2; } else if (pn[0] == 'x' && pn[1] == 'z') { filebehave = FILE_XZ; pn += 2; } else if (pn[0] == 'l' && pn[1] == 'z') { filebehave = FILE_LZMA; pn += 2; } else if (pn[0] == 'z') { filebehave = FILE_GZIP; pn += 1; } switch (pn[0]) { case 'e': grepbehave = GREP_EXTENDED; break; case 'f': grepbehave = GREP_FIXED; break; } lastc = '\0'; newarg = 1; prevoptind = 1; needpattern = 1; eopts = getenv("GREP_OPTIONS"); /* support for extra arguments in GREP_OPTIONS */ eargc = 0; if (eopts != NULL && eopts[0] != '\0') { char *str; /* make an estimation of how many extra arguments we have */ for (unsigned int j = 0; j < strlen(eopts); j++) if (eopts[j] == ' ') eargc++; eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); eargc = 0; /* parse extra arguments */ while ((str = strsep(&eopts, " ")) != NULL) if (str[0] != '\0') eargv[eargc++] = grep_strdup(str); aargv = (char **)grep_calloc(eargc + argc + 1, sizeof(char *)); aargv[0] = argv[0]; for (i = 0; i < eargc; i++) aargv[i + 1] = eargv[i]; for (int j = 1; j < argc; j++, i++) aargv[i + 1] = argv[j]; aargc = eargc + argc; } else { aargv = argv; aargc = argc; } while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != -1)) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (newarg || !isdigit(lastc)) Aflag = 0; else if (Aflag > LLONG_MAX / 10) { errno = ERANGE; err(2, NULL); } Aflag = Bflag = (Aflag * 10) + (c - '0'); break; case 'C': if (optarg == NULL) { Aflag = Bflag = 2; break; } /* FALLTHROUGH */ case 'A': /* FALLTHROUGH */ case 'B': errno = 0; l = strtoull(optarg, &ep, 10); if (((errno == ERANGE) && (l == ULLONG_MAX)) || ((errno == EINVAL) && (l == 0))) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } if (c == 'A') Aflag = l; else if (c == 'B') Bflag = l; else Aflag = Bflag = l; break; case 'a': binbehave = BINFILE_TEXT; break; case 'b': bflag = true; break; case 'c': cflag = true; break; case 'D': if (strcasecmp(optarg, "skip") == 0) devbehave = DEV_SKIP; else if (strcasecmp(optarg, "read") == 0) devbehave = DEV_READ; else errx(2, getstr(3), "--devices"); break; case 'd': if (strcasecmp("recurse", optarg) == 0) { Hflag = true; dirbehave = DIR_RECURSE; } else if (strcasecmp("skip", optarg) == 0) dirbehave = DIR_SKIP; else if (strcasecmp("read", optarg) == 0) dirbehave = DIR_READ; else errx(2, getstr(3), "--directories"); break; case 'E': grepbehave = GREP_EXTENDED; break; case 'e': add_pattern(optarg, strlen(optarg)); needpattern = 0; break; case 'F': grepbehave = GREP_FIXED; break; case 'f': read_patterns(optarg); needpattern = 0; break; case 'G': grepbehave = GREP_BASIC; break; case 'H': Hflag = true; break; case 'h': Hflag = false; hflag = true; break; case 'I': binbehave = BINFILE_SKIP; break; case 'i': case 'y': iflag = true; cflags |= REG_ICASE; break; case 'J': +#ifdef WITHOUT_BZIP2 + errno = EOPNOTSUPP; + err(2, "bzip2 support was disabled at compile-time"); +#endif filebehave = FILE_BZIP; break; case 'L': lflag = false; Lflag = true; break; case 'l': Lflag = false; lflag = true; break; case 'm': mflag = true; errno = 0; mcount = strtoll(optarg, &ep, 10); if (((errno == ERANGE) && (mcount == LLONG_MAX)) || ((errno == EINVAL) && (mcount == 0))) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } break; case 'M': filebehave = FILE_LZMA; break; case 'n': nflag = true; break; case 'O': linkbehave = LINK_EXPLICIT; break; case 'o': oflag = true; cflags &= ~REG_NOSUB; break; case 'p': linkbehave = LINK_SKIP; break; case 'q': qflag = true; break; case 'S': linkbehave = LINK_READ; break; case 'R': case 'r': dirbehave = DIR_RECURSE; Hflag = true; break; case 's': sflag = true; break; case 'U': binbehave = BINFILE_BIN; break; case 'u': case MMAP_OPT: filebehave = FILE_MMAP; break; case 'V': - printf(getstr(9), __progname, VERSION); + printf(getstr(9), getprogname(), VERSION); exit(0); case 'v': vflag = true; break; case 'w': wflag = true; cflags &= ~REG_NOSUB; break; case 'x': xflag = true; cflags &= ~REG_NOSUB; break; case 'X': filebehave = FILE_XZ; break; case 'Z': filebehave = FILE_GZIP; break; case BIN_OPT: if (strcasecmp("binary", optarg) == 0) binbehave = BINFILE_BIN; else if (strcasecmp("without-match", optarg) == 0) binbehave = BINFILE_SKIP; else if (strcasecmp("text", optarg) == 0) binbehave = BINFILE_TEXT; else errx(2, getstr(3), "--binary-files"); break; case COLOR_OPT: color = NULL; if (optarg == NULL || strcasecmp("auto", optarg) == 0 || strcasecmp("tty", optarg) == 0 || strcasecmp("if-tty", optarg) == 0) { char *term; term = getenv("TERM"); if (isatty(STDOUT_FILENO) && term != NULL && strcasecmp(term, "dumb") != 0) color = init_color("01;31"); } else if (strcasecmp("always", optarg) == 0 || strcasecmp("yes", optarg) == 0 || strcasecmp("force", optarg) == 0) { color = init_color("01;31"); } else if (strcasecmp("never", optarg) != 0 && strcasecmp("none", optarg) != 0 && strcasecmp("no", optarg) != 0) errx(2, getstr(3), "--color"); cflags &= ~REG_NOSUB; break; case LABEL_OPT: label = optarg; break; case LINEBUF_OPT: lbflag = true; break; case NULL_OPT: nullflag = true; break; case R_INCLUDE_OPT: finclude = true; add_fpattern(optarg, INCL_PAT); break; case R_EXCLUDE_OPT: fexclude = true; add_fpattern(optarg, EXCL_PAT); break; case R_DINCLUDE_OPT: dinclude = true; add_dpattern(optarg, INCL_PAT); break; case R_DEXCLUDE_OPT: dexclude = true; add_dpattern(optarg, EXCL_PAT); break; case HELP_OPT: default: usage(); } lastc = c; newarg = optind != prevoptind; prevoptind = optind; } aargc -= optind; aargv += optind; /* Empty pattern file matches nothing */ if (!needpattern && (patterns == 0)) exit(1); /* Fail if we don't have any pattern */ if (aargc == 0 && needpattern) usage(); /* Process patterns from command line */ if (aargc != 0 && needpattern) { add_pattern(*aargv, strlen(*aargv)); --aargc; ++aargv; } switch (grepbehave) { case GREP_BASIC: break; case GREP_FIXED: /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ cflags |= 0020; break; case GREP_EXTENDED: cflags |= REG_EXTENDED; break; default: /* NOTREACHED */ usage(); } fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); /* Check if cheating is allowed (always is for fgrep). */ for (i = 0; i < patterns; ++i) { if (fastncomp(&fg_pattern[i], pattern[i].pat, pattern[i].len, cflags) != 0) { /* Fall back to full regex library */ c = regcomp(&r_pattern[i], pattern[i].pat, cflags); if (c != 0) { regerror(c, &r_pattern[i], re_error, RE_ERROR_BUF); errx(2, "%s", re_error); } } } if (lbflag) setlinebuf(stdout); if ((aargc == 0 || aargc == 1) && !Hflag) hflag = true; if (aargc == 0) exit(!procfile("-")); if (dirbehave == DIR_RECURSE) c = grep_tree(aargv); else for (c = 0; aargc--; ++aargv) { if ((finclude || fexclude) && !file_matching(*aargv)) continue; c+= procfile(*aargv); } #ifndef WITHOUT_NLS catclose(catalog); #endif /* Find out the correct return value according to the results and the command line option. */ exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); } Index: head/usr.bin/grep/regex/tre-fastmatch.c =================================================================== --- head/usr.bin/grep/regex/tre-fastmatch.c (revision 226270) +++ head/usr.bin/grep/regex/tre-fastmatch.c (revision 226271) @@ -1,1042 +1,1042 @@ -/* $FreeBSD$ */ +/* $FreeBSD$ */ /*- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav * Copyright (C) 2008-2011 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 DAMAGE. */ #include "glue.h" #include #include #include #include #include #include #ifdef TRE_WCHAR #include #include #endif #include "hashtable.h" #include "tre-fastmatch.h" #include "xmalloc.h" static int fastcmp(const fastmatch_t *fg, const void *data, tre_str_type_t type); /* * Clean up if pattern compilation fails. */ #define FAIL_COMP(errcode) \ { \ if (fg->pattern) \ xfree(fg->pattern); \ if (fg->wpattern) \ xfree(fg->wpattern); \ if (fg->qsBc_table) \ hashtable_free(fg->qsBc_table); \ fg = NULL; \ return errcode; \ } /* * Skips n characters in the input string and assigns the start * address to startptr. Note: as per IEEE Std 1003.1-2008 * matching is based on bit pattern not character representations * so we can handle MB strings as byte sequences just like * SB strings. */ #define SKIP_CHARS(n) \ switch (type) \ { \ case STR_WIDE: \ startptr = str_wide + n; \ break; \ default: \ startptr = str_byte + n; \ } /* * Converts the wide string pattern to SB/MB string and stores * it in fg->pattern. Sets fg->len to the byte length of the * converted string. */ #define STORE_MBS_PAT \ { \ size_t siz; \ \ siz = wcstombs(NULL, fg->wpattern, 0); \ if (siz == (size_t)-1) \ return REG_BADPAT; \ fg->len = siz; \ fg->pattern = xmalloc(siz + 1); \ if (fg->pattern == NULL) \ return REG_ESPACE; \ wcstombs(fg->pattern, fg->wpattern, siz); \ fg->pattern[siz] = '\0'; \ } \ #define IS_OUT_OF_BOUNDS \ ((!fg->reversed \ ? ((type == STR_WIDE) ? ((j + fg->wlen) > len) \ : ((j + fg->len) > len)) \ : (j < 0))) /* * Checks whether the new position after shifting in the input string * is out of the bounds and break out from the loop if so. */ #define CHECKBOUNDS \ if (IS_OUT_OF_BOUNDS) \ break; \ /* * Shifts in the input string after a mismatch. The position of the * mismatch is stored in the mismatch variable. */ #define SHIFT \ CHECKBOUNDS; \ \ { \ int r = -1; \ unsigned int bc = 0, gs = 0, ts; \ \ switch (type) \ { \ case STR_WIDE: \ if (!fg->hasdot) \ { \ if (u != 0 && (unsigned)mismatch == fg->wlen - 1 - shift) \ mismatch -= u; \ v = fg->wlen - 1 - mismatch; \ r = hashtable_get(fg->qsBc_table, \ &str_wide[!fg->reversed ? (size_t)j + fg->wlen \ : (size_t)j - 1], &bc); \ gs = fg->bmGs[mismatch]; \ } \ bc = (r == HASH_OK) ? bc : fg->defBc; \ DPRINT(("tre_fast_match: mismatch on character" CHF ", " \ "BC %d, GS %d\n", \ ((const tre_char_t *)startptr)[mismatch + 1], \ bc, gs)); \ break; \ default: \ if (!fg->hasdot) \ { \ if (u != 0 && (unsigned)mismatch == fg->len - 1 - shift) \ mismatch -= u; \ v = fg->len - 1 - mismatch; \ gs = fg->sbmGs[mismatch]; \ } \ bc = fg->qsBc[((const unsigned char *)str_byte) \ [!fg->reversed ? (size_t)j + fg->len \ : (size_t)j - 1]]; \ DPRINT(("tre_fast_match: mismatch on character %c, " \ "BC %d, GS %d\n", \ ((const unsigned char *)startptr)[mismatch + 1], \ bc, gs)); \ } \ if (fg->hasdot) \ shift = bc; \ else \ { \ ts = (u >= v) ? (u - v) : 0; \ shift = MAX(ts, bc); \ shift = MAX(shift, gs); \ if (shift == gs) \ u = MIN((type == STR_WIDE ? fg->wlen : fg->len) - shift, v); \ else \ { \ if (ts < bc) \ shift = MAX(shift, u + 1); \ u = 0; \ } \ } \ DPRINT(("tre_fast_match: shifting %u characters\n", shift)); \ j = !fg->reversed ? j + shift : j - shift; \ } /* * Normal Quick Search would require a shift based on the position the * next character after the comparison is within the pattern. With * wildcards, the position of the last dot effects the maximum shift * distance. * The closer to the end the wild card is the slower the search. * * Examples: * Pattern Max shift * ------- --------- * this 5 * .his 4 * t.is 3 * th.s 2 * thi. 1 */ /* * Fills in the bad character shift array for SB/MB strings. */ #define FILL_QSBC \ if (fg->reversed) \ { \ _FILL_QSBC_REVERSED \ } \ else \ { \ _FILL_QSBC \ } #define _FILL_QSBC \ for (unsigned int i = 0; i <= UCHAR_MAX; i++) \ fg->qsBc[i] = fg->len - hasdot; \ for (unsigned int i = hasdot + 1; i < fg->len; i++) \ { \ fg->qsBc[(unsigned char)fg->pattern[i]] = fg->len - i; \ DPRINT(("BC shift for char %c is %zu\n", fg->pattern[i], \ fg->len - i)); \ if (fg->icase) \ { \ char c = islower((unsigned char)fg->pattern[i]) ? \ toupper((unsigned char)fg->pattern[i]) : \ tolower((unsigned char)fg->pattern[i]); \ fg->qsBc[(unsigned char)c] = fg->len - i; \ DPRINT(("BC shift for char %c is %zu\n", c, fg->len - i)); \ } \ } #define _FILL_QSBC_REVERSED \ for (unsigned int i = 0; i <= UCHAR_MAX; i++) \ fg->qsBc[i] = firstdot + 1; \ for (int i = firstdot - 1; i >= 0; i--) \ { \ fg->qsBc[(unsigned char)fg->pattern[i]] = i + 1; \ DPRINT(("Reverse BC shift for char %c is %d\n", fg->pattern[i], \ i + 1)); \ if (fg->icase) \ { \ char c = islower((unsigned char)fg->pattern[i]) ? \ toupper((unsigned char)fg->pattern[i]) : \ tolower((unsigned char)fg->pattern[i]); \ fg->qsBc[(unsigned char)c] = i + 1; \ DPRINT(("Reverse BC shift for char %c is %d\n", c, i + 1)); \ } \ } /* * Fills in the bad character shifts into a hastable for wide strings. * With wide characters it is not possible any more to use a normal * array because there are too many characters and we could not * provide enough memory. Fortunately, we only have to store distinct * values for so many characters as the number of distinct characters * in the pattern, so we can store them in a hashtable and store a * default shift value for the rest. */ #define FILL_QSBC_WIDE \ if (fg->reversed) \ { \ _FILL_QSBC_WIDE_REVERSED \ } \ else \ { \ _FILL_QSBC_WIDE \ } #define _FILL_QSBC_WIDE \ /* Adjust the shift based on location of the last dot ('.'). */ \ fg->defBc = fg->wlen - whasdot; \ \ /* Preprocess pattern. */ \ fg->qsBc_table = hashtable_init(fg->wlen * (fg->icase ? 8 : 4), \ sizeof(tre_char_t), sizeof(int)); \ if (!fg->qsBc_table) \ FAIL_COMP(REG_ESPACE); \ for (unsigned int i = whasdot + 1; i < fg->wlen; i++) \ { \ int k = fg->wlen - i; \ int r; \ \ r = hashtable_put(fg->qsBc_table, &fg->wpattern[i], &k); \ if ((r == HASH_FAIL) || (r == HASH_FULL)) \ FAIL_COMP(REG_ESPACE); \ DPRINT(("BC shift for wide char " CHF " is %d\n", fg->wpattern[i],\ k)); \ if (fg->icase) \ { \ tre_char_t wc = iswlower(fg->wpattern[i]) ? \ towupper(fg->wpattern[i]) : towlower(fg->wpattern[i]); \ r = hashtable_put(fg->qsBc_table, &wc, &k); \ if ((r == HASH_FAIL) || (r == HASH_FULL)) \ FAIL_COMP(REG_ESPACE); \ DPRINT(("BC shift for wide char " CHF " is %d\n", wc, k)); \ } \ } #define _FILL_QSBC_WIDE_REVERSED \ /* Adjust the shift based on location of the last dot ('.'). */ \ fg->defBc = (size_t)wfirstdot; \ \ /* Preprocess pattern. */ \ fg->qsBc_table = hashtable_init(fg->wlen * (fg->icase ? 8 : 4), \ sizeof(tre_char_t), sizeof(int)); \ if (!fg->qsBc_table) \ FAIL_COMP(REG_ESPACE); \ for (int i = wfirstdot - 1; i >= 0; i--) \ { \ int k = i + 1; \ int r; \ \ r = hashtable_put(fg->qsBc_table, &fg->wpattern[i], &k); \ if ((r == HASH_FAIL) || (r == HASH_FULL)) \ FAIL_COMP(REG_ESPACE); \ DPRINT(("Reverse BC shift for wide char " CHF " is %d\n", \ fg->wpattern[i], k)); \ if (fg->icase) \ { \ tre_char_t wc = iswlower(fg->wpattern[i]) ? \ towupper(fg->wpattern[i]) : towlower(fg->wpattern[i]); \ r = hashtable_put(fg->qsBc_table, &wc, &k); \ if ((r == HASH_FAIL) || (r == HASH_FULL)) \ FAIL_COMP(REG_ESPACE); \ DPRINT(("Reverse BC shift for wide char " CHF " is %d\n", wc, \ k)); \ } \ } #ifdef _GREP_DEBUG #define DPRINT_BMGS(len, fmt_str, sh) \ for (unsigned int i = 0; i < len; i++) \ DPRINT((fmt_str, i, sh[i])); #else #define DPRINT_BMGS(len, fmt_str, sh) \ do { } while(/*CONSTCOND*/0) #endif /* * Fills in the good suffix table for SB/MB strings. */ #define FILL_BMGS \ if (!fg->hasdot) \ { \ fg->sbmGs = xmalloc(fg->len * sizeof(int)); \ if (!fg->sbmGs) \ return REG_ESPACE; \ if (fg->len == 1) \ fg->sbmGs[0] = 1; \ else \ _FILL_BMGS(fg->sbmGs, fg->pattern, fg->len, false); \ DPRINT_BMGS(fg->len, "GS shift for pos %d is %d\n", fg->sbmGs); \ } /* * Fills in the good suffix table for wide strings. */ #define FILL_BMGS_WIDE \ if (!fg->hasdot) \ { \ fg->bmGs = xmalloc(fg->wlen * sizeof(int)); \ if (!fg->bmGs) \ return REG_ESPACE; \ if (fg->wlen == 1) \ fg->bmGs[0] = 1; \ else \ _FILL_BMGS(fg->bmGs, fg->wpattern, fg->wlen, true); \ DPRINT_BMGS(fg->wlen, "GS shift (wide) for pos %d is %d\n", \ fg->bmGs); \ } #define _FILL_BMGS(arr, pat, plen, wide) \ { \ char *p; \ tre_char_t *wp; \ \ if (wide) \ { \ if (fg->icase) \ { \ wp = xmalloc(plen * sizeof(tre_char_t)); \ if (wp == NULL) \ return REG_ESPACE; \ for (unsigned int i = 0; i < plen; i++) \ wp[i] = towlower(pat[i]); \ _CALC_BMGS(arr, wp, plen); \ xfree(wp); \ } \ else \ _CALC_BMGS(arr, pat, plen); \ } \ else \ { \ if (fg->icase) \ { \ p = xmalloc(plen); \ if (p == NULL) \ return REG_ESPACE; \ for (unsigned int i = 0; i < plen; i++) \ p[i] = tolower(pat[i]); \ _CALC_BMGS(arr, p, plen); \ xfree(p); \ } \ else \ _CALC_BMGS(arr, pat, plen); \ } \ } #define _CALC_BMGS(arr, pat, plen) \ { \ int f = 0, g; \ \ int *suff = xmalloc(plen * sizeof(int)); \ if (suff == NULL) \ return REG_ESPACE; \ \ suff[plen - 1] = plen; \ g = plen - 1; \ for (int i = plen - 2; i >= 0; i--) \ { \ if (i > g && suff[i + plen - 1 - f] < i - g) \ suff[i] = suff[i + plen - 1 - f]; \ else \ { \ if (i < g) \ g = i; \ f = i; \ while (g >= 0 && pat[g] == pat[g + plen - 1 - f]) \ g--; \ suff[i] = f - g; \ } \ } \ \ for (unsigned int i = 0; i < plen; i++) \ arr[i] = plen; \ g = 0; \ for (int i = plen - 1; i >= 0; i--) \ if (suff[i] == i + 1) \ for(; (unsigned long)g < plen - 1 - i; g++) \ if (arr[g] == plen) \ arr[g] = plen - 1 - i; \ for (unsigned int i = 0; i <= plen - 2; i++) \ arr[plen - 1 - suff[i]] = plen - 1 - i; \ \ xfree(suff); \ } /* * Copies the pattern pat having lenght n to p and stores * the size in l. */ #define SAVE_PATTERN(src, srclen, dst, dstlen) \ dstlen = srclen; \ dst = xmalloc((dstlen + 1) * sizeof(tre_char_t)); \ if (dst == NULL) \ return REG_ESPACE; \ if (dstlen > 0) \ memcpy(dst, src, dstlen * sizeof(tre_char_t)); \ dst[dstlen] = TRE_CHAR('\0'); /* * Initializes pattern compiling. */ #define INIT_COMP \ /* Initialize. */ \ memset(fg, 0, sizeof(*fg)); \ fg->icase = (cflags & REG_ICASE); \ fg->word = (cflags & REG_WORD); \ fg->newline = (cflags & REG_NEWLINE); \ fg->nosub = (cflags & REG_NOSUB); \ \ /* Cannot handle REG_ICASE with MB string */ \ if (fg->icase && (TRE_MB_CUR_MAX > 1)) \ { \ DPRINT(("Cannot use fast matcher for MBS with REG_ICASE\n")); \ return REG_BADPAT; \ } /* * Checks whether we have a 0-length pattern that will match * anything. If literal is set to false, the EOL anchor is also * taken into account. */ #define CHECK_MATCHALL(literal) \ if (!literal && n == 1 && pat[0] == TRE_CHAR('$')) \ { \ n--; \ fg->eol = true; \ } \ \ if (n == 0) \ { \ fg->matchall = true; \ fg->pattern = xmalloc(sizeof(char)); \ if (!fg->pattern) \ FAIL_COMP(REG_ESPACE); \ fg->pattern[0] = '\0'; \ fg->wpattern = xmalloc(sizeof(tre_char_t)); \ if (!fg->wpattern) \ FAIL_COMP(REG_ESPACE); \ fg->wpattern[0] = TRE_CHAR('\0'); \ DPRINT(("Matching every input\n")); \ return REG_OK; \ } /* * Returns: REG_OK on success, error code otherwise */ int tre_compile_literal(fastmatch_t *fg, const tre_char_t *pat, size_t n, int cflags) { size_t hasdot = 0, whasdot = 0; ssize_t firstdot = -1, wfirstdot = -1; INIT_COMP; CHECK_MATCHALL(true); /* Cannot handle word boundaries with MB string */ if (fg->word && (TRE_MB_CUR_MAX > 1)) return REG_BADPAT; #ifdef TRE_WCHAR SAVE_PATTERN(pat, n, fg->wpattern, fg->wlen); STORE_MBS_PAT; #else SAVE_PATTERN(pat, n, fg->pattern, fg->len); #endif DPRINT(("tre_compile_literal: pattern: %s, len %zu, icase: %c, word: %c, " "newline %c\n", fg->pattern, fg->len, fg->icase ? 'y' : 'n', fg->word ? 'y' : 'n', fg->newline ? 'y' : 'n')); FILL_QSBC; FILL_BMGS; #ifdef TRE_WCHAR FILL_QSBC_WIDE; FILL_BMGS_WIDE; #endif return REG_OK; } /* * Returns: REG_OK on success, error code otherwise */ int tre_compile_fast(fastmatch_t *fg, const tre_char_t *pat, size_t n, int cflags) { tre_char_t *tmp; - size_t pos = 0, hasdot = 0, whasdot = 0;; + size_t pos = 0, hasdot = 0, whasdot = 0; ssize_t firstdot = -1, wfirstdot = -1; bool escaped = false; bool *_escmap = NULL; INIT_COMP; /* Remove beginning-of-line character ('^'). */ if (pat[0] == TRE_CHAR('^')) { fg->bol = true; n--; pat++; } CHECK_MATCHALL(false); /* Handle word-boundary matching when GNU extensions are enabled */ if ((cflags & REG_GNU) && (n >= 14) && (memcmp(pat, TRE_CHAR("[[:<:]]"), 7 * sizeof(tre_char_t)) == 0) && (memcmp(pat + n - 7, TRE_CHAR("[[:>:]]"), 7 * sizeof(tre_char_t)) == 0)) { n -= 14; pat += 7; fg->word = true; } /* Cannot handle word boundaries with MB string */ if (fg->word && (TRE_MB_CUR_MAX > 1)) return REG_BADPAT; tmp = xmalloc((n + 1) * sizeof(tre_char_t)); if (tmp == NULL) return REG_ESPACE; /* Copies the char into the stored pattern and skips to the next char. */ #define STORE_CHAR \ do \ { \ tmp[pos++] = pat[i]; \ escaped = false; \ continue; \ } while (0) /* Traverse the input pattern for processing */ for (unsigned int i = 0; i < n; i++) { switch (pat[i]) { case TRE_CHAR('\\'): if (escaped) STORE_CHAR; else if (i == n - 1) goto badpat; else escaped = true; continue; case TRE_CHAR('['): if (escaped) STORE_CHAR; else goto badpat; continue; case TRE_CHAR('*'): if (escaped || (!(cflags & REG_EXTENDED) && (i == 0))) STORE_CHAR; else goto badpat; continue; case TRE_CHAR('+'): case TRE_CHAR('?'): if ((cflags & REG_EXTENDED) && (i == 0)) continue; else if ((cflags & REG_EXTENDED) ^ !escaped) STORE_CHAR; else goto badpat; continue; case TRE_CHAR('.'): if (escaped) { if (!_escmap) _escmap = xmalloc(n * sizeof(bool)); if (!_escmap) { xfree(tmp); return REG_ESPACE; } _escmap[i] = true; STORE_CHAR; } else { whasdot = i; if (wfirstdot == -1) wfirstdot = i; STORE_CHAR; } continue; case TRE_CHAR('^'): STORE_CHAR; continue; case TRE_CHAR('$'): if (!escaped && (i == n - 1)) fg->eol = true; else STORE_CHAR; continue; case TRE_CHAR('('): if ((cflags & REG_EXTENDED) ^ escaped) goto badpat; else STORE_CHAR; continue; case TRE_CHAR('{'): if (!(cflags & REG_EXTENDED) ^ escaped) STORE_CHAR; else if (!(cflags & REG_EXTENDED) && (i == 0)) STORE_CHAR; else if ((cflags & REG_EXTENDED) && (i == 0)) continue; else goto badpat; continue; case TRE_CHAR('|'): if ((cflags & REG_EXTENDED) ^ escaped) goto badpat; else STORE_CHAR; continue; default: if (escaped) goto badpat; else STORE_CHAR; continue; } continue; badpat: xfree(tmp); DPRINT(("tre_compile_fast: compilation of pattern failed, falling" "back to NFA\n")); return REG_BADPAT; } - fg->hasdot = whasdot; + fg->hasdot = wfirstdot > -1; /* * The pattern has been processed and copied to tmp as a literal string * with escapes, anchors (^$) and the word boundary match character * classes stripped out. */ #ifdef TRE_WCHAR SAVE_PATTERN(tmp, pos, fg->wpattern, fg->wlen); fg->wescmap = _escmap; STORE_MBS_PAT; /* * The position of dots and escaped dots is different in the MB string * than in to the wide string so traverse the converted string, as well, * to store these positions. */ if (fg->hasdot || (fg->wescmap != NULL)) { if (fg->wescmap != NULL) { fg->escmap = xmalloc(fg->len * sizeof(bool)); if (!fg->escmap) { tre_free_fast(fg); return REG_ESPACE; } } escaped = false; for (unsigned int i = 0; i < fg->len; i++) if (fg->pattern[i] == '\\') escaped = !escaped; else if (fg->pattern[i] == '.' && escaped) { fg->escmap[i] = true; escaped = false; } else if (fg->pattern[i] == '.' && !escaped) { hasdot = i; if (firstdot == -1) firstdot = i; } else escaped = false; } #else SAVE_PATTERN(tmp, pos, fg->pattern, fg->len); fg->escmap = _escmap; #endif xfree(tmp); DPRINT(("tre_compile_fast: pattern: %s, len %zu, bol %c, eol %c, " "icase: %c, word: %c, newline %c\n", fg->pattern, fg->len, fg->bol ? 'y' : 'n', fg->eol ? 'y' : 'n', fg->icase ? 'y' : 'n', fg->word ? 'y' : 'n', fg->newline ? 'y' : 'n')); /* Check whether reverse QS algorithm is more efficient */ if ((wfirstdot > -1) && (fg->wlen - whasdot + 1 < (size_t)wfirstdot) && fg->nosub) { fg->reversed = true; DPRINT(("tre_compile_fast: using reverse QS algorithm\n")); } FILL_QSBC; FILL_BMGS; #ifdef TRE_WCHAR FILL_QSBC_WIDE; FILL_BMGS_WIDE; #endif return REG_OK; } #define _SHIFT_ONE \ { \ shift = 1; \ j = !fg->reversed ? j + shift : j - shift; \ continue; \ } #define _BBOUND_COND \ ((type == STR_WIDE) ? \ ((j == 0) || !(tre_isalnum(str_wide[j - 1]) || \ (str_wide[j - 1] == TRE_CHAR('_')))) : \ ((j == 0) || !(tre_isalnum(str_byte[j - 1]) || \ (str_byte[j - 1] == '_')))) #define _EBOUND_COND \ ((type == STR_WIDE) ? \ ((j + fg->wlen == len) || !(tre_isalnum(str_wide[j + fg->wlen]) || \ (str_wide[j + fg->wlen] == TRE_CHAR('_')))) : \ ((j + fg->len == len) || !(tre_isalnum(str_byte[j + fg->len]) || \ (str_byte[j + fg->len] == '_')))) /* * Condition to check whether the match on position j is on a * word boundary. */ #define IS_ON_WORD_BOUNDARY \ (_BBOUND_COND && _EBOUND_COND) /* * Checks word boundary and shifts one if match is not on a * boundary. */ #define CHECK_WORD_BOUNDARY \ if (!IS_ON_WORD_BOUNDARY) \ _SHIFT_ONE; #define _BOL_COND \ ((j == 0) || ((type == STR_WIDE) ? (str_wide[j - 1] == TRE_CHAR('\n'))\ : (str_byte[j - 1] == '\n'))) /* * Checks BOL anchor and shifts one if match is not on a * boundary. */ #define CHECK_BOL_ANCHOR \ if (!_BOL_COND) \ _SHIFT_ONE; #define _EOL_COND \ ((type == STR_WIDE) \ ? ((j + fg->wlen == len) || \ (str_wide[j + fg->wlen] == TRE_CHAR('\n'))) \ : ((j + fg->len == len) || (str_byte[j + fg->wlen] == '\n'))) /* * Checks EOL anchor and shifts one if match is not on a * boundary. */ #define CHECK_EOL_ANCHOR \ if (!_EOL_COND) \ _SHIFT_ONE; /* * Executes matching of the precompiled pattern on the input string. * Returns REG_OK or REG_NOMATCH depending on if we find a match or not. */ int tre_match_fast(const fastmatch_t *fg, const void *data, size_t len, tre_str_type_t type, int nmatch, regmatch_t pmatch[], int eflags) { unsigned int shift, u = 0, v = 0; ssize_t j = 0; int ret = REG_NOMATCH; int mismatch; const char *str_byte = data; const void *startptr = NULL; const tre_char_t *str_wide = data; /* Calculate length if unspecified. */ if (len == (size_t)-1) switch (type) { case STR_WIDE: len = tre_strlen(str_wide); break; default: len = strlen(str_byte); break; } /* Shortcut for empty pattern */ if (fg->matchall) { if (!fg->nosub && nmatch >= 1) { pmatch[0].rm_so = 0; pmatch[0].rm_eo = len; } if (fg->bol && fg->eol) return (len == 0) ? REG_OK : REG_NOMATCH; else return REG_OK; } /* No point in going farther if we do not have enough data. */ switch (type) { case STR_WIDE: if (len < fg->wlen) return ret; shift = fg->wlen; break; default: if (len < fg->len) return ret; shift = fg->len; } /* * REG_NOTBOL means not anchoring ^ to the beginning of the line, so we * can shift one because there can't be a match at the beginning. */ if (fg->bol && (eflags & REG_NOTBOL)) j = 1; /* * Like above, we cannot have a match at the very end when anchoring to * the end and REG_NOTEOL is specified. */ if (fg->eol && (eflags & REG_NOTEOL)) len--; if (fg->reversed) j = len - (type == STR_WIDE ? fg->wlen : fg->len); /* Only try once at the beginning or ending of the line. */ if ((fg->bol || fg->eol) && !fg->newline && !(eflags & REG_NOTBOL) && !(eflags & REG_NOTEOL)) { /* Simple text comparison. */ if (!((fg->bol && fg->eol) && (type == STR_WIDE ? (len != fg->wlen) : (len != fg->len)))) { /* Determine where in data to start search at. */ j = fg->eol ? len - (type == STR_WIDE ? fg->wlen : fg->len) : 0; SKIP_CHARS(j); mismatch = fastcmp(fg, startptr, type); if (mismatch == REG_OK) { if (fg->word && !IS_ON_WORD_BOUNDARY) return ret; if (!fg->nosub && nmatch >= 1) { pmatch[0].rm_so = j; pmatch[0].rm_eo = j + (type == STR_WIDE ? fg->wlen : fg->len); } return REG_OK; } } } else { /* Quick Search / Turbo Boyer-Moore algorithm. */ do { SKIP_CHARS(j); mismatch = fastcmp(fg, startptr, type); if (mismatch == REG_OK) { if (fg->word) CHECK_WORD_BOUNDARY; if (fg->bol) CHECK_BOL_ANCHOR; if (fg->eol) CHECK_EOL_ANCHOR; if (!fg->nosub && nmatch >= 1) { pmatch[0].rm_so = j; pmatch[0].rm_eo = j + ((type == STR_WIDE) ? fg->wlen : fg->len); } return REG_OK; } else if (mismatch > 0) return mismatch; mismatch = -mismatch - 1; SHIFT; } while (!IS_OUT_OF_BOUNDS); } return ret; } /* * Frees the resources that were allocated when the pattern was compiled. */ void tre_free_fast(fastmatch_t *fg) { DPRINT(("tre_fast_free: freeing structures for pattern %s\n", fg->pattern)); #ifdef TRE_WCHAR hashtable_free(fg->qsBc_table); if (!fg->hasdot) xfree(fg->bmGs); if (fg->wescmap) xfree(fg->wescmap); xfree(fg->wpattern); #endif if (!fg->hasdot) xfree(fg->sbmGs); if (fg->escmap) xfree(fg->escmap); xfree(fg->pattern); } /* * Returns: -(i + 1) on failure (position that it failed with minus sign) * error code on error * REG_OK on success */ static inline int fastcmp(const fastmatch_t *fg, const void *data, tre_str_type_t type) { const char *str_byte = data; const char *pat_byte = fg->pattern; const tre_char_t *str_wide = data; const tre_char_t *pat_wide = fg->wpattern; const bool *escmap = (type == STR_WIDE) ? fg->wescmap : fg->escmap; size_t len = (type == STR_WIDE) ? fg->wlen : fg->len; int ret = REG_OK; /* Compare the pattern and the input char-by-char from the last position. */ for (int i = len - 1; i >= 0; i--) { switch (type) { case STR_WIDE: /* Check dot */ if (fg->hasdot && pat_wide[i] == TRE_CHAR('.') && (!escmap || !escmap[i]) && (!fg->newline || (str_wide[i] != TRE_CHAR('\n')))) continue; /* Compare */ if (fg->icase ? (towlower(pat_wide[i]) == towlower(str_wide[i])) : (pat_wide[i] == str_wide[i])) continue; break; default: /* Check dot */ if (fg->hasdot && pat_byte[i] == '.' && (!escmap || !escmap[i]) && (!fg->newline || (str_byte[i] != '\n'))) continue; /* Compare */ if (fg->icase ? (tolower(pat_byte[i]) == tolower(str_byte[i])) : (pat_byte[i] == str_byte[i])) continue; } DPRINT(("fastcmp: mismatch at position %d\n", i)); ret = -(i + 1); break; } return ret; }