From 74515e9b0909ad880ff059dccb9d2856b9b42621 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 20 Nov 1999 09:40:28 +0000 Subject: [PATCH] Back out the botched attempt to update to gnu grep 2.3 (lots of history was lost). Restore original version to try and avoid breaking the build while David O'brien does a proper set of imports and merges. Requested by: obrien --- gnu/usr.bin/grep/Makefile | 45 +- gnu/usr.bin/grep/PROJECTS | 15 + gnu/usr.bin/grep/README | 28 + gnu/usr.bin/grep/config.h | 188 -- gnu/usr.bin/grep/dfa.c | 2550 ++++++++++++++++++++++++++ gnu/usr.bin/grep/dfa.h | 360 ++++ gnu/usr.bin/grep/doc/Makefile | 11 - gnu/usr.bin/grep/getopt.c | 731 ++++++++ gnu/usr.bin/grep/getopt.h | 129 ++ gnu/usr.bin/grep/getpagesize.h | 42 + gnu/usr.bin/grep/grep.1 | 410 +++++ gnu/usr.bin/grep/grep.c | 1103 +++++++++++ gnu/usr.bin/grep/grep.h | 53 + gnu/usr.bin/grep/kwset.c | 807 ++++++++ gnu/usr.bin/grep/kwset.h | 69 + gnu/usr.bin/grep/obstack.c | 454 +++++ gnu/usr.bin/grep/obstack.h | 484 +++++ gnu/usr.bin/grep/search.c | 481 +++++ gnu/usr.bin/grep/tests/check.sh | 24 + gnu/usr.bin/grep/tests/scriptgen.awk | 10 + gnu/usr.bin/grep/tests/spencer.tests | 122 ++ 21 files changed, 7886 insertions(+), 230 deletions(-) create mode 100644 gnu/usr.bin/grep/PROJECTS create mode 100644 gnu/usr.bin/grep/README delete mode 100644 gnu/usr.bin/grep/config.h create mode 100644 gnu/usr.bin/grep/dfa.c create mode 100644 gnu/usr.bin/grep/dfa.h delete mode 100644 gnu/usr.bin/grep/doc/Makefile create mode 100644 gnu/usr.bin/grep/getopt.c create mode 100644 gnu/usr.bin/grep/getopt.h create mode 100644 gnu/usr.bin/grep/getpagesize.h create mode 100644 gnu/usr.bin/grep/grep.1 create mode 100644 gnu/usr.bin/grep/grep.c create mode 100644 gnu/usr.bin/grep/grep.h create mode 100644 gnu/usr.bin/grep/kwset.c create mode 100644 gnu/usr.bin/grep/kwset.h create mode 100644 gnu/usr.bin/grep/obstack.c create mode 100644 gnu/usr.bin/grep/obstack.h create mode 100644 gnu/usr.bin/grep/search.c create mode 100644 gnu/usr.bin/grep/tests/check.sh create mode 100644 gnu/usr.bin/grep/tests/scriptgen.awk create mode 100644 gnu/usr.bin/grep/tests/spencer.tests diff --git a/gnu/usr.bin/grep/Makefile b/gnu/usr.bin/grep/Makefile index 1b14fc3a0f07..0502d670f8c2 100644 --- a/gnu/usr.bin/grep/Makefile +++ b/gnu/usr.bin/grep/Makefile @@ -1,15 +1,15 @@ # $FreeBSD$ -GREP_LIBZ=YES +MAINTAINER= wosch -GREPDIR=${.CURDIR}/../../../contrib/grep - -.PATH: ${GREPDIR}/src ${GREPDIR}/doc +GREP_LIBZ= YES +GREP_FTS= YES PROG= grep -SRCS= dfa.c getopt.c getopt1.c grep.c kwset.c obstack.c \ - savedir.c search.c stpcpy.c -CFLAGS+=-I${.CURDIR} -DHAVE_CONFIG_H +SRCS= dfa.c grep.c getopt.c kwset.c obstack.c search.c +CFLAGS+=-DGREP -DHAVE_STRING_H=1 -DHAVE_SYS_PARAM_H=1 -DHAVE_UNISTD_H=1 \ + -DHAVE_GETPAGESIZE=1 -DHAVE_MEMCHR=1 -DHAVE_STRERROR=1 \ + -DHAVE_VALLOC=1 -DHAVE_WORKING_MMAP=1 LINKS+= ${BINDIR}/grep ${BINDIR}/egrep \ ${BINDIR}/grep ${BINDIR}/fgrep @@ -21,34 +21,17 @@ LDADD+= -lgnuregex .if defined(GREP_LIBZ) && !empty(GREP_LIBZ) LDADD+= -lz DPADD+= ${LIBZ} -CFLAGS+=-DHAVE_LIBZ=1 +CFLAGS+= -DHAVE_LIBZ=1 LINKS+= ${BINDIR}/grep ${BINDIR}/zgrep \ ${BINDIR}/grep ${BINDIR}/zegrep \ - ${BINDIR}/grep ${BINDIR}/zfgrep -MLINKS+=grep.1 zgrep.1 grep.1 zegrep.1 grep.1 zfgrep.1 + ${BINDIR}/grep ${BINDIR}/zfgrep +MLINKS+= grep.1 zgrep.1 grep.1 zegrep.1 grep.1 zfgrep.1 +.endif +.if defined(GREP_FTS) && !empty(GREP_FTS) +CFLAGS+= -DHAVE_FTS=1 .endif -SUBDIR+=doc - check: all - @failed=0; total=0; \ - for tst in ${TESTS}; do \ - total=$$(($$total+1)); \ - if GREP=${.OBJDIR}/${PROG} srcdir=${GREPDIR}/tests \ - ${GREPDIR}/tests/$$tst; then \ - echo "PASS: $$tst"; \ - else \ - failed=$$(($$failed+1)); \ - echo "FAIL: $$tst"; \ - fi; \ - done; \ - if [ "$$failed" -eq 0 ]; then \ - echo "All $$total tests passed"; \ - else \ - echo "$$failed of $$total tests failed"; \ - fi - -TESTS= warning.sh khadafy.sh spencer1.sh bre.sh ere.sh \ - status.sh empty.sh options.sh + sh ${.CURDIR}/tests/check.sh ${.CURDIR}/tests .include diff --git a/gnu/usr.bin/grep/PROJECTS b/gnu/usr.bin/grep/PROJECTS new file mode 100644 index 000000000000..67e9a2aad6ab --- /dev/null +++ b/gnu/usr.bin/grep/PROJECTS @@ -0,0 +1,15 @@ +Write Texinfo documentation for grep. The manual page would be a good +place to start, but Info documents are also supposed to contain a +tutorial and examples. + +Fix the DFA matcher to never use exponential space. (Fortunately, these +cases are rare.) + +Improve the performance of the regex backtracking matcher. This matcher +is agonizingly slow, and is responsible for grep sometimes being slower +than Unix grep when backreferences are used. + +Provide support for the Posix [= =] and [. .] constructs. This is +difficult because it requires locale-dependent details of the character +set and collating sequence, but Posix does not standardize any method +for accessing this information! diff --git a/gnu/usr.bin/grep/README b/gnu/usr.bin/grep/README new file mode 100644 index 000000000000..bc34a859063d --- /dev/null +++ b/gnu/usr.bin/grep/README @@ -0,0 +1,28 @@ +This is GNU grep 2.0, the "fastest grep in the west" (we hope). All +bugs reported in previous releases have been fixed. Many exciting new +bugs have probably been introduced in this major revision. + +GNU grep is provided "as is" with no warranty. The exact terms +under which you may use and (re)distribute this program are detailed +in the GNU General Public License, in the file COPYING. + +GNU grep is based on a fast lazy-state deterministic matcher (about +twice as fast as stock Unix egrep) hybridized with a Boyer-Moore-Gosper +search for a fixed string that eliminates impossible text from being +considered by the full regexp matcher without necessarily having to +look at every character. The result is typically many times faster +than Unix grep or egrep. (Regular expressions containing backreferencing +will run more slowly, however.) + +See the file AUTHORS for a list of authors and other contributors. + +See the file INSTALL for compilation and installation instructions. + +See the file MANIFEST for a list of files in this distribution. + +See the file NEWS for a description of major changes in this release. + +See the file PROJECTS if you want to be mentioned in AUTHORS. + +Send bug reports to bug-gnu-utils@prep.ai.mit.edu. Be sure to +include the word "grep" in your Subject: header field. diff --git a/gnu/usr.bin/grep/config.h b/gnu/usr.bin/grep/config.h deleted file mode 100644 index 415df26b1997..000000000000 --- a/gnu/usr.bin/grep/config.h +++ /dev/null @@ -1,188 +0,0 @@ -/* $FreeBSD$ */ - -/* config.h. Generated automatically by configure. */ -/* config.hin. Generated automatically from configure.in by autoheader. */ - -/* Define if using alloca.c. */ -/* #undef C_ALLOCA */ - -/* Define if the closedir function returns void instead of int. */ -/* #undef CLOSEDIR_VOID */ - -/* Define to empty if the keyword does not work. */ -/* #undef const */ - -/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. - This function is required for alloca.c support on those systems. */ -/* #undef CRAY_STACKSEG_END */ - -/* Define if you have alloca, as a function or macro. */ -#define HAVE_ALLOCA 1 - -/* Define if you have and it should be used (not on Ultrix). */ -/* #undef HAVE_ALLOCA_H */ - -/* Define if you have a working `mmap' system call. */ -#define HAVE_MMAP 1 - -/* Define as __inline if that's what the C compiler calls it. */ -/* #undef inline */ - -/* Define to `long' if doesn't define. */ -/* #undef off_t */ - -/* Define to `unsigned' if doesn't define. */ -/* #undef size_t */ - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at run-time. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown - */ -/* #undef STACK_DIRECTION */ - -/* Define if the `S_IS*' macros in do not work properly. */ -/* #undef STAT_MACROS_BROKEN */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to use grep's error-checking malloc in the kwset routines. */ -#define GREP 1 - -/* Package name. */ -#define PACKAGE "grep" - -/* Version number. */ -#define VERSION "2.3" - -/* Hack for Visual C++ suggested by irox. */ -/* #undef alloca */ - -/* #undef HAVE_STPCPY */ - -/* #undef ENABLE_NLS */ - -/* #undef HAVE_CATGETS */ - -/* #undef HAVE_GETTEXT */ - -#define HAVE_LC_MESSAGES 1 - -/* - * DOS specific - */ -/* #undef HAVE_DOS_FILE_NAMES */ - -/* Define if you have the __argz_count function. */ -/* #undef HAVE___ARGZ_COUNT */ - -/* Define if you have the __argz_next function. */ -/* #undef HAVE___ARGZ_NEXT */ - -/* Define if you have the __argz_stringify function. */ -/* #undef HAVE___ARGZ_STRINGIFY */ - -/* Define if you have the btowc function. */ -/* #undef HAVE_BTOWC */ - -/* Define if you have the dcgettext function. */ -/* #undef HAVE_DCGETTEXT */ - -/* Define if you have the getcwd function. */ -#define HAVE_GETCWD 1 - -/* Define if you have the getpagesize function. */ -#define HAVE_GETPAGESIZE 1 - -/* Define if you have the isascii function. */ -#define HAVE_ISASCII 1 - -/* Define if you have the memchr function. */ -#define HAVE_MEMCHR 1 - -/* Define if you have the munmap function. */ -#define HAVE_MUNMAP 1 - -/* Define if you have the putenv function. */ -#define HAVE_PUTENV 1 - -/* Define if you have the setenv function. */ -#define HAVE_SETENV 1 - -/* Define if you have the setlocale function. */ -#define HAVE_SETLOCALE 1 - -/* Define if you have the setmode function. */ -#define HAVE_SETMODE 1 - -/* Define if you have the stpcpy function. */ -/* #undef HAVE_STPCPY */ - -/* Define if you have the strcasecmp function. */ -#define HAVE_STRCASECMP 1 - -/* Define if you have the strchr function. */ -#define HAVE_STRCHR 1 - -/* Define if you have the strdup function. */ -#define HAVE_STRDUP 1 - -/* Define if you have the strerror function. */ -#define HAVE_STRERROR 1 - -/* Define if you have the header file. */ -/* #undef HAVE_ARGZ_H */ - -/* Define if you have the header file. */ -#define HAVE_DIRENT_H 1 - -/* Define if you have the header file. */ -/* #undef HAVE_LIBINTL_H */ - -/* Define if you have the header file. */ -#define HAVE_LIMITS_H 1 - -/* Define if you have the header file. */ -#define HAVE_LOCALE_H 1 - -/* Define if you have the header file. */ -/* #undef HAVE_MALLOC_H */ - -/* Define if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define if you have the header file. */ -/* #undef HAVE_NDIR_H */ - -/* Define if you have the header file. */ -#define HAVE_NL_TYPES_H 1 - -/* Define if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define if you have the header file. */ -/* #undef HAVE_SYS_DIR_H */ - -/* Define if you have the header file. */ -/* #undef HAVE_SYS_NDIR_H */ - -/* Define if you have the header file. */ -#define HAVE_SYS_PARAM_H 1 - -/* Define if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define if you have the header file. */ -/* #undef HAVE_WCHAR_H */ - -/* Define if you have the header file. */ -/* #undef HAVE_WCTYPE_H */ - -/* Define if you have the i library (-li). */ -/* #undef HAVE_LIBI */ diff --git a/gnu/usr.bin/grep/dfa.c b/gnu/usr.bin/grep/dfa.c new file mode 100644 index 000000000000..4b9eb2eda08a --- /dev/null +++ b/gnu/usr.bin/grep/dfa.c @@ -0,0 +1,2550 @@ +/* dfa.c - deterministic extended regexp routines for GNU + Copyright (C) 1988 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written June, 1988 by Mike Haertel + Modified July, 1988 by Arthur David Olson to assist BMG speedups */ + +#include +#include +#include + +#ifdef STDC_HEADERS +#include +#else +#include +extern char *calloc(), *malloc(), *realloc(); +extern void free(); +#endif + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#undef index +#define index strchr +#else +#include +#endif + +#ifndef isgraph +#define isgraph(C) (isprint((unsigned char)C) && !isspace((unsigned char)C)) +#endif + +#define ISALPHA(C) isalpha((unsigned char)C) +#define ISUPPER(C) isupper((unsigned char)C) +#define ISLOWER(C) islower((unsigned char)C) +#define ISDIGIT(C) isdigit((unsigned char)C) +#define ISXDIGIT(C) isxdigit((unsigned char)C) +#define ISSPACE(C) isspace((unsigned char)C) +#define ISPUNCT(C) ispunct((unsigned char)C) +#define ISALNUM(C) isalnum((unsigned char)C) +#define ISPRINT(C) isprint((unsigned char)C) +#define ISGRAPH(C) isgraph((unsigned char)C) +#define ISCNTRL(C) iscntrl((unsigned char)C) + +#include "dfa.h" +#include + +#if __STDC__ +typedef void *ptr_t; +#else +typedef char *ptr_t; +#endif + +static void dfamust(); + +#ifdef __FreeBSD__ +static int collate_range_cmp (a, b) + int a, b; +{ + int r; + static char s[2][2]; + + if ((unsigned char)a == (unsigned char)b) + return 0; + s[0][0] = a; + s[1][0] = b; + if ((r = strcoll(s[0], s[1])) == 0) + r = (unsigned char)a - (unsigned char)b; + return r; +} +#endif + +static ptr_t +xcalloc(n, s) + int n; + size_t s; +{ + ptr_t r = calloc(n, s); + + if (!r) + dfaerror("Memory exhausted"); + return r; +} + +static ptr_t +xmalloc(n) + size_t n; +{ + ptr_t r = malloc(n); + + assert(n != 0); + if (!r) + dfaerror("Memory exhausted"); + return r; +} + +static ptr_t +xrealloc(p, n) + ptr_t p; + size_t n; +{ + ptr_t r = realloc(p, n); + + assert(n != 0); + if (!r) + dfaerror("Memory exhausted"); + return r; +} + +#define CALLOC(p, t, n) ((p) = (t *) xcalloc((n), sizeof (t))) +#define MALLOC(p, t, n) ((p) = (t *) xmalloc((n) * sizeof (t))) +#define REALLOC(p, t, n) ((p) = (t *) xrealloc((ptr_t) (p), (n) * sizeof (t))) + +/* Reallocate an array of type t if nalloc is too small for index. */ +#define REALLOC_IF_NECESSARY(p, t, nalloc, index) \ + if ((index) >= (nalloc)) \ + { \ + while ((index) >= (nalloc)) \ + (nalloc) *= 2; \ + REALLOC(p, t, nalloc); \ + } + +#ifdef DEBUG + +static void +prtok(t) + token t; +{ + char *s; + + if (t < 0) + fprintf(stderr, "END"); + else if (t < NOTCHAR) + fprintf(stderr, "%c", t); + else + { + switch (t) + { + case EMPTY: s = "EMPTY"; break; + case BACKREF: s = "BACKREF"; break; + case BEGLINE: s = "BEGLINE"; break; + case ENDLINE: s = "ENDLINE"; break; + case BEGWORD: s = "BEGWORD"; break; + case ENDWORD: s = "ENDWORD"; break; + case LIMWORD: s = "LIMWORD"; break; + case NOTLIMWORD: s = "NOTLIMWORD"; break; + case QMARK: s = "QMARK"; break; + case STAR: s = "STAR"; break; + case PLUS: s = "PLUS"; break; + case CAT: s = "CAT"; break; + case OR: s = "OR"; break; + case ORTOP: s = "ORTOP"; break; + case LPAREN: s = "LPAREN"; break; + case RPAREN: s = "RPAREN"; break; + default: s = "CSET"; break; + } + fprintf(stderr, "%s", s); + } +} +#endif /* DEBUG */ + +/* Stuff pertaining to charclasses. */ + +static int +tstbit(b, c) + int b; + charclass c; +{ + return c[b / INTBITS] & 1 << b % INTBITS; +} + +static void +setbit(b, c) + int b; + charclass c; +{ + c[b / INTBITS] |= 1 << b % INTBITS; +} + +static void +clrbit(b, c) + int b; + charclass c; +{ + c[b / INTBITS] &= ~(1 << b % INTBITS); +} + +static void +copyset(src, dst) + charclass src; + charclass dst; +{ + int i; + + for (i = 0; i < CHARCLASS_INTS; ++i) + dst[i] = src[i]; +} + +static void +zeroset(s) + charclass s; +{ + int i; + + for (i = 0; i < CHARCLASS_INTS; ++i) + s[i] = 0; +} + +static void +notset(s) + charclass s; +{ + int i; + + for (i = 0; i < CHARCLASS_INTS; ++i) + s[i] = ~s[i]; +} + +static int +equal(s1, s2) + charclass s1; + charclass s2; +{ + int i; + + for (i = 0; i < CHARCLASS_INTS; ++i) + if (s1[i] != s2[i]) + return 0; + return 1; +} + +/* A pointer to the current dfa is kept here during parsing. */ +static struct dfa *dfa; + +/* Find the index of charclass s in dfa->charclasses, or allocate a new charclass. */ +static int +charclass_index(s) + charclass s; +{ + int i; + + for (i = 0; i < dfa->cindex; ++i) + if (equal(s, dfa->charclasses[i])) + return i; + REALLOC_IF_NECESSARY(dfa->charclasses, charclass, dfa->calloc, dfa->cindex); + ++dfa->cindex; + copyset(s, dfa->charclasses[i]); + return i; +} + +/* Syntax bits controlling the behavior of the lexical analyzer. */ +static int syntax_bits, syntax_bits_set; + +/* Flag for case-folding letters into sets. */ +static int case_fold; + +/* Entry point to set syntax options. */ +void +dfasyntax(bits, fold) + int bits; + int fold; +{ + syntax_bits_set = 1; + syntax_bits = bits; + case_fold = fold; +} + +/* Lexical analyzer. All the dross that deals with the obnoxious + GNU Regex syntax bits is located here. The poor, suffering + reader is referred to the GNU Regex documentation for the + meaning of the @#%!@#%^!@ syntax bits. */ + +static char *lexstart; /* Pointer to beginning of input string. */ +static char *lexptr; /* Pointer to next input character. */ +static lexleft; /* Number of characters remaining. */ +static token lasttok; /* Previous token returned; initially END. */ +static int laststart; /* True if we're separated from beginning or (, | + only by zero-width characters. */ +static int parens; /* Count of outstanding left parens. */ +static int minrep, maxrep; /* Repeat counts for {m,n}. */ + +/* Note that characters become unsigned here. */ +#define FETCH(c, eoferr) \ + { \ + if (! lexleft) \ + if (eoferr != 0) \ + dfaerror(eoferr); \ + else \ + return END; \ + (c) = (unsigned char) *lexptr++; \ + --lexleft; \ + } + +#define FUNC(F, P) static int F(c) int c; { return P(c); } + +FUNC(is_alpha, ISALPHA) +FUNC(is_upper, ISUPPER) +FUNC(is_lower, ISLOWER) +FUNC(is_digit, ISDIGIT) +FUNC(is_xdigit, ISXDIGIT) +FUNC(is_space, ISSPACE) +FUNC(is_punct, ISPUNCT) +FUNC(is_alnum, ISALNUM) +FUNC(is_print, ISPRINT) +FUNC(is_graph, ISGRAPH) +FUNC(is_cntrl, ISCNTRL) + +/* The following list maps the names of the Posix named character classes + to predicate functions that determine whether a given character is in + the class. The leading [ has already been eaten by the lexical analyzer. */ +static struct { + char *name; + int (*pred)(); +} prednames[] = { + ":alpha:]", is_alpha, + ":upper:]", is_upper, + ":lower:]", is_lower, + ":digit:]", is_digit, + ":xdigit:]", is_xdigit, + ":space:]", is_space, + ":punct:]", is_punct, + ":alnum:]", is_alnum, + ":print:]", is_print, + ":graph:]", is_graph, + ":cntrl:]", is_cntrl, + 0 +}; + +static int +looking_at(s) + char *s; +{ + int len; + + len = strlen(s); + if (lexleft < len) + return 0; + return strncmp(s, lexptr, len) == 0; +} + +static token +lex() +{ + token c, c1, c2; + int backslash = 0, invert; + charclass ccl; + int i; + + /* Basic plan: We fetch a character. If it's a backslash, + we set the backslash flag and go through the loop again. + On the plus side, this avoids having a duplicate of the + main switch inside the backslash case. On the minus side, + it means that just about every case begins with + "if (backslash) ...". */ + for (i = 0; i < 2; ++i) + { + FETCH(c, 0); + switch (c) + { + case '\\': + if (backslash) + goto normal_char; + if (lexleft == 0) + dfaerror("Unfinished \\ escape"); + backslash = 1; + break; + + case '^': + if (backslash) + goto normal_char; + if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS + || lasttok == END + || lasttok == LPAREN + || lasttok == OR) + return lasttok = BEGLINE; + goto normal_char; + + case '$': + if (backslash) + goto normal_char; + if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS + || lexleft == 0 + || (syntax_bits & RE_NO_BK_PARENS + ? lexleft > 0 && *lexptr == ')' + : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == ')') + || (syntax_bits & RE_NO_BK_VBAR + ? lexleft > 0 && *lexptr == '|' + : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == '|') + || ((syntax_bits & RE_NEWLINE_ALT) + && lexleft > 0 && *lexptr == '\n')) + return lasttok = ENDLINE; + goto normal_char; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (backslash && !(syntax_bits & RE_NO_BK_REFS)) + { + laststart = 0; + return lasttok = BACKREF; + } + goto normal_char; + + case '<': + if (backslash) + return lasttok = BEGWORD; + goto normal_char; + + case '>': + if (backslash) + return lasttok = ENDWORD; + goto normal_char; + + case 'b': + if (backslash) + return lasttok = LIMWORD; + goto normal_char; + + case 'B': + if (backslash) + return lasttok = NOTLIMWORD; + goto normal_char; + + case '?': + if (syntax_bits & RE_LIMITED_OPS) + goto normal_char; + if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0)) + goto normal_char; + if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart) + goto normal_char; + return lasttok = QMARK; + + case '*': + if (backslash) + goto normal_char; + if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart) + goto normal_char; + return lasttok = STAR; + + case '+': + if (syntax_bits & RE_LIMITED_OPS) + goto normal_char; + if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0)) + goto normal_char; + if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart) + goto normal_char; + return lasttok = PLUS; + + case '{': + if (!(syntax_bits & RE_INTERVALS)) + goto normal_char; + if (backslash != ((syntax_bits & RE_NO_BK_BRACES) == 0)) + goto normal_char; + minrep = maxrep = 0; + /* Cases: + {M} - exact count + {M,} - minimum count, maximum is infinity + {,M} - 0 through M + {M,N} - M through N */ + FETCH(c, "unfinished repeat count"); + if (ISDIGIT(c)) + { + minrep = c - '0'; + for (;;) + { + FETCH(c, "unfinished repeat count"); + if (!ISDIGIT(c)) + break; + minrep = 10 * minrep + c - '0'; + } + } + else if (c != ',') + dfaerror("malformed repeat count"); + if (c == ',') + for (;;) + { + FETCH(c, "unfinished repeat count"); + if (!ISDIGIT(c)) + break; + maxrep = 10 * maxrep + c - '0'; + } + else + maxrep = minrep; + if (!(syntax_bits & RE_NO_BK_BRACES)) + { + if (c != '\\') + dfaerror("malformed repeat count"); + FETCH(c, "unfinished repeat count"); + } + if (c != '}') + dfaerror("malformed repeat count"); + laststart = 0; + return lasttok = REPMN; + + case '|': + if (syntax_bits & RE_LIMITED_OPS) + goto normal_char; + if (backslash != ((syntax_bits & RE_NO_BK_VBAR) == 0)) + goto normal_char; + laststart = 1; + return lasttok = OR; + + case '\n': + if (syntax_bits & RE_LIMITED_OPS + || backslash + || !(syntax_bits & RE_NEWLINE_ALT)) + goto normal_char; + laststart = 1; + return lasttok = OR; + + case '(': + if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0)) + goto normal_char; + ++parens; + laststart = 1; + return lasttok = LPAREN; + + case ')': + if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0)) + goto normal_char; + if (parens == 0 && syntax_bits & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + --parens; + laststart = 0; + return lasttok = RPAREN; + + case '.': + if (backslash) + goto normal_char; + zeroset(ccl); + notset(ccl); + if (!(syntax_bits & RE_DOT_NEWLINE)) + clrbit('\n', ccl); + if (syntax_bits & RE_DOT_NOT_NULL) + clrbit('\0', ccl); + laststart = 0; + return lasttok = CSET + charclass_index(ccl); + + case 'w': + case 'W': + if (!backslash) + goto normal_char; + zeroset(ccl); + for (c2 = 0; c2 < NOTCHAR; ++c2) + if (ISALNUM(c2)) + setbit(c2, ccl); + if (c == 'W') + notset(ccl); + laststart = 0; + return lasttok = CSET + charclass_index(ccl); + + case '[': + if (backslash) + goto normal_char; + zeroset(ccl); + FETCH(c, "Unbalanced ["); + if (c == '^') + { + FETCH(c, "Unbalanced ["); + invert = 1; + } + else + invert = 0; + do + { + /* Nobody ever said this had to be fast. :-) + Note that if we're looking at some other [:...:] + construct, we just treat it as a bunch of ordinary + characters. We can do this because we assume + regex has checked for syntax errors before + dfa is ever called. */ + if (c == '[' && (syntax_bits & RE_CHAR_CLASSES)) + for (c1 = 0; prednames[c1].name; ++c1) + if (looking_at(prednames[c1].name)) + { + for (c2 = 0; c2 < NOTCHAR; ++c2) + if ((*prednames[c1].pred)(c2)) + setbit(c2, ccl); + lexptr += strlen(prednames[c1].name); + lexleft -= strlen(prednames[c1].name); + FETCH(c1, "Unbalanced ["); + goto skip; + } + if (c == '\\' && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS)) + FETCH(c, "Unbalanced ["); + FETCH(c1, "Unbalanced ["); + if (c1 == '-') + { + FETCH(c2, "Unbalanced ["); + if (c2 == ']') + { + /* In the case [x-], the - is an ordinary hyphen, + which is left in c1, the lookahead character. */ + --lexptr; + ++lexleft; + c2 = c; + } + else + { + if (c2 == '\\' + && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS)) + FETCH(c2, "Unbalanced ["); + FETCH(c1, "Unbalanced ["); + } + } + else + c2 = c; +#ifdef __FreeBSD__ + { token c3; + + if (collate_range_cmp(c, c2) > 0) { + FETCH(c2, "Invalid range"); + goto skip; + } + + for (c3 = 0; c3 < NOTCHAR; ++c3) + if ( collate_range_cmp(c, c3) <= 0 + && collate_range_cmp(c3, c2) <= 0 + ) { + setbit(c3, ccl); + if (case_fold) + if (ISUPPER(c3)) + setbit(tolower((unsigned char)c3), ccl); + else if (ISLOWER(c3)) + setbit(toupper((unsigned char)c3), ccl); + } + } +#else + while (c <= c2) + { + setbit(c, ccl); + if (case_fold) + if (ISUPPER(c)) + setbit(tolower((unsigned char)c), ccl); + else if (ISLOWER(c)) + setbit(toupper((unsigned char)c), ccl); + ++c; + } +#endif + skip: + ; + } + while ((c = c1) != ']'); + if (invert) + { + notset(ccl); + if (syntax_bits & RE_HAT_LISTS_NOT_NEWLINE) + clrbit('\n', ccl); + } + laststart = 0; + return lasttok = CSET + charclass_index(ccl); + + default: + normal_char: + laststart = 0; + if (case_fold && ISALPHA(c)) + { + zeroset(ccl); + setbit(c, ccl); + if (isupper((unsigned char)c)) + setbit(tolower((unsigned char)c), ccl); + else + setbit(toupper((unsigned char)c), ccl); + return lasttok = CSET + charclass_index(ccl); + } + return c; + } + } + + /* The above loop should consume at most a backslash + and some other character. */ + abort(); +} + +/* Recursive descent parser for regular expressions. */ + +static token tok; /* Lookahead token. */ +static depth; /* Current depth of a hypothetical stack + holding deferred productions. This is + used to determine the depth that will be + required of the real stack later on in + dfaanalyze(). */ + +/* Add the given token to the parse tree, maintaining the depth count and + updating the maximum depth if necessary. */ +static void +addtok(t) + token t; +{ + REALLOC_IF_NECESSARY(dfa->tokens, token, dfa->talloc, dfa->tindex); + dfa->tokens[dfa->tindex++] = t; + + switch (t) + { + case QMARK: + case STAR: + case PLUS: + break; + + case CAT: + case OR: + case ORTOP: + --depth; + break; + + default: + ++dfa->nleaves; + case EMPTY: + ++depth; + break; + } + if (depth > dfa->depth) + dfa->depth = depth; +} + +/* The grammar understood by the parser is as follows. + + regexp: + regexp OR branch + branch + + branch: + branch closure + closure + + closure: + closure QMARK + closure STAR + closure PLUS + atom + + atom: + + CSET + BACKREF + BEGLINE + ENDLINE + BEGWORD + ENDWORD + LIMWORD + NOTLIMWORD + + + The parser builds a parse tree in postfix form in an array of tokens. */ + +#if __STDC__ +static void regexp(int); +#else +static void regexp(); +#endif + +static void +atom() +{ + if ((tok >= 0 && tok < NOTCHAR) || tok >= CSET || tok == BACKREF + || tok == BEGLINE || tok == ENDLINE || tok == BEGWORD + || tok == ENDWORD || tok == LIMWORD || tok == NOTLIMWORD) + { + addtok(tok); + tok = lex(); + } + else if (tok == LPAREN) + { + tok = lex(); + regexp(0); + if (tok != RPAREN) + dfaerror("Unbalanced ("); + tok = lex(); + } + else + addtok(EMPTY); +} + +/* Return the number of tokens in the given subexpression. */ +static int +nsubtoks(tindex) +{ + int ntoks1; + + switch (dfa->tokens[tindex - 1]) + { + default: + return 1; + case QMARK: + case STAR: + case PLUS: + return 1 + nsubtoks(tindex - 1); + case CAT: + case OR: + case ORTOP: + ntoks1 = nsubtoks(tindex - 1); + return 1 + ntoks1 + nsubtoks(tindex - 1 - ntoks1); + } +} + +/* Copy the given subexpression to the top of the tree. */ +static void +copytoks(tindex, ntokens) + int tindex, ntokens; +{ + int i; + + for (i = 0; i < ntokens; ++i) + addtok(dfa->tokens[tindex + i]); +} + +static void +closure() +{ + int tindex, ntokens, i; + + atom(); + while (tok == QMARK || tok == STAR || tok == PLUS || tok == REPMN) + if (tok == REPMN) + { + ntokens = nsubtoks(dfa->tindex); + tindex = dfa->tindex - ntokens; + if (maxrep == 0) + addtok(PLUS); + if (minrep == 0) + addtok(QMARK); + for (i = 1; i < minrep; ++i) + { + copytoks(tindex, ntokens); + addtok(CAT); + } + for (; i < maxrep; ++i) + { + copytoks(tindex, ntokens); + addtok(QMARK); + addtok(CAT); + } + tok = lex(); + } + else + { + addtok(tok); + tok = lex(); + } +} + +static void +branch() +{ + closure(); + while (tok != RPAREN && tok != OR && tok >= 0) + { + closure(); + addtok(CAT); + } +} + +static void +regexp(toplevel) + int toplevel; +{ + branch(); + while (tok == OR) + { + tok = lex(); + branch(); + if (toplevel) + addtok(ORTOP); + else + addtok(OR); + } +} + +/* Main entry point for the parser. S is a string to be parsed, len is the + length of the string, so s can include NUL characters. D is a pointer to + the struct dfa to parse into. */ +void +dfaparse(s, len, d) + char *s; + size_t len; + struct dfa *d; + +{ + dfa = d; + lexstart = lexptr = s; + lexleft = len; + lasttok = END; + laststart = 1; + parens = 0; + + if (! syntax_bits_set) + dfaerror("No syntax specified"); + + tok = lex(); + depth = d->depth; + + regexp(1); + + if (tok != END) + dfaerror("Unbalanced )"); + + addtok(END - d->nregexps); + addtok(CAT); + + if (d->nregexps) + addtok(ORTOP); + + ++d->nregexps; +} + +/* Some primitives for operating on sets of positions. */ + +/* Copy one set to another; the destination must be large enough. */ +static void +copy(src, dst) + position_set *src; + position_set *dst; +{ + int i; + + for (i = 0; i < src->nelem; ++i) + dst->elems[i] = src->elems[i]; + dst->nelem = src->nelem; +} + +/* Insert a position in a set. Position sets are maintained in sorted + order according to index. If position already exists in the set with + the same index then their constraints are logically or'd together. + S->elems must point to an array large enough to hold the resulting set. */ +static void +insert(p, s) + position p; + position_set *s; +{ + int i; + position t1, t2; + + for (i = 0; i < s->nelem && p.index < s->elems[i].index; ++i) + ; + if (i < s->nelem && p.index == s->elems[i].index) + s->elems[i].constraint |= p.constraint; + else + { + t1 = p; + ++s->nelem; + while (i < s->nelem) + { + t2 = s->elems[i]; + s->elems[i++] = t1; + t1 = t2; + } + } +} + +/* Merge two sets of positions into a third. The result is exactly as if + the positions of both sets were inserted into an initially empty set. */ +static void +merge(s1, s2, m) + position_set *s1; + position_set *s2; + position_set *m; +{ + int i = 0, j = 0; + + m->nelem = 0; + while (i < s1->nelem && j < s2->nelem) + if (s1->elems[i].index > s2->elems[j].index) + m->elems[m->nelem++] = s1->elems[i++]; + else if (s1->elems[i].index < s2->elems[j].index) + m->elems[m->nelem++] = s2->elems[j++]; + else + { + m->elems[m->nelem] = s1->elems[i++]; + m->elems[m->nelem++].constraint |= s2->elems[j++].constraint; + } + while (i < s1->nelem) + m->elems[m->nelem++] = s1->elems[i++]; + while (j < s2->nelem) + m->elems[m->nelem++] = s2->elems[j++]; +} + +/* Delete a position from a set. */ +static void +delete(p, s) + position p; + position_set *s; +{ + int i; + + for (i = 0; i < s->nelem; ++i) + if (p.index == s->elems[i].index) + break; + if (i < s->nelem) + for (--s->nelem; i < s->nelem; ++i) + s->elems[i] = s->elems[i + 1]; +} + +/* Find the index of the state corresponding to the given position set with + the given preceding context, or create a new state if there is no such + state. Newline and letter tell whether we got here on a newline or + letter, respectively. */ +static int +state_index(d, s, newline, letter) + struct dfa *d; + position_set *s; + int newline; + int letter; +{ + int hash = 0; + int constraint; + int i, j; + + newline = newline ? 1 : 0; + letter = letter ? 1 : 0; + + for (i = 0; i < s->nelem; ++i) + hash ^= s->elems[i].index + s->elems[i].constraint; + + /* Try to find a state that exactly matches the proposed one. */ + for (i = 0; i < d->sindex; ++i) + { + if (hash != d->states[i].hash || s->nelem != d->states[i].elems.nelem + || newline != d->states[i].newline || letter != d->states[i].letter) + continue; + for (j = 0; j < s->nelem; ++j) + if (s->elems[j].constraint + != d->states[i].elems.elems[j].constraint + || s->elems[j].index != d->states[i].elems.elems[j].index) + break; + if (j == s->nelem) + return i; + } + + /* We'll have to create a new state. */ + REALLOC_IF_NECESSARY(d->states, dfa_state, d->salloc, d->sindex); + d->states[i].hash = hash; + MALLOC(d->states[i].elems.elems, position, s->nelem); + copy(s, &d->states[i].elems); + d->states[i].newline = newline; + d->states[i].letter = letter; + d->states[i].backref = 0; + d->states[i].constraint = 0; + d->states[i].first_end = 0; + for (j = 0; j < s->nelem; ++j) + if (d->tokens[s->elems[j].index] < 0) + { + constraint = s->elems[j].constraint; + if (SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 0) + || SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 1) + || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 0) + || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 1)) + d->states[i].constraint |= constraint; + if (! d->states[i].first_end) + d->states[i].first_end = d->tokens[s->elems[j].index]; + } + else if (d->tokens[s->elems[j].index] == BACKREF) + { + d->states[i].constraint = NO_CONSTRAINT; + d->states[i].backref = 1; + } + + ++d->sindex; + + return i; +} + +/* Find the epsilon closure of a set of positions. If any position of the set + contains a symbol that matches the empty string in some context, replace + that position with the elements of its follow labeled with an appropriate + constraint. Repeat exhaustively until no funny positions are left. + S->elems must be large enough to hold the result. */ +void +epsclosure(s, d) + position_set *s; + struct dfa *d; +{ + int i, j; + int *visited; + position p, old; + + MALLOC(visited, int, d->tindex); + for (i = 0; i < d->tindex; ++i) + visited[i] = 0; + + for (i = 0; i < s->nelem; ++i) + if (d->tokens[s->elems[i].index] >= NOTCHAR + && d->tokens[s->elems[i].index] != BACKREF + && d->tokens[s->elems[i].index] < CSET) + { + old = s->elems[i]; + p.constraint = old.constraint; + delete(s->elems[i], s); + if (visited[old.index]) + { + --i; + continue; + } + visited[old.index] = 1; + switch (d->tokens[old.index]) + { + case BEGLINE: + p.constraint &= BEGLINE_CONSTRAINT; + break; + case ENDLINE: + p.constraint &= ENDLINE_CONSTRAINT; + break; + case BEGWORD: + p.constraint &= BEGWORD_CONSTRAINT; + break; + case ENDWORD: + p.constraint &= ENDWORD_CONSTRAINT; + break; + case LIMWORD: + p.constraint &= LIMWORD_CONSTRAINT; + break; + case NOTLIMWORD: + p.constraint &= NOTLIMWORD_CONSTRAINT; + break; + default: + break; + } + for (j = 0; j < d->follows[old.index].nelem; ++j) + { + p.index = d->follows[old.index].elems[j].index; + insert(p, s); + } + /* Force rescan to start at the beginning. */ + i = -1; + } + + free(visited); +} + +/* Perform bottom-up analysis on the parse tree, computing various functions. + Note that at this point, we're pretending constructs like \< are real + characters rather than constraints on what can follow them. + + Nullable: A node is nullable if it is at the root of a regexp that can + match the empty string. + * EMPTY leaves are nullable. + * No other leaf is nullable. + * A QMARK or STAR node is nullable. + * A PLUS node is nullable if its argument is nullable. + * A CAT node is nullable if both its arguments are nullable. + * An OR node is nullable if either argument is nullable. + + Firstpos: The firstpos of a node is the set of positions (nonempty leaves) + that could correspond to the first character of a string matching the + regexp rooted at the given node. + * EMPTY leaves have empty firstpos. + * The firstpos of a nonempty leaf is that leaf itself. + * The firstpos of a QMARK, STAR, or PLUS node is the firstpos of its + argument. + * The firstpos of a CAT node is the firstpos of the left argument, union + the firstpos of the right if the left argument is nullable. + * The firstpos of an OR node is the union of firstpos of each argument. + + Lastpos: The lastpos of a node is the set of positions that could + correspond to the last character of a string matching the regexp at + the given node. + * EMPTY leaves have empty lastpos. + * The lastpos of a nonempty leaf is that leaf itself. + * The lastpos of a QMARK, STAR, or PLUS node is the lastpos of its + argument. + * The lastpos of a CAT node is the lastpos of its right argument, union + the lastpos of the left if the right argument is nullable. + * The lastpos of an OR node is the union of the lastpos of each argument. + + Follow: The follow of a position is the set of positions that could + correspond to the character following a character matching the node in + a string matching the regexp. At this point we consider special symbols + that match the empty string in some context to be just normal characters. + Later, if we find that a special symbol is in a follow set, we will + replace it with the elements of its follow, labeled with an appropriate + constraint. + * Every node in the firstpos of the argument of a STAR or PLUS node is in + the follow of every node in the lastpos. + * Every node in the firstpos of the second argument of a CAT node is in + the follow of every node in the lastpos of the first argument. + + Because of the postfix representation of the parse tree, the depth-first + analysis is conveniently done by a linear scan with the aid of a stack. + Sets are stored as arrays of the elements, obeying a stack-like allocation + scheme; the number of elements in each set deeper in the stack can be + used to determine the address of a particular set's array. */ +void +dfaanalyze(d, searchflag) + struct dfa *d; + int searchflag; +{ + int *nullable; /* Nullable stack. */ + int *nfirstpos; /* Element count stack for firstpos sets. */ + position *firstpos; /* Array where firstpos elements are stored. */ + int *nlastpos; /* Element count stack for lastpos sets. */ + position *lastpos; /* Array where lastpos elements are stored. */ + int *nalloc; /* Sizes of arrays allocated to follow sets. */ + position_set tmp; /* Temporary set for merging sets. */ + position_set merged; /* Result of merging sets. */ + int wants_newline; /* True if some position wants newline info. */ + int *o_nullable; + int *o_nfirst, *o_nlast; + position *o_firstpos, *o_lastpos; + int i, j; + position *pos; + +#ifdef DEBUG + fprintf(stderr, "dfaanalyze:\n"); + for (i = 0; i < d->tindex; ++i) + { + fprintf(stderr, " %d:", i); + prtok(d->tokens[i]); + } + putc('\n', stderr); +#endif + + d->searchflag = searchflag; + + MALLOC(nullable, int, d->depth); + o_nullable = nullable; + MALLOC(nfirstpos, int, d->depth); + o_nfirst = nfirstpos; + MALLOC(firstpos, position, d->nleaves); + o_firstpos = firstpos, firstpos += d->nleaves; + MALLOC(nlastpos, int, d->depth); + o_nlast = nlastpos; + MALLOC(lastpos, position, d->nleaves); + o_lastpos = lastpos, lastpos += d->nleaves; + MALLOC(nalloc, int, d->tindex); + for (i = 0; i < d->tindex; ++i) + nalloc[i] = 0; + MALLOC(merged.elems, position, d->nleaves); + + CALLOC(d->follows, position_set, d->tindex); + + for (i = 0; i < d->tindex; ++i) +#ifdef DEBUG + { /* Nonsyntactic #ifdef goo... */ +#endif + switch (d->tokens[i]) + { + case EMPTY: + /* The empty set is nullable. */ + *nullable++ = 1; + + /* The firstpos and lastpos of the empty leaf are both empty. */ + *nfirstpos++ = *nlastpos++ = 0; + break; + + case STAR: + case PLUS: + /* Every element in the firstpos of the argument is in the follow + of every element in the lastpos. */ + tmp.nelem = nfirstpos[-1]; + tmp.elems = firstpos; + pos = lastpos; + for (j = 0; j < nlastpos[-1]; ++j) + { + merge(&tmp, &d->follows[pos[j].index], &merged); + REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position, + nalloc[pos[j].index], merged.nelem - 1); + copy(&merged, &d->follows[pos[j].index]); + } + + case QMARK: + /* A QMARK or STAR node is automatically nullable. */ + if (d->tokens[i] != PLUS) + nullable[-1] = 1; + break; + + case CAT: + /* Every element in the firstpos of the second argument is in the + follow of every element in the lastpos of the first argument. */ + tmp.nelem = nfirstpos[-1]; + tmp.elems = firstpos; + pos = lastpos + nlastpos[-1]; + for (j = 0; j < nlastpos[-2]; ++j) + { + merge(&tmp, &d->follows[pos[j].index], &merged); + REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position, + nalloc[pos[j].index], merged.nelem - 1); + copy(&merged, &d->follows[pos[j].index]); + } + + /* The firstpos of a CAT node is the firstpos of the first argument, + union that of the second argument if the first is nullable. */ + if (nullable[-2]) + nfirstpos[-2] += nfirstpos[-1]; + else + firstpos += nfirstpos[-1]; + --nfirstpos; + + /* The lastpos of a CAT node is the lastpos of the second argument, + union that of the first argument if the second is nullable. */ + if (nullable[-1]) + nlastpos[-2] += nlastpos[-1]; + else + { + pos = lastpos + nlastpos[-2]; + for (j = nlastpos[-1] - 1; j >= 0; --j) + pos[j] = lastpos[j]; + lastpos += nlastpos[-2]; + nlastpos[-2] = nlastpos[-1]; + } + --nlastpos; + + /* A CAT node is nullable if both arguments are nullable. */ + nullable[-2] = nullable[-1] && nullable[-2]; + --nullable; + break; + + case OR: + case ORTOP: + /* The firstpos is the union of the firstpos of each argument. */ + nfirstpos[-2] += nfirstpos[-1]; + --nfirstpos; + + /* The lastpos is the union of the lastpos of each argument. */ + nlastpos[-2] += nlastpos[-1]; + --nlastpos; + + /* An OR node is nullable if either argument is nullable. */ + nullable[-2] = nullable[-1] || nullable[-2]; + --nullable; + break; + + default: + /* Anything else is a nonempty position. (Note that special + constructs like \< are treated as nonempty strings here; + an "epsilon closure" effectively makes them nullable later. + Backreferences have to get a real position so we can detect + transitions on them later. But they are nullable. */ + *nullable++ = d->tokens[i] == BACKREF; + + /* This position is in its own firstpos and lastpos. */ + *nfirstpos++ = *nlastpos++ = 1; + --firstpos, --lastpos; + firstpos->index = lastpos->index = i; + firstpos->constraint = lastpos->constraint = NO_CONSTRAINT; + + /* Allocate the follow set for this position. */ + nalloc[i] = 1; + MALLOC(d->follows[i].elems, position, nalloc[i]); + break; + } +#ifdef DEBUG + /* ... balance the above nonsyntactic #ifdef goo... */ + fprintf(stderr, "node %d:", i); + prtok(d->tokens[i]); + putc('\n', stderr); + fprintf(stderr, nullable[-1] ? " nullable: yes\n" : " nullable: no\n"); + fprintf(stderr, " firstpos:"); + for (j = nfirstpos[-1] - 1; j >= 0; --j) + { + fprintf(stderr, " %d:", firstpos[j].index); + prtok(d->tokens[firstpos[j].index]); + } + fprintf(stderr, "\n lastpos:"); + for (j = nlastpos[-1] - 1; j >= 0; --j) + { + fprintf(stderr, " %d:", lastpos[j].index); + prtok(d->tokens[lastpos[j].index]); + } + putc('\n', stderr); + } +#endif + + /* For each follow set that is the follow set of a real position, replace + it with its epsilon closure. */ + for (i = 0; i < d->tindex; ++i) + if (d->tokens[i] < NOTCHAR || d->tokens[i] == BACKREF + || d->tokens[i] >= CSET) + { +#ifdef DEBUG + fprintf(stderr, "follows(%d:", i); + prtok(d->tokens[i]); + fprintf(stderr, "):"); + for (j = d->follows[i].nelem - 1; j >= 0; --j) + { + fprintf(stderr, " %d:", d->follows[i].elems[j].index); + prtok(d->tokens[d->follows[i].elems[j].index]); + } + putc('\n', stderr); +#endif + copy(&d->follows[i], &merged); + epsclosure(&merged, d); + if (d->follows[i].nelem < merged.nelem) + REALLOC(d->follows[i].elems, position, merged.nelem); + copy(&merged, &d->follows[i]); + } + + /* Get the epsilon closure of the firstpos of the regexp. The result will + be the set of positions of state 0. */ + merged.nelem = 0; + for (i = 0; i < nfirstpos[-1]; ++i) + insert(firstpos[i], &merged); + epsclosure(&merged, d); + + /* Check if any of the positions of state 0 will want newline context. */ + wants_newline = 0; + for (i = 0; i < merged.nelem; ++i) + if (PREV_NEWLINE_DEPENDENT(merged.elems[i].constraint)) + wants_newline = 1; + + /* Build the initial state. */ + d->salloc = 1; + d->sindex = 0; + MALLOC(d->states, dfa_state, d->salloc); + state_index(d, &merged, wants_newline, 0); + + free(o_nullable); + free(o_nfirst); + free(o_firstpos); + free(o_nlast); + free(o_lastpos); + free(nalloc); + free(merged.elems); +} + +/* Find, for each character, the transition out of state s of d, and store + it in the appropriate slot of trans. + + We divide the positions of s into groups (positions can appear in more + than one group). Each group is labeled with a set of characters that + every position in the group matches (taking into account, if necessary, + preceding context information of s). For each group, find the union + of the its elements' follows. This set is the set of positions of the + new state. For each character in the group's label, set the transition + on this character to be to a state corresponding to the set's positions, + and its associated backward context information, if necessary. + + If we are building a searching matcher, we include the positions of state + 0 in every state. + + The collection of groups is constructed by building an equivalence-class + partition of the positions of s. + + For each position, find the set of characters C that it matches. Eliminate + any characters from C that fail on grounds of backward context. + + Search through the groups, looking for a group whose label L has nonempty + intersection with C. If L - C is nonempty, create a new group labeled + L - C and having the same positions as the current group, and set L to + the intersection of L and C. Insert the position in this group, set + C = C - L, and resume scanning. + + If after comparing with every group there are characters remaining in C, + create a new group labeled with the characters of C and insert this + position in that group. */ +void +dfastate(s, d, trans) + int s; + struct dfa *d; + int trans[]; +{ + position_set grps[NOTCHAR]; /* As many as will ever be needed. */ + charclass labels[NOTCHAR]; /* Labels corresponding to the groups. */ + int ngrps = 0; /* Number of groups actually used. */ + position pos; /* Current position being considered. */ + charclass matches; /* Set of matching characters. */ + int matchesf; /* True if matches is nonempty. */ + charclass intersect; /* Intersection with some label set. */ + int intersectf; /* True if intersect is nonempty. */ + charclass leftovers; /* Stuff in the label that didn't match. */ + int leftoversf; /* True if leftovers is nonempty. */ + static charclass letters; /* Set of characters considered letters. */ + static charclass newline; /* Set of characters that aren't newline. */ + position_set follows; /* Union of the follows of some group. */ + position_set tmp; /* Temporary space for merging sets. */ + int state; /* New state. */ + int wants_newline; /* New state wants to know newline context. */ + int state_newline; /* New state on a newline transition. */ + int wants_letter; /* New state wants to know letter context. */ + int state_letter; /* New state on a letter transition. */ + static initialized; /* Flag for static initialization. */ + int i, j, k; + + /* Initialize the set of letters, if necessary. */ + if (! initialized) + { + initialized = 1; + for (i = 0; i < NOTCHAR; ++i) + if (ISALNUM(i)) + setbit(i, letters); + setbit('\n', newline); + } + + zeroset(matches); + + for (i = 0; i < d->states[s].elems.nelem; ++i) + { + pos = d->states[s].elems.elems[i]; + if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR) + setbit(d->tokens[pos.index], matches); + else if (d->tokens[pos.index] >= CSET) + copyset(d->charclasses[d->tokens[pos.index] - CSET], matches); + else + continue; + + /* Some characters may need to be eliminated from matches because + they fail in the current context. */ + if (pos.constraint != 0xFF) + { + if (! MATCHES_NEWLINE_CONTEXT(pos.constraint, + d->states[s].newline, 1)) + clrbit('\n', matches); + if (! MATCHES_NEWLINE_CONTEXT(pos.constraint, + d->states[s].newline, 0)) + for (j = 0; j < CHARCLASS_INTS; ++j) + matches[j] &= newline[j]; + if (! MATCHES_LETTER_CONTEXT(pos.constraint, + d->states[s].letter, 1)) + for (j = 0; j < CHARCLASS_INTS; ++j) + matches[j] &= ~letters[j]; + if (! MATCHES_LETTER_CONTEXT(pos.constraint, + d->states[s].letter, 0)) + for (j = 0; j < CHARCLASS_INTS; ++j) + matches[j] &= letters[j]; + + /* If there are no characters left, there's no point in going on. */ + for (j = 0; j < CHARCLASS_INTS && !matches[j]; ++j) + ; + if (j == CHARCLASS_INTS) + continue; + } + + for (j = 0; j < ngrps; ++j) + { + /* If matches contains a single character only, and the current + group's label doesn't contain that character, go on to the + next group. */ + if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR + && !tstbit(d->tokens[pos.index], labels[j])) + continue; + + /* Check if this group's label has a nonempty intersection with + matches. */ + intersectf = 0; + for (k = 0; k < CHARCLASS_INTS; ++k) + (intersect[k] = matches[k] & labels[j][k]) ? intersectf = 1 : 0; + if (! intersectf) + continue; + + /* It does; now find the set differences both ways. */ + leftoversf = matchesf = 0; + for (k = 0; k < CHARCLASS_INTS; ++k) + { + /* Even an optimizing compiler can't know this for sure. */ + int match = matches[k], label = labels[j][k]; + + (leftovers[k] = ~match & label) ? leftoversf = 1 : 0; + (matches[k] = match & ~label) ? matchesf = 1 : 0; + } + + /* If there were leftovers, create a new group labeled with them. */ + if (leftoversf) + { + copyset(leftovers, labels[ngrps]); + copyset(intersect, labels[j]); + MALLOC(grps[ngrps].elems, position, d->nleaves); + copy(&grps[j], &grps[ngrps]); + ++ngrps; + } + + /* Put the position in the current group. Note that there is no + reason to call insert() here. */ + grps[j].elems[grps[j].nelem++] = pos; + + /* If every character matching the current position has been + accounted for, we're done. */ + if (! matchesf) + break; + } + + /* If we've passed the last group, and there are still characters + unaccounted for, then we'll have to create a new group. */ + if (j == ngrps) + { + copyset(matches, labels[ngrps]); + zeroset(matches); + MALLOC(grps[ngrps].elems, position, d->nleaves); + grps[ngrps].nelem = 1; + grps[ngrps].elems[0] = pos; + ++ngrps; + } + } + + MALLOC(follows.elems, position, d->nleaves); + MALLOC(tmp.elems, position, d->nleaves); + + /* If we are a searching matcher, the default transition is to a state + containing the positions of state 0, otherwise the default transition + is to fail miserably. */ + if (d->searchflag) + { + wants_newline = 0; + wants_letter = 0; + for (i = 0; i < d->states[0].elems.nelem; ++i) + { + if (PREV_NEWLINE_DEPENDENT(d->states[0].elems.elems[i].constraint)) + wants_newline = 1; + if (PREV_LETTER_DEPENDENT(d->states[0].elems.elems[i].constraint)) + wants_letter = 1; + } + copy(&d->states[0].elems, &follows); + state = state_index(d, &follows, 0, 0); + if (wants_newline) + state_newline = state_index(d, &follows, 1, 0); + else + state_newline = state; + if (wants_letter) + state_letter = state_index(d, &follows, 0, 1); + else + state_letter = state; + for (i = 0; i < NOTCHAR; ++i) + if (i == '\n') + trans[i] = state_newline; + else if (ISALNUM(i)) + trans[i] = state_letter; + else + trans[i] = state; + } + else + for (i = 0; i < NOTCHAR; ++i) + trans[i] = -1; + + for (i = 0; i < ngrps; ++i) + { + follows.nelem = 0; + + /* Find the union of the follows of the positions of the group. + This is a hideously inefficient loop. Fix it someday. */ + for (j = 0; j < grps[i].nelem; ++j) + for (k = 0; k < d->follows[grps[i].elems[j].index].nelem; ++k) + insert(d->follows[grps[i].elems[j].index].elems[k], &follows); + + /* If we are building a searching matcher, throw in the positions + of state 0 as well. */ + if (d->searchflag) + for (j = 0; j < d->states[0].elems.nelem; ++j) + insert(d->states[0].elems.elems[j], &follows); + + /* Find out if the new state will want any context information. */ + wants_newline = 0; + if (tstbit('\n', labels[i])) + for (j = 0; j < follows.nelem; ++j) + if (PREV_NEWLINE_DEPENDENT(follows.elems[j].constraint)) + wants_newline = 1; + + wants_letter = 0; + for (j = 0; j < CHARCLASS_INTS; ++j) + if (labels[i][j] & letters[j]) + break; + if (j < CHARCLASS_INTS) + for (j = 0; j < follows.nelem; ++j) + if (PREV_LETTER_DEPENDENT(follows.elems[j].constraint)) + wants_letter = 1; + + /* Find the state(s) corresponding to the union of the follows. */ + state = state_index(d, &follows, 0, 0); + if (wants_newline) + state_newline = state_index(d, &follows, 1, 0); + else + state_newline = state; + if (wants_letter) + state_letter = state_index(d, &follows, 0, 1); + else + state_letter = state; + + /* Set the transitions for each character in the current label. */ + for (j = 0; j < CHARCLASS_INTS; ++j) + for (k = 0; k < INTBITS; ++k) + if (labels[i][j] & 1 << k) + { + int c = j * INTBITS + k; + + if (c == '\n') + trans[c] = state_newline; + else if (ISALNUM(c)) + trans[c] = state_letter; + else if (c < NOTCHAR) + trans[c] = state; + } + } + + for (i = 0; i < ngrps; ++i) + free(grps[i].elems); + free(follows.elems); + free(tmp.elems); +} + +/* Some routines for manipulating a compiled dfa's transition tables. + Each state may or may not have a transition table; if it does, and it + is a non-accepting state, then d->trans[state] points to its table. + If it is an accepting state then d->fails[state] points to its table. + If it has no table at all, then d->trans[state] is NULL. + TODO: Improve this comment, get rid of the unnecessary redundancy. */ + +static void +build_state(s, d) + int s; + struct dfa *d; +{ + int *trans; /* The new transition table. */ + int i; + + /* Set an upper limit on the number of transition tables that will ever + exist at once. 1024 is arbitrary. The idea is that the frequently + used transition tables will be quickly rebuilt, whereas the ones that + were only needed once or twice will be cleared away. */ + if (d->trcount >= 1024) + { + for (i = 0; i < d->tralloc; ++i) + if (d->trans[i]) + { + free((ptr_t) d->trans[i]); + d->trans[i] = NULL; + } + else if (d->fails[i]) + { + free((ptr_t) d->fails[i]); + d->fails[i] = NULL; + } + d->trcount = 0; + } + + ++d->trcount; + + /* Set up the success bits for this state. */ + d->success[s] = 0; + if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 1, d->states[s].letter, 0, + s, *d)) + d->success[s] |= 4; + if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 1, + s, *d)) + d->success[s] |= 2; + if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 0, + s, *d)) + d->success[s] |= 1; + + MALLOC(trans, int, NOTCHAR); + dfastate(s, d, trans); + + /* Now go through the new transition table, and make sure that the trans + and fail arrays are allocated large enough to hold a pointer for the + largest state mentioned in the table. */ + for (i = 0; i < NOTCHAR; ++i) + if (trans[i] >= d->tralloc) + { + int oldalloc = d->tralloc; + + while (trans[i] >= d->tralloc) + d->tralloc *= 2; + REALLOC(d->realtrans, int *, d->tralloc + 1); + d->trans = d->realtrans + 1; + REALLOC(d->fails, int *, d->tralloc); + REALLOC(d->success, int, d->tralloc); + REALLOC(d->newlines, int, d->tralloc); + while (oldalloc < d->tralloc) + { + d->trans[oldalloc] = NULL; + d->fails[oldalloc++] = NULL; + } + } + + /* Keep the newline transition in a special place so we can use it as + a sentinel. */ + d->newlines[s] = trans['\n']; + trans['\n'] = -1; + + if (ACCEPTING(s, *d)) + d->fails[s] = trans; + else + d->trans[s] = trans; +} + +static void +build_state_zero(d) + struct dfa *d; +{ + d->tralloc = 1; + d->trcount = 0; + CALLOC(d->realtrans, int *, d->tralloc + 1); + d->trans = d->realtrans + 1; + CALLOC(d->fails, int *, d->tralloc); + MALLOC(d->success, int, d->tralloc); + MALLOC(d->newlines, int, d->tralloc); + build_state(0, d); +} + +/* Search through a buffer looking for a match to the given struct dfa. + Find the first occurrence of a string matching the regexp in the buffer, + and the shortest possible version thereof. Return a pointer to the first + character after the match, or NULL if none is found. Begin points to + the beginning of the buffer, and end points to the first character after + its end. We store a newline in *end to act as a sentinel, so end had + better point somewhere valid. Newline is a flag indicating whether to + allow newlines to be in the matching string. If count is non- + NULL it points to a place we're supposed to increment every time we + see a newline. Finally, if backref is non-NULL it points to a place + where we're supposed to store a 1 if backreferencing happened and the + match needs to be verified by a backtracking matcher. Otherwise + we store a 0 in *backref. */ +char * +dfaexec(d, begin, end, newline, count, backref) + struct dfa *d; + char *begin; + char *end; + int newline; + int *count; + int *backref; +{ + register s, s1, tmp; /* Current state. */ + register unsigned char *p; /* Current input character. */ + register **trans, *t; /* Copy of d->trans so it can be optimized + into a register. */ + static sbit[NOTCHAR]; /* Table for anding with d->success. */ + static sbit_init; + + if (! sbit_init) + { + int i; + + sbit_init = 1; + for (i = 0; i < NOTCHAR; ++i) + if (i == '\n') + sbit[i] = 4; + else if (ISALNUM(i)) + sbit[i] = 2; + else + sbit[i] = 1; + } + + if (! d->tralloc) + build_state_zero(d); + + s = s1 = 0; + p = (unsigned char *) begin; + trans = d->trans; + *end = '\n'; + + for (;;) + { + /* The dreaded inner loop. */ + if ((t = trans[s]) != 0) + do + { + s1 = t[*p++]; + if (! (t = trans[s1])) + goto last_was_s; + s = t[*p++]; + } + while ((t = trans[s]) != 0); + goto last_was_s1; + last_was_s: + tmp = s, s = s1, s1 = tmp; + last_was_s1: + + if (s >= 0 && p <= (unsigned char *) end && d->fails[s]) + { + if (d->success[s] & sbit[*p]) + { + if (backref) + if (d->states[s].backref) + *backref = 1; + else + *backref = 0; + return (char *) p; + } + + s1 = s; + s = d->fails[s][*p++]; + continue; + } + + /* If the previous character was a newline, count it. */ + if (count && (char *) p <= end && p[-1] == '\n') + ++*count; + + /* Check if we've run off the end of the buffer. */ + if ((char *) p > end) + return NULL; + + if (s >= 0) + { + build_state(s, d); + trans = d->trans; + continue; + } + + if (p[-1] == '\n' && newline) + { + s = d->newlines[s1]; + continue; + } + + s = 0; + } +} + +/* Initialize the components of a dfa that the other routines don't + initialize for themselves. */ +void +dfainit(d) + struct dfa *d; +{ + d->calloc = 1; + MALLOC(d->charclasses, charclass, d->calloc); + d->cindex = 0; + + d->talloc = 1; + MALLOC(d->tokens, token, d->talloc); + d->tindex = d->depth = d->nleaves = d->nregexps = 0; + + d->searchflag = 0; + d->tralloc = 0; + + d->musts = 0; +} + +/* Parse and analyze a single string of the given length. */ +void +dfacomp(s, len, d, searchflag) + char *s; + size_t len; + struct dfa *d; + int searchflag; +{ + if (case_fold) /* dummy folding in service of dfamust() */ + { + char *copy; + int i; + + copy = malloc(len); + if (!copy) + dfaerror("out of memory"); + + /* This is a kludge. */ + case_fold = 0; + for (i = 0; i < len; ++i) + if (ISUPPER(s[i])) + copy[i] = tolower((unsigned char)s[i]); + else + copy[i] = s[i]; + + dfainit(d); + dfaparse(copy, len, d); + free(copy); + dfamust(d); + d->cindex = d->tindex = d->depth = d->nleaves = d->nregexps = 0; + case_fold = 1; + dfaparse(s, len, d); + dfaanalyze(d, searchflag); + } + else + { + dfainit(d); + dfaparse(s, len, d); + dfamust(d); + dfaanalyze(d, searchflag); + } +} + +/* Free the storage held by the components of a dfa. */ +void +dfafree(d) + struct dfa *d; +{ + int i; + struct dfamust *dm, *ndm; + + free((ptr_t) d->charclasses); + free((ptr_t) d->tokens); + for (i = 0; i < d->sindex; ++i) + free((ptr_t) d->states[i].elems.elems); + free((ptr_t) d->states); + for (i = 0; i < d->tindex; ++i) + if (d->follows[i].elems) + free((ptr_t) d->follows[i].elems); + free((ptr_t) d->follows); + for (i = 0; i < d->tralloc; ++i) + if (d->trans[i]) + free((ptr_t) d->trans[i]); + else if (d->fails[i]) + free((ptr_t) d->fails[i]); + free((ptr_t) d->realtrans); + free((ptr_t) d->fails); + free((ptr_t) d->newlines); + for (dm = d->musts; dm; dm = ndm) + { + ndm = dm->next; + free(dm->must); + free((ptr_t) dm); + } +} + +/* Having found the postfix representation of the regular expression, + try to find a long sequence of characters that must appear in any line + containing the r.e. + Finding a "longest" sequence is beyond the scope here; + we take an easy way out and hope for the best. + (Take "(ab|a)b"--please.) + + We do a bottom-up calculation of sequences of characters that must appear + in matches of r.e.'s represented by trees rooted at the nodes of the postfix + representation: + sequences that must appear at the left of the match ("left") + sequences that must appear at the right of the match ("right") + lists of sequences that must appear somewhere in the match ("in") + sequences that must constitute the match ("is") + + When we get to the root of the tree, we use one of the longest of its + calculated "in" sequences as our answer. The sequence we find is returned in + d->must (where "d" is the single argument passed to "dfamust"); + the length of the sequence is returned in d->mustn. + + The sequences calculated for the various types of node (in pseudo ANSI c) + are shown below. "p" is the operand of unary operators (and the left-hand + operand of binary operators); "q" is the right-hand operand of binary + operators. + + "ZERO" means "a zero-length sequence" below. + + Type left right is in + ---- ---- ----- -- -- + char c # c # c # c # c + + CSET ZERO ZERO ZERO ZERO + + STAR ZERO ZERO ZERO ZERO + + QMARK ZERO ZERO ZERO ZERO + + PLUS p->left p->right ZERO p->in + + CAT (p->is==ZERO)? (q->is==ZERO)? (p->is!=ZERO && p->in plus + p->left : q->right : q->is!=ZERO) ? q->in plus + p->is##q->left p->right##q->is p->is##q->is : p->right##q->left + ZERO + + OR longest common longest common (do p->is and substrings common to + leading trailing q->is have same p->in and q->in + (sub)sequence (sub)sequence length and + of p->left of p->right content) ? + and q->left and q->right p->is : NULL + + If there's anything else we recognize in the tree, all four sequences get set + to zero-length sequences. If there's something we don't recognize in the tree, + we just return a zero-length sequence. + + Break ties in favor of infrequent letters (choosing 'zzz' in preference to + 'aaa')? + + And. . .is it here or someplace that we might ponder "optimizations" such as + egrep 'psi|epsilon' -> egrep 'psi' + egrep 'pepsi|epsilon' -> egrep 'epsi' + (Yes, we now find "epsi" as a "string + that must occur", but we might also + simplify the *entire* r.e. being sought) + grep '[c]' -> grep 'c' + grep '(ab|a)b' -> grep 'ab' + grep 'ab*' -> grep 'a' + grep 'a*b' -> grep 'b' + + There are several issues: + + Is optimization easy (enough)? + + Does optimization actually accomplish anything, + or is the automaton you get from "psi|epsilon" (for example) + the same as the one you get from "psi" (for example)? + + Are optimizable r.e.'s likely to be used in real-life situations + (something like 'ab*' is probably unlikely; something like is + 'psi|epsilon' is likelier)? */ + +static char * +icatalloc(old, new) + char *old; + char *new; +{ + char *result; + int oldsize, newsize; + + newsize = (new == NULL) ? 0 : strlen(new); + if (old == NULL) + oldsize = 0; + else if (newsize == 0) + return old; + else oldsize = strlen(old); + if (old == NULL) + result = (char *) malloc(newsize + 1); + else + result = (char *) realloc((void *) old, oldsize + newsize + 1); + if (result != NULL && new != NULL) + (void) strcpy(result + oldsize, new); + return result; +} + +static char * +icpyalloc(string) + char *string; +{ + return icatalloc((char *) NULL, string); +} + +static char * +istrstr(lookin, lookfor) + char *lookin; + char *lookfor; +{ + char *cp; + int len; + + len = strlen(lookfor); + for (cp = lookin; *cp != '\0'; ++cp) + if (strncmp(cp, lookfor, len) == 0) + return cp; + return NULL; +} + +static void +ifree(cp) + char *cp; +{ + if (cp != NULL) + free(cp); +} + +static void +freelist(cpp) + char **cpp; +{ + int i; + + if (cpp == NULL) + return; + for (i = 0; cpp[i] != NULL; ++i) + { + free(cpp[i]); + cpp[i] = NULL; + } +} + +static char ** +enlist(cpp, new, len) + char **cpp; + char *new; + int len; +{ + int i, j; + + if (cpp == NULL) + return NULL; + if ((new = icpyalloc(new)) == NULL) + { + freelist(cpp); + return NULL; + } + new[len] = '\0'; + /* Is there already something in the list that's new (or longer)? */ + for (i = 0; cpp[i] != NULL; ++i) + if (istrstr(cpp[i], new) != NULL) + { + free(new); + return cpp; + } + /* Eliminate any obsoleted strings. */ + j = 0; + while (cpp[j] != NULL) + if (istrstr(new, cpp[j]) == NULL) + ++j; + else + { + free(cpp[j]); + if (--i == j) + break; + cpp[j] = cpp[i]; + cpp[i] = NULL; + } + /* Add the new string. */ + cpp = (char **) realloc((char *) cpp, (i + 2) * sizeof *cpp); + if (cpp == NULL) + return NULL; + cpp[i] = new; + cpp[i + 1] = NULL; + return cpp; +} + +/* Given pointers to two strings, return a pointer to an allocated + list of their distinct common substrings. Return NULL if something + seems wild. */ +static char ** +comsubs(left, right) + char *left; + char *right; +{ + char **cpp; + char *lcp; + char *rcp; + int i, len; + + if (left == NULL || right == NULL) + return NULL; + cpp = (char **) malloc(sizeof *cpp); + if (cpp == NULL) + return NULL; + cpp[0] = NULL; + for (lcp = left; *lcp != '\0'; ++lcp) + { + len = 0; + rcp = index(right, *lcp); + while (rcp != NULL) + { + for (i = 1; lcp[i] != '\0' && lcp[i] == rcp[i]; ++i) + ; + if (i > len) + len = i; + rcp = index(rcp + 1, *lcp); + } + if (len == 0) + continue; + if ((cpp = enlist(cpp, lcp, len)) == NULL) + break; + } + return cpp; +} + +static char ** +addlists(old, new) +char **old; +char **new; +{ + int i; + + if (old == NULL || new == NULL) + return NULL; + for (i = 0; new[i] != NULL; ++i) + { + old = enlist(old, new[i], strlen(new[i])); + if (old == NULL) + break; + } + return old; +} + +/* Given two lists of substrings, return a new list giving substrings + common to both. */ +static char ** +inboth(left, right) + char **left; + char **right; +{ + char **both; + char **temp; + int lnum, rnum; + + if (left == NULL || right == NULL) + return NULL; + both = (char **) malloc(sizeof *both); + if (both == NULL) + return NULL; + both[0] = NULL; + for (lnum = 0; left[lnum] != NULL; ++lnum) + { + for (rnum = 0; right[rnum] != NULL; ++rnum) + { + temp = comsubs(left[lnum], right[rnum]); + if (temp == NULL) + { + freelist(both); + return NULL; + } + both = addlists(both, temp); + freelist(temp); + if (both == NULL) + return NULL; + } + } + return both; +} + +typedef struct +{ + char **in; + char *left; + char *right; + char *is; +} must; + +static void +resetmust(mp) +must *mp; +{ + mp->left[0] = mp->right[0] = mp->is[0] = '\0'; + freelist(mp->in); +} + +static void +dfamust(dfa) +struct dfa *dfa; +{ + must *musts; + must *mp; + char *result; + int ri; + int i; + int exact; + token t; + static must must0; + struct dfamust *dm; + + result = ""; + exact = 0; + musts = (must *) malloc((dfa->tindex + 1) * sizeof *musts); + if (musts == NULL) + return; + mp = musts; + for (i = 0; i <= dfa->tindex; ++i) + mp[i] = must0; + for (i = 0; i <= dfa->tindex; ++i) + { + mp[i].in = (char **) malloc(sizeof *mp[i].in); + mp[i].left = malloc(2); + mp[i].right = malloc(2); + mp[i].is = malloc(2); + if (mp[i].in == NULL || mp[i].left == NULL || + mp[i].right == NULL || mp[i].is == NULL) + goto done; + mp[i].left[0] = mp[i].right[0] = mp[i].is[0] = '\0'; + mp[i].in[0] = NULL; + } +#ifdef DEBUG + fprintf(stderr, "dfamust:\n"); + for (i = 0; i < dfa->tindex; ++i) + { + fprintf(stderr, " %d:", i); + prtok(dfa->tokens[i]); + } + putc('\n', stderr); +#endif + for (ri = 0; ri < dfa->tindex; ++ri) + { + switch (t = dfa->tokens[ri]) + { + case LPAREN: + case RPAREN: + goto done; /* "cannot happen" */ + case EMPTY: + case BEGLINE: + case ENDLINE: + case BEGWORD: + case ENDWORD: + case LIMWORD: + case NOTLIMWORD: + case BACKREF: + resetmust(mp); + break; + case STAR: + case QMARK: + if (mp <= musts) + goto done; /* "cannot happen" */ + --mp; + resetmust(mp); + break; + case OR: + case ORTOP: + if (mp < &musts[2]) + goto done; /* "cannot happen" */ + { + char **new; + must *lmp; + must *rmp; + int j, ln, rn, n; + + rmp = --mp; + lmp = --mp; + /* Guaranteed to be. Unlikely, but. . . */ + if (strcmp(lmp->is, rmp->is) != 0) + lmp->is[0] = '\0'; + /* Left side--easy */ + i = 0; + while (lmp->left[i] != '\0' && lmp->left[i] == rmp->left[i]) + ++i; + lmp->left[i] = '\0'; + /* Right side */ + ln = strlen(lmp->right); + rn = strlen(rmp->right); + n = ln; + if (n > rn) + n = rn; + for (i = 0; i < n; ++i) + if (lmp->right[ln - i - 1] != rmp->right[rn - i - 1]) + break; + for (j = 0; j < i; ++j) + lmp->right[j] = lmp->right[(ln - i) + j]; + lmp->right[j] = '\0'; + new = inboth(lmp->in, rmp->in); + if (new == NULL) + goto done; + freelist(lmp->in); + free((char *) lmp->in); + lmp->in = new; + } + break; + case PLUS: + if (mp <= musts) + goto done; /* "cannot happen" */ + --mp; + mp->is[0] = '\0'; + break; + case END: + if (mp != &musts[1]) + goto done; /* "cannot happen" */ + for (i = 0; musts[0].in[i] != NULL; ++i) + if (strlen(musts[0].in[i]) > strlen(result)) + result = musts[0].in[i]; + if (strcmp(result, musts[0].is) == 0) + exact = 1; + goto done; + case CAT: + if (mp < &musts[2]) + goto done; /* "cannot happen" */ + { + must *lmp; + must *rmp; + + rmp = --mp; + lmp = --mp; + /* In. Everything in left, plus everything in + right, plus catenation of + left's right and right's left. */ + lmp->in = addlists(lmp->in, rmp->in); + if (lmp->in == NULL) + goto done; + if (lmp->right[0] != '\0' && + rmp->left[0] != '\0') + { + char *tp; + + tp = icpyalloc(lmp->right); + if (tp == NULL) + goto done; + tp = icatalloc(tp, rmp->left); + if (tp == NULL) + goto done; + lmp->in = enlist(lmp->in, tp, + strlen(tp)); + free(tp); + if (lmp->in == NULL) + goto done; + } + /* Left-hand */ + if (lmp->is[0] != '\0') + { + lmp->left = icatalloc(lmp->left, + rmp->left); + if (lmp->left == NULL) + goto done; + } + /* Right-hand */ + if (rmp->is[0] == '\0') + lmp->right[0] = '\0'; + lmp->right = icatalloc(lmp->right, rmp->right); + if (lmp->right == NULL) + goto done; + /* Guaranteed to be */ + if (lmp->is[0] != '\0' && rmp->is[0] != '\0') + { + lmp->is = icatalloc(lmp->is, rmp->is); + if (lmp->is == NULL) + goto done; + } + else + lmp->is[0] = '\0'; + } + break; + default: + if (t < END) + { + /* "cannot happen" */ + goto done; + } + else if (t == '\0') + { + /* not on *my* shift */ + goto done; + } + else if (t >= CSET) + { + /* easy enough */ + resetmust(mp); + } + else + { + /* plain character */ + resetmust(mp); + mp->is[0] = mp->left[0] = mp->right[0] = t; + mp->is[1] = mp->left[1] = mp->right[1] = '\0'; + mp->in = enlist(mp->in, mp->is, 1); + if (mp->in == NULL) + goto done; + } + break; + } +#ifdef DEBUG + fprintf(stderr, " node: %d:", ri); + prtok(dfa->tokens[ri]); + fprintf(stderr, "\n in:"); + for (i = 0; mp->in[i]; ++i) + fprintf(stderr, " \"%s\"", mp->in[i]); + fprintf(stderr, "\n is: \"%s\"\n", mp->is); + fprintf(stderr, " left: \"%s\"\n", mp->left); + fprintf(stderr, " right: \"%s\"\n", mp->right); +#endif + ++mp; + } + done: + if (strlen(result)) + { + dm = (struct dfamust *) malloc(sizeof (struct dfamust)); + dm->exact = exact; + dm->must = malloc(strlen(result) + 1); + strcpy(dm->must, result); + dm->next = dfa->musts; + dfa->musts = dm; + } + mp = musts; + for (i = 0; i <= dfa->tindex; ++i) + { + freelist(mp[i].in); + ifree((char *) mp[i].in); + ifree(mp[i].left); + ifree(mp[i].right); + ifree(mp[i].is); + } + free((char *) mp); +} diff --git a/gnu/usr.bin/grep/dfa.h b/gnu/usr.bin/grep/dfa.h new file mode 100644 index 000000000000..32e05fc2aa4a --- /dev/null +++ b/gnu/usr.bin/grep/dfa.h @@ -0,0 +1,360 @@ +/* dfa.h - declarations for GNU deterministic regexp compiler + Copyright (C) 1988 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written June, 1988 by Mike Haertel */ + +/* FIXME: + 2. We should not export so much of the DFA internals. + In addition to clobbering modularity, we eat up valuable + name space. */ + +/* Number of bits in an unsigned char. */ +#define CHARBITS 8 + +/* First integer value that is greater than any character code. */ +#define NOTCHAR (1 << CHARBITS) + +/* INTBITS need not be exact, just a lower bound. */ +#define INTBITS (CHARBITS * sizeof (int)) + +/* Number of ints required to hold a bit for every character. */ +#define CHARCLASS_INTS ((NOTCHAR + INTBITS - 1) / INTBITS) + +/* Sets of unsigned characters are stored as bit vectors in arrays of ints. */ +typedef int charclass[CHARCLASS_INTS]; + +/* The regexp is parsed into an array of tokens in postfix form. Some tokens + are operators and others are terminal symbols. Most (but not all) of these + codes are returned by the lexical analyzer. */ + +typedef enum +{ + END = -1, /* END is a terminal symbol that matches the + end of input; any value of END or less in + the parse tree is such a symbol. Accepting + states of the DFA are those that would have + a transition on END. */ + + /* Ordinary character values are terminal symbols that match themselves. */ + + EMPTY = NOTCHAR, /* EMPTY is a terminal symbol that matches + the empty string. */ + + BACKREF, /* BACKREF is generated by \; it + it not completely handled. If the scanner + detects a transition on backref, it returns + a kind of "semi-success" indicating that + the match will have to be verified with + a backtracking matcher. */ + + BEGLINE, /* BEGLINE is a terminal symbol that matches + the empty string if it is at the beginning + of a line. */ + + ENDLINE, /* ENDLINE is a terminal symbol that matches + the empty string if it is at the end of + a line. */ + + BEGWORD, /* BEGWORD is a terminal symbol that matches + the empty string if it is at the beginning + of a word. */ + + ENDWORD, /* ENDWORD is a terminal symbol that matches + the empty string if it is at the end of + a word. */ + + LIMWORD, /* LIMWORD is a terminal symbol that matches + the empty string if it is at the beginning + or the end of a word. */ + + NOTLIMWORD, /* NOTLIMWORD is a terminal symbol that + matches the empty string if it is not at + the beginning or end of a word. */ + + QMARK, /* QMARK is an operator of one argument that + matches zero or one occurences of its + argument. */ + + STAR, /* STAR is an operator of one argument that + matches the Kleene closure (zero or more + occurrences) of its argument. */ + + PLUS, /* PLUS is an operator of one argument that + matches the positive closure (one or more + occurrences) of its argument. */ + + REPMN, /* REPMN is a lexical token corresponding + to the {m,n} construct. REPMN never + appears in the compiled token vector. */ + + CAT, /* CAT is an operator of two arguments that + matches the concatenation of its + arguments. CAT is never returned by the + lexical analyzer. */ + + OR, /* OR is an operator of two arguments that + matches either of its arguments. */ + + ORTOP, /* OR at the toplevel in the parse tree. + This is used for a boyer-moore heuristic. */ + + LPAREN, /* LPAREN never appears in the parse tree, + it is only a lexeme. */ + + RPAREN, /* RPAREN never appears in the parse tree. */ + + CSET /* CSET and (and any value greater) is a + terminal symbol that matches any of a + class of characters. */ +} token; + +/* Sets are stored in an array in the compiled dfa; the index of the + array corresponding to a given set token is given by SET_INDEX(t). */ +#define SET_INDEX(t) ((t) - CSET) + +/* Sometimes characters can only be matched depending on the surrounding + context. Such context decisions depend on what the previous character + was, and the value of the current (lookahead) character. Context + dependent constraints are encoded as 8 bit integers. Each bit that + is set indicates that the constraint succeeds in the corresponding + context. + + bit 7 - previous and current are newlines + bit 6 - previous was newline, current isn't + bit 5 - previous wasn't newline, current is + bit 4 - neither previous nor current is a newline + bit 3 - previous and current are word-constituents + bit 2 - previous was word-constituent, current isn't + bit 1 - previous wasn't word-constituent, current is + bit 0 - neither previous nor current is word-constituent + + Word-constituent characters are those that satisfy isalnum(). + + The macro SUCCEEDS_IN_CONTEXT determines whether a a given constraint + succeeds in a particular context. Prevn is true if the previous character + was a newline, currn is true if the lookahead character is a newline. + Prevl and currl similarly depend upon whether the previous and current + characters are word-constituent letters. */ +#define MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \ + ((constraint) & 1 << (((prevn) ? 2 : 0) + ((currn) ? 1 : 0) + 4)) +#define MATCHES_LETTER_CONTEXT(constraint, prevl, currl) \ + ((constraint) & 1 << (((prevl) ? 2 : 0) + ((currl) ? 1 : 0))) +#define SUCCEEDS_IN_CONTEXT(constraint, prevn, currn, prevl, currl) \ + (MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \ + && MATCHES_LETTER_CONTEXT(constraint, prevl, currl)) + +/* The following macros give information about what a constraint depends on. */ +#define PREV_NEWLINE_DEPENDENT(constraint) \ + (((constraint) & 0xc0) >> 2 != ((constraint) & 0x30)) +#define PREV_LETTER_DEPENDENT(constraint) \ + (((constraint) & 0x0c) >> 2 != ((constraint) & 0x03)) + +/* Tokens that match the empty string subject to some constraint actually + work by applying that constraint to determine what may follow them, + taking into account what has gone before. The following values are + the constraints corresponding to the special tokens previously defined. */ +#define NO_CONSTRAINT 0xff +#define BEGLINE_CONSTRAINT 0xcf +#define ENDLINE_CONSTRAINT 0xaf +#define BEGWORD_CONSTRAINT 0xf2 +#define ENDWORD_CONSTRAINT 0xf4 +#define LIMWORD_CONSTRAINT 0xf6 +#define NOTLIMWORD_CONSTRAINT 0xf9 + +/* States of the recognizer correspond to sets of positions in the parse + tree, together with the constraints under which they may be matched. + So a position is encoded as an index into the parse tree together with + a constraint. */ +typedef struct +{ + unsigned index; /* Index into the parse array. */ + unsigned constraint; /* Constraint for matching this position. */ +} position; + +/* Sets of positions are stored as arrays. */ +typedef struct +{ + position *elems; /* Elements of this position set. */ + int nelem; /* Number of elements in this set. */ +} position_set; + +/* A state of the dfa consists of a set of positions, some flags, + and the token value of the lowest-numbered position of the state that + contains an END token. */ +typedef struct +{ + int hash; /* Hash of the positions of this state. */ + position_set elems; /* Positions this state could match. */ + char newline; /* True if previous state matched newline. */ + char letter; /* True if previous state matched a letter. */ + char backref; /* True if this state matches a \. */ + unsigned char constraint; /* Constraint for this state to accept. */ + int first_end; /* Token value of the first END in elems. */ +} dfa_state; + +/* Element of a list of strings, at least one of which is known to + appear in any R.E. matching the DFA. */ +struct dfamust +{ + int exact; + char *must; + struct dfamust *next; +}; + +/* A compiled regular expression. */ +struct dfa +{ + /* Stuff built by the scanner. */ + charclass *charclasses; /* Array of character sets for CSET tokens. */ + int cindex; /* Index for adding new charclasses. */ + int calloc; /* Number of charclasses currently allocated. */ + + /* Stuff built by the parser. */ + token *tokens; /* Postfix parse array. */ + int tindex; /* Index for adding new tokens. */ + int talloc; /* Number of tokens currently allocated. */ + int depth; /* Depth required of an evaluation stack + used for depth-first traversal of the + parse tree. */ + int nleaves; /* Number of leaves on the parse tree. */ + int nregexps; /* Count of parallel regexps being built + with dfaparse(). */ + + /* Stuff owned by the state builder. */ + dfa_state *states; /* States of the dfa. */ + int sindex; /* Index for adding new states. */ + int salloc; /* Number of states currently allocated. */ + + /* Stuff built by the structure analyzer. */ + position_set *follows; /* Array of follow sets, indexed by position + index. The follow of a position is the set + of positions containing characters that + could conceivably follow a character + matching the given position in a string + matching the regexp. Allocated to the + maximum possible position index. */ + int searchflag; /* True if we are supposed to build a searching + as opposed to an exact matcher. A searching + matcher finds the first and shortest string + matching a regexp anywhere in the buffer, + whereas an exact matcher finds the longest + string matching, but anchored to the + beginning of the buffer. */ + + /* Stuff owned by the executor. */ + int tralloc; /* Number of transition tables that have + slots so far. */ + int trcount; /* Number of transition tables that have + actually been built. */ + int **trans; /* Transition tables for states that can + never accept. If the transitions for a + state have not yet been computed, or the + state could possibly accept, its entry in + this table is NULL. */ + int **realtrans; /* Trans always points to realtrans + 1; this + is so trans[-1] can contain NULL. */ + int **fails; /* Transition tables after failing to accept + on a state that potentially could do so. */ + int *success; /* Table of acceptance conditions used in + dfaexec and computed in build_state. */ + int *newlines; /* Transitions on newlines. The entry for a + newline in any transition table is always + -1 so we can count lines without wasting + too many cycles. The transition for a + newline is stored separately and handled + as a special case. Newline is also used + as a sentinel at the end of the buffer. */ + struct dfamust *musts; /* List of strings, at least one of which + is known to appear in any r.e. matching + the dfa. */ +}; + +/* Some macros for user access to dfa internals. */ + +/* ACCEPTING returns true if s could possibly be an accepting state of r. */ +#define ACCEPTING(s, r) ((r).states[s].constraint) + +/* ACCEPTS_IN_CONTEXT returns true if the given state accepts in the + specified context. */ +#define ACCEPTS_IN_CONTEXT(prevn, currn, prevl, currl, state, dfa) \ + SUCCEEDS_IN_CONTEXT((dfa).states[state].constraint, \ + prevn, currn, prevl, currl) + +/* FIRST_MATCHING_REGEXP returns the index number of the first of parallel + regexps that a given state could accept. Parallel regexps are numbered + starting at 1. */ +#define FIRST_MATCHING_REGEXP(state, dfa) (-(dfa).states[state].first_end) + +/* Entry points. */ + +#if __STDC__ + +/* dfasyntax() takes two arguments; the first sets the syntax bits described + earlier in this file, and the second sets the case-folding flag. */ +extern void dfasyntax(int, int); + +/* Compile the given string of the given length into the given struct dfa. + Final argument is a flag specifying whether to build a searching or an + exact matcher. */ +extern void dfacomp(char *, size_t, struct dfa *, int); + +/* Execute the given struct dfa on the buffer of characters. The + first char * points to the beginning, and the second points to the + first character after the end of the buffer, which must be a writable + place so a sentinel end-of-buffer marker can be stored there. The + second-to-last argument is a flag telling whether to allow newlines to + be part of a string matching the regexp. The next-to-last argument, + if non-NULL, points to a place to increment every time we see a + newline. The final argument, if non-NULL, points to a flag that will + be set if further examination by a backtracking matcher is needed in + order to verify backreferencing; otherwise the flag will be cleared. + Returns NULL if no match is found, or a pointer to the first + character after the first & shortest matching string in the buffer. */ +extern char *dfaexec(struct dfa *, char *, char *, int, int *, int *); + +/* Free the storage held by the components of a struct dfa. */ +extern void dfafree(struct dfa *); + +/* Entry points for people who know what they're doing. */ + +/* Initialize the components of a struct dfa. */ +extern void dfainit(struct dfa *); + +/* Incrementally parse a string of given length into a struct dfa. */ +extern void dfaparse(char *, size_t, struct dfa *); + +/* Analyze a parsed regexp; second argument tells whether to build a searching + or an exact matcher. */ +extern void dfaanalyze(struct dfa *, int); + +/* Compute, for each possible character, the transitions out of a given + state, storing them in an array of integers. */ +extern void dfastate(int, struct dfa *, int []); + +/* Error handling. */ + +/* dfaerror() is called by the regexp routines whenever an error occurs. It + takes a single argument, a NUL-terminated string describing the error. + The default dfaerror() prints the error message to stderr and exits. + The user can provide a different dfafree() if so desired. */ +extern void dfaerror(char *); + +#else /* ! __STDC__ */ +extern void dfasyntax(), dfacomp(), dfafree(), dfainit(), dfaparse(); +extern void dfaanalyze(), dfastate(), dfaerror(); +extern char *dfaexec(); +#endif /* ! __STDC__ */ diff --git a/gnu/usr.bin/grep/doc/Makefile b/gnu/usr.bin/grep/doc/Makefile deleted file mode 100644 index 26319de69d14..000000000000 --- a/gnu/usr.bin/grep/doc/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# $FreeBSD$ - -GREPDIR=${.CURDIR}/../../../../contrib/grep - -.PATH: ${GREPDIR}/doc - -INFO= grep -INFOSECTION= "System Utilities" -MAKEINFOFLAGS+= -I ${GREPDIR}/doc - -.include diff --git a/gnu/usr.bin/grep/getopt.c b/gnu/usr.bin/grep/getopt.c new file mode 100644 index 000000000000..a59a01339835 --- /dev/null +++ b/gnu/usr.bin/grep/getopt.c @@ -0,0 +1,731 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* NOTE!!! AIX requires this to be the first thing in the file. + Do not put ANYTHING before it! */ +#if !defined (__GNUC__) && defined (_AIX) + #pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) +#include +#else +#ifndef _AIX +char *alloca (); +#endif +#endif /* alloca.h */ +#endif /* not __GNUC__ */ + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#undef alloca +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#else /* Not GNU C library. */ +#define __alloca alloca +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +static void +my_bcopy (from, to, size) + const char *from; + char *to; + int size; +{ + int i; + for (i = 0; i < size; i++) + to[i] = from[i]; +} +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) __alloca (nonopts_size); + + /* Interchange the two blocks of data in ARGV. */ + + my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); + my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + my_bcopy ((char *) temp, + (char *) &argv[first_nonopt + optind - last_nonopt], + nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/gnu/usr.bin/grep/getopt.h b/gnu/usr.bin/grep/getopt.h new file mode 100644 index 000000000000..45541f5ac0f9 --- /dev/null +++ b/gnu/usr.bin/grep/getopt.h @@ -0,0 +1,129 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/gnu/usr.bin/grep/getpagesize.h b/gnu/usr.bin/grep/getpagesize.h new file mode 100644 index 000000000000..e6bd5610678a --- /dev/null +++ b/gnu/usr.bin/grep/getpagesize.h @@ -0,0 +1,42 @@ +#ifdef BSD +#ifndef BSD4_1 +#define HAVE_GETPAGESIZE +#endif +#endif + +#ifndef HAVE_GETPAGESIZE + +#ifdef VMS +#define getpagesize() 512 +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _SC_PAGESIZE +#define getpagesize() sysconf(_SC_PAGESIZE) +#else + +#ifdef HAVE_SYS_PARAM_H +#include + +#ifdef EXEC_PAGESIZE +#define getpagesize() EXEC_PAGESIZE +#else +#ifdef NBPG +#define getpagesize() NBPG * CLSIZE +#ifndef CLSIZE +#define CLSIZE 1 +#endif /* no CLSIZE */ +#else /* no NBPG */ +#define getpagesize() NBPC +#endif /* no NBPG */ +#endif /* no EXEC_PAGESIZE */ +#else /* !HAVE_SYS_PARAM_H */ +#define getpagesize() 8192 /* punt totally */ +#endif /* !HAVE_SYS_PARAM_H */ +#endif /* no _SC_PAGESIZE */ + +#endif /* not HAVE_GETPAGESIZE */ + diff --git a/gnu/usr.bin/grep/grep.1 b/gnu/usr.bin/grep/grep.1 new file mode 100644 index 000000000000..6f5c48a9bfd9 --- /dev/null +++ b/gnu/usr.bin/grep/grep.1 @@ -0,0 +1,410 @@ +.TH GREP 1 "1992 September 10" "GNU Project" +.SH NAME +grep, egrep, fgrep, zgrep \- print lines matching a pattern +.SH SYNOPSIS +.B grep +[\-[AB] num] +[\-HRPS] +[\-CEFGLVabchilnqsvwx] +[\-e expr] +[\-f file] +files... + +.SH DESCRIPTION +.PP +.B Grep +searches the named input +.I files +(or standard input if no files are named, or +the file name +.B \- +is given) +for lines containing a match to the given +.IR pattern . +By default, +.B grep +prints the matching lines. +.PP +There are three major variants of +.BR grep , +controlled by the following options. +.PD 0 +.TP +.B \-G +Interpret +.I pattern +as a basic regular expression (see below). This is the default. +.TP +.B \-E +Interpret +.I pattern +as an extended regular expression (see below). +.TP +.B \-F +Interpret +.I pattern +as a list of fixed strings, separated by newlines, +any of which is to be matched. +.LP +In addition, two variant programs +.B egrep +and +.B fgrep +are available. +.B Egrep +is similar (but not identical) to +.BR "grep\ \-E" , +and is compatible with the historical Unix +.BR egrep . +.B Fgrep +is the same as +.BR "grep\ \-F" . +When called as +.BR zgrep , +the +.BR \-Z +option is assumed. +.PD +.LP +All variants of +.B grep +understand the following options: +.PD 0 +.TP +.BI \- num +Matches will be printed with +.I num +lines of leading and trailing context. However, +.B grep +will never print any given line more than once. +.TP +.BI \-A " num" +Print +.I num +lines of trailing context after matching lines. +.TP +.BI \-B " num" +Print +.I num +lines of leading context before matching lines. +.TP +.B \-C +Equivalent to +.BR \-2 . +.TP +.B \-V +Print the version number of +.B grep +to standard error. This version number should +be included in all bug reports (see below). +.TP +.B \-a +Don't search in binary files. +.TP +.B \-b +Print the byte offset within the input file before +each line of output. +.TP +.B \-c +Suppress normal output; instead print a count of +matching lines for each input file. +With the +.B \-v +option (see below), count non-matching lines. +.TP +.BI \-e " pattern" +Use +.I pattern +as the pattern; useful to protect patterns beginning with +.BR \- . +.TP +.BI \-f " file" +Obtain the pattern from +.IR file . +.TP +.B \-h +Suppress the prefixing of filenames on output +when multiple files are searched. +.TP +.B \-i +Ignore case distinctions in both the +.I pattern +and the input files. +.TP +.B \-L +Suppress normal output; instead print the name +of each input file from which no output would +normally have been printed. +.TP +.B \-l +Suppress normal output; instead print +the name of each input file from which output +would normally have been printed. +.TP +.B \-n +Prefix each line of output with the line number +within its input file. +.TP +.B \-q +Quiet; suppress normal output. +.TP +.B \-s +Suppress error messages about nonexistent or unreadable files. +.TP +.B \-v +Invert the sense of matching, to select non-matching lines. +.TP +.B \-w +Select only those lines containing matches that form whole words. +The test is that the matching substring must either be at the +beginning of the line, or preceded by a non-word constituent +character. Similarly, it must be either at the end of the line +or followed by a non-word constituent character. Word-constituent +characters are letters, digits, and the underscore. +.TP +.B \-x +Select only those matches that exactly match the whole line. + +.PP +Following options are only available if compiled with FTS library: +.PD 0 +.TP +.BI \-H +If the +.I \-R +option is specified, symbolic links on the command line +are followed. (Symbolic links encountered in the tree traversal +are not followed.) +.TP +.BI \-L +If the +.I \-R +option is specified, all symbolic links are followed. +.TP +.BI \-P +If the +.I \-R +option is specified, no symbolic links are followed. +.TP +.BI \-R +Search in the file hierarchies +rooted in the files instead of just the files themselves. + +.LP +Following option is only available if compiled with zlib library: +.PD 0 +.TP +.BI \-Z +If the +.I \-Z +option is specified, the input data will be +decompressed before searching. +.TP +.PD +.SH "REGULAR EXPRESSIONS" +.PP +A regular expression is a pattern that describes a set of strings. +Regular expressions are constructed analogously to arithmetic +expressions, by using various operators to combine smaller expressions. +.PP +.B Grep +understands two different versions of regular expression syntax: +``basic'' and ``extended.'' In +.RB "GNU\ " grep , +there is no difference in available functionality using either syntax. +In other implementations, basic regular expressions are less powerful. +The following description applies to extended regular expressions; +differences for basic regular expressions are summarized afterwards. +.PP +The fundamental building blocks are the regular expressions that match +a single character. Most characters, including all letters and digits, +are regular expressions that match themselves. Any metacharacter with +special meaning may be quoted by preceding it with a backslash. +.PP +A list of characters enclosed by +.B [ +and +.B ] +matches any single +character in that list; if the first character of the list +is the caret +.B ^ +then it matches any character +.I not +in the list. +For example, the regular expression +.B [0123456789] +matches any single digit. A range of ASCII characters +may be specified by giving the first and last characters, separated +by a hyphen. +Finally, certain named classes of characters are predefined. +Their names are self explanatory, and they are +.BR [:alnum:] , +.BR [:alpha:] , +.BR [:cntrl:] , +.BR [:digit:] , +.BR [:graph:] , +.BR [:lower:] , +.BR [:print:] , +.BR [:punct:] , +.BR [:space:] , +.BR [:upper:] , +and +.BR [:xdigit:]. +For example, +.B [[:alnum:]] +means +.BR [0-9A-Za-z] , +except the latter form is dependent upon the ASCII character encoding, +whereas the former is portable. +(Note that the brackets in these class names are part of the symbolic +names, and must be included in addition to the brackets delimiting +the bracket list.) Most metacharacters lose their special meaning +inside lists. To include a literal +.B ] +place it first in the list. Similarly, to include a literal +.B ^ +place it anywhere but first. Finally, to include a literal +.B \- +place it last. +.PP +The period +.B . +matches any single character. +The symbol +.B \ew +is a synonym for +.B [[:alnum:]] +and +.B \eW +is a synonym for +.BR [^[:alnum]] . +.PP +The caret +.B ^ +and the dollar sign +.B $ +are metacharacters that respectively match the empty string at the +beginning and end of a line. +The symbols +.B \e< +and +.B \e> +respectively match the empty string at the beginning and end of a word. +The symbol +.B \eb +matches the empty string at the edge of a word, +and +.B \eB +matches the empty string provided it's +.I not +at the edge of a word. +.PP +A regular expression matching a single character may be followed +by one of several repetition operators: +.PD 0 +.TP +.B ? +The preceding item is optional and matched at most once. +.TP +.B * +The preceding item will be matched zero or more times. +.TP +.B + +The preceding item will be matched one or more times. +.TP +.BI { n } +The preceding item is matched exactly +.I n +times. +.TP +.BI { n ,} +The preceding item is matched +.I n +or more times. +.TP +.BI {, m } +The preceding item is optional and is matched at most +.I m +times. +.TP +.BI { n , m } +The preceding item is matched at least +.I n +times, but not more than +.I m +times. +.PD +.PP +Two regular expressions may be concatenated; the resulting +regular expression matches any string formed by concatenating +two substrings that respectively match the concatenated +subexpressions. +.PP +Two regular expressions may be joined by the infix operator +.BR | ; +the resulting regular expression matches any string matching +either subexpression. +.PP +Repetition takes precedence over concatenation, which in turn +takes precedence over alternation. A whole subexpression may be +enclosed in parentheses to override these precedence rules. +.PP +The backreference +.BI \e n\c +\&, where +.I n +is a single digit, matches the substring +previously matched by the +.IR n th +parenthesized subexpression of the regular expression. +.PP +In basic regular expressions the metacharacters +.BR ? , +.BR + , +.BR { , +.BR | , +.BR ( , +and +.BR ) +lose their special meaning; instead use the backslashed +versions +.BR \e? , +.BR \e+ , +.BR \e{ , +.BR \e| , +.BR \e( , +and +.BR \e) . +.PP +In +.B egrep +the metacharacter +.B { +loses its special meaning; instead use +.BR \e{ . +.SH DIAGNOSTICS +.PP +Normally, exit status is 0 if matches were found, +and 1 if no matches were found. (The +.B \-v +option inverts the sense of the exit status.) +Exit status is 2 if there were syntax errors +in the pattern, inaccessible input files, or +other system errors. +.SH BUGS +.PP +Email bug reports to +.BR bug-gnu-utils@prep.ai.mit.edu . +Be sure to include the word ``grep'' somewhere in the ``Subject:'' field. +.PP +Large repetition counts in the +.BI { m , n } +construct may cause grep to use lots of memory. +In addition, +certain other obscure regular expressions require exponential time +and space, and may cause +.B grep +to run out of memory. +.PP +Backreferences are very slow, and may require exponential time. diff --git a/gnu/usr.bin/grep/grep.c b/gnu/usr.bin/grep/grep.c new file mode 100644 index 000000000000..72349860c9f6 --- /dev/null +++ b/gnu/usr.bin/grep/grep.c @@ -0,0 +1,1103 @@ +/* grep.c - main driver file for grep. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written July 1992 by Mike Haertel. + + Recursive searching and builtin decompression (libz) + 1996/1997 by Wolfram Schneider . */ + +#include +#include +#ifdef __FreeBSD__ +#include +#endif + +#ifndef errno +extern int errno; +#endif + +#ifdef STDC_HEADERS +#include +#else +#include +extern char *malloc(), *realloc(); +extern void free(); +#endif + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#ifdef NEED_MEMORY_H +#include +#endif +#else +#include +#ifdef __STDC__ +extern void *memchr(); +#else +extern char *memchr(); +#endif +#define strrchr rindex +#endif + +#ifdef HAVE_UNISTD_H +#include +#include +#include +#else +#define O_RDONLY 0 +#define STDIN_FILENO 0 +extern int open(), read(), close(); +#endif + +#include "getpagesize.h" +#include "grep.h" + +#undef MAX +#define MAX(A,B) ((A) > (B) ? (A) : (B)) + +/* Provide missing ANSI features if necessary. */ + +#ifndef HAVE_STRERROR +extern int sys_nerr; +extern char *sys_errlist[]; +#define strerror(E) ((E) < sys_nerr ? sys_errlist[(E)] : "bogus error number") +#endif + +#ifndef HAVE_MEMCHR +#ifdef __STDC__ +#define VOID void +#else +#define VOID char +#endif +VOID * +memchr(vp, c, n) + VOID *vp; + int c; + size_t n; +{ + unsigned char *p; + + for (p = (unsigned char *) vp; n--; ++p) + if (*p == c) + return (VOID *) p; + return 0; +} +#endif + +/* traverse a file hierarchy library */ +#if HAVE_FTS > 0 +#include +#include +#include +#endif + +/* don't search in binary files */ +int aflag; + +/* Define flags declared in grep.h. */ +char *matcher; +int match_icase; +int match_words; +int match_lines; + +/* Functions we'll use to search. */ +static void (*compile)(); +static char *(*execute)(); + +/* For error messages. */ +static char *prog; +static char *filename; +static int errseen; + +/* Print a message and possibly an error string. Remember + that something awful happened. */ +static void +error(mesg, errnum) +#ifdef __STDC__ + const +#endif + char *mesg; + int errnum; +{ + if (errnum) + fprintf(stderr, "%s: %s: %s\n", prog, mesg, strerror(errnum)); + else + fprintf(stderr, "%s: %s\n", prog, mesg); + errseen = 1; +} + +/* Like error(), but die horribly after printing. */ +void +fatal(mesg, errnum) +#ifdef __STDC__ + const +#endif + char *mesg; + int errnum; +{ + error(mesg, errnum); + exit(2); +} + +/* Interface to handle errors and fix library lossage. */ +char * +xmalloc(size) + size_t size; +{ + char *result; + + result = malloc(size); + if (size && !result) + fatal("memory exhausted", 0); + return result; +} + +/* Interface to handle errors and fix some library lossage. */ +char * +xrealloc(ptr, size) + char *ptr; + size_t size; +{ + char *result; + + if (ptr) + result = realloc(ptr, size); + else + result = malloc(size); + if (size && !result) + fatal("memory exhausted", 0); + return result; +} + +#if !defined(HAVE_VALLOC) +#define valloc malloc +#else +#ifdef __STDC__ +extern void *valloc(size_t); +#else +extern char *valloc(); +#endif +#endif + +/* Hairy buffering mechanism for grep. The intent is to keep + all reads aligned on a page boundary and multiples of the + page size. */ + +static char *buffer; /* Base of buffer. */ +static size_t bufsalloc; /* Allocated size of buffer save region. */ +static size_t bufalloc; /* Total buffer size. */ +static int bufdesc; /* File descriptor. */ +static char *bufbeg; /* Beginning of user-visible stuff. */ +static char *buflim; /* Limit of user-visible stuff. */ + +#if defined(HAVE_WORKING_MMAP) +#include +#include +#include + +static int bufmapped; /* True for ordinary files. */ +static struct stat bufstat; /* From fstat(). */ +static off_t bufoffset; /* What read() normally remembers. */ +#endif + +#if HAVE_LIBZ > 0 +#include +static gzFile gzbufdesc; /* zlib file descriptor. */ +static int Zflag; /* uncompress before searching */ +#endif + +/* Reset the buffer for a new file. Initialize + on the first time through. */ +void +reset(fd) + int fd; +{ + static int initialized; + + if (!initialized) + { + initialized = 1; +#ifndef BUFSALLOC + bufsalloc = MAX(8192, getpagesize()); +#else + bufsalloc = BUFSALLOC; +#endif + bufalloc = 5 * bufsalloc; + /* The 1 byte of overflow is a kludge for dfaexec(), which + inserts a sentinel newline at the end of the buffer + being searched. There's gotta be a better way... */ + buffer = valloc(bufalloc + 1); + if (!buffer) + fatal("memory exhausted", 0); + bufbeg = buffer; + buflim = buffer; + } +#if HAVE_LIBZ > 0 + if (Zflag) { + gzbufdesc = gzdopen(fd, "r"); + if (gzbufdesc == NULL) + fatal("memory exhausted", 0); + } +#endif + bufdesc = fd; +#if defined(HAVE_WORKING_MMAP) + if ( +#if HAVE_LIBZ > 0 + Zflag || +#endif + fstat(fd, &bufstat) < 0 || !S_ISREG(bufstat.st_mode)) + bufmapped = 0; + else + { + bufmapped = 1; + bufoffset = lseek(fd, 0, 1); + } +#endif +} + +/* Read new stuff into the buffer, saving the specified + amount of old stuff. When we're done, 'bufbeg' points + to the beginning of the buffer contents, and 'buflim' + points just after the end. Return count of new stuff. */ +static int +fillbuf(save) + size_t save; +{ + char *nbuffer, *dp, *sp; + int cc; +#if defined(HAVE_WORKING_MMAP) + caddr_t maddr; +#endif + static int pagesize; + + if (pagesize == 0 && (pagesize = getpagesize()) == 0) + abort(); + + if (save > bufsalloc) + { + while (save > bufsalloc) + bufsalloc *= 2; + bufalloc = 5 * bufsalloc; + nbuffer = valloc(bufalloc + 1); + if (!nbuffer) + fatal("memory exhausted", 0); + } + else + nbuffer = buffer; + + sp = buflim - save; + dp = nbuffer + bufsalloc - save; + bufbeg = dp; + while (save--) + *dp++ = *sp++; + + /* We may have allocated a new, larger buffer. Since + there is no portable vfree(), we just have to forget + about the old one. Sorry. */ + buffer = nbuffer; + +#if defined(HAVE_WORKING_MMAP) + if (bufmapped && bufoffset % pagesize == 0 + && bufstat.st_size - bufoffset >= bufalloc - bufsalloc) + { + maddr = buffer + bufsalloc; + maddr = mmap(maddr, bufalloc - bufsalloc, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, bufdesc, bufoffset); + if (maddr == (caddr_t) -1) + { + fprintf(stderr, "%s: warning: %s: %s\n", filename, + strerror(errno)); + goto tryread; + } +#if 0 + /* You might thing this (or MADV_WILLNEED) would help, + but it doesn't, at least not on a Sun running 4.1. + In fact, it actually slows us down about 30%! */ + madvise(maddr, bufalloc - bufsalloc, MADV_SEQUENTIAL); +#endif + cc = bufalloc - bufsalloc; + bufoffset += cc; + } + else + { + tryread: + /* We come here when we're not going to use mmap() any more. + Note that we need to synchronize the file offset the + first time through. */ + if (bufmapped) + { + bufmapped = 0; + lseek(bufdesc, bufoffset, 0); + } +#if HAVE_LIBZ > 0 + if (Zflag) + cc = gzread(gzbufdesc, buffer + bufsalloc, bufalloc - bufsalloc); + else +#endif + cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc); + } +#else +#if HAVE_LIBZ > 0 + if (Zflag) + cc = gzread(gzbufdesc, buffer + bufsalloc, bufalloc - bufsalloc); + else +#endif + cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc); +#endif + if (cc > 0) + buflim = buffer + bufsalloc + cc; + else + buflim = buffer + bufsalloc; + return cc; +} + +/* Flags controlling the style of output. */ +static int out_quiet; /* Suppress all normal output. */ +static int out_invert; /* Print nonmatching stuff. */ +static int out_file; /* Print filenames. */ +static int out_line; /* Print line numbers. */ +static int out_byte; /* Print byte offsets. */ +static int out_before; /* Lines of leading context. */ +static int out_after; /* Lines of trailing context. */ +static int count_matches; /* print a count of matching lines */ + +/* Internal variables to keep track of byte count, context, etc. */ +static size_t totalcc; /* Total character count before bufbeg. */ +static char *lastnl; /* Pointer after last newline counted. */ +static char *lastout; /* Pointer after last character output; + NULL if no character has been output + or if it's conceptually before bufbeg. */ +static size_t totalnl; /* Total newline count before lastnl. */ +static int pending; /* Pending lines of output. */ + +static void +nlscan(lim) + char *lim; +{ + char *beg; + + for (beg = lastnl; beg < lim; ++beg) + if (*beg == '\n') + ++totalnl; + lastnl = beg; +} + +static void +prline(beg, lim, sep) + char *beg; + char *lim; + char sep; +{ + if (out_file) + printf("%s%c", filename, sep); + if (out_line) + { + nlscan(beg); + printf("%d%c", ++totalnl, sep); + lastnl = lim; + } + if (out_byte) + printf("%lu%c", (unsigned long)(totalcc + (beg - bufbeg)), sep); + fwrite(beg, 1, lim - beg, stdout); + if (ferror(stdout)) + error("writing output", errno); + lastout = lim; +} + +/* Print pending lines of trailing context prior to LIM. */ +static void +prpending(lim) + char *lim; +{ + char *nl; + + if (!lastout) + lastout = bufbeg; + while (pending > 0 && lastout < lim) + { + --pending; + if ((nl = memchr(lastout, '\n', lim - lastout)) != 0) + ++nl; + else + nl = lim; + prline(lastout, nl, '-'); + } +} + +/* Print the lines between BEG and LIM. Deal with context crap. + If NLINESP is non-null, store a count of lines between BEG and LIM. */ +static void +prtext(beg, lim, nlinesp) + char *beg; + char *lim; + int *nlinesp; +{ + static int used; /* avoid printing "--" before any output */ + char *bp, *p, *nl; + int i, n; + + if (!out_quiet && pending > 0) + prpending(beg); + + p = beg; + + if (!out_quiet) + { + /* Deal with leading context crap. */ + + bp = lastout ? lastout : bufbeg; + for (i = 0; i < out_before; ++i) + if (p > bp) + do + --p; + while (p > bp && p[-1] != '\n'); + + /* We only print the "--" separator if our output is + discontiguous from the last output in the file. */ + if ((out_before || out_after) && used && p != lastout) + puts("--"); + + while (p < beg) + { + nl = memchr(p, '\n', beg - p); + prline(p, nl + 1, '-'); + p = nl + 1; + } + } + + if (nlinesp) + { + /* Caller wants a line count. */ + for (n = 0; p < lim; ++n) + { + if ((nl = memchr(p, '\n', lim - p)) != 0) + ++nl; + else + nl = lim; + if (!out_quiet) + prline(p, nl, ':'); + p = nl; + } + *nlinesp = n; + } + else + if (!out_quiet) + prline(beg, lim, ':'); + + pending = out_after; + used = 1; +} + +/* Scan the specified portion of the buffer, matching lines (or + between matching lines if OUT_INVERT is true). Return a count of + lines printed. */ +static int +grepbuf(beg, lim) + char *beg; + char *lim; +{ + int nlines, n; + register char *p, *b; + char *endp; + + nlines = 0; + p = beg; + while ((b = (*execute)(p, lim - p, &endp)) != 0) + { + /* Avoid matching the empty line at the end of the buffer. */ + if (b == lim && ((b > beg && b[-1] == '\n') || b == beg)) + break; + if (!out_invert) + { + prtext(b, endp, (int *) 0); + nlines += 1; + } + else if (p < b) + { + prtext(p, b, &n); + nlines += n; + } + p = endp; + } + if (out_invert && p < lim) + { + prtext(p, lim, &n); + nlines += n; + } + return nlines; +} + + +/* + * try to guess if buf belong to a binary file + */ + +int isBinaryFile(buf, len) + char *buf; + int len; +{ +#define BINARY_BUF_LEN 32 + int i; + + len = (len < BINARY_BUF_LEN ? len : BINARY_BUF_LEN); + + /* look for non-printable chars */ + for(i = 0; i < len; i++, buf++) + if (!isprint(*buf) && !isspace(*buf)) + return(1); + + return(0); +} + + + +/* Search a given file. Return a count of lines printed. */ +static int +grep(fd) + int fd; +{ + int nlines, i; + size_t residue, save; + char *beg, *lim; + int first, cc; + + reset(fd); + + totalcc = 0; + lastout = 0; + totalnl = 0; + pending = 0; + + nlines = 0; + residue = 0; + save = 0; + first = 0; + cc = 0; + + for (;;) + { + if ((cc = fillbuf(save)) < 0) + { + error(filename, errno); + return nlines; + } + + /* skip binary files */ + if (!first && aflag && isBinaryFile(bufbeg, cc)) + return(0); + first++; + + lastnl = bufbeg; + if (lastout) + lastout = bufbeg; + if (buflim - bufbeg == save) + break; + beg = bufbeg + save - residue; + for (lim = buflim; lim > beg && lim[-1] != '\n'; --lim) + ; + residue = buflim - lim; + if (beg < lim) + { + nlines += grepbuf(beg, lim); + if (pending) + prpending(lim); + /* optimization */ + if (nlines && out_quiet && !count_matches) + return(nlines); + } + i = 0; + beg = lim; + while (i < out_before && beg > bufbeg && beg != lastout) + { + ++i; + do + --beg; + while (beg > bufbeg && beg[-1] != '\n'); + } + if (beg != lastout) + lastout = 0; + save = residue + lim - beg; + totalcc += buflim - bufbeg - save; + if (out_line) + nlscan(beg); + } + if (residue) + { + nlines += grepbuf(bufbeg + save - residue, buflim); + if (pending) + prpending(buflim); + } + return nlines; +} + +static char version[] = "GNU grep version 2.0"; + +#define GETOPT_STD "0123456789A:B:CEFGLVX:abce:f:hilnqsvwxy" +#if HAVE_FTS > 0 +#define GETOPT_FTS "HPRS" +#else +#define GETOPT_FTS "" +#endif +#if HAVE_LIBZ > 0 +#define GETOPT_Z "Z" +#else +#define GETOPT_Z "" +#endif + +static void +usage() +{ + fprintf(stderr, "usage: %s [-[AB] ] [-CEFGLVX%s%s%s", + prog, GETOPT_FTS, GETOPT_Z, + "abchilnqsvwxy]\n [-e ] [-f file] [files ...]\n"); + exit(2); +} + +/* Go through the matchers vector and look for the specified matcher. + If we find it, install it in compile and execute, and return 1. */ +int +setmatcher(name) + char *name; +{ + int i; + + for (i = 0; matchers[i].name; ++i) + if (strcmp(name, matchers[i].name) == 0) + { + compile = matchers[i].compile; + execute = matchers[i].execute; + return 1; + } + return 0; +} + + + +int +main(argc, argv) + int argc; + char *argv[]; +{ + char *keys; + size_t keycc, oldcc, keyalloc; + int keyfound, no_filenames, list_files, suppress_errors; + int opt, cc, desc, count, status; + FILE *fp; + extern char *optarg; + extern int optind; +#if HAVE_FTS > 0 + int Rflag, Hflag, Pflag, Lflag; + FTS *ftsp; + FTSENT *ftsent; + int fts_options; +#endif + +#ifdef __FreeBSD__ + (void) setlocale(LC_ALL, ""); +#endif + prog = argv[0]; + if (prog && strrchr(prog, '/')) + prog = strrchr(prog, '/') + 1; + + keys = NULL; + keycc = 0; + keyfound = 0; + count_matches = 0; + no_filenames = 0; + list_files = 0; + suppress_errors = 0; + matcher = NULL; + aflag = 0; +#if HAVE_FTS > 0 + Rflag = Hflag = Pflag = Lflag = 0; +#endif +#if HAVE_LIBZ > 0 + if (*prog == 'z') { + prog++; + Zflag = 1; + } +#endif + + while ((opt = getopt(argc, argv, + GETOPT_STD/**/GETOPT_FTS/**/GETOPT_Z)) != -1) + switch (opt) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + out_before = 10 * out_before + opt - '0'; + out_after = 10 * out_after + opt - '0'; + break; + case 'A': + out_after = atoi(optarg); + if (out_after < 0) + usage(); + break; + case 'B': + out_before = atoi(optarg); + if (out_before < 0) + usage(); + break; + case 'C': + out_before = out_after = 2; + break; + case 'E': + if (matcher && strcmp(matcher, "egrep") != 0) + fatal("you may specify only one of -E, -F, or -G", 0); + matcher = "posix-egrep"; + break; + case 'F': + if (matcher && strcmp(matcher, "fgrep") != 0) + fatal("you may specify only one of -E, -F, or -G", 0);; + matcher = "fgrep"; + break; + case 'G': + if (matcher && strcmp(matcher, "grep") != 0) + fatal("you may specify only one of -E, -F, or -G", 0); + matcher = "grep"; + break; + case 'V': + fprintf(stderr, "%s\n", version); + break; + case 'X': + if (matcher) + fatal("matcher already specified", 0); + matcher = optarg; + break; +#if HAVE_LIBZ > 0 + case 'Z': + Zflag = 1; + break; +#endif +#if HAVE_FTS > 0 + /* symbolic links on the command line are followed */ + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + + /* no symbolic links are followed */ + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + + /* traverse file hierarchies */ + case 'R': + Rflag = 1; + break; + + /* all symbolic links are followed */ + case 'S': + Lflag = 1; + Hflag = Pflag = 0; + break; +#endif + case 'a': + aflag = 1; + break; + case 'b': + out_byte = 1; + break; + case 'c': + out_quiet = 1; + count_matches = 1; + break; + case 'e': + cc = strlen(optarg); + keys = xrealloc(keys, keycc + cc + 1); + if (keyfound) + keys[keycc++] = '\n'; + strcpy(&keys[keycc], optarg); + keycc += cc; + keyfound = 1; + break; + case 'f': + fp = strcmp(optarg, "-") != 0 ? fopen(optarg, "r") : stdin; + if (!fp) + fatal(optarg, errno); + for (keyalloc = 1; keyalloc <= keycc; keyalloc *= 2) + ; + keys = xrealloc(keys, keyalloc); + oldcc = keycc; + if (keyfound) + keys[keycc++] = '\n'; + while (!feof(fp) + && (cc = fread(keys + keycc, 1, keyalloc - keycc, fp)) > 0) + { + keycc += cc; + if (keycc == keyalloc) + keys = xrealloc(keys, keyalloc *= 2); + } + if (fp != stdin) + fclose(fp); + /* Nuke the final newline to avoid matching a null string. */ + if (keycc - oldcc > 0 && keys[keycc - 1] == '\n') + --keycc; + keyfound = 1; + break; + case 'h': + no_filenames = 1; + break; + case 'i': + case 'y': /* For old-timers . . . */ + match_icase = 1; + break; + case 'L': + /* Like -l, except list files that don't contain matches. + Inspired by the same option in Hume's gre. */ + out_quiet = 1; + list_files = -1; + break; + case 'l': + out_quiet = 1; + list_files = 1; + break; + case 'n': + out_line = 1; + break; + case 'q': + out_quiet = 1; + break; + case 's': + suppress_errors = 1; + break; + case 'v': + out_invert = 1; + break; + case 'w': + match_words = 1; + break; + case 'x': + match_lines = 1; + break; + default: + usage(); + break; + } + + if (!keyfound) + if (optind < argc) + { + keys = argv[optind++]; + keycc = strlen(keys); + } + else + usage(); + + if (!matcher) + matcher = prog; + + if (!setmatcher(matcher) && !setmatcher("default")) + abort(); + + (*compile)(keys, keycc); + +#if HAVE_FTS > 0 + if ((argc - optind > 1 || Rflag) && !no_filenames) +#else + if (argc - optind > 1 && !no_filenames) +#endif + out_file = 1; + + status = 1; + +#if HAVE_FTS > 0 + if (Rflag) { + fts_options = FTS_PHYSICAL | FTS_NOCHDIR; + + if (Hflag) + fts_options |= FTS_COMFOLLOW; + + if (Lflag) { + fts_options |= FTS_LOGICAL; + fts_options &= ~FTS_PHYSICAL; + } + + if (Pflag) { + fts_options &= ~FTS_LOGICAL & ~FTS_COMFOLLOW; + fts_options |= FTS_PHYSICAL; + } + } + + if (Rflag && optind < argc) { + int i; + + /* replace "-" with "/dev/stdin" */ + for (i = optind; i < argc; i++) + if (strcmp(argv[i], "-") == 0) + *(argv + i) = "/dev/stdin"; + + if ((ftsp = fts_open(argv + optind, fts_options, + (int(*)())NULL)) == NULL) { + if (!suppress_errors) + error("", errno); + } else { + + while((ftsent = fts_read(ftsp)) != NULL) { + filename = ftsent->fts_accpath; + + switch(ftsent->fts_info) { + + /* regular file */ + case FTS_F: + break; + + /* directory */ + case FTS_D: + case FTS_DC: + case FTS_DP: + continue; break; + + /* errors */ + case FTS_DNR: + error(filename, errno); + continue; break; + + case FTS_ERR: + case FTS_NS: + error(filename, ftsent->fts_errno); + continue; break; + + /* dead symlink */ + case FTS_SLNONE: + continue; break; + + /* symlink, don't skip */ + case FTS_SL: + break; + + default: + /* + if (!suppress_errors) + fprintf(stderr, "%s: ignored\n", filename); + continue; break; + */ + + } + + if ((desc = open(filename, O_RDONLY)) == -1) { + error(filename, errno); + continue; + } + + count = grep(desc); + if (count_matches) + { + if (out_file) + printf("%s:", filename); + printf("%d\n", count); + } + if (count) + { + status = 0; + if (list_files == 1) + printf("%s\n", filename); + } + else if (list_files == -1) + printf("%s\n", filename); + + if (desc != STDIN_FILENO) { +#if HAVE_LIBZ > 0 + if (Zflag) + gzclose(gzbufdesc); + else +#endif + close(desc); + } + } + + if (fts_close(ftsp) == -1) + error("fts_close", errno); + } + + /* ! Rflag */ + } else + +#endif /* HAVE_FTS */ + + /* search in file names from arguments, not from stdin */ + if (optind < argc) + + while (optind < argc) + { + desc = strcmp(argv[optind], "-") ? + open(argv[optind], O_RDONLY) : STDIN_FILENO; + if (desc < 0) + { + if (!suppress_errors) + error(argv[optind], errno); + } + else + { + filename = desc == STDIN_FILENO ? + "(standard input)" : argv[optind]; + count = grep(desc); + if (count_matches) + { + if (out_file) + printf("%s:", filename); + printf("%d\n", count); + } + if (count) + { + status = 0; + if (list_files == 1) + printf("%s\n", filename); + } + else if (list_files == -1) + printf("%s\n", filename); + + if (desc != STDIN_FILENO) { +#if HAVE_LIBZ > 0 + if (Zflag) + gzclose(gzbufdesc); + else +#endif + close(desc); + + } + } + ++optind; + } + + /* read input from stdin */ + else + { + filename = "(standard input)"; + count = grep(STDIN_FILENO); + if (count_matches) + printf("%d\n", count); + if (count) + { + status = 0; + if (list_files == 1) + printf("(standard input)\n"); + } + else if (list_files == -1) + printf("(standard input)\n"); + } + + exit(errseen ? 2 : status); +} diff --git a/gnu/usr.bin/grep/grep.h b/gnu/usr.bin/grep/grep.h new file mode 100644 index 000000000000..a3316c501fb1 --- /dev/null +++ b/gnu/usr.bin/grep/grep.h @@ -0,0 +1,53 @@ +/* grep.h - interface to grep driver for searching subroutines. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if __STDC__ + +extern void fatal(const char *, int); + +/* Grep.c expects the matchers vector to be terminated + by an entry with a NULL name, and to contain at least + an entry named "default". */ + +extern struct matcher +{ + char *name; + void (*compile)(char *, size_t); + char *(*execute)(char *, size_t, char **); +} matchers[]; + +#else + +extern void fatal(); + +extern struct matcher +{ + char *name; + void (*compile)(); + char *(*execute)(); +} matchers[]; + +#endif + +/* Exported from grep.c. */ +extern char *matcher; + +/* The following flags are exported from grep for the matchers + to look at. */ +extern int match_icase; /* -i */ +extern int match_words; /* -w */ +extern int match_lines; /* -x */ diff --git a/gnu/usr.bin/grep/kwset.c b/gnu/usr.bin/grep/kwset.c new file mode 100644 index 000000000000..68871cb36a66 --- /dev/null +++ b/gnu/usr.bin/grep/kwset.c @@ -0,0 +1,807 @@ +/* kwset.c - search for any of a set of keywords. + Copyright 1989 Free Software Foundation + Written August 1989 by Mike Haertel. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +/* The algorithm implemented by these routines bears a startling resemblence + to one discovered by Beate Commentz-Walter, although it is not identical. + See "A String Matching Algorithm Fast on the Average," Technical Report, + IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900 + Heidelberg, Germany. See also Aho, A.V., and M. Corasick, "Efficient + String Matching: An Aid to Bibliographic Search," CACM June 1975, + Vol. 18, No. 6, which describes the failure function used below. */ + +/* $FreeBSD$ */ + + +#ifdef STDC_HEADERS +#include +#include +#else +#define INT_MAX 2147483647 +#define UCHAR_MAX 255 +#ifdef __STDC__ +#include +#else +#include +#endif +extern char *malloc(); +extern void free(); +#endif + +#ifdef HAVE_MEMCHR +#include +#ifdef NEED_MEMORY_H +#include +#endif +#else +#ifdef __STDC__ +extern void *memchr(); +#else +extern char *memchr(); +#endif +#endif + +#ifdef GREP +extern char *xmalloc(); +#define malloc xmalloc +#endif + +#include "kwset.h" +#include "obstack.h" + +#define NCHAR (UCHAR_MAX + 1) +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + +/* Balanced tree of edges and labels leaving a given trie node. */ +struct tree +{ + struct tree *llink; /* Left link; MUST be first field. */ + struct tree *rlink; /* Right link (to larger labels). */ + struct trie *trie; /* Trie node pointed to by this edge. */ + unsigned char label; /* Label on this edge. */ + char balance; /* Difference in depths of subtrees. */ +}; + +/* Node of a trie representing a set of reversed keywords. */ +struct trie +{ + unsigned int accepting; /* Word index of accepted word, or zero. */ + struct tree *links; /* Tree of edges leaving this node. */ + struct trie *parent; /* Parent of this node. */ + struct trie *next; /* List of all trie nodes in level order. */ + struct trie *fail; /* Aho-Corasick failure function. */ + int depth; /* Depth of this node from the root. */ + int shift; /* Shift function for search failures. */ + int maxshift; /* Max shift of self and descendents. */ +}; + +/* Structure returned opaquely to the caller, containing everything. */ +struct kwset +{ + struct obstack obstack; /* Obstack for node allocation. */ + int words; /* Number of words in the trie. */ + struct trie *trie; /* The trie itself. */ + int mind; /* Minimum depth of an accepting node. */ + int maxd; /* Maximum depth of any node. */ + unsigned char delta[NCHAR]; /* Delta table for rapid search. */ + struct trie *next[NCHAR]; /* Table of children of the root. */ + char *target; /* Target string if there's only one. */ + int mind2; /* Used in Boyer-Moore search for one string. */ + char *trans; /* Character translation table. */ +}; + +/* Allocate and initialize a keyword set object, returning an opaque + pointer to it. Return NULL if memory is not available. */ +kwset_t +kwsalloc(trans) + char *trans; +{ + struct kwset *kwset; + + kwset = (struct kwset *) malloc(sizeof (struct kwset)); + if (!kwset) + return 0; + + obstack_init(&kwset->obstack); + kwset->words = 0; + kwset->trie + = (struct trie *) obstack_alloc(&kwset->obstack, sizeof (struct trie)); + if (!kwset->trie) + { + kwsfree((kwset_t) kwset); + return 0; + } + kwset->trie->accepting = 0; + kwset->trie->links = 0; + kwset->trie->parent = 0; + kwset->trie->next = 0; + kwset->trie->fail = 0; + kwset->trie->depth = 0; + kwset->trie->shift = 0; + kwset->mind = INT_MAX; + kwset->maxd = -1; + kwset->target = 0; + kwset->trans = trans; + + return (kwset_t) kwset; +} + +/* Add the given string to the contents of the keyword set. Return NULL + for success, an error message otherwise. */ +char * +kwsincr(kws, text, len) + kwset_t kws; + char *text; + size_t len; +{ + struct kwset *kwset; + register struct trie *trie; + register unsigned char label; + register struct tree *link; + register int depth; + struct tree *links[12]; + enum { L, R } dirs[12]; + struct tree *t, *r, *l, *rl, *lr; + + kwset = (struct kwset *) kws; + trie = kwset->trie; + text += len; + + /* Descend the trie (built of reversed keywords) character-by-character, + installing new nodes when necessary. */ + while (len--) + { + label = kwset->trans ? kwset->trans[(unsigned char) *--text] : *--text; + + /* Descend the tree of outgoing links for this trie node, + looking for the current character and keeping track + of the path followed. */ + link = trie->links; + links[0] = (struct tree *) &trie->links; + dirs[0] = L; + depth = 1; + + while (link && label != link->label) + { + links[depth] = link; + if (label < link->label) + dirs[depth++] = L, link = link->llink; + else + dirs[depth++] = R, link = link->rlink; + } + + /* The current character doesn't have an outgoing link at + this trie node, so build a new trie node and install + a link in the current trie node's tree. */ + if (!link) + { + link = (struct tree *) obstack_alloc(&kwset->obstack, + sizeof (struct tree)); + if (!link) + return "memory exhausted"; + link->llink = 0; + link->rlink = 0; + link->trie = (struct trie *) obstack_alloc(&kwset->obstack, + sizeof (struct trie)); + if (!link->trie) + return "memory exhausted"; + link->trie->accepting = 0; + link->trie->links = 0; + link->trie->parent = trie; + link->trie->next = 0; + link->trie->fail = 0; + link->trie->depth = trie->depth + 1; + link->trie->shift = 0; + link->label = label; + link->balance = 0; + + /* Install the new tree node in its parent. */ + if (dirs[--depth] == L) + links[depth]->llink = link; + else + links[depth]->rlink = link; + + /* Back up the tree fixing the balance flags. */ + while (depth && !links[depth]->balance) + { + if (dirs[depth] == L) + --links[depth]->balance; + else + ++links[depth]->balance; + --depth; + } + + /* Rebalance the tree by pointer rotations if necessary. */ + if (depth && ((dirs[depth] == L && --links[depth]->balance) + || (dirs[depth] == R && ++links[depth]->balance))) + { + switch (links[depth]->balance) + { + case (char) -2: + switch (dirs[depth + 1]) + { + case L: + r = links[depth], t = r->llink, rl = t->rlink; + t->rlink = r, r->llink = rl; + t->balance = r->balance = 0; + break; + case R: + r = links[depth], l = r->llink, t = l->rlink; + rl = t->rlink, lr = t->llink; + t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl; + l->balance = t->balance != 1 ? 0 : -1; + r->balance = t->balance != (char) -1 ? 0 : 1; + t->balance = 0; + break; + } + break; + case 2: + switch (dirs[depth + 1]) + { + case R: + l = links[depth], t = l->rlink, lr = t->llink; + t->llink = l, l->rlink = lr; + t->balance = l->balance = 0; + break; + case L: + l = links[depth], r = l->rlink, t = r->llink; + lr = t->llink, rl = t->rlink; + t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl; + l->balance = t->balance != 1 ? 0 : -1; + r->balance = t->balance != (char) -1 ? 0 : 1; + t->balance = 0; + break; + } + break; + } + + if (dirs[depth - 1] == L) + links[depth - 1]->llink = t; + else + links[depth - 1]->rlink = t; + } + } + + trie = link->trie; + } + + /* Mark the node we finally reached as accepting, encoding the + index number of this word in the keyword set so far. */ + if (!trie->accepting) + trie->accepting = 1 + 2 * kwset->words; + ++kwset->words; + + /* Keep track of the longest and shortest string of the keyword set. */ + if (trie->depth < kwset->mind) + kwset->mind = trie->depth; + if (trie->depth > kwset->maxd) + kwset->maxd = trie->depth; + + return 0; +} + +/* Enqueue the trie nodes referenced from the given tree in the + given queue. */ +static void +enqueue(tree, last) + struct tree *tree; + struct trie **last; +{ + if (!tree) + return; + enqueue(tree->llink, last); + enqueue(tree->rlink, last); + (*last) = (*last)->next = tree->trie; +} + +/* Compute the Aho-Corasick failure function for the trie nodes referenced + from the given tree, given the failure function for their parent as + well as a last resort failure node. */ +static void +treefails(tree, fail, recourse) + register struct tree *tree; + struct trie *fail; + struct trie *recourse; +{ + register struct tree *link; + + if (!tree) + return; + + treefails(tree->llink, fail, recourse); + treefails(tree->rlink, fail, recourse); + + /* Find, in the chain of fails going back to the root, the first + node that has a descendent on the current label. */ + while (fail) + { + link = fail->links; + while (link && tree->label != link->label) + if (tree->label < link->label) + link = link->llink; + else + link = link->rlink; + if (link) + { + tree->trie->fail = link->trie; + return; + } + fail = fail->fail; + } + + tree->trie->fail = recourse; +} + +/* Set delta entries for the links of the given tree such that + the preexisting delta value is larger than the current depth. */ +static void +treedelta(tree, depth, delta) + register struct tree *tree; + register unsigned int depth; + unsigned char delta[]; +{ + if (!tree) + return; + treedelta(tree->llink, depth, delta); + treedelta(tree->rlink, depth, delta); + if (depth < delta[tree->label]) + delta[tree->label] = depth; +} + +/* Return true if A has every label in B. */ +static int +hasevery(a, b) + register struct tree *a; + register struct tree *b; +{ + if (!b) + return 1; + if (!hasevery(a, b->llink)) + return 0; + if (!hasevery(a, b->rlink)) + return 0; + while (a && b->label != a->label) + if (b->label < a->label) + a = a->llink; + else + a = a->rlink; + return !!a; +} + +/* Compute a vector, indexed by character code, of the trie nodes + referenced from the given tree. */ +static void +treenext(tree, next) + struct tree *tree; + struct trie *next[]; +{ + if (!tree) + return; + treenext(tree->llink, next); + treenext(tree->rlink, next); + next[tree->label] = tree->trie; +} + +/* Compute the shift for each trie node, as well as the delta + table and next cache for the given keyword set. */ +char * +kwsprep(kws) + kwset_t kws; +{ + register struct kwset *kwset; + register int i; + register struct trie *curr, *fail; + register char *trans; + unsigned char delta[NCHAR]; + struct trie *last, *next[NCHAR]; + + kwset = (struct kwset *) kws; + + /* Initial values for the delta table; will be changed later. The + delta entry for a given character is the smallest depth of any + node at which an outgoing edge is labeled by that character. */ + if (kwset->mind < 256) + for (i = 0; i < NCHAR; ++i) + delta[i] = kwset->mind; + else + for (i = 0; i < NCHAR; ++i) + delta[i] = 255; + + /* Check if we can use the simple boyer-moore algorithm, instead + of the hairy commentz-walter algorithm. */ + if (kwset->words == 1 && kwset->trans == 0) + { + /* Looking for just one string. Extract it from the trie. */ + kwset->target = obstack_alloc(&kwset->obstack, kwset->mind); + for (i = kwset->mind - 1, curr = kwset->trie; i >= 0; --i) + { + kwset->target[i] = curr->links->label; + curr = curr->links->trie; + } + /* Build the Boyer Moore delta. Boy that's easy compared to CW. */ + for (i = 0; i < kwset->mind; ++i) + delta[(unsigned char) kwset->target[i]] = kwset->mind - (i + 1); + kwset->mind2 = kwset->mind; + /* Find the minimal delta2 shift that we might make after + a backwards match has failed. */ + for (i = 0; i < kwset->mind - 1; ++i) + if (kwset->target[i] == kwset->target[kwset->mind - 1]) + kwset->mind2 = kwset->mind - (i + 1); + } + else + { + /* Traverse the nodes of the trie in level order, simultaneously + computing the delta table, failure function, and shift function. */ + for (curr = last = kwset->trie; curr; curr = curr->next) + { + /* Enqueue the immediate descendents in the level order queue. */ + enqueue(curr->links, &last); + + curr->shift = kwset->mind; + curr->maxshift = kwset->mind; + + /* Update the delta table for the descendents of this node. */ + treedelta(curr->links, curr->depth, delta); + + /* Compute the failure function for the decendents of this node. */ + treefails(curr->links, curr->fail, kwset->trie); + + /* Update the shifts at each node in the current node's chain + of fails back to the root. */ + for (fail = curr->fail; fail; fail = fail->fail) + { + /* If the current node has some outgoing edge that the fail + doesn't, then the shift at the fail should be no larger + than the difference of their depths. */ + if (!hasevery(fail->links, curr->links)) + if (curr->depth - fail->depth < fail->shift) + fail->shift = curr->depth - fail->depth; + + /* If the current node is accepting then the shift at the + fail and its descendents should be no larger than the + difference of their depths. */ + if (curr->accepting && fail->maxshift > curr->depth - fail->depth) + fail->maxshift = curr->depth - fail->depth; + } + } + + /* Traverse the trie in level order again, fixing up all nodes whose + shift exceeds their inherited maxshift. */ + for (curr = kwset->trie->next; curr; curr = curr->next) + { + if (curr->maxshift > curr->parent->maxshift) + curr->maxshift = curr->parent->maxshift; + if (curr->shift > curr->maxshift) + curr->shift = curr->maxshift; + } + + /* Create a vector, indexed by character code, of the outgoing links + from the root node. */ + for (i = 0; i < NCHAR; ++i) + next[i] = 0; + treenext(kwset->trie->links, next); + + if ((trans = kwset->trans) != 0) + for (i = 0; i < NCHAR; ++i) + kwset->next[i] = next[(unsigned char) trans[i]]; + else + for (i = 0; i < NCHAR; ++i) + kwset->next[i] = next[i]; + } + + /* Fix things up for any translation table. */ + if ((trans = kwset->trans) != 0) + for (i = 0; i < NCHAR; ++i) + kwset->delta[i] = delta[(unsigned char) trans[i]]; + else + for (i = 0; i < NCHAR; ++i) + kwset->delta[i] = delta[i]; + + return 0; +} + +#define U(C) ((unsigned char) (C)) + +/* Fast boyer-moore search. */ +static char * +bmexec(kws, text, size) + kwset_t kws; + char *text; + size_t size; +{ + struct kwset *kwset; + register unsigned char *d1; + register char *ep, *sp, *tp; + register int d, gc, i, len, md2; + + kwset = (struct kwset *) kws; + len = kwset->mind; + + if (len == 0) + return text; + if (len > size) + return 0; + if (len == 1) + return memchr(text, kwset->target[0], size); + + d1 = kwset->delta; + sp = kwset->target + len; + gc = U(sp[-2]); + md2 = kwset->mind2; + tp = text + len; + + /* Significance of 12: 1 (initial offset) + 10 (skip loop) + 1 (md2). */ + if (size > 12 * len) + /* 11 is not a bug, the initial offset happens only once. */ + for (ep = text + size - 11 * len;;) + { + while (tp <= ep) + { + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + if (d == 0) + goto found; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + if (d == 0) + goto found; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + if (d == 0) + goto found; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + } + break; + found: + if (U(tp[-2]) == gc) + { + for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i) + ; + if (i > len) + return tp - len; + } + tp += md2; + } + + /* Now we have only a few characters left to search. We + carefully avoid ever producing an out-of-bounds pointer. */ + ep = text + size; + d = d1[U(tp[-1])]; + while (d <= ep - tp) + { + d = d1[U((tp += d)[-1])]; + if (d != 0) + continue; + if (U(tp[-2]) == gc) + { + for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i) + ; + if (i > len) + return tp - len; + } + d = md2; + } + + return 0; +} + +/* Hairy multiple string search. */ +static char * +cwexec(kws, text, len, kwsmatch) + kwset_t kws; + char *text; + size_t len; + struct kwsmatch *kwsmatch; +{ + struct kwset *kwset; + struct trie **next, *trie, *accept; + char *beg, *lim, *mch, *lmch; + register unsigned char c, *delta; + register int d; + register char *end, *qlim; + register struct tree *tree; + register char *trans; + + /* Initialize register copies and look for easy ways out. */ + kwset = (struct kwset *) kws; + if (len < kwset->mind) + return 0; + next = kwset->next; + delta = kwset->delta; + trans = kwset->trans; + lim = text + len; + end = text; + if ((d = kwset->mind) != 0) + mch = 0; + else + { + mch = text, accept = kwset->trie; + goto match; + } + + if (len >= 4 * kwset->mind) + qlim = lim - 4 * kwset->mind; + else + qlim = 0; + + while (lim - end >= d) + { + if (qlim && end <= qlim) + { + end += d - 1; + while ((d = delta[c = *end]) && end < qlim) + { + end += d; + end += delta[(unsigned char) *end]; + end += delta[(unsigned char) *end]; + } + ++end; + } + else + d = delta[c = (end += d)[-1]]; + if (d) + continue; + beg = end - 1; + trie = next[c]; + if (trie->accepting) + { + mch = beg; + accept = trie; + } + d = trie->shift; + while (beg > text) + { + c = trans ? trans[(unsigned char) *--beg] : *--beg; + tree = trie->links; + while (tree && c != tree->label) + if (c < tree->label) + tree = tree->llink; + else + tree = tree->rlink; + if (tree) + { + trie = tree->trie; + if (trie->accepting) + { + mch = beg; + accept = trie; + } + } + else + break; + d = trie->shift; + } + if (mch) + goto match; + } + return 0; + + match: + /* Given a known match, find the longest possible match anchored + at or before its starting point. This is nearly a verbatim + copy of the preceding main search loops. */ + if (lim - mch > kwset->maxd) + lim = mch + kwset->maxd; + lmch = 0; + d = 1; + while (lim - end >= d) + { + if ((d = delta[c = (end += d)[-1]]) != 0) + continue; + beg = end - 1; + if (!(trie = next[c])) + { + d = 1; + continue; + } + if (trie->accepting && beg <= mch) + { + lmch = beg; + accept = trie; + } + d = trie->shift; + while (beg > text) + { + c = trans ? trans[(unsigned char) *--beg] : *--beg; + tree = trie->links; + while (tree && c != tree->label) + if (c < tree->label) + tree = tree->llink; + else + tree = tree->rlink; + if (tree) + { + trie = tree->trie; + if (trie->accepting && beg <= mch) + { + lmch = beg; + accept = trie; + } + } + else + break; + d = trie->shift; + } + if (lmch) + { + mch = lmch; + goto match; + } + if (!d) + d = 1; + } + + if (kwsmatch) + { + kwsmatch->index = accept->accepting / 2; + kwsmatch->beg[0] = mch; + kwsmatch->size[0] = accept->depth; + } + return mch; +} + +/* Search through the given text for a match of any member of the + given keyword set. Return a pointer to the first character of + the matching substring, or NULL if no match is found. If FOUNDLEN + is non-NULL store in the referenced location the length of the + matching substring. Similarly, if FOUNDIDX is non-NULL, store + in the referenced location the index number of the particular + keyword matched. */ +char * +kwsexec(kws, text, size, kwsmatch) + kwset_t kws; + char *text; + size_t size; + struct kwsmatch *kwsmatch; +{ + struct kwset *kwset; + char *ret; + + kwset = (struct kwset *) kws; + if (kwset->words == 1 && kwset->trans == 0) + { + ret = bmexec(kws, text, size); + if (kwsmatch != 0 && ret != 0) + { + kwsmatch->index = 0; + kwsmatch->beg[0] = ret; + kwsmatch->size[0] = kwset->mind; + } + return ret; + } + else + return cwexec(kws, text, size, kwsmatch); +} + +/* Free the components of the given keyword set. */ +void +kwsfree(kws) + kwset_t kws; +{ + struct kwset *kwset; + + kwset = (struct kwset *) kws; + obstack_free(&kwset->obstack, 0); + free(kws); +} diff --git a/gnu/usr.bin/grep/kwset.h b/gnu/usr.bin/grep/kwset.h new file mode 100644 index 000000000000..95f62e715d98 --- /dev/null +++ b/gnu/usr.bin/grep/kwset.h @@ -0,0 +1,69 @@ +/* kwset.h - header declaring the keyword set library. + Copyright 1989 Free Software Foundation + Written August 1989 by Mike Haertel. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +struct kwsmatch +{ + int index; /* Index number of matching keyword. */ + char *beg[1]; /* Begin pointer for each submatch. */ + size_t size[1]; /* Length of each submatch. */ +}; + +#if __STDC__ + +typedef void *kwset_t; + +/* Return an opaque pointer to a newly allocated keyword set, or NULL + if enough memory cannot be obtained. The argument if non-NULL + specifies a table of character translations to be applied to all + pattern and search text. */ +extern kwset_t kwsalloc(char *); + +/* Incrementally extend the keyword set to include the given string. + Return NULL for success, or an error message. Remember an index + number for each keyword included in the set. */ +extern char *kwsincr(kwset_t, char *, size_t); + +/* When the keyword set has been completely built, prepare it for + use. Return NULL for success, or an error message. */ +extern char *kwsprep(kwset_t); + +/* Search through the given buffer for a member of the keyword set. + Return a pointer to the leftmost longest match found, or NULL if + no match is found. If foundlen is non-NULL, store the length of + the matching substring in the integer it points to. Similarly, + if foundindex is non-NULL, store the index of the particular + keyword found therein. */ +extern char *kwsexec(kwset_t, char *, size_t, struct kwsmatch *); + +/* Deallocate the given keyword set and all its associated storage. */ +extern void kwsfree(kwset_t); + +#else + +typedef char *kwset_t; + +extern kwset_t kwsalloc(); +extern char *kwsincr(); +extern char *kwsprep(); +extern char *kwsexec(); +extern void kwsfree(); + +#endif diff --git a/gnu/usr.bin/grep/obstack.c b/gnu/usr.bin/grep/obstack.c new file mode 100644 index 000000000000..7b9d3b9046d4 --- /dev/null +++ b/gnu/usr.bin/grep/obstack.c @@ -0,0 +1,454 @@ +/* obstack.c - subroutines used implicitly by object stack macros + Copyright (C) 1988, 1993 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "obstack.h" + +/* This is just to get __GNU_LIBRARY__ defined. */ +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +#ifdef __STDC__ +#define POINTER void * +#else +#define POINTER char * +#endif + +/* Determine default alignment. */ +struct fooalign {char x; double d;}; +#define DEFAULT_ALIGNMENT \ + ((PTR_INT_TYPE) ((char *)&((struct fooalign *) 0)->d - (char *)0)) +/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. + But in fact it might be less smart and round addresses to as much as + DEFAULT_ROUNDING. So we prepare for it to do that. */ +union fooround {long x; double d;}; +#define DEFAULT_ROUNDING (sizeof (union fooround)) + +/* When we copy a long block of data, this is the unit to do it with. + On some machines, copying successive ints does not work; + in such a case, redefine COPYING_UNIT to `long' (if that works) + or `char' as a last resort. */ +#ifndef COPYING_UNIT +#define COPYING_UNIT int +#endif + +/* The non-GNU-C macros copy the obstack into this global variable + to avoid multiple evaluation. */ + +struct obstack *_obstack; + +/* Define a macro that either calls functions with the traditional malloc/free + calling interface, or calls functions with the mmalloc/mfree interface + (that adds an extra first argument), based on the state of use_extra_arg. + For free, do not use ?:, since some compilers, like the MIPS compilers, + do not allow (expr) ? void : void. */ + +#define CALL_CHUNKFUN(h, size) \ + (((h) -> use_extra_arg) \ + ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \ + : (*(h)->chunkfun) ((size))) + +#define CALL_FREEFUN(h, old_chunk) \ + do { \ + if ((h) -> use_extra_arg) \ + (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \ + else \ + (*(h)->freefun) ((old_chunk)); \ + } while (0) + + +/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). + Objects start on multiples of ALIGNMENT (0 means use default). + CHUNKFUN is the function to use to allocate chunks, + and FREEFUN the function to free them. */ + +void +_obstack_begin (h, size, alignment, chunkfun, freefun) + struct obstack *h; + int size; + int alignment; + POINTER (*chunkfun) (); + void (*freefun) (); +{ + register struct _obstack_chunk* chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun; + h->freefun = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->use_extra_arg = 0; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + h->next_free = h->object_base = chunk->contents; + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; +} + +void +_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg) + struct obstack *h; + int size; + int alignment; + POINTER (*chunkfun) (); + void (*freefun) (); + POINTER arg; +{ + register struct _obstack_chunk* chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun; + h->freefun = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->extra_arg = arg; + h->use_extra_arg = 1; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + h->next_free = h->object_base = chunk->contents; + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; +} + +/* Allocate a new current chunk for the obstack *H + on the assumption that LENGTH bytes need to be added + to the current object, or a new object of length LENGTH allocated. + Copies any partial object from the end of the old chunk + to the beginning of the new one. */ + +void +_obstack_newchunk (h, length) + struct obstack *h; + int length; +{ + register struct _obstack_chunk* old_chunk = h->chunk; + register struct _obstack_chunk* new_chunk; + register long new_size; + register int obj_size = h->next_free - h->object_base; + register int i; + int already; + + /* Compute size for new chunk. */ + new_size = (obj_size + length) + (obj_size >> 3) + 100; + if (new_size < h->chunk_size) + new_size = h->chunk_size; + + /* Allocate and initialize the new chunk. */ + new_chunk = h->chunk = CALL_CHUNKFUN (h, new_size); + new_chunk->prev = old_chunk; + new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; + + /* Move the existing object to the new chunk. + Word at a time is fast and is safe if the object + is sufficiently aligned. */ + if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT) + { + for (i = obj_size / sizeof (COPYING_UNIT) - 1; + i >= 0; i--) + ((COPYING_UNIT *)new_chunk->contents)[i] + = ((COPYING_UNIT *)h->object_base)[i]; + /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT, + but that can cross a page boundary on a machine + which does not do strict alignment for COPYING_UNITS. */ + already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT); + } + else + already = 0; + /* Copy remaining bytes one by one. */ + for (i = already; i < obj_size; i++) + new_chunk->contents[i] = h->object_base[i]; + + /* If the object just copied was the only data in OLD_CHUNK, + free that chunk and remove it from the chain. + But not if that chunk might contain an empty object. */ + if (h->object_base == old_chunk->contents && ! h->maybe_empty_object) + { + new_chunk->prev = old_chunk->prev; + CALL_FREEFUN (h, old_chunk); + } + + h->object_base = new_chunk->contents; + h->next_free = h->object_base + obj_size; + /* The new chunk certainly contains no empty object yet. */ + h->maybe_empty_object = 0; +} + +/* Return nonzero if object OBJ has been allocated from obstack H. + This is here for debugging. + If you use it in a program, you are probably losing. */ + +int +_obstack_allocated_p (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + /* We use >= rather than > since the object cannot be exactly at + the beginning of the chunk but might be an empty object exactly + at the end of an adjacent chunk. */ + while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj)) + { + plp = lp->prev; + lp = plp; + } + return lp != 0; +} + +/* Free objects in obstack H, including OBJ and everything allocate + more recently than OBJ. If OBJ is zero, free everything in H. */ + +#undef obstack_free + +/* This function has two names with identical definitions. + This is the first one, called from non-ANSI code. */ + +void +_obstack_free (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = h->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj)) + { + plp = lp->prev; + CALL_FREEFUN (h, lp); + lp = plp; + /* If we switch chunks, we can't tell whether the new current + chunk contains an empty object, so assume that it may. */ + h->maybe_empty_object = 1; + } + if (lp) + { + h->object_base = h->next_free = (char *)(obj); + h->chunk_limit = lp->limit; + h->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +/* This function is used from ANSI code. */ + +void +obstack_free (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = h->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj)) + { + plp = lp->prev; + CALL_FREEFUN (h, lp); + lp = plp; + /* If we switch chunks, we can't tell whether the new current + chunk contains an empty object, so assume that it may. */ + h->maybe_empty_object = 1; + } + if (lp) + { + h->object_base = h->next_free = (char *)(obj); + h->chunk_limit = lp->limit; + h->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +#if 0 +/* These are now turned off because the applications do not use it + and it uses bcopy via obstack_grow, which causes trouble on sysV. */ + +/* Now define the functional versions of the obstack macros. + Define them to simply use the corresponding macros to do the job. */ + +#ifdef __STDC__ +/* These function definitions do not work with non-ANSI preprocessors; + they won't pass through the macro names in parentheses. */ + +/* The function names appear in parentheses in order to prevent + the macro-definitions of the names from being expanded there. */ + +POINTER (obstack_base) (obstack) + struct obstack *obstack; +{ + return obstack_base (obstack); +} + +POINTER (obstack_next_free) (obstack) + struct obstack *obstack; +{ + return obstack_next_free (obstack); +} + +int (obstack_object_size) (obstack) + struct obstack *obstack; +{ + return obstack_object_size (obstack); +} + +int (obstack_room) (obstack) + struct obstack *obstack; +{ + return obstack_room (obstack); +} + +void (obstack_grow) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow (obstack, pointer, length); +} + +void (obstack_grow0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow0 (obstack, pointer, length); +} + +void (obstack_1grow) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow (obstack, character); +} + +void (obstack_blank) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank (obstack, length); +} + +void (obstack_1grow_fast) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow_fast (obstack, character); +} + +void (obstack_blank_fast) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank_fast (obstack, length); +} + +POINTER (obstack_finish) (obstack) + struct obstack *obstack; +{ + return obstack_finish (obstack); +} + +POINTER (obstack_alloc) (obstack, length) + struct obstack *obstack; + int length; +{ + return obstack_alloc (obstack, length); +} + +POINTER (obstack_copy) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy (obstack, pointer, length); +} + +POINTER (obstack_copy0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy0 (obstack, pointer, length); +} + +#endif /* __STDC__ */ + +#endif /* 0 */ + +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/gnu/usr.bin/grep/obstack.h b/gnu/usr.bin/grep/obstack.h new file mode 100644 index 000000000000..09d2c324b38c --- /dev/null +++ b/gnu/usr.bin/grep/obstack.h @@ -0,0 +1,484 @@ +/* obstack.h - object stack macros + Copyright (C) 1988, 1992 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Summary: + +All the apparent functions defined here are macros. The idea +is that you would use these pre-tested macros to solve a +very specific set of problems, and they would run fast. +Caution: no side-effects in arguments please!! They may be +evaluated MANY times!! + +These macros operate a stack of objects. Each object starts life +small, and may grow to maturity. (Consider building a word syllable +by syllable.) An object can move while it is growing. Once it has +been "finished" it never changes address again. So the "top of the +stack" is typically an immature growing object, while the rest of the +stack is of mature, fixed size and fixed address objects. + +These routines grab large chunks of memory, using a function you +supply, called `obstack_chunk_alloc'. On occasion, they free chunks, +by calling `obstack_chunk_free'. You must define them and declare +them before using any obstack macros. + +Each independent stack is represented by a `struct obstack'. +Each of the obstack macros expects a pointer to such a structure +as the first argument. + +One motivation for this package is the problem of growing char strings +in symbol tables. Unless you are "fascist pig with a read-only mind" +--Gosper's immortal quote from HAKMEM item 154, out of context--you +would not like to put any arbitrary upper limit on the length of your +symbols. + +In practice this often means you will build many short symbols and a +few long symbols. At the time you are reading a symbol you don't know +how long it is. One traditional method is to read a symbol into a +buffer, realloc()ating the buffer every time you try to read a symbol +that is longer than the buffer. This is beaut, but you still will +want to copy the symbol from the buffer to a more permanent +symbol-table entry say about half the time. + +With obstacks, you can work differently. Use one obstack for all symbol +names. As you read a symbol, grow the name in the obstack gradually. +When the name is complete, finalize it. Then, if the symbol exists already, +free the newly read name. + +The way we do this is to take a large chunk, allocating memory from +low addresses. When you want to build a symbol in the chunk you just +add chars above the current "high water mark" in the chunk. When you +have finished adding chars, because you got to the end of the symbol, +you know how long the chars are, and you can create a new object. +Mostly the chars will not burst over the highest address of the chunk, +because you would typically expect a chunk to be (say) 100 times as +long as an average object. + +In case that isn't clear, when we have enough chars to make up +the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) +so we just point to it where it lies. No moving of chars is +needed and this is the second win: potentially long strings need +never be explicitly shuffled. Once an object is formed, it does not +change its address during its lifetime. + +When the chars burst over a chunk boundary, we allocate a larger +chunk, and then copy the partly formed object from the end of the old +chunk to the beginning of the new larger chunk. We then carry on +accreting characters to the end of the object as we normally would. + +A special macro is provided to add a single char at a time to a +growing object. This allows the use of register variables, which +break the ordinary 'growth' macro. + +Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can `unwind' an obstack + back to a previous state. (You may remove objects much + as you would with a stack.) +*/ + + +/* Don't do the contents of this file more than once. */ + +#ifndef __OBSTACKS__ +#define __OBSTACKS__ + +/* We use subtraction of (char *)0 instead of casting to int + because on word-addressable machines a simple cast to int + may ignore the byte-within-word field of the pointer. */ + +#ifndef __PTR_TO_INT +#define __PTR_TO_INT(P) ((P) - (char *)0) +#endif + +#ifndef __INT_TO_PTR +#define __INT_TO_PTR(P) ((P) + (char *)0) +#endif + +/* We need the type of the resulting object. In ANSI C it is ptrdiff_t + but in traditional C it is usually long. If we are in ANSI C and + don't already have ptrdiff_t get it. */ + +#if defined (__STDC__) && ! defined (offsetof) +#if defined (__GNUC__) && defined (IN_GCC) +/* On Next machine, the system's stddef.h screws up if included + after we have defined just ptrdiff_t, so include all of gstddef.h. + Otherwise, define just ptrdiff_t, which is all we need. */ +#ifndef __NeXT__ +#define __need_ptrdiff_t +#endif + +/* While building GCC, the stddef.h that goes with GCC has this name. */ +#include "gstddef.h" +#else +#include +#endif +#endif + +#ifdef __STDC__ +#define PTR_INT_TYPE ptrdiff_t +#else +#define PTR_INT_TYPE long +#endif + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + long chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + PTR_INT_TYPE temp; /* Temporary for some macros. */ + int alignment_mask; /* Mask of alignment for each object. */ + struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */ + void (*freefun) (); /* User's function to free a chunk. */ + char *extra_arg; /* first arg for chunk alloc/dealloc funcs */ + unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */ + unsigned maybe_empty_object:1;/* There is a possibility that the current + chunk contains a zero-length object. This + prevents freeing the chunk if we allocate + a bigger chunk to replace it. */ +}; + +/* Declare the external functions we use; they are in obstack.c. */ + +#ifdef __STDC__ +extern void _obstack_newchunk (struct obstack *, int); +extern void _obstack_free (struct obstack *, void *); +extern void _obstack_begin (struct obstack *, int, int, + void *(*) (), void (*) ()); +extern void _obstack_begin_1 (struct obstack *, int, int, + void *(*) (), void (*) (), void *); +#else +extern void _obstack_newchunk (); +extern void _obstack_free (); +extern void _obstack_begin (); +extern void _obstack_begin_1 (); +#endif + +#ifdef __STDC__ + +/* Do the function-declarations after the structs + but before defining the macros. */ + +void obstack_init (struct obstack *obstack); + +void * obstack_alloc (struct obstack *obstack, int size); + +void * obstack_copy (struct obstack *obstack, void *address, int size); +void * obstack_copy0 (struct obstack *obstack, void *address, int size); + +void obstack_free (struct obstack *obstack, void *block); + +void obstack_blank (struct obstack *obstack, int size); + +void obstack_grow (struct obstack *obstack, void *data, int size); +void obstack_grow0 (struct obstack *obstack, void *data, int size); + +void obstack_1grow (struct obstack *obstack, int data_char); +void obstack_ptr_grow (struct obstack *obstack, void *data); +void obstack_int_grow (struct obstack *obstack, int data); + +void * obstack_finish (struct obstack *obstack); + +int obstack_object_size (struct obstack *obstack); + +int obstack_room (struct obstack *obstack); +void obstack_1grow_fast (struct obstack *obstack, int data_char); +void obstack_ptr_grow_fast (struct obstack *obstack, void *data); +void obstack_int_grow_fast (struct obstack *obstack, int data); +void obstack_blank_fast (struct obstack *obstack, int size); + +void * obstack_base (struct obstack *obstack); +void * obstack_next_free (struct obstack *obstack); +int obstack_alignment_mask (struct obstack *obstack); +int obstack_chunk_size (struct obstack *obstack); + +#endif /* __STDC__ */ + +/* Non-ANSI C cannot really support alternative functions for these macros, + so we do not declare them. */ + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +#define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free) + +#define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free) + +#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \ + _obstack_begin ((h), (size), (alignment), \ + (void *(*) ()) (chunkfun), (void (*) ()) (freefun)) + +#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \ + _obstack_begin_1 ((h), (size), (alignment), \ + (void *(*) ()) (chunkfun), (void (*) ()) (freefun), (arg)) + +#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar) + +#define obstack_blank_fast(h,n) ((h)->next_free += (n)) + +#if defined (__GNUC__) && defined (__STDC__) +#if __GNUC__ < 2 || defined(NeXT) +#define __extension__ +#endif + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the `temp' slot, to make faster code. */ + +#define obstack_object_size(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->next_free - __o->object_base); }) + +#define obstack_room(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->chunk_limit - __o->next_free); }) + +/* Note that the call to _obstack_newchunk is enclosed in (..., 0) + so that we can avoid having void expressions + in the arms of the conditional expression. + Casting the third operand to void was tried before, + but some compilers won't accept it. */ +#define obstack_grow(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, __len), 0) : 0); \ + bcopy (where, __o->next_free, __len); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_grow0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len + 1 > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, __len + 1), 0) : 0), \ + bcopy (where, __o->next_free, __len), \ + __o->next_free += __len, \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +#define obstack_1grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + 1 > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, 1), 0) : 0), \ + *(__o->next_free)++ = (datum); \ + (void) 0; }) + +/* These assume that the obstack alignment is good enough for pointers or ints, + and that the data added so far to the current object + shares that much alignment. */ + +#define obstack_ptr_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + sizeof (void *) > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, sizeof (void *)), 0) : 0), \ + *((void **)__o->next_free)++ = ((void *)datum); \ + (void) 0; }) + +#define obstack_int_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + sizeof (int) > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, sizeof (int)), 0) : 0), \ + *((int *)__o->next_free)++ = ((int)datum); \ + (void) 0; }) + +#define obstack_ptr_grow_fast(h,aptr) (*((void **)(h)->next_free)++ = (void *)aptr) +#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) + +#define obstack_blank(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->chunk_limit - __o->next_free < __len) \ + ? (_obstack_newchunk (__o, __len), 0) : 0); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_alloc(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +#define obstack_copy(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +#define obstack_copy0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +/* The local variable is named __o1 to avoid a name conflict + when obstack_blank is called. */ +#define obstack_finish(OBSTACK) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + void *value = (void *) __o1->object_base; \ + if (__o1->next_free == value) \ + __o1->maybe_empty_object = 1; \ + __o1->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\ + & ~ (__o1->alignment_mask)); \ + ((__o1->next_free - (char *)__o1->chunk \ + > __o1->chunk_limit - (char *)__o1->chunk) \ + ? (__o1->next_free = __o1->chunk_limit) : 0); \ + __o1->object_base = __o1->next_free; \ + value; }) + +#define obstack_free(OBSTACK, OBJ) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ + __o->next_free = __o->object_base = __obj; \ + else (obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ or not __STDC__ */ + +#define obstack_object_size(h) \ + (unsigned) ((h)->next_free - (h)->object_base) + +#define obstack_room(h) \ + (unsigned) ((h)->chunk_limit - (h)->next_free) + +#define obstack_grow(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp) + +#define obstack_grow0(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp, \ + *((h)->next_free)++ = 0) + +#define obstack_1grow(h,datum) \ +( (((h)->next_free + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), 1), 0) : 0), \ + *((h)->next_free)++ = (datum)) + +#define obstack_ptr_grow(h,datum) \ +( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \ + *((char **)(((h)->next_free+=sizeof(char *))-sizeof(char *))) = ((char *)datum)) + +#define obstack_int_grow(h,datum) \ +( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \ + *((int *)(((h)->next_free+=sizeof(int))-sizeof(int))) = ((int)datum)) + +#define obstack_ptr_grow_fast(h,aptr) (*((char **)(h)->next_free)++ = (char *)aptr) +#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) + +#define obstack_blank(h,length) \ +( (h)->temp = (length), \ + (((h)->chunk_limit - (h)->next_free < (h)->temp) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + (h)->next_free += (h)->temp) + +#define obstack_alloc(h,length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +#define obstack_copy(h,where,length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_copy0(h,where,length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_finish(h) \ +( ((h)->next_free == (h)->object_base \ + ? (((h)->maybe_empty_object = 1), 0) \ + : 0), \ + (h)->temp = __PTR_TO_INT ((h)->object_base), \ + (h)->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \ + & ~ ((h)->alignment_mask)), \ + (((h)->next_free - (char *)(h)->chunk \ + > (h)->chunk_limit - (char *)(h)->chunk) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + __INT_TO_PTR ((h)->temp)) + +#ifdef __STDC__ +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0))) +#else +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0))) +#endif + +#endif /* not __GNUC__ or not __STDC__ */ + +#endif /* not __OBSTACKS__ */ diff --git a/gnu/usr.bin/grep/search.c b/gnu/usr.bin/grep/search.c new file mode 100644 index 000000000000..f0e3d5cfc35a --- /dev/null +++ b/gnu/usr.bin/grep/search.c @@ -0,0 +1,481 @@ +/* search.c - searching subroutines using dfa, kwset and regex for grep. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written August 1992 by Mike Haertel. */ + +#include + +#ifdef STDC_HEADERS +#include +#include +#else +#define UCHAR_MAX 255 +#include +extern char *malloc(); +#endif + +#ifdef HAVE_MEMCHR +#include +#ifdef NEED_MEMORY_H +#include +#endif +#else +#ifdef __STDC__ +extern void *memchr(); +#else +extern char *memchr(); +#endif +#endif + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#undef bcopy +#define bcopy(s, d, n) memcpy((d), (s), (n)) +#endif + +#if defined(isascii) && !defined(__FreeBSD__) +#define ISALNUM(C) (isascii(C) && isalnum(C)) +#define ISUPPER(C) (isascii(C) && isupper(C)) +#else +#define ISALNUM(C) isalnum((unsigned char)C) +#define ISUPPER(C) isupper((unsigned char)C) +#endif + +#define TOLOWER(C) (ISUPPER(C) ? tolower((unsigned char)C) : (C)) + +#include "grep.h" +#include "dfa.h" +#include "kwset.h" +#include "gnuregex.h" + +#define NCHAR (UCHAR_MAX + 1) + +#if __STDC__ +static void Gcompile(char *, size_t); +static void Ecompile(char *, size_t); +static char *EGexecute(char *, size_t, char **); +static void Fcompile(char *, size_t); +static char *Fexecute(char *, size_t, char **); +#else +static void Gcompile(); +static void Ecompile(); +static char *EGexecute(); +static void Fcompile(); +static char *Fexecute(); +#endif + +/* Here is the matchers vector for the main program. */ +struct matcher matchers[] = { + { "default", Gcompile, EGexecute }, + { "grep", Gcompile, EGexecute }, + { "ggrep", Gcompile, EGexecute }, + { "egrep", Ecompile, EGexecute }, + { "posix-egrep", Ecompile, EGexecute }, + { "gegrep", Ecompile, EGexecute }, + { "fgrep", Fcompile, Fexecute }, + { "gfgrep", Fcompile, Fexecute }, + { 0, 0, 0 }, +}; + +/* For -w, we also consider _ to be word constituent. */ +#define WCHAR(C) (ISALNUM(C) || (C) == '_') + +/* DFA compiled regexp. */ +static struct dfa dfa; + +/* Regex compiled regexp. */ +static struct re_pattern_buffer regex; + +/* KWset compiled pattern. For Ecompile and Gcompile, we compile + a list of strings, at least one of which is known to occur in + any string matching the regexp. */ +static kwset_t kwset; + +/* Last compiled fixed string known to exactly match the regexp. + If kwsexec() returns < lastexact, then we don't need to + call the regexp matcher at all. */ +static int lastexact; + +void +dfaerror(mesg) + char *mesg; +{ + fatal(mesg, 0); +} + +static void +kwsinit() +{ + static char trans[NCHAR]; + int i; + + if (match_icase) + for (i = 0; i < NCHAR; ++i) + trans[i] = TOLOWER(i); + + if (!(kwset = kwsalloc(match_icase ? trans : (char *) 0))) + fatal("memory exhausted", 0); +} + +/* If the DFA turns out to have some set of fixed strings one of + which must occur in the match, then we build a kwset matcher + to find those strings, and thus quickly filter out impossible + matches. */ +static void +kwsmusts() +{ + struct dfamust *dm; + char *err; + + if (dfa.musts) + { + kwsinit(); + /* First, we compile in the substrings known to be exact + matches. The kwset matcher will return the index + of the matching string that it chooses. */ + for (dm = dfa.musts; dm; dm = dm->next) + { + if (!dm->exact) + continue; + ++lastexact; + if ((err = kwsincr(kwset, dm->must, strlen(dm->must))) != 0) + fatal(err, 0); + } + /* Now, we compile the substrings that will require + the use of the regexp matcher. */ + for (dm = dfa.musts; dm; dm = dm->next) + { + if (dm->exact) + continue; + if ((err = kwsincr(kwset, dm->must, strlen(dm->must))) != 0) + fatal(err, 0); + } + if ((err = kwsprep(kwset)) != 0) + fatal(err, 0); + } +} + +static void +Gcompile(pattern, size) + char *pattern; + size_t size; +{ +#ifdef __STDC__ + const +#endif + char *err; + + re_set_syntax(RE_SYNTAX_GREP | RE_HAT_LISTS_NOT_NEWLINE); + dfasyntax(RE_SYNTAX_GREP | RE_HAT_LISTS_NOT_NEWLINE, match_icase); + + if ((err = re_compile_pattern(pattern, size, ®ex)) != 0) + fatal(err, 0); + + dfainit(&dfa); + + /* In the match_words and match_lines cases, we use a different pattern + for the DFA matcher that will quickly throw out cases that won't work. + Then if DFA succeeds we do some hairy stuff using the regex matcher + to decide whether the match should really count. */ + if (match_words || match_lines) + { + /* In the whole-word case, we use the pattern: + (^|[^A-Za-z_])(userpattern)([^A-Za-z_]|$). + In the whole-line case, we use the pattern: + ^(userpattern)$. + BUG: Using [A-Za-z_] is locale-dependent! */ + + char *n = malloc(size + 50); + int i = 0; + + strcpy(n, ""); + + if (match_lines) + strcpy(n, "^\\("); + if (match_words) + strcpy(n, "\\(^\\|[^0-9A-Za-z_]\\)\\("); + + i = strlen(n); + bcopy(pattern, n + i, size); + i += size; + + if (match_words) + strcpy(n + i, "\\)\\([^0-9A-Za-z_]\\|$\\)"); + if (match_lines) + strcpy(n + i, "\\)$"); + + i += strlen(n + i); + dfacomp(n, i, &dfa, 1); + } + else + dfacomp(pattern, size, &dfa, 1); + + kwsmusts(); +} + +static void +Ecompile(pattern, size) + char *pattern; + size_t size; +{ +#ifdef __STDC__ + const +#endif + char *err; + + if (strcmp(matcher, "posix-egrep") == 0) + { + re_set_syntax(RE_SYNTAX_POSIX_EGREP); + dfasyntax(RE_SYNTAX_POSIX_EGREP, match_icase); + } + else + { + re_set_syntax(RE_SYNTAX_EGREP); + dfasyntax(RE_SYNTAX_EGREP, match_icase); + } + + if ((err = re_compile_pattern(pattern, size, ®ex)) != 0) + fatal(err, 0); + + dfainit(&dfa); + + /* In the match_words and match_lines cases, we use a different pattern + for the DFA matcher that will quickly throw out cases that won't work. + Then if DFA succeeds we do some hairy stuff using the regex matcher + to decide whether the match should really count. */ + if (match_words || match_lines) + { + /* In the whole-word case, we use the pattern: + (^|[^A-Za-z_])(userpattern)([^A-Za-z_]|$). + In the whole-line case, we use the pattern: + ^(userpattern)$. + BUG: Using [A-Za-z_] is locale-dependent! */ + + char *n = malloc(size + 50); + int i = 0; + + strcpy(n, ""); + + if (match_lines) + strcpy(n, "^("); + if (match_words) + strcpy(n, "(^|[^0-9A-Za-z_])("); + + i = strlen(n); + bcopy(pattern, n + i, size); + i += size; + + if (match_words) + strcpy(n + i, ")([^0-9A-Za-z_]|$)"); + if (match_lines) + strcpy(n + i, ")$"); + + i += strlen(n + i); + dfacomp(n, i, &dfa, 1); + } + else + dfacomp(pattern, size, &dfa, 1); + + kwsmusts(); +} + +static char * +EGexecute(buf, size, endp) + char *buf; + size_t size; + char **endp; +{ + register char *buflim, *beg, *end, save; + int backref, start, len; + struct kwsmatch kwsm; + static struct re_registers regs; /* This is static on account of a BRAIN-DEAD + Q@#%!# library interface in regex.c. */ + + buflim = buf + size; + + for (beg = end = buf; end < buflim; beg = end + 1) + { + if (kwset) + { + /* Find a possible match using the KWset matcher. */ + beg = kwsexec(kwset, beg, buflim - beg, &kwsm); + if (!beg) + goto failure; + /* Narrow down to the line containing the candidate, and + run it through DFA. */ + end = memchr(beg, '\n', buflim - beg); + if (!end) + end = buflim; + while (beg > buf && beg[-1] != '\n') + --beg; + save = *end; + if (kwsm.index < lastexact) + goto success; + if (!dfaexec(&dfa, beg, end, 0, (int *) 0, &backref)) + { + *end = save; + continue; + } + *end = save; + /* Successful, no backreferences encountered. */ + if (!backref) + goto success; + } + else + { + /* No good fixed strings; start with DFA. */ + save = *buflim; + beg = dfaexec(&dfa, beg, buflim, 0, (int *) 0, &backref); + *buflim = save; + if (!beg) + goto failure; + /* Narrow down to the line we've found. */ + end = memchr(beg, '\n', buflim - beg); + if (!end) + end = buflim; + while (beg > buf && beg[-1] != '\n') + --beg; + /* Successful, no backreferences encountered! */ + if (!backref) + goto success; + } + /* If we've made it to this point, this means DFA has seen + a probable match, and we need to run it through Regex. */ + regex.not_eol = 0; + if ((start = re_search(®ex, beg, end - beg, 0, end - beg, ®s)) >= 0) + { + len = regs.end[0] - start; + if (!match_lines && !match_words || match_lines && len == end - beg) + goto success; + /* If -w, check if the match aligns with word boundaries. + We do this iteratively because: + (a) the line may contain more than one occurence of the pattern, and + (b) Several alternatives in the pattern might be valid at a given + point, and we may need to consider a shorter one to find a word + boundary. */ + if (match_words) + while (start >= 0) + { + if ((start == 0 || !WCHAR(beg[start - 1])) + && (len == end - beg || !WCHAR(beg[start + len]))) + goto success; + if (len > 0) + { + /* Try a shorter length anchored at the same place. */ + --len; + regex.not_eol = 1; + len = re_match(®ex, beg, start + len, start, ®s); + } + if (len <= 0) + { + /* Try looking further on. */ + if (start == end - beg) + break; + ++start; + regex.not_eol = 0; + start = re_search(®ex, beg, end - beg, + start, end - beg - start, ®s); + len = regs.end[0] - start; + } + } + } + } + + failure: + return 0; + + success: + *endp = end < buflim ? end + 1 : end; + return beg; +} + +static void +Fcompile(pattern, size) + char *pattern; + size_t size; +{ + char *beg, *lim, *err; + + kwsinit(); + beg = pattern; + do + { + for (lim = beg; lim < pattern + size && *lim != '\n'; ++lim) + ; + if ((err = kwsincr(kwset, beg, lim - beg)) != 0) + fatal(err, 0); + if (lim < pattern + size) + ++lim; + beg = lim; + } + while (beg < pattern + size); + + if ((err = kwsprep(kwset)) != 0) + fatal(err, 0); +} + +static char * +Fexecute(buf, size, endp) + char *buf; + size_t size; + char **endp; +{ + register char *beg, *try, *end; + register size_t len; + struct kwsmatch kwsmatch; + + for (beg = buf; beg <= buf + size; ++beg) + { + if (!(beg = kwsexec(kwset, beg, buf + size - beg, &kwsmatch))) + return 0; + len = kwsmatch.size[0]; + if (match_lines) + { + if (beg > buf && beg[-1] != '\n') + continue; + if (beg + len < buf + size && beg[len] != '\n') + continue; + goto success; + } + else if (match_words) + for (try = beg; len && try;) + { + if (try > buf && WCHAR((unsigned char) try[-1])) + break; + if (try + len < buf + size && WCHAR((unsigned char) try[len])) + { + try = kwsexec(kwset, beg, --len, &kwsmatch); + len = kwsmatch.size[0]; + } + else + goto success; + } + else + goto success; + } + + return 0; + + success: + if ((end = memchr(beg + len, '\n', (buf + size) - (beg + len))) != 0) + ++end; + else + end = buf + size; + *endp = end; + while (beg > buf && beg[-1] != '\n') + --beg; + return beg; +} diff --git a/gnu/usr.bin/grep/tests/check.sh b/gnu/usr.bin/grep/tests/check.sh new file mode 100644 index 000000000000..d2c8fdbc64dc --- /dev/null +++ b/gnu/usr.bin/grep/tests/check.sh @@ -0,0 +1,24 @@ +#! /bin/sh +# Regression test for GNU grep. +# Usage: regress.sh [testdir] + +testdir=${1-tests} + +failures=0 + +# The Khadafy test is brought to you by Scott Anderson . . . +./grep -E -f $testdir/khadafy.regexp $testdir/khadafy.lines > khadafy.out +if cmp $testdir/khadafy.lines khadafy.out +then + : +else + echo Khadafy test failed -- output left on khadafy.out + failures=1 +fi + +# . . . and the following by Henry Spencer. + +${AWK-awk} -F: -f $testdir/scriptgen.awk $testdir/spencer.tests > tmp.script + +sh tmp.script && exit $failures +exit 1 diff --git a/gnu/usr.bin/grep/tests/scriptgen.awk b/gnu/usr.bin/grep/tests/scriptgen.awk new file mode 100644 index 000000000000..44ef4df16e53 --- /dev/null +++ b/gnu/usr.bin/grep/tests/scriptgen.awk @@ -0,0 +1,10 @@ +BEGIN { print "failures=0"; } +$0 !~ /^#/ && NF == 3 { + print "echo '" $3 "' | ./grep -E -e '" $2 "' > /dev/null 2>&1"; + print "if [ $? != " $1 " ]" + print "then" + printf "\techo Spencer test \\#%d failed\n", ++n + print "\tfailures=1" + print "fi" +} +END { print "exit $failures"; } diff --git a/gnu/usr.bin/grep/tests/spencer.tests b/gnu/usr.bin/grep/tests/spencer.tests new file mode 100644 index 000000000000..cb506d79af0b --- /dev/null +++ b/gnu/usr.bin/grep/tests/spencer.tests @@ -0,0 +1,122 @@ +0:abc:abc +1:abc:xbc +1:abc:axc +1:abc:abx +0:abc:xabcy +0:abc:ababc +0:ab*c:abc +0:ab*bc:abc +0:ab*bc:abbc +0:ab*bc:abbbbc +0:ab+bc:abbc +1:ab+bc:abc +1:ab+bc:abq +0:ab+bc:abbbbc +0:ab?bc:abbc +0:ab?bc:abc +1:ab?bc:abbbbc +0:ab?c:abc +0:^abc$:abc +1:^abc$:abcc +0:^abc:abcc +1:^abc$:aabc +0:abc$:aabc +0:^:abc +0:$:abc +0:a.c:abc +0:a.c:axc +0:a.*c:axyzc +1:a.*c:axyzd +1:a[bc]d:abc +0:a[bc]d:abd +1:a[b-d]e:abd +0:a[b-d]e:ace +0:a[b-d]:aac +0:a[-b]:a- +0:a[b-]:a- +2:a[b-a]:- +2:a[]b:- +2:a[:- +0:a]:a] +0:a[]]b:a]b +0:a[^bc]d:aed +1:a[^bc]d:abd +0:a[^-b]c:adc +1:a[^-b]c:a-c +1:a[^]b]c:a]c +0:a[^]b]c:adc +0:ab|cd:abc +0:ab|cd:abcd +0:()ef:def +0:()*:- +1:*a:- +0:^*:- +0:$*:- +1:(*)b:- +1:$b:b +2:a\:- +0:a\(b:a(b +0:a\(*b:ab +0:a\(*b:a((b +1:a\x:a\x +2:abc):- +2:(abc:- +0:((a)):abc +0:(a)b(c):abc +0:a+b+c:aabbabc +0:a**:- +0:a*?:- +0:(a*)*:- +0:(a*)+:- +0:(a|)*:- +0:(a*|b)*:- +0:(a+|b)*:ab +0:(a+|b)+:ab +0:(a+|b)?:ab +0:[^ab]*:cde +0:(^)*:- +0:(ab|)*:- +2:)(:- +1:abc: +1:abc: +0:a*: +0:([abc])*d:abbbcd +0:([abc])*bcd:abcd +0:a|b|c|d|e:e +0:(a|b|c|d|e)f:ef +0:((a*|b))*:- +0:abcd*efg:abcdefg +0:ab*:xabyabbbz +0:ab*:xayabbbz +0:(ab|cd)e:abcde +0:[abhgefdc]ij:hij +1:^(ab|cd)e:abcde +0:(abc|)ef:abcdef +0:(a|b)c*d:abcd +0:(ab|ab*)bc:abc +0:a([bc]*)c*:abc +0:a([bc]*)(c*d):abcd +0:a([bc]+)(c*d):abcd +0:a([bc]*)(c+d):abcd +0:a[bcd]*dcdcde:adcdcde +1:a[bcd]+dcdcde:adcdcde +0:(ab|a)b*c:abc +0:((a)(b)c)(d):abcd +0:[A-Za-z_][A-Za-z0-9_]*:alpha +0:^a(bc+|b[eh])g|.h$:abh +0:(bc+d$|ef*g.|h?i(j|k)):effgz +0:(bc+d$|ef*g.|h?i(j|k)):ij +1:(bc+d$|ef*g.|h?i(j|k)):effg +1:(bc+d$|ef*g.|h?i(j|k)):bcdd +0:(bc+d$|ef*g.|h?i(j|k)):reffgz +1:((((((((((a)))))))))):- +0:(((((((((a))))))))):a +1:multiple words of text:uh-uh +0:multiple words:multiple words, yeah +0:(.*)c(.*):abcde +1:\((.*),:(.*)\) +1:[k]:ab +0:abcd:abcd +0:a(bc)d:abcd +0:a[-]?c:ac +0:(....).*\1:beriberi