# Rules for configuration files
-run/cf/%: custom/cf/% $(o)/config.mk $(s)/build/genconf
+run/cf/%: $(s)/cf/% $(o)/config.mk $(s)/build/genconf
$(M)CF $<
$(Q)$(s)/build/genconf $< $@ $(o)/config.mk
-run/cf/%: $(s)/cf/% $(o)/config.mk $(s)/build/genconf
+$(o)/%.cf: $(s)/%.cf $(o)/config.mk $(s)/build/genconf
$(M)CF $<
$(Q)$(s)/build/genconf $< $@ $(o)/config.mk
+ $(Q)cp $@ run/cf/$(basename $(@F))
# Rules for libraries
-Sherlock Holmes Libraries 3.12.3
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(c) 1997--2007 Martin Mares <mj@ucw.cz>
-(c) 2000--2007 Robert Spalek <robert@ucw.cz>
+Sherlock Holmes Libraries 3.13
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(c) 1997--2008 Martin Mares <mj@ucw.cz>
+(c) 2000--2008 Robert Spalek <robert@ucw.cz>
This package contains all libraries from the freely distributable version
of the Sherlock Holmes search engine (see http://www.ucw.cz/holmes/).
# Liblang settings
Set("CONFIG_LANG");
-Set("MAX_WORD_LEN" => 64);
+Set("MAX_WORD_CHARS" => 64);
+Set("MAX_WORD_BYTES" => 192);
# Libimages settings
Set("CONFIG_IMAGES");
ipaccess \
profile \
fastbuf ff-binary ff-string ff-printf ff-unicode \
- fb-file carefulio fb-mem fb-temp fb-mmap fb-limfd fb-buffer fb-grow fb-pool fb-atomic fb-param \
+ fb-file carefulio fb-mem fb-temp fb-mmap fb-limfd fb-buffer fb-grow fb-pool fb-atomic fb-param fb-socket \
str_ctype str_upper str_lower unicode stkstring \
wildmatch wordsplit ctmatch patimatch patmatch regex \
prime primetable random timer randomkey \
bitops.h \
conf.h getopt.h ipaccess.h \
profile.h \
- fastbuf.h lfs.h ff-unicode.h ff-utf8.h ff-binary.h \
+ fastbuf.h lfs.h ff-unicode.h ff-binary.h \
chartype.h unicode.h stkstring.h \
wildmatch.h patmatch.h \
db.h \
LIBUCW_INCLUDES+=workqueue.h semaphore.h asio.h
endif
-ifdef CONFIG_OWN_REGEX
-include $(s)/lib/regex/Makefile
-endif
-
ifdef CONFIG_OWN_GETOPT
include $(s)/lib/getopt/Makefile
endif
$(o)/lib/trie-test: $(o)/lib/trie-test.o $(LIBUCW)
TESTS+=$(addprefix $(o)/lib/,regex.test unicode.test hash-test.test mempool.test stkstring.test \
- slists.test kmp-test.test bbuf.test getopt.test fastbuf.test ff-unicode.test eltpool.test trie-test.test)
+ slists.test kmp-test.test bbuf.test getopt.test fastbuf.test ff-unicode.test eltpool.test \
+ fb-socket.test trie-test.test)
$(o)/lib/regex.test: $(o)/lib/regex-t
$(o)/lib/unicode.test: $(o)/lib/unicode-t
$(o)/lib/fastbuf.test: $(o)/lib/fb-file-t $(o)/lib/fb-grow-t $(o)/lib/fb-pool-t
$(o)/lib/ff-unicode.test: $(o)/lib/ff-unicode-t
$(o)/lib/eltpool.test: $(o)/lib/eltpool-t
+$(o)/lib/fb-socket.test: $(o)/lib/fb-socket-t
$(o)/lib/trie-test.test: $(o)/lib/trie-test
ifdef CONFIG_UCW_THREADS
} elsif ($gccver == 3004) {
Append("CWARNS" => "-Wundef -Wredundant-decls");
Append("COPT" => "-finline-limit=2000 --param large-function-insns=5000 --param inline-unit-growth=200 --param large-function-growth=400");
-} elsif ($gccver == 4000 || $gccver == 4001) {
+} elsif ($gccver >= 4000) {
Append("CWARNS" => "-Wundef -Wredundant-decls -Wno-pointer-sign -Wdisabled-optimization -Wno-missing-field-initializers");
Append("CWARNS_OFF" => "-Wno-pointer-sign");
Append("COPT" => "-finline-limit=5000 --param large-function-insns=5000 --param inline-unit-growth=200 --param large-function-growth=400");
-} elsif ($gccver == 4002) {
- Append("CWARNS" => "-Wundef -Wredundant-decls -Wno-pointer-sign -Wdisabled-optimization -Wno-missing-field-initializers");
- Append("CWARNS_OFF" => "-Wno-pointer-sign");
- Append("COPT" => "-finline-limit=5000 --param large-function-insns=5000 --param inline-unit-growth=200 --param large-function-growth=400 -fgnu89-inline");
+ if ($gccver >= 4002) {
+ Append("COPT" => "-fgnu89-inline");
+ }
} else {
Warn "Don't know anything about this GCC version, using default switches.\n";
}
# Configuration variables of the UCW library and their default values
-# (c) 2005--2007 Martin Mares <mj@ucw.cz>
+# (c) 2005--2008 Martin Mares <mj@ucw.cz>
# Version of the whole package
-Set("SHERLOCK_VERSION" => "3.12.3");
+Set("SHERLOCK_VERSION" => "3.13-dev");
# Compile everything with debug information and ASSERT's
UnSet("CONFIG_DEBUG");
# Use shared libraries
UnSet("CONFIG_SHARED");
-# If your system doesn't contain GNU libc 2.3 or newer, it's recommended to let Sherlock
-# use its own regex library (a copy of the glibc one), because the default regex library
-# is likely to be crappy.
-Set("CONFIG_OWN_REGEX");
-
# If your system can't reset getopt with 'optind = 0', you need to compile our internal copy
# of GNU libc's getopt. This should not be necessary on GNU libc.
UnSet("CONFIG_OWN_GETOPT");
--- /dev/null
+/*
+ * UCW Library -- Fast Buffered I/O on Sockets with Timeouts
+ *
+ * (c) 2008 Martin Mares <mj@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#include "lib/lib.h"
+#include "lib/fastbuf.h"
+#include "lib/fb-socket.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <poll.h>
+#include <errno.h>
+
+struct fb_sock {
+ struct fastbuf fb;
+ struct fbsock_params par;
+ byte buf[0];
+};
+
+#define FB_SOCK(f) ((struct fb_sock *)(f)->is_fastbuf)
+
+static int
+fbs_refill(struct fastbuf *f)
+{
+ struct fbsock_params *p = &FB_SOCK(f)->par;
+ struct pollfd pf = {
+ .fd = p->fd,
+ .events = POLLIN
+ };
+
+ for (;;)
+ {
+ int e = poll(&pf, 1, p->timeout_ms);
+ if (e < 0)
+ {
+ p->err(p->data, FBSOCK_READ, "read error");
+ return 0;
+ }
+ if (!e)
+ {
+ p->err(p->data, FBSOCK_READ | FBSOCK_TIMEOUT, "read timeout");
+ return 0;
+ }
+
+ f->bptr = f->buffer;
+ int l = read(p->fd, f->buffer, f->bufend-f->buffer);
+ if (l < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ p->err(p->data, FBSOCK_READ, "read error");
+ return 0;
+ }
+ f->bstop = f->buffer + l;
+ f->pos += l;
+ return l;
+ }
+}
+
+static void
+fbs_spout(struct fastbuf *f)
+{
+ struct fbsock_params *p = &FB_SOCK(f)->par;
+ struct pollfd pf = {
+ .fd = p->fd,
+ .events = POLLOUT,
+ };
+
+ int l = f->bptr - f->buffer;
+ f->bptr = f->buffer;
+ char *buf = f->buffer;
+
+ while (l)
+ {
+ int e = poll(&pf, 1, p->timeout_ms);
+ if (e < 0)
+ {
+ p->err(p->data, FBSOCK_WRITE, "write error");
+ return;
+ }
+ if (!e)
+ {
+ p->err(p->data, FBSOCK_WRITE | FBSOCK_TIMEOUT, "write timeout");
+ return;
+ }
+
+ e = write(p->fd, buf, l);
+ if (e < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ p->err(p->data, FBSOCK_WRITE, "write error");
+ return;
+ }
+ buf += e;
+ l -= e;
+ }
+}
+
+static void
+fbs_close(struct fastbuf *f)
+{
+ close(FB_SOCK(f)->par.fd);
+ xfree(f);
+}
+
+struct fastbuf *
+fbsock_create(struct fbsock_params *p)
+{
+ struct fb_sock *F = xmalloc(sizeof(*F) + p->bufsize);
+ struct fastbuf *f = &F->fb;
+
+ bzero(F, sizeof(*F));
+ F->par = *p;
+ f->buffer = F->buf;
+ f->bptr = f->bstop = f->buffer;
+ f->bufend = f->buffer + p->bufsize;
+ f->name = "<socket>";
+ f->refill = fbs_refill;
+ f->spout = fbs_spout;
+ f->close = fbs_close;
+ f->can_overwrite_buffer = 1;
+ return f;
+}
+
+#ifdef TEST
+
+#include <stdlib.h>
+
+static void test_err(void *x UNUSED, uns flags, char *msg UNUSED)
+{
+ if (flags & FBSOCK_READ)
+ printf("READ");
+ else if (flags & FBSOCK_WRITE)
+ printf("WRITE");
+ if (flags & FBSOCK_TIMEOUT)
+ printf(" TIMEOUT\n");
+ else
+ printf(" ERROR\n");
+ exit(0);
+}
+
+int main(void)
+{
+ int fd[2];
+ if (pipe(fd) < 0)
+ ASSERT(0);
+
+ struct fbsock_params p = {
+ .fd = fd[0],
+ .bufsize = 16,
+ .timeout_ms = 100,
+ .err = test_err
+ };
+ struct fastbuf *f = fbsock_create(&p);
+
+ bputsn(f, "Oook!"); // This fits in PIPE_BUF
+ bflush(f);
+
+ char buf[256];
+ if (!bgets(f, buf, sizeof(buf)))
+ die("bgets failed");
+ if (strcmp(buf, "Oook!"))
+ die("Misread input");
+
+ bgets(f, buf, sizeof(buf));
+ puts("WRONG");
+ exit(0);
+}
+
+#endif
--- /dev/null
+/*
+ * UCW Library -- Fast Buffered I/O on Sockets with Timeouts
+ *
+ * (c) 2008 Martin Mares <mj@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#ifndef _UCW_FB_SOCKET_H
+#define _UCW_FB_SOCKET_H
+
+#include "lib/fastbuf.h"
+
+struct fbsock_params {
+ int fd;
+ uns bufsize;
+ uns timeout_ms;
+ void (*err)(void *data, uns flags, char *msg);
+ void *data; // Passed to the err callback
+};
+
+enum fbsock_err_flags {
+ FBSOCK_READ = 1, // Happened during read
+ FBSOCK_WRITE = 2, // Happened during write
+ FBSOCK_TIMEOUT = 4, // The error is a timeout
+};
+
+struct fastbuf *fbsock_create(struct fbsock_params *par);
+
+#endif
--- /dev/null
+# Tests for the fb-socket module
+
+Name: fb-socket
+Run: ../obj/lib/fb-socket-t
+Out: WRITE TIMEOUT
+++ /dev/null
-/*
- * UCW Library: An alias for lib/ff-unicode.h (for backwards compatibility)
- *
- * (c) 2008 Pavel Charvat <pchar@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU Lesser General Public License.
- */
-
-#ifndef _UCW_FF_UTF8_H
-#define _UCW_FF_UTF8_H
-
-#include "lib/ff-unicode.h"
-
-#endif
/*
* The UCW Library -- Miscellaneous Functions
*
- * (c) 1997--2007 Martin Mares <mj@ucw.cz>
+ * (c) 1997--2008 Martin Mares <mj@ucw.cz>
* (c) 2005 Tomas Valla <tom@ucw.cz>
* (c) 2006 Robert Spalek <robert@ucw.cz>
* (c) 2007 Pavel Charvat <pchar@ucw.cz>
#define L_ERROR_R 'e'
#define L_FATAL '!' /* die() */
-extern char *log_title; /* NULL - print no title, default is log_progname */
+#define L_SIGHANDLER 0x10000 /* Avoid operations that are unsafe in signal handlers */
+
+extern char *log_title; /* NULL - print no title, default is program name given to log_init() */
extern char *log_filename; /* Expanded name of the current log file */
-extern volatile int log_switch_nest; /* log_switch() nesting counter, increment to disable automatic switches */
extern int log_pid; /* 0 if shouldn't be logged */
extern int log_precise_timings; /* Include microsecond timestamps in log messages */
extern void (*log_die_hook)(void);
void die(const char *, ...) NONRET FORMAT_CHECK(printf,1,2);
void log_init(const char *argv0);
void log_file(const char *name);
-void log_fork(void);
+void log_fork(void); /* Call after fork() to update log_pid */
+
+/* If the log name contains metacharacters for date and time, we switch the logs
+ * automatically whenever the name changes. You can disable it and switch explicitly. */
int log_switch(void);
+void log_switch_disable(void);
+void log_switch_enable(void);
void assert_failed(const char *assertion, const char *file, int line) NONRET;
void assert_failed_noinfo(void) NONRET;
#define DBG(x,y...) do { } while(0)
#endif
-static inline void log_switch_disable(void) { log_switch_nest++; }
-static inline void log_switch_enable(void) { ASSERT(log_switch_nest); log_switch_nest--; }
-
/* Memory allocation */
#define xmalloc sh_xmalloc
static char *log_name_patt;
static int log_params;
static int log_filename_size;
-volatile int log_switch_nest;
+static int log_switch_nest;
static int
do_log_switch(struct tm *tm)
log_pid = getpid();
}
+void
+log_switch_disable(void)
+{
+ log_switch_nest++;
+}
+
+void
+log_switch_enable(void)
+{
+ ASSERT(log_switch_nest);
+ log_switch_nest--;
+}
+
#ifdef TEST
int main(int argc, char **argv)
/*
* UCW Library -- Logging
*
- * (c) 1997--2006 Martin Mares <mj@ucw.cz>
+ * (c) 1997--2008 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
vmsg(unsigned int cat, const char *fmt, va_list args)
{
struct timeval tv;
+ int have_tm = 0;
struct tm tm;
byte *buf, *p;
int buflen = 256;
int l, l0, r;
va_list args2;
- gettimeofday(&tv, NULL);
- if (!localtime_r(&tv.tv_sec, &tm))
- bzero(&tm, sizeof(tm));
+ if (!(cat & L_SIGHANDLER))
+ {
+ /* CAVEAT: These calls are not safe in signal handlers. */
+ gettimeofday(&tv, NULL);
+ if (localtime_r(&tv.tv_sec, &tm))
+ have_tm = 1;
+ if (log_switch_hook)
+ log_switch_hook(&tm);
+ }
- if (log_switch_hook)
- log_switch_hook(&tm);
while (1)
{
p = buf = alloca(buflen);
- *p++ = cat;
- /* We cannot use strftime() here, because it's not re-entrant */
- p += sprintf(p, " %4d-%02d-%02d %02d:%02d:%02d", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- if (log_precise_timings)
- p += sprintf(p, ".%06d", (int)tv.tv_usec);
+ *p++ = cat & 0xff;
+ if (have_tm)
+ {
+ p += strftime(p, buflen, " %Y-%m-%d %H:%M:%S", &tm);
+ if (log_precise_timings)
+ p += sprintf(p, ".%06d", (int)tv.tv_usec);
+ }
+ else
+ {
+ p += sprintf(p, " \?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?");
+ if (log_precise_timings)
+ p += sprintf(p, ".\?\?\?\?\?\?");
+ }
*p++ = ' ';
if (log_title)
{
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
$VERSION = 1.0;
@ISA = qw(Exporter);
- @EXPORT = qw(&Init &Log &Notice &Warn &Fail &IsSet &Set &UnSet &Append &Override &Get &Test &Include &Finish &FindFile &TryFindFile);
+ @EXPORT = qw(&Init &Log &Notice &Warn &Fail &IsSet &IsGiven &Set &UnSet &Append &Override &Get &Test &Include &Finish &FindFile &TryFindFile &TryCmd &PkgConfig &TrivConfig);
@EXPORT_OK = qw();
%EXPORT_TAGS = ();
}
return exists $vars{$x};
}
+sub IsGiven($) {
+ my ($x) = @_;
+ return exists $overriden{$x};
+}
+
sub Get($) {
my ($x) = @_;
return $vars{$x};
sub Init($$) {
my ($srcdir,$defconfig) = @_;
- if ((!defined $defconfig && !@ARGV) || @ARGV && $ARGV[0] eq "--help") {
- print STDERR "Usage: [<srcdir>/]configure " . (defined $defconfig ? "[" : "") . "<config-name>" . (defined $defconfig ? "]" : "") .
+ sub usage($) {
+ my ($dc) = @_;
+ print STDERR "Usage: [<srcdir>/]configure " . (defined $dc ? "[" : "") . "<config-name>" . (defined $dc ? "]" : "") .
" [<option>[=<value>] | -<option>] ...\n";
exit 1;
}
- if (@ARGV && $ARGV[0] !~ /=/) {
- Set('CONFIG' => shift @ARGV);
- } else {
- Set('CONFIG' => $defconfig);
+ Set('CONFIG' => $defconfig) if defined $defconfig;
+ if (@ARGV) {
+ usage($defconfig) if $ARGV[0] eq "--help";
+ if (!defined($defconfig) || $ARGV[0] !~ /^-?[A-Z][A-Z0-9_]*(=|$)/) {
+ # This does not look like an option, so read it as a file name
+ Set('CONFIG' => shift @ARGV);
+ }
}
Set("SRCDIR", $srcdir);
}
}
+ defined Get("CONFIG") or usage($defconfig);
if (!TryFindFile(Get("CONFIG"))) {
TryFindFile(Get("CONFIG")."/config") or Fail "Cannot find configuration " . Get("CONFIG");
Override("CONFIG" => Get("CONFIG")."/config");
Log "done\n";
}
+sub TryCmd($) {
+ my ($cmd) = @_;
+ my $res = `$cmd`;
+ chomp $res;
+ return $res unless $?;
+ return;
+}
+
+sub maybe_manually($) {
+ my ($n) = @_;
+ if (IsGiven($n)) {
+ if (Get("$n")) { Log "YES (set manually)\n"; }
+ else { Log "NO (set manually)\n"; }
+ return 1;
+ }
+ return 0;
+}
+
+sub PkgConfig($@) {
+ my $pkg = shift @_;
+ my %opts = @_;
+ my $upper = $pkg; $upper =~ tr/a-z/A-Z/; $upper =~ s/[^0-9A-Z]+/_/g;
+ Log "Checking for package $pkg ... ";
+ maybe_manually("CONFIG_HAVE_$upper") and return Get("CONFIG_HAVE_$upper");
+ my $ver = TryCmd("pkg-config --modversion $pkg 2>/dev/null");
+ if (!defined $ver) {
+ Log("NONE\n");
+ return 0;
+ }
+ if (defined($opts{minversion})) {
+ my $min = $opts{minversion};
+ if (!defined TryCmd("pkg-config --atleast-version=$min $pkg")) {
+ Log("NO: version $ver is too old (need >= $min)\n");
+ return 0;
+ }
+ }
+ Log("YES: version $ver\n");
+ Set("CONFIG_HAVE_$upper" => 1);
+ Set("CONFIG_VER_$upper" => $ver);
+ my $cf = TryCmd("pkg-config --cflags $pkg");
+ Set("CFLAGS_$upper" => $cf) if defined $cf;
+ my $lf = TryCmd("pkg-config --libs $pkg");
+ Set("LIBS_$upper" => $lf) if defined $lf;
+ return 1;
+}
+
+sub ver_norm($) {
+ my ($v) = @_;
+ return join(".", map { sprintf("%05s", $_) } split(/\./, $v));
+}
+
+sub TrivConfig($@) {
+ my $pkg = shift @_;
+ my %opts = @_;
+ my $upper = $pkg; $upper =~ tr/a-z/A-Z/; $upper =~ s/[^0-9A-Z]+/_/g;
+ Log "Checking for package $pkg ... ";
+ maybe_manually("CONFIG_HAVE_$upper") and return Get("CONFIG_HAVE_$upper");
+ my $pc = $opts{script};
+ my $ver = TryCmd("$pc --version 2>/dev/null");
+ if (!defined $ver) {
+ Log("NONE\n");
+ return 0;
+ }
+ if (defined($opts{minversion})) {
+ my $min = $opts{minversion};
+ if (ver_norm($ver) lt ver_norm($min)) {
+ Log("NO: version $ver is too old (need >= $min)\n");
+ return 0;
+ }
+ }
+ Log("YES: version $ver\n");
+ Set("CONFIG_HAVE_$upper" => 1);
+ Set("CONFIG_VER_$upper" => $ver);
+ my $cf = TryCmd("$pc --cflags");
+ Set("CFLAGS_$upper" => $cf) if defined $cf;
+ my $lf = TryCmd("$pc --libs");
+ Set("LIBS_$upper" => $lf) if defined $lf;
+ return 1;
+}
+
1; # OK
#include <stdio.h>
#include <string.h>
-#if defined(CONFIG_OWN_REGEX) || defined(CONFIG_POSIX_REGEX)
+#ifdef CONFIG_POSIX_REGEX
/* POSIX regular expression library */
-#ifdef CONFIG_OWN_REGEX
-#include "lib/regex/regex-sh.h"
-#else
#include <regex.h>
-#endif
struct regex {
regex_t rx;
+++ /dev/null
-# Makefile for the UCW Regex Library (c) 2004 Martin Mares <mj@ucw.cz>
-
-DIRS+=lib/regex
-
-LIBUCW_MODS+=regex/regex
-
-$(o)/lib/regex/regex.o $(o)/lib/regex/regex.oo: CWARNS=
+++ /dev/null
-This directory contains regular expression routines from the GNU libc 2.3.2
-which are significantly faster than the default regex libraries on most systems.
-
-They are distributed under the GNU LGPL.
-
-All files are exact copies of the original distribution, I only provided my
-own regex.c, regex-sh.h and Makefile.
-
- Martin Mares, March 2004
+++ /dev/null
-/* Extended regular expression matching and search library.
- Copyright (C) 2002, 2003 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
-
-static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
- int length, reg_syntax_t syntax);
-static void re_compile_fastmap_iter (regex_t *bufp,
- const re_dfastate_t *init_state,
- char *fastmap);
-static reg_errcode_t init_dfa (re_dfa_t *dfa, int pat_len);
-static reg_errcode_t init_word_char (re_dfa_t *dfa);
-#ifdef RE_ENABLE_I18N
-static void free_charset (re_charset_t *cset);
-#endif /* RE_ENABLE_I18N */
-static void free_workarea_compile (regex_t *preg);
-static reg_errcode_t create_initial_state (re_dfa_t *dfa);
-static reg_errcode_t analyze (re_dfa_t *dfa);
-static reg_errcode_t analyze_tree (re_dfa_t *dfa, bin_tree_t *node);
-static void calc_first (re_dfa_t *dfa, bin_tree_t *node);
-static void calc_next (re_dfa_t *dfa, bin_tree_t *node);
-static void calc_epsdest (re_dfa_t *dfa, bin_tree_t *node);
-static reg_errcode_t duplicate_node_closure (re_dfa_t *dfa, int top_org_node,
- int top_clone_node, int root_node,
- unsigned int constraint);
-static reg_errcode_t duplicate_node (int *new_idx, re_dfa_t *dfa, int org_idx,
- unsigned int constraint);
-static int search_duplicated_node (re_dfa_t *dfa, int org_node,
- unsigned int constraint);
-static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
-static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
- int node, int root);
-static void calc_inveclosure (re_dfa_t *dfa);
-static int fetch_number (re_string_t *input, re_token_t *token,
- reg_syntax_t syntax);
-static re_token_t fetch_token (re_string_t *input, reg_syntax_t syntax);
-static int peek_token (re_token_t *token, re_string_t *input,
- reg_syntax_t syntax);
-static int peek_token_bracket (re_token_t *token, re_string_t *input,
- reg_syntax_t syntax);
-static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
- reg_syntax_t syntax, reg_errcode_t *err);
-static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
- re_token_t *token, reg_syntax_t syntax,
- int nest, reg_errcode_t *err);
-static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
- re_token_t *token, reg_syntax_t syntax,
- int nest, reg_errcode_t *err);
-static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
- re_token_t *token, reg_syntax_t syntax,
- int nest, reg_errcode_t *err);
-static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
- re_token_t *token, reg_syntax_t syntax,
- int nest, reg_errcode_t *err);
-static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
- re_dfa_t *dfa, re_token_t *token,
- reg_syntax_t syntax, reg_errcode_t *err);
-static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
- re_token_t *token, reg_syntax_t syntax,
- reg_errcode_t *err);
-static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
- re_string_t *regexp,
- re_token_t *token, int token_len,
- re_dfa_t *dfa,
- reg_syntax_t syntax);
-static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
- re_string_t *regexp,
- re_token_t *token);
-#ifndef _LIBC
-# ifdef RE_ENABLE_I18N
-static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
- re_charset_t *mbcset, int *range_alloc,
- bracket_elem_t *start_elem,
- bracket_elem_t *end_elem);
-static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
- re_charset_t *mbcset,
- int *coll_sym_alloc,
- const unsigned char *name);
-# else /* not RE_ENABLE_I18N */
-static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
- bracket_elem_t *start_elem,
- bracket_elem_t *end_elem);
-static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
- const unsigned char *name);
-# endif /* not RE_ENABLE_I18N */
-#endif /* not _LIBC */
-#ifdef RE_ENABLE_I18N
-static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
- re_charset_t *mbcset,
- int *equiv_class_alloc,
- const unsigned char *name);
-static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
- re_charset_t *mbcset,
- int *char_class_alloc,
- const unsigned char *class_name,
- reg_syntax_t syntax);
-#else /* not RE_ENABLE_I18N */
-static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
- const unsigned char *name);
-static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
- const unsigned char *class_name,
- reg_syntax_t syntax);
-#endif /* not RE_ENABLE_I18N */
-static bin_tree_t *build_word_op (re_dfa_t *dfa, int not, reg_errcode_t *err);
-static void free_bin_tree (bin_tree_t *tree);
-static bin_tree_t *create_tree (bin_tree_t *left, bin_tree_t *right,
- re_token_type_t type, int index);
-static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
-\f
-/* This table gives an error message for each of the error codes listed
- in regex.h. Obviously the order here has to be same as there.
- POSIX doesn't require that we do anything for REG_NOERROR,
- but why not be nice? */
-
-const char __re_error_msgid[] attribute_hidden =
- {
-#define REG_NOERROR_IDX 0
- gettext_noop ("Success") /* REG_NOERROR */
- "\0"
-#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
- gettext_noop ("No match") /* REG_NOMATCH */
- "\0"
-#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
- gettext_noop ("Invalid regular expression") /* REG_BADPAT */
- "\0"
-#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
- gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
- "\0"
-#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
- gettext_noop ("Invalid character class name") /* REG_ECTYPE */
- "\0"
-#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
- gettext_noop ("Trailing backslash") /* REG_EESCAPE */
- "\0"
-#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
- gettext_noop ("Invalid back reference") /* REG_ESUBREG */
- "\0"
-#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
- gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
- "\0"
-#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
- gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
- "\0"
-#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
- gettext_noop ("Unmatched \\{") /* REG_EBRACE */
- "\0"
-#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
- gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
- "\0"
-#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
- gettext_noop ("Invalid range end") /* REG_ERANGE */
- "\0"
-#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
- gettext_noop ("Memory exhausted") /* REG_ESPACE */
- "\0"
-#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
- gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
- "\0"
-#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
- gettext_noop ("Premature end of regular expression") /* REG_EEND */
- "\0"
-#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
- gettext_noop ("Regular expression too big") /* REG_ESIZE */
- "\0"
-#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
- gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
- };
-
-const size_t __re_error_msgid_idx[] attribute_hidden =
- {
- REG_NOERROR_IDX,
- REG_NOMATCH_IDX,
- REG_BADPAT_IDX,
- REG_ECOLLATE_IDX,
- REG_ECTYPE_IDX,
- REG_EESCAPE_IDX,
- REG_ESUBREG_IDX,
- REG_EBRACK_IDX,
- REG_EPAREN_IDX,
- REG_EBRACE_IDX,
- REG_BADBR_IDX,
- REG_ERANGE_IDX,
- REG_ESPACE_IDX,
- REG_BADRPT_IDX,
- REG_EEND_IDX,
- REG_ESIZE_IDX,
- REG_ERPAREN_IDX
- };
-\f
-/* Entry points for GNU code. */
-
-/* re_compile_pattern is the GNU regular expression compiler: it
- compiles PATTERN (of length LENGTH) and puts the result in BUFP.
- Returns 0 if the pattern was valid, otherwise an error string.
-
- Assumes the `allocated' (and perhaps `buffer') and `translate' fields
- are set in BUFP on entry. */
-
-const char *
-re_compile_pattern (pattern, length, bufp)
- const char *pattern;
- size_t length;
- struct re_pattern_buffer *bufp;
-{
- reg_errcode_t ret;
-
- /* And GNU code determines whether or not to get register information
- by passing null for the REGS argument to re_match, etc., not by
- setting no_sub. */
- bufp->no_sub = 0;
-
- /* Match anchors at newline. */
- bufp->newline_anchor = 1;
-
- ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
-
- if (!ret)
- return NULL;
- return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
-}
-#ifdef _LIBC
-weak_alias (__re_compile_pattern, re_compile_pattern)
-#endif
-
-/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
- also be assigned to arbitrarily: each pattern buffer stores its own
- syntax, so it can be changed between regex compilations. */
-/* This has no initializer because initialized variables in Emacs
- become read-only after dumping. */
-reg_syntax_t re_syntax_options;
-
-
-/* Specify the precise syntax of regexps for compilation. This provides
- for compatibility for various utilities which historically have
- different, incompatible syntaxes.
-
- The argument SYNTAX is a bit mask comprised of the various bits
- defined in regex.h. We return the old syntax. */
-
-reg_syntax_t
-re_set_syntax (syntax)
- reg_syntax_t syntax;
-{
- reg_syntax_t ret = re_syntax_options;
-
- re_syntax_options = syntax;
- return ret;
-}
-#ifdef _LIBC
-weak_alias (__re_set_syntax, re_set_syntax)
-#endif
-
-int
-re_compile_fastmap (bufp)
- struct re_pattern_buffer *bufp;
-{
- re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
- char *fastmap = bufp->fastmap;
-
- memset (fastmap, '\0', sizeof (char) * SBC_MAX);
- re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
- if (dfa->init_state != dfa->init_state_word)
- re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
- if (dfa->init_state != dfa->init_state_nl)
- re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
- if (dfa->init_state != dfa->init_state_begbuf)
- re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
- bufp->fastmap_accurate = 1;
- return 0;
-}
-#ifdef _LIBC
-weak_alias (__re_compile_fastmap, re_compile_fastmap)
-#endif
-
-static inline void
-re_set_fastmap (char *fastmap, int icase, int ch)
-{
- fastmap[ch] = 1;
- if (icase)
- fastmap[tolower (ch)] = 1;
-}
-
-/* Helper function for re_compile_fastmap.
- Compile fastmap for the initial_state INIT_STATE. */
-
-static void
-re_compile_fastmap_iter (bufp, init_state, fastmap)
- regex_t *bufp;
- const re_dfastate_t *init_state;
- char *fastmap;
-{
- re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
- int node_cnt;
- int icase = (MB_CUR_MAX == 1 && (bufp->syntax & RE_ICASE));
- for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
- {
- int node = init_state->nodes.elems[node_cnt];
- re_token_type_t type = dfa->nodes[node].type;
-
- if (type == CHARACTER)
- re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
- else if (type == SIMPLE_BRACKET)
- {
- int i, j, ch;
- for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
- for (j = 0; j < UINT_BITS; ++j, ++ch)
- if (dfa->nodes[node].opr.sbcset[i] & (1 << j))
- re_set_fastmap (fastmap, icase, ch);
- }
-#ifdef RE_ENABLE_I18N
- else if (type == COMPLEX_BRACKET)
- {
- int i;
- re_charset_t *cset = dfa->nodes[node].opr.mbcset;
- if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes
- || cset->nranges || cset->nchar_classes)
- {
-# ifdef _LIBC
- if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
- {
- /* In this case we want to catch the bytes which are
- the first byte of any collation elements.
- e.g. In da_DK, we want to catch 'a' since "aa"
- is a valid collation element, and don't catch
- 'b' since 'b' is the only collation element
- which starts from 'b'. */
- int j, ch;
- const int32_t *table = (const int32_t *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
- for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
- for (j = 0; j < UINT_BITS; ++j, ++ch)
- if (table[ch] < 0)
- re_set_fastmap (fastmap, icase, ch);
- }
-# else
- if (MB_CUR_MAX > 1)
- for (i = 0; i < SBC_MAX; ++i)
- if (__btowc (i) == WEOF)
- re_set_fastmap (fastmap, icase, i);
-# endif /* not _LIBC */
- }
- for (i = 0; i < cset->nmbchars; ++i)
- {
- char buf[256];
- mbstate_t state;
- memset (&state, '\0', sizeof (state));
- __wcrtomb (buf, cset->mbchars[i], &state);
- re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
- }
- }
-#endif /* RE_ENABLE_I18N */
- else if (type == END_OF_RE || type == OP_PERIOD)
- {
- memset (fastmap, '\1', sizeof (char) * SBC_MAX);
- if (type == END_OF_RE)
- bufp->can_be_null = 1;
- return;
- }
- }
-}
-\f
-/* Entry point for POSIX code. */
-/* regcomp takes a regular expression as a string and compiles it.
-
- PREG is a regex_t *. We do not expect any fields to be initialized,
- since POSIX says we shouldn't. Thus, we set
-
- `buffer' to the compiled pattern;
- `used' to the length of the compiled pattern;
- `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
- REG_EXTENDED bit in CFLAGS is set; otherwise, to
- RE_SYNTAX_POSIX_BASIC;
- `newline_anchor' to REG_NEWLINE being set in CFLAGS;
- `fastmap' to an allocated space for the fastmap;
- `fastmap_accurate' to zero;
- `re_nsub' to the number of subexpressions in PATTERN.
-
- PATTERN is the address of the pattern string.
-
- CFLAGS is a series of bits which affect compilation.
-
- If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
- use POSIX basic syntax.
-
- If REG_NEWLINE is set, then . and [^...] don't match newline.
- Also, regexec will try a match beginning after every newline.
-
- If REG_ICASE is set, then we considers upper- and lowercase
- versions of letters to be equivalent when matching.
-
- If REG_NOSUB is set, then when PREG is passed to regexec, that
- routine will report only success or failure, and nothing about the
- registers.
-
- It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
- the return codes and their meanings.) */
-
-int
-regcomp (preg, pattern, cflags)
- regex_t *__restrict preg;
- const char *__restrict pattern;
- int cflags;
-{
- reg_errcode_t ret;
- reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
- : RE_SYNTAX_POSIX_BASIC);
-
- preg->buffer = NULL;
- preg->allocated = 0;
- preg->used = 0;
-
- /* Try to allocate space for the fastmap. */
- preg->fastmap = re_malloc (char, SBC_MAX);
- if (BE (preg->fastmap == NULL, 0))
- return REG_ESPACE;
-
- syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
-
- /* If REG_NEWLINE is set, newlines are treated differently. */
- if (cflags & REG_NEWLINE)
- { /* REG_NEWLINE implies neither . nor [^...] match newline. */
- syntax &= ~RE_DOT_NEWLINE;
- syntax |= RE_HAT_LISTS_NOT_NEWLINE;
- /* It also changes the matching behavior. */
- preg->newline_anchor = 1;
- }
- else
- preg->newline_anchor = 0;
- preg->no_sub = !!(cflags & REG_NOSUB);
- preg->translate = NULL;
-
- ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
-
- /* POSIX doesn't distinguish between an unmatched open-group and an
- unmatched close-group: both are REG_EPAREN. */
- if (ret == REG_ERPAREN)
- ret = REG_EPAREN;
-
- /* We have already checked preg->fastmap != NULL. */
- if (BE (ret == REG_NOERROR, 1))
- /* Compute the fastmap now, since regexec cannot modify the pattern
- buffer. This function nevers fails in this implementation. */
- (void) re_compile_fastmap (preg);
- else
- {
- /* Some error occurred while compiling the expression. */
- re_free (preg->fastmap);
- preg->fastmap = NULL;
- }
-
- return (int) ret;
-}
-#ifdef _LIBC
-weak_alias (__regcomp, regcomp)
-#endif
-
-/* Returns a message corresponding to an error code, ERRCODE, returned
- from either regcomp or regexec. We don't use PREG here. */
-
-size_t
-regerror (errcode, preg, errbuf, errbuf_size)
- int errcode;
- const regex_t *preg;
- char *errbuf;
- size_t errbuf_size;
-{
- const char *msg;
- size_t msg_size;
-
- if (BE (errcode < 0
- || errcode >= (int) (sizeof (__re_error_msgid_idx)
- / sizeof (__re_error_msgid_idx[0])), 0))
- /* Only error codes returned by the rest of the code should be passed
- to this routine. If we are given anything else, or if other regex
- code generates an invalid error code, then the program has a bug.
- Dump core so we can fix it. */
- abort ();
-
- msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
-
- msg_size = strlen (msg) + 1; /* Includes the null. */
-
- if (BE (errbuf_size != 0, 1))
- {
- if (BE (msg_size > errbuf_size, 0))
- {
-#if defined HAVE_MEMPCPY || defined _LIBC
- *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
-#else
- memcpy (errbuf, msg, errbuf_size - 1);
- errbuf[errbuf_size - 1] = 0;
-#endif
- }
- else
- memcpy (errbuf, msg, msg_size);
- }
-
- return msg_size;
-}
-#ifdef _LIBC
-weak_alias (__regerror, regerror)
-#endif
-
-
-static void
-free_dfa_content (re_dfa_t *dfa)
-{
- int i, j;
-
- re_free (dfa->subexps);
-
- for (i = 0; i < dfa->nodes_len; ++i)
- {
- re_token_t *node = dfa->nodes + i;
-#ifdef RE_ENABLE_I18N
- if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
- free_charset (node->opr.mbcset);
- else
-#endif /* RE_ENABLE_I18N */
- if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
- re_free (node->opr.sbcset);
- }
- re_free (dfa->nexts);
- for (i = 0; i < dfa->nodes_len; ++i)
- {
- if (dfa->eclosures != NULL)
- re_node_set_free (dfa->eclosures + i);
- if (dfa->inveclosures != NULL)
- re_node_set_free (dfa->inveclosures + i);
- if (dfa->edests != NULL)
- re_node_set_free (dfa->edests + i);
- }
- re_free (dfa->edests);
- re_free (dfa->eclosures);
- re_free (dfa->inveclosures);
- re_free (dfa->nodes);
-
- for (i = 0; i <= dfa->state_hash_mask; ++i)
- {
- struct re_state_table_entry *entry = dfa->state_table + i;
- for (j = 0; j < entry->num; ++j)
- {
- re_dfastate_t *state = entry->array[j];
- free_state (state);
- }
- re_free (entry->array);
- }
- re_free (dfa->state_table);
-
- if (dfa->word_char != NULL)
- re_free (dfa->word_char);
-#ifdef DEBUG
- re_free (dfa->re_str);
-#endif
-
- re_free (dfa);
-}
-
-
-/* Free dynamically allocated space used by PREG. */
-
-void
-regfree (preg)
- regex_t *preg;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- if (BE (dfa != NULL, 1))
- free_dfa_content (dfa);
-
- re_free (preg->fastmap);
-}
-#ifdef _LIBC
-weak_alias (__regfree, regfree)
-#endif
-\f
-/* Entry points compatible with 4.2 BSD regex library. We don't define
- them unless specifically requested. */
-
-#if defined _REGEX_RE_COMP || defined _LIBC
-
-/* BSD has one and only one pattern buffer. */
-static struct re_pattern_buffer re_comp_buf;
-
-char *
-# ifdef _LIBC
-/* Make these definitions weak in libc, so POSIX programs can redefine
- these names if they don't use our functions, and still use
- regcomp/regexec above without link errors. */
-weak_function
-# endif
-re_comp (s)
- const char *s;
-{
- reg_errcode_t ret;
- char *fastmap;
-
- if (!s)
- {
- if (!re_comp_buf.buffer)
- return gettext ("No previous regular expression");
- return 0;
- }
-
- if (re_comp_buf.buffer)
- {
- fastmap = re_comp_buf.fastmap;
- re_comp_buf.fastmap = NULL;
- __regfree (&re_comp_buf);
- memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
- re_comp_buf.fastmap = fastmap;
- }
-
- if (re_comp_buf.fastmap == NULL)
- {
- re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
- if (re_comp_buf.fastmap == NULL)
- return (char *) gettext (__re_error_msgid
- + __re_error_msgid_idx[(int) REG_ESPACE]);
- }
-
- /* Since `re_exec' always passes NULL for the `regs' argument, we
- don't need to initialize the pattern buffer fields which affect it. */
-
- /* Match anchors at newlines. */
- re_comp_buf.newline_anchor = 1;
-
- ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
-
- if (!ret)
- return NULL;
-
- /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
- return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
-}
-
-#ifdef _LIBC
-libc_freeres_fn (free_mem)
-{
- __regfree (&re_comp_buf);
-}
-#endif
-
-#endif /* _REGEX_RE_COMP */
-\f
-/* Internal entry point.
- Compile the regular expression PATTERN, whose length is LENGTH.
- SYNTAX indicate regular expression's syntax. */
-
-static reg_errcode_t
-re_compile_internal (preg, pattern, length, syntax)
- regex_t *preg;
- const char * pattern;
- int length;
- reg_syntax_t syntax;
-{
- reg_errcode_t err = REG_NOERROR;
- re_dfa_t *dfa;
- re_string_t regexp;
-
- /* Initialize the pattern buffer. */
- preg->fastmap_accurate = 0;
- preg->syntax = syntax;
- preg->not_bol = preg->not_eol = 0;
- preg->used = 0;
- preg->re_nsub = 0;
- preg->can_be_null = 0;
- preg->regs_allocated = REGS_UNALLOCATED;
-
- /* Initialize the dfa. */
- dfa = (re_dfa_t *) preg->buffer;
- if (preg->allocated < sizeof (re_dfa_t))
- {
- /* If zero allocated, but buffer is non-null, try to realloc
- enough space. This loses if buffer's address is bogus, but
- that is the user's responsibility. If ->buffer is NULL this
- is a simple allocation. */
- dfa = re_realloc (preg->buffer, re_dfa_t, 1);
- if (dfa == NULL)
- return REG_ESPACE;
- preg->allocated = sizeof (re_dfa_t);
- }
- preg->buffer = (unsigned char *) dfa;
- preg->used = sizeof (re_dfa_t);
-
- err = init_dfa (dfa, length);
- if (BE (err != REG_NOERROR, 0))
- {
- re_free (dfa);
- preg->buffer = NULL;
- preg->allocated = 0;
- return err;
- }
-#ifdef DEBUG
- dfa->re_str = re_malloc (char, length + 1);
- strncpy (dfa->re_str, pattern, length + 1);
-#endif
-
- err = re_string_construct (®exp, pattern, length, preg->translate,
- syntax & RE_ICASE);
- if (BE (err != REG_NOERROR, 0))
- {
- re_free (dfa);
- preg->buffer = NULL;
- preg->allocated = 0;
- return err;
- }
-
- /* Parse the regular expression, and build a structure tree. */
- preg->re_nsub = 0;
- dfa->str_tree = parse (®exp, preg, syntax, &err);
- if (BE (dfa->str_tree == NULL, 0))
- goto re_compile_internal_free_return;
-
- /* Analyze the tree and collect information which is necessary to
- create the dfa. */
- err = analyze (dfa);
- if (BE (err != REG_NOERROR, 0))
- goto re_compile_internal_free_return;
-
- /* Then create the initial state of the dfa. */
- err = create_initial_state (dfa);
-
- /* Release work areas. */
- free_workarea_compile (preg);
- re_string_destruct (®exp);
-
- if (BE (err != REG_NOERROR, 0))
- {
- re_compile_internal_free_return:
- free_dfa_content (dfa);
- preg->buffer = NULL;
- preg->allocated = 0;
- }
-
- return err;
-}
-
-/* Initialize DFA. We use the length of the regular expression PAT_LEN
- as the initial length of some arrays. */
-
-static reg_errcode_t
-init_dfa (dfa, pat_len)
- re_dfa_t *dfa;
- int pat_len;
-{
- int table_size;
-
- memset (dfa, '\0', sizeof (re_dfa_t));
-
- dfa->nodes_alloc = pat_len + 1;
- dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
-
- dfa->states_alloc = pat_len + 1;
-
- /* table_size = 2 ^ ceil(log pat_len) */
- for (table_size = 1; table_size > 0; table_size <<= 1)
- if (table_size > pat_len)
- break;
-
- dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
- dfa->state_hash_mask = table_size - 1;
-
- dfa->subexps_alloc = 1;
- dfa->subexps = re_malloc (re_subexp_t, dfa->subexps_alloc);
- dfa->word_char = NULL;
-
- if (BE (dfa->nodes == NULL || dfa->state_table == NULL
- || dfa->subexps == NULL, 0))
- {
- /* We don't bother to free anything which was allocated. Very
- soon the process will go down anyway. */
- dfa->subexps = NULL;
- dfa->state_table = NULL;
- dfa->nodes = NULL;
- return REG_ESPACE;
- }
- return REG_NOERROR;
-}
-
-/* Initialize WORD_CHAR table, which indicate which character is
- "word". In this case "word" means that it is the word construction
- character used by some operators like "\<", "\>", etc. */
-
-static reg_errcode_t
-init_word_char (dfa)
- re_dfa_t *dfa;
-{
- int i, j, ch;
- dfa->word_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1);
- if (BE (dfa->word_char == NULL, 0))
- return REG_ESPACE;
- for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
- for (j = 0; j < UINT_BITS; ++j, ++ch)
- if (isalnum (ch) || ch == '_')
- dfa->word_char[i] |= 1 << j;
- return REG_NOERROR;
-}
-
-/* Free the work area which are only used while compiling. */
-
-static void
-free_workarea_compile (preg)
- regex_t *preg;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- free_bin_tree (dfa->str_tree);
- dfa->str_tree = NULL;
- re_free (dfa->org_indices);
- dfa->org_indices = NULL;
-}
-
-/* Create initial states for all contexts. */
-
-static reg_errcode_t
-create_initial_state (dfa)
- re_dfa_t *dfa;
-{
- int first, i;
- reg_errcode_t err;
- re_node_set init_nodes;
-
- /* Initial states have the epsilon closure of the node which is
- the first node of the regular expression. */
- first = dfa->str_tree->first;
- dfa->init_node = first;
- err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
- if (BE (err != REG_NOERROR, 0))
- return err;
-
- /* The back-references which are in initial states can epsilon transit,
- since in this case all of the subexpressions can be null.
- Then we add epsilon closures of the nodes which are the next nodes of
- the back-references. */
- if (dfa->nbackref > 0)
- for (i = 0; i < init_nodes.nelem; ++i)
- {
- int node_idx = init_nodes.elems[i];
- re_token_type_t type = dfa->nodes[node_idx].type;
-
- int clexp_idx;
- if (type != OP_BACK_REF)
- continue;
- for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
- {
- re_token_t *clexp_node;
- clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
- if (clexp_node->type == OP_CLOSE_SUBEXP
- && clexp_node->opr.idx + 1 == dfa->nodes[node_idx].opr.idx)
- break;
- }
- if (clexp_idx == init_nodes.nelem)
- continue;
-
- if (type == OP_BACK_REF)
- {
- int dest_idx = dfa->edests[node_idx].elems[0];
- if (!re_node_set_contains (&init_nodes, dest_idx))
- {
- re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx);
- i = 0;
- }
- }
- }
-
- /* It must be the first time to invoke acquire_state. */
- dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
- /* We don't check ERR here, since the initial state must not be NULL. */
- if (BE (dfa->init_state == NULL, 0))
- return err;
- if (dfa->init_state->has_constraint)
- {
- dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
- CONTEXT_WORD);
- dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
- CONTEXT_NEWLINE);
- dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
- &init_nodes,
- CONTEXT_NEWLINE
- | CONTEXT_BEGBUF);
- if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
- || dfa->init_state_begbuf == NULL, 0))
- return err;
- }
- else
- dfa->init_state_word = dfa->init_state_nl
- = dfa->init_state_begbuf = dfa->init_state;
-
- re_node_set_free (&init_nodes);
- return REG_NOERROR;
-}
-\f
-/* Analyze the structure tree, and calculate "first", "next", "edest",
- "eclosure", and "inveclosure". */
-
-static reg_errcode_t
-analyze (dfa)
- re_dfa_t *dfa;
-{
- int i;
- reg_errcode_t ret;
-
- /* Allocate arrays. */
- dfa->nexts = re_malloc (int, dfa->nodes_alloc);
- dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
- dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
- dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
- dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_alloc);
- if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
- || dfa->eclosures == NULL || dfa->inveclosures == NULL, 0))
- return REG_ESPACE;
- /* Initialize them. */
- for (i = 0; i < dfa->nodes_len; ++i)
- {
- dfa->nexts[i] = -1;
- re_node_set_init_empty (dfa->edests + i);
- re_node_set_init_empty (dfa->eclosures + i);
- re_node_set_init_empty (dfa->inveclosures + i);
- }
-
- ret = analyze_tree (dfa, dfa->str_tree);
- if (BE (ret == REG_NOERROR, 1))
- {
- ret = calc_eclosure (dfa);
- if (ret == REG_NOERROR)
- calc_inveclosure (dfa);
- }
- return ret;
-}
-
-/* Helper functions for analyze.
- This function calculate "first", "next", and "edest" for the subtree
- whose root is NODE. */
-
-static reg_errcode_t
-analyze_tree (dfa, node)
- re_dfa_t *dfa;
- bin_tree_t *node;
-{
- reg_errcode_t ret;
- if (node->first == -1)
- calc_first (dfa, node);
- if (node->next == -1)
- calc_next (dfa, node);
- if (node->eclosure.nelem == 0)
- calc_epsdest (dfa, node);
- /* Calculate "first" etc. for the left child. */
- if (node->left != NULL)
- {
- ret = analyze_tree (dfa, node->left);
- if (BE (ret != REG_NOERROR, 0))
- return ret;
- }
- /* Calculate "first" etc. for the right child. */
- if (node->right != NULL)
- {
- ret = analyze_tree (dfa, node->right);
- if (BE (ret != REG_NOERROR, 0))
- return ret;
- }
- return REG_NOERROR;
-}
-
-/* Calculate "first" for the node NODE. */
-static void
-calc_first (dfa, node)
- re_dfa_t *dfa;
- bin_tree_t *node;
-{
- int idx, type;
- idx = node->node_idx;
- type = (node->type == 0) ? dfa->nodes[idx].type : node->type;
-
- switch (type)
- {
-#ifdef DEBUG
- case OP_OPEN_BRACKET:
- case OP_CLOSE_BRACKET:
- case OP_OPEN_DUP_NUM:
- case OP_CLOSE_DUP_NUM:
- case OP_NON_MATCH_LIST:
- case OP_OPEN_COLL_ELEM:
- case OP_CLOSE_COLL_ELEM:
- case OP_OPEN_EQUIV_CLASS:
- case OP_CLOSE_EQUIV_CLASS:
- case OP_OPEN_CHAR_CLASS:
- case OP_CLOSE_CHAR_CLASS:
- /* These must not be appeared here. */
- assert (0);
-#endif
- case END_OF_RE:
- case CHARACTER:
- case OP_PERIOD:
- case OP_DUP_ASTERISK:
- case OP_DUP_QUESTION:
-#ifdef RE_ENABLE_I18N
- case COMPLEX_BRACKET:
-#endif /* RE_ENABLE_I18N */
- case SIMPLE_BRACKET:
- case OP_BACK_REF:
- case ANCHOR:
- case OP_OPEN_SUBEXP:
- case OP_CLOSE_SUBEXP:
- node->first = idx;
- break;
- case OP_DUP_PLUS:
-#ifdef DEBUG
- assert (node->left != NULL);
-#endif
- if (node->left->first == -1)
- calc_first (dfa, node->left);
- node->first = node->left->first;
- break;
- case OP_ALT:
- node->first = idx;
- break;
- /* else fall through */
- default:
-#ifdef DEBUG
- assert (node->left != NULL);
-#endif
- if (node->left->first == -1)
- calc_first (dfa, node->left);
- node->first = node->left->first;
- break;
- }
-}
-
-/* Calculate "next" for the node NODE. */
-
-static void
-calc_next (dfa, node)
- re_dfa_t *dfa;
- bin_tree_t *node;
-{
- int idx, type;
- bin_tree_t *parent = node->parent;
- if (parent == NULL)
- {
- node->next = -1;
- idx = node->node_idx;
- if (node->type == 0)
- dfa->nexts[idx] = node->next;
- return;
- }
-
- idx = parent->node_idx;
- type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type;
-
- switch (type)
- {
- case OP_DUP_ASTERISK:
- case OP_DUP_PLUS:
- node->next = idx;
- break;
- case CONCAT:
- if (parent->left == node)
- {
- if (parent->right->first == -1)
- calc_first (dfa, parent->right);
- node->next = parent->right->first;
- break;
- }
- /* else fall through */
- default:
- if (parent->next == -1)
- calc_next (dfa, parent);
- node->next = parent->next;
- break;
- }
- idx = node->node_idx;
- if (node->type == 0)
- dfa->nexts[idx] = node->next;
-}
-
-/* Calculate "edest" for the node NODE. */
-
-static void
-calc_epsdest (dfa, node)
- re_dfa_t *dfa;
- bin_tree_t *node;
-{
- int idx;
- idx = node->node_idx;
- if (node->type == 0)
- {
- if (dfa->nodes[idx].type == OP_DUP_ASTERISK
- || dfa->nodes[idx].type == OP_DUP_PLUS
- || dfa->nodes[idx].type == OP_DUP_QUESTION)
- {
- if (node->left->first == -1)
- calc_first (dfa, node->left);
- if (node->next == -1)
- calc_next (dfa, node);
- re_node_set_init_2 (dfa->edests + idx, node->left->first,
- node->next);
- }
- else if (dfa->nodes[idx].type == OP_ALT)
- {
- int left, right;
- if (node->left != NULL)
- {
- if (node->left->first == -1)
- calc_first (dfa, node->left);
- left = node->left->first;
- }
- else
- {
- if (node->next == -1)
- calc_next (dfa, node);
- left = node->next;
- }
- if (node->right != NULL)
- {
- if (node->right->first == -1)
- calc_first (dfa, node->right);
- right = node->right->first;
- }
- else
- {
- if (node->next == -1)
- calc_next (dfa, node);
- right = node->next;
- }
- re_node_set_init_2 (dfa->edests + idx, left, right);
- }
- else if (dfa->nodes[idx].type == ANCHOR
- || dfa->nodes[idx].type == OP_OPEN_SUBEXP
- || dfa->nodes[idx].type == OP_CLOSE_SUBEXP
- || dfa->nodes[idx].type == OP_BACK_REF)
- re_node_set_init_1 (dfa->edests + idx, node->next);
- }
-}
-
-/* Duplicate the epsilon closure of the node ROOT_NODE.
- Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
- to their own constraint. */
-
-static reg_errcode_t
-duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
- init_constraint)
- re_dfa_t *dfa;
- int top_org_node, top_clone_node, root_node;
- unsigned int init_constraint;
-{
- reg_errcode_t err;
- int org_node, clone_node, ret;
- unsigned int constraint = init_constraint;
- for (org_node = top_org_node, clone_node = top_clone_node;;)
- {
- int org_dest, clone_dest;
- if (dfa->nodes[org_node].type == OP_BACK_REF)
- {
- /* If the back reference epsilon-transit, its destination must
- also have the constraint. Then duplicate the epsilon closure
- of the destination of the back reference, and store it in
- edests of the back reference. */
- org_dest = dfa->nexts[org_node];
- re_node_set_empty (dfa->edests + clone_node);
- err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
- if (BE (err != REG_NOERROR, 0))
- return err;
- dfa->nexts[clone_node] = dfa->nexts[org_node];
- ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
- if (BE (ret < 0, 0))
- return REG_ESPACE;
- }
- else if (dfa->edests[org_node].nelem == 0)
- {
- /* In case of the node can't epsilon-transit, don't duplicate the
- destination and store the original destination as the
- destination of the node. */
- dfa->nexts[clone_node] = dfa->nexts[org_node];
- break;
- }
- else if (dfa->edests[org_node].nelem == 1)
- {
- /* In case of the node can epsilon-transit, and it has only one
- destination. */
- org_dest = dfa->edests[org_node].elems[0];
- re_node_set_empty (dfa->edests + clone_node);
- if (dfa->nodes[org_node].type == ANCHOR)
- {
- /* In case of the node has another constraint, append it. */
- if (org_node == root_node && clone_node != org_node)
- {
- /* ...but if the node is root_node itself, it means the
- epsilon closure have a loop, then tie it to the
- destination of the root_node. */
- ret = re_node_set_insert (dfa->edests + clone_node,
- org_dest);
- if (BE (ret < 0, 0))
- return REG_ESPACE;
- break;
- }
- constraint |= dfa->nodes[org_node].opr.ctx_type;
- }
- err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
- if (BE (err != REG_NOERROR, 0))
- return err;
- ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
- if (BE (ret < 0, 0))
- return REG_ESPACE;
- }
- else /* dfa->edests[org_node].nelem == 2 */
- {
- /* In case of the node can epsilon-transit, and it has two
- destinations. E.g. '|', '*', '+', '?'. */
- org_dest = dfa->edests[org_node].elems[0];
- re_node_set_empty (dfa->edests + clone_node);
- /* Search for a duplicated node which satisfies the constraint. */
- clone_dest = search_duplicated_node (dfa, org_dest, constraint);
- if (clone_dest == -1)
- {
- /* There are no such a duplicated node, create a new one. */
- err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
- if (BE (err != REG_NOERROR, 0))
- return err;
- ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
- if (BE (ret < 0, 0))
- return REG_ESPACE;
- err = duplicate_node_closure (dfa, org_dest, clone_dest,
- root_node, constraint);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- else
- {
- /* There are a duplicated node which satisfy the constraint,
- use it to avoid infinite loop. */
- ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
- if (BE (ret < 0, 0))
- return REG_ESPACE;
- }
-
- org_dest = dfa->edests[org_node].elems[1];
- err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
- if (BE (err != REG_NOERROR, 0))
- return err;
- ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
- if (BE (ret < 0, 0))
- return REG_ESPACE;
- }
- org_node = org_dest;
- clone_node = clone_dest;
- }
- return REG_NOERROR;
-}
-
-/* Search for a node which is duplicated from the node ORG_NODE, and
- satisfies the constraint CONSTRAINT. */
-
-static int
-search_duplicated_node (dfa, org_node, constraint)
- re_dfa_t *dfa;
- int org_node;
- unsigned int constraint;
-{
- int idx;
- for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
- {
- if (org_node == dfa->org_indices[idx]
- && constraint == dfa->nodes[idx].constraint)
- return idx; /* Found. */
- }
- return -1; /* Not found. */
-}
-
-/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
- The new index will be stored in NEW_IDX and return REG_NOERROR if succeeded,
- otherwise return the error code. */
-
-static reg_errcode_t
-duplicate_node (new_idx, dfa, org_idx, constraint)
- re_dfa_t *dfa;
- int *new_idx, org_idx;
- unsigned int constraint;
-{
- re_token_t dup;
- int dup_idx;
-
- dup = dfa->nodes[org_idx];
- dup_idx = re_dfa_add_node (dfa, dup, 1);
- if (BE (dup_idx == -1, 0))
- return REG_ESPACE;
- dfa->nodes[dup_idx].constraint = constraint;
- if (dfa->nodes[org_idx].type == ANCHOR)
- dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type;
- dfa->nodes[dup_idx].duplicated = 1;
- re_node_set_init_empty (dfa->edests + dup_idx);
- re_node_set_init_empty (dfa->eclosures + dup_idx);
- re_node_set_init_empty (dfa->inveclosures + dup_idx);
-
- /* Store the index of the original node. */
- dfa->org_indices[dup_idx] = org_idx;
- *new_idx = dup_idx;
- return REG_NOERROR;
-}
-
-static void
-calc_inveclosure (dfa)
- re_dfa_t *dfa;
-{
- int src, idx, dest;
- for (src = 0; src < dfa->nodes_len; ++src)
- {
- for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
- {
- dest = dfa->eclosures[src].elems[idx];
- re_node_set_insert (dfa->inveclosures + dest, src);
- }
- }
-}
-
-/* Calculate "eclosure" for all the node in DFA. */
-
-static reg_errcode_t
-calc_eclosure (dfa)
- re_dfa_t *dfa;
-{
- int node_idx, incomplete;
-#ifdef DEBUG
- assert (dfa->nodes_len > 0);
-#endif
- incomplete = 0;
- /* For each nodes, calculate epsilon closure. */
- for (node_idx = 0; ; ++node_idx)
- {
- reg_errcode_t err;
- re_node_set eclosure_elem;
- if (node_idx == dfa->nodes_len)
- {
- if (!incomplete)
- break;
- incomplete = 0;
- node_idx = 0;
- }
-
-#ifdef DEBUG
- assert (dfa->eclosures[node_idx].nelem != -1);
-#endif
- /* If we have already calculated, skip it. */
- if (dfa->eclosures[node_idx].nelem != 0)
- continue;
- /* Calculate epsilon closure of `node_idx'. */
- err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
- if (BE (err != REG_NOERROR, 0))
- return err;
-
- if (dfa->eclosures[node_idx].nelem == 0)
- {
- incomplete = 1;
- re_node_set_free (&eclosure_elem);
- }
- }
- return REG_NOERROR;
-}
-
-/* Calculate epsilon closure of NODE. */
-
-static reg_errcode_t
-calc_eclosure_iter (new_set, dfa, node, root)
- re_node_set *new_set;
- re_dfa_t *dfa;
- int node, root;
-{
- reg_errcode_t err;
- unsigned int constraint;
- int i, incomplete;
- re_node_set eclosure;
- incomplete = 0;
- err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
- if (BE (err != REG_NOERROR, 0))
- return err;
-
- /* This indicates that we are calculating this node now.
- We reference this value to avoid infinite loop. */
- dfa->eclosures[node].nelem = -1;
-
- constraint = ((dfa->nodes[node].type == ANCHOR)
- ? dfa->nodes[node].opr.ctx_type : 0);
- /* If the current node has constraints, duplicate all nodes.
- Since they must inherit the constraints. */
- if (constraint && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
- {
- int org_node, cur_node;
- org_node = cur_node = node;
- err = duplicate_node_closure (dfa, node, node, node, constraint);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
-
- /* Expand each epsilon destination nodes. */
- if (IS_EPSILON_NODE(dfa->nodes[node].type))
- for (i = 0; i < dfa->edests[node].nelem; ++i)
- {
- re_node_set eclosure_elem;
- int edest = dfa->edests[node].elems[i];
- /* If calculating the epsilon closure of `edest' is in progress,
- return intermediate result. */
- if (dfa->eclosures[edest].nelem == -1)
- {
- incomplete = 1;
- continue;
- }
- /* If we haven't calculated the epsilon closure of `edest' yet,
- calculate now. Otherwise use calculated epsilon closure. */
- if (dfa->eclosures[edest].nelem == 0)
- {
- err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- else
- eclosure_elem = dfa->eclosures[edest];
- /* Merge the epsilon closure of `edest'. */
- re_node_set_merge (&eclosure, &eclosure_elem);
- /* If the epsilon closure of `edest' is incomplete,
- the epsilon closure of this node is also incomplete. */
- if (dfa->eclosures[edest].nelem == 0)
- {
- incomplete = 1;
- re_node_set_free (&eclosure_elem);
- }
- }
-
- /* Epsilon closures include itself. */
- re_node_set_insert (&eclosure, node);
- if (incomplete && !root)
- dfa->eclosures[node].nelem = 0;
- else
- dfa->eclosures[node] = eclosure;
- *new_set = eclosure;
- return REG_NOERROR;
-}
-\f
-/* Functions for token which are used in the parser. */
-
-/* Fetch a token from INPUT.
- We must not use this function inside bracket expressions. */
-
-static re_token_t
-fetch_token (input, syntax)
- re_string_t *input;
- reg_syntax_t syntax;
-{
- re_token_t token;
- int consumed_byte;
- consumed_byte = peek_token (&token, input, syntax);
- re_string_skip_bytes (input, consumed_byte);
- return token;
-}
-
-/* Peek a token from INPUT, and return the length of the token.
- We must not use this function inside bracket expressions. */
-
-static int
-peek_token (token, input, syntax)
- re_token_t *token;
- re_string_t *input;
- reg_syntax_t syntax;
-{
- unsigned char c;
-
- if (re_string_eoi (input))
- {
- token->type = END_OF_RE;
- return 0;
- }
-
- c = re_string_peek_byte (input, 0);
- token->opr.c = c;
-
-#ifdef RE_ENABLE_I18N
- token->mb_partial = 0;
- if (MB_CUR_MAX > 1 &&
- !re_string_first_byte (input, re_string_cur_idx (input)))
- {
- token->type = CHARACTER;
- token->mb_partial = 1;
- return 1;
- }
-#endif
- if (c == '\\')
- {
- unsigned char c2;
- if (re_string_cur_idx (input) + 1 >= re_string_length (input))
- {
- token->type = BACK_SLASH;
- return 1;
- }
-
- c2 = re_string_peek_byte_case (input, 1);
- token->opr.c = c2;
- token->type = CHARACTER;
- switch (c2)
- {
- case '|':
- if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
- token->type = OP_ALT;
- break;
- case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- if (!(syntax & RE_NO_BK_REFS))
- {
- token->type = OP_BACK_REF;
- token->opr.idx = c2 - '0';
- }
- break;
- case '<':
- if (!(syntax & RE_NO_GNU_OPS))
- {
- token->type = ANCHOR;
- token->opr.idx = WORD_FIRST;
- }
- break;
- case '>':
- if (!(syntax & RE_NO_GNU_OPS))
- {
- token->type = ANCHOR;
- token->opr.idx = WORD_LAST;
- }
- break;
- case 'b':
- if (!(syntax & RE_NO_GNU_OPS))
- {
- token->type = ANCHOR;
- token->opr.idx = WORD_DELIM;
- }
- break;
- case 'B':
- if (!(syntax & RE_NO_GNU_OPS))
- {
- token->type = ANCHOR;
- token->opr.idx = INSIDE_WORD;
- }
- break;
- case 'w':
- if (!(syntax & RE_NO_GNU_OPS))
- token->type = OP_WORD;
- break;
- case 'W':
- if (!(syntax & RE_NO_GNU_OPS))
- token->type = OP_NOTWORD;
- break;
- case '`':
- if (!(syntax & RE_NO_GNU_OPS))
- {
- token->type = ANCHOR;
- token->opr.idx = BUF_FIRST;
- }
- break;
- case '\'':
- if (!(syntax & RE_NO_GNU_OPS))
- {
- token->type = ANCHOR;
- token->opr.idx = BUF_LAST;
- }
- break;
- case '(':
- if (!(syntax & RE_NO_BK_PARENS))
- token->type = OP_OPEN_SUBEXP;
- break;
- case ')':
- if (!(syntax & RE_NO_BK_PARENS))
- token->type = OP_CLOSE_SUBEXP;
- break;
- case '+':
- if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
- token->type = OP_DUP_PLUS;
- break;
- case '?':
- if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
- token->type = OP_DUP_QUESTION;
- break;
- case '{':
- if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
- token->type = OP_OPEN_DUP_NUM;
- break;
- case '}':
- if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
- token->type = OP_CLOSE_DUP_NUM;
- break;
- default:
- break;
- }
- return 2;
- }
-
- token->type = CHARACTER;
- switch (c)
- {
- case '\n':
- if (syntax & RE_NEWLINE_ALT)
- token->type = OP_ALT;
- break;
- case '|':
- if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
- token->type = OP_ALT;
- break;
- case '*':
- token->type = OP_DUP_ASTERISK;
- break;
- case '+':
- if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
- token->type = OP_DUP_PLUS;
- break;
- case '?':
- if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
- token->type = OP_DUP_QUESTION;
- break;
- case '{':
- if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
- token->type = OP_OPEN_DUP_NUM;
- break;
- case '}':
- if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
- token->type = OP_CLOSE_DUP_NUM;
- break;
- case '(':
- if (syntax & RE_NO_BK_PARENS)
- token->type = OP_OPEN_SUBEXP;
- break;
- case ')':
- if (syntax & RE_NO_BK_PARENS)
- token->type = OP_CLOSE_SUBEXP;
- break;
- case '[':
- token->type = OP_OPEN_BRACKET;
- break;
- case '.':
- token->type = OP_PERIOD;
- break;
- case '^':
- if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
- re_string_cur_idx (input) != 0)
- {
- char prev = re_string_peek_byte (input, -1);
- if (prev != '|' && prev != '(' &&
- (!(syntax & RE_NEWLINE_ALT) || prev != '\n'))
- break;
- }
- token->type = ANCHOR;
- token->opr.idx = LINE_FIRST;
- break;
- case '$':
- if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
- re_string_cur_idx (input) + 1 != re_string_length (input))
- {
- re_token_t next;
- re_string_skip_bytes (input, 1);
- peek_token (&next, input, syntax);
- re_string_skip_bytes (input, -1);
- if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
- break;
- }
- token->type = ANCHOR;
- token->opr.idx = LINE_LAST;
- break;
- default:
- break;
- }
- return 1;
-}
-
-/* Peek a token from INPUT, and return the length of the token.
- We must not use this function out of bracket expressions. */
-
-static int
-peek_token_bracket (token, input, syntax)
- re_token_t *token;
- re_string_t *input;
- reg_syntax_t syntax;
-{
- unsigned char c;
- if (re_string_eoi (input))
- {
- token->type = END_OF_RE;
- return 0;
- }
- c = re_string_peek_byte (input, 0);
- token->opr.c = c;
-
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1 &&
- !re_string_first_byte (input, re_string_cur_idx (input)))
- {
- token->type = CHARACTER;
- return 1;
- }
-#endif /* RE_ENABLE_I18N */
-
- if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS))
- {
- /* In this case, '\' escape a character. */
- unsigned char c2;
- re_string_skip_bytes (input, 1);
- c2 = re_string_peek_byte (input, 0);
- token->opr.c = c2;
- token->type = CHARACTER;
- return 1;
- }
- if (c == '[') /* '[' is a special char in a bracket exps. */
- {
- unsigned char c2;
- int token_len;
- c2 = re_string_peek_byte (input, 1);
- token->opr.c = c2;
- token_len = 2;
- switch (c2)
- {
- case '.':
- token->type = OP_OPEN_COLL_ELEM;
- break;
- case '=':
- token->type = OP_OPEN_EQUIV_CLASS;
- break;
- case ':':
- if (syntax & RE_CHAR_CLASSES)
- {
- token->type = OP_OPEN_CHAR_CLASS;
- break;
- }
- /* else fall through. */
- default:
- token->type = CHARACTER;
- token->opr.c = c;
- token_len = 1;
- break;
- }
- return token_len;
- }
- switch (c)
- {
- case '-':
- token->type = OP_CHARSET_RANGE;
- break;
- case ']':
- token->type = OP_CLOSE_BRACKET;
- break;
- case '^':
- token->type = OP_NON_MATCH_LIST;
- break;
- default:
- token->type = CHARACTER;
- }
- return 1;
-}
-\f
-/* Functions for parser. */
-
-/* Entry point of the parser.
- Parse the regular expression REGEXP and return the structure tree.
- If an error is occured, ERR is set by error code, and return NULL.
- This function build the following tree, from regular expression <reg_exp>:
- CAT
- / \
- / \
- <reg_exp> EOR
-
- CAT means concatenation.
- EOR means end of regular expression. */
-
-static bin_tree_t *
-parse (regexp, preg, syntax, err)
- re_string_t *regexp;
- regex_t *preg;
- reg_syntax_t syntax;
- reg_errcode_t *err;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- bin_tree_t *tree, *eor, *root;
- re_token_t current_token;
- int new_idx;
- current_token = fetch_token (regexp, syntax);
- tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
- new_idx = re_dfa_add_node (dfa, current_token, 0);
- eor = create_tree (NULL, NULL, 0, new_idx);
- if (tree != NULL)
- root = create_tree (tree, eor, CONCAT, 0);
- else
- root = eor;
- if (BE (new_idx == -1 || eor == NULL || root == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- return root;
-}
-
-/* This function build the following tree, from regular expression
- <branch1>|<branch2>:
- ALT
- / \
- / \
- <branch1> <branch2>
-
- ALT means alternative, which represents the operator `|'. */
-
-static bin_tree_t *
-parse_reg_exp (regexp, preg, token, syntax, nest, err)
- re_string_t *regexp;
- regex_t *preg;
- re_token_t *token;
- reg_syntax_t syntax;
- int nest;
- reg_errcode_t *err;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- bin_tree_t *tree, *branch = NULL;
- int new_idx;
- tree = parse_branch (regexp, preg, token, syntax, nest, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
-
- while (token->type == OP_ALT)
- {
- re_token_t alt_token = *token;
- new_idx = re_dfa_add_node (dfa, alt_token, 0);
- *token = fetch_token (regexp, syntax);
- if (token->type != OP_ALT && token->type != END_OF_RE
- && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
- {
- branch = parse_branch (regexp, preg, token, syntax, nest, err);
- if (BE (*err != REG_NOERROR && branch == NULL, 0))
- {
- free_bin_tree (tree);
- return NULL;
- }
- }
- else
- branch = NULL;
- tree = create_tree (tree, branch, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- dfa->has_plural_match = 1;
- }
- return tree;
-}
-
-/* This function build the following tree, from regular expression
- <exp1><exp2>:
- CAT
- / \
- / \
- <exp1> <exp2>
-
- CAT means concatenation. */
-
-static bin_tree_t *
-parse_branch (regexp, preg, token, syntax, nest, err)
- re_string_t *regexp;
- regex_t *preg;
- re_token_t *token;
- reg_syntax_t syntax;
- int nest;
- reg_errcode_t *err;
-{
- bin_tree_t *tree, *exp;
- tree = parse_expression (regexp, preg, token, syntax, nest, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
-
- while (token->type != OP_ALT && token->type != END_OF_RE
- && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
- {
- exp = parse_expression (regexp, preg, token, syntax, nest, err);
- if (BE (*err != REG_NOERROR && exp == NULL, 0))
- {
- free_bin_tree (tree);
- return NULL;
- }
- if (tree != NULL && exp != NULL)
- {
- tree = create_tree (tree, exp, CONCAT, 0);
- if (tree == NULL)
- {
- *err = REG_ESPACE;
- return NULL;
- }
- }
- else if (tree == NULL)
- tree = exp;
- /* Otherwise exp == NULL, we don't need to create new tree. */
- }
- return tree;
-}
-
-/* This function build the following tree, from regular expression a*:
- *
- |
- a
-*/
-
-static bin_tree_t *
-parse_expression (regexp, preg, token, syntax, nest, err)
- re_string_t *regexp;
- regex_t *preg;
- re_token_t *token;
- reg_syntax_t syntax;
- int nest;
- reg_errcode_t *err;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- bin_tree_t *tree;
- int new_idx;
- switch (token->type)
- {
- case CHARACTER:
- new_idx = re_dfa_add_node (dfa, *token, 0);
- tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- {
- while (!re_string_eoi (regexp)
- && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
- {
- bin_tree_t *mbc_remain;
- *token = fetch_token (regexp, syntax);
- new_idx = re_dfa_add_node (dfa, *token, 0);
- mbc_remain = create_tree (NULL, NULL, 0, new_idx);
- tree = create_tree (tree, mbc_remain, CONCAT, 0);
- if (BE (new_idx == -1 || mbc_remain == NULL || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- }
- }
-#endif
- break;
- case OP_OPEN_SUBEXP:
- tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
- break;
- case OP_OPEN_BRACKET:
- tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
- break;
- case OP_BACK_REF:
- if (BE (preg->re_nsub < token->opr.idx
- || dfa->subexps[token->opr.idx - 1].end == -1, 0))
- {
- *err = REG_ESUBREG;
- return NULL;
- }
- dfa->used_bkref_map |= 1 << (token->opr.idx - 1);
- new_idx = re_dfa_add_node (dfa, *token, 0);
- tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- ++dfa->nbackref;
- dfa->has_mb_node = 1;
- break;
- case OP_DUP_ASTERISK:
- case OP_DUP_PLUS:
- case OP_DUP_QUESTION:
- case OP_OPEN_DUP_NUM:
- if (syntax & RE_CONTEXT_INVALID_OPS)
- {
- *err = REG_BADRPT;
- return NULL;
- }
- else if (syntax & RE_CONTEXT_INDEP_OPS)
- {
- *token = fetch_token (regexp, syntax);
- return parse_expression (regexp, preg, token, syntax, nest, err);
- }
- /* else fall through */
- case OP_CLOSE_SUBEXP:
- if ((token->type == OP_CLOSE_SUBEXP) &&
- !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
- {
- *err = REG_ERPAREN;
- return NULL;
- }
- /* else fall through */
- case OP_CLOSE_DUP_NUM:
- /* We treat it as a normal character. */
-
- /* Then we can these characters as normal characters. */
- token->type = CHARACTER;
- new_idx = re_dfa_add_node (dfa, *token, 0);
- tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- break;
- case ANCHOR:
- if (dfa->word_char == NULL)
- {
- *err = init_word_char (dfa);
- if (BE (*err != REG_NOERROR, 0))
- return NULL;
- }
- if (token->opr.ctx_type == WORD_DELIM)
- {
- bin_tree_t *tree_first, *tree_last;
- int idx_first, idx_last;
- token->opr.ctx_type = WORD_FIRST;
- idx_first = re_dfa_add_node (dfa, *token, 0);
- tree_first = create_tree (NULL, NULL, 0, idx_first);
- token->opr.ctx_type = WORD_LAST;
- idx_last = re_dfa_add_node (dfa, *token, 0);
- tree_last = create_tree (NULL, NULL, 0, idx_last);
- token->type = OP_ALT;
- new_idx = re_dfa_add_node (dfa, *token, 0);
- tree = create_tree (tree_first, tree_last, 0, new_idx);
- if (BE (idx_first == -1 || idx_last == -1 || new_idx == -1
- || tree_first == NULL || tree_last == NULL
- || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- }
- else
- {
- new_idx = re_dfa_add_node (dfa, *token, 0);
- tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- }
- /* We must return here, since ANCHORs can't be followed
- by repetition operators.
- eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
- it must not be "<ANCHOR(^)><REPEAT(*)>". */
- *token = fetch_token (regexp, syntax);
- return tree;
- case OP_PERIOD:
- new_idx = re_dfa_add_node (dfa, *token, 0);
- tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- if (MB_CUR_MAX > 1)
- dfa->has_mb_node = 1;
- break;
- case OP_WORD:
- tree = build_word_op (dfa, 0, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
- break;
- case OP_NOTWORD:
- tree = build_word_op (dfa, 1, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
- break;
- case OP_ALT:
- case END_OF_RE:
- return NULL;
- case BACK_SLASH:
- *err = REG_EESCAPE;
- return NULL;
- default:
- /* Must not happen? */
-#ifdef DEBUG
- assert (0);
-#endif
- return NULL;
- }
- *token = fetch_token (regexp, syntax);
-
- while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
- || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
- {
- tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
- dfa->has_plural_match = 1;
- }
-
- return tree;
-}
-
-/* This function build the following tree, from regular expression
- (<reg_exp>):
- SUBEXP
- |
- <reg_exp>
-*/
-
-static bin_tree_t *
-parse_sub_exp (regexp, preg, token, syntax, nest, err)
- re_string_t *regexp;
- regex_t *preg;
- re_token_t *token;
- reg_syntax_t syntax;
- int nest;
- reg_errcode_t *err;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- bin_tree_t *tree, *left_par, *right_par;
- size_t cur_nsub;
- int new_idx;
- cur_nsub = preg->re_nsub++;
- if (dfa->subexps_alloc < preg->re_nsub)
- {
- re_subexp_t *new_array;
- dfa->subexps_alloc *= 2;
- new_array = re_realloc (dfa->subexps, re_subexp_t, dfa->subexps_alloc);
- if (BE (new_array == NULL, 0))
- {
- dfa->subexps_alloc /= 2;
- *err = REG_ESPACE;
- return NULL;
- }
- dfa->subexps = new_array;
- }
- dfa->subexps[cur_nsub].start = dfa->nodes_len;
- dfa->subexps[cur_nsub].end = -1;
-
- new_idx = re_dfa_add_node (dfa, *token, 0);
- left_par = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || left_par == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- dfa->nodes[new_idx].opr.idx = cur_nsub;
- *token = fetch_token (regexp, syntax);
-
- /* The subexpression may be a null string. */
- if (token->type == OP_CLOSE_SUBEXP)
- tree = NULL;
- else
- {
- tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
- }
- if (BE (token->type != OP_CLOSE_SUBEXP, 0))
- {
- free_bin_tree (tree);
- *err = REG_BADPAT;
- return NULL;
- }
- new_idx = re_dfa_add_node (dfa, *token, 0);
- dfa->subexps[cur_nsub].end = dfa->nodes_len;
- right_par = create_tree (NULL, NULL, 0, new_idx);
- tree = ((tree == NULL) ? right_par
- : create_tree (tree, right_par, CONCAT, 0));
- tree = create_tree (left_par, tree, CONCAT, 0);
- if (BE (new_idx == -1 || right_par == NULL || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- dfa->nodes[new_idx].opr.idx = cur_nsub;
-
- return tree;
-}
-
-/* This function parse repetition operators like "*", "+", "{1,3}" etc. */
-
-static bin_tree_t *
-parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
- bin_tree_t *dup_elem;
- re_string_t *regexp;
- re_dfa_t *dfa;
- re_token_t *token;
- reg_syntax_t syntax;
- reg_errcode_t *err;
-{
- re_token_t dup_token;
- bin_tree_t *tree = dup_elem, *work_tree;
- int new_idx, start_idx = re_string_cur_idx (regexp);
- re_token_t start_token = *token;
- if (token->type == OP_OPEN_DUP_NUM)
- {
- int i;
- int end = 0;
- int start = fetch_number (regexp, token, syntax);
- bin_tree_t *elem;
- if (start == -1)
- {
- if (token->type == CHARACTER && token->opr.c == ',')
- start = 0; /* We treat "{,m}" as "{0,m}". */
- else
- {
- *err = REG_BADBR; /* <re>{} is invalid. */
- return NULL;
- }
- }
- if (BE (start != -2, 1))
- {
- /* We treat "{n}" as "{n,n}". */
- end = ((token->type == OP_CLOSE_DUP_NUM) ? start
- : ((token->type == CHARACTER && token->opr.c == ',')
- ? fetch_number (regexp, token, syntax) : -2));
- }
- if (BE (start == -2 || end == -2, 0))
- {
- /* Invalid sequence. */
- if (token->type == OP_CLOSE_DUP_NUM)
- goto parse_dup_op_invalid_interval;
- else
- goto parse_dup_op_ebrace;
- }
- if (BE (start == 0 && end == 0, 0))
- {
- /* We treat "<re>{0}" and "<re>{0,0}" as null string. */
- *token = fetch_token (regexp, syntax);
- free_bin_tree (dup_elem);
- return NULL;
- }
-
- /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */
- elem = tree;
- for (i = 0; i < start; ++i)
- if (i != 0)
- {
- work_tree = duplicate_tree (elem, dfa);
- tree = create_tree (tree, work_tree, CONCAT, 0);
- if (BE (work_tree == NULL || tree == NULL, 0))
- goto parse_dup_op_espace;
- }
-
- if (end == -1)
- {
- /* We treat "<re>{0,}" as "<re>*". */
- dup_token.type = OP_DUP_ASTERISK;
- if (start > 0)
- {
- elem = duplicate_tree (elem, dfa);
- new_idx = re_dfa_add_node (dfa, dup_token, 0);
- work_tree = create_tree (elem, NULL, 0, new_idx);
- tree = create_tree (tree, work_tree, CONCAT, 0);
- if (BE (elem == NULL || new_idx == -1 || work_tree == NULL
- || tree == NULL, 0))
- goto parse_dup_op_espace;
- }
- else
- {
- new_idx = re_dfa_add_node (dfa, dup_token, 0);
- tree = create_tree (elem, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- goto parse_dup_op_espace;
- }
- }
- else if (end - start > 0)
- {
- /* Then extract "<re>{0,m}" to "<re>?<re>?...<re>?". */
- dup_token.type = OP_DUP_QUESTION;
- if (start > 0)
- {
- elem = duplicate_tree (elem, dfa);
- new_idx = re_dfa_add_node (dfa, dup_token, 0);
- elem = create_tree (elem, NULL, 0, new_idx);
- tree = create_tree (tree, elem, CONCAT, 0);
- if (BE (elem == NULL || new_idx == -1 || tree == NULL, 0))
- goto parse_dup_op_espace;
- }
- else
- {
- new_idx = re_dfa_add_node (dfa, dup_token, 0);
- tree = elem = create_tree (elem, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- goto parse_dup_op_espace;
- }
- for (i = 1; i < end - start; ++i)
- {
- work_tree = duplicate_tree (elem, dfa);
- tree = create_tree (tree, work_tree, CONCAT, 0);
- if (BE (work_tree == NULL || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- }
- }
- }
- else
- {
- new_idx = re_dfa_add_node (dfa, *token, 0);
- tree = create_tree (tree, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- {
- *err = REG_ESPACE;
- return NULL;
- }
- }
- *token = fetch_token (regexp, syntax);
- return tree;
-
- parse_dup_op_espace:
- free_bin_tree (tree);
- *err = REG_ESPACE;
- return NULL;
-
- parse_dup_op_ebrace:
- if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
- {
- *err = REG_EBRACE;
- return NULL;
- }
- goto parse_dup_op_rollback;
- parse_dup_op_invalid_interval:
- if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
- {
- *err = REG_BADBR;
- return NULL;
- }
- parse_dup_op_rollback:
- re_string_set_index (regexp, start_idx);
- *token = start_token;
- token->type = CHARACTER;
- return dup_elem;
-}
-
-/* Size of the names for collating symbol/equivalence_class/character_class.
- I'm not sure, but maybe enough. */
-#define BRACKET_NAME_BUF_SIZE 32
-
-#ifndef _LIBC
- /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
- Build the range expression which starts from START_ELEM, and ends
- at END_ELEM. The result are written to MBCSET and SBCSET.
- RANGE_ALLOC is the allocated size of mbcset->range_starts, and
- mbcset->range_ends, is a pointer argument sinse we may
- update it. */
-
-static reg_errcode_t
-# ifdef RE_ENABLE_I18N
-build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
- re_charset_t *mbcset;
- int *range_alloc;
-# else /* not RE_ENABLE_I18N */
-build_range_exp (sbcset, start_elem, end_elem)
-# endif /* not RE_ENABLE_I18N */
- re_bitset_ptr_t sbcset;
- bracket_elem_t *start_elem, *end_elem;
-{
- unsigned int start_ch, end_ch;
- /* Equivalence Classes and Character Classes can't be a range start/end. */
- if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
- || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
- 0))
- return REG_ERANGE;
-
- /* We can handle no multi character collating elements without libc
- support. */
- if (BE ((start_elem->type == COLL_SYM
- && strlen ((char *) start_elem->opr.name) > 1)
- || (end_elem->type == COLL_SYM
- && strlen ((char *) end_elem->opr.name) > 1), 0))
- return REG_ECOLLATE;
-
-# ifdef RE_ENABLE_I18N
- {
- wchar_t wc, start_wc, end_wc;
- wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
-
- start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
- : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
- : 0));
- end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
- : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
- : 0));
- start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
- ? __btowc (start_ch) : start_elem->opr.wch);
- end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
- ? __btowc (end_ch) : end_elem->opr.wch);
- cmp_buf[0] = start_wc;
- cmp_buf[4] = end_wc;
- if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
- return REG_ERANGE;
-
- /* Check the space of the arrays. */
- if (*range_alloc == mbcset->nranges)
- {
- /* There are not enough space, need realloc. */
- wchar_t *new_array_start, *new_array_end;
- int new_nranges;
-
- /* +1 in case of mbcset->nranges is 0. */
- new_nranges = 2 * mbcset->nranges + 1;
- /* Use realloc since mbcset->range_starts and mbcset->range_ends
- are NULL if *range_alloc == 0. */
- new_array_start = re_realloc (mbcset->range_starts, wchar_t,
- new_nranges);
- new_array_end = re_realloc (mbcset->range_ends, wchar_t,
- new_nranges);
-
- if (BE (new_array_start == NULL || new_array_end == NULL, 0))
- return REG_ESPACE;
-
- mbcset->range_starts = new_array_start;
- mbcset->range_ends = new_array_end;
- *range_alloc = new_nranges;
- }
-
- mbcset->range_starts[mbcset->nranges] = start_wc;
- mbcset->range_ends[mbcset->nranges++] = end_wc;
-
- /* Build the table for single byte characters. */
- for (wc = 0; wc <= SBC_MAX; ++wc)
- {
- cmp_buf[2] = wc;
- if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
- && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
- bitset_set (sbcset, wc);
- }
- }
-# else /* not RE_ENABLE_I18N */
- {
- unsigned int ch;
- start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
- : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
- : 0));
- end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
- : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
- : 0));
- if (start_ch > end_ch)
- return REG_ERANGE;
- /* Build the table for single byte characters. */
- for (ch = 0; ch <= SBC_MAX; ++ch)
- if (start_ch <= ch && ch <= end_ch)
- bitset_set (sbcset, ch);
- }
-# endif /* not RE_ENABLE_I18N */
- return REG_NOERROR;
-}
-#endif /* not _LIBC */
-
-#ifndef _LIBC
-/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
- Build the collating element which is represented by NAME.
- The result are written to MBCSET and SBCSET.
- COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
- pointer argument since we may update it. */
-
-static reg_errcode_t
-# ifdef RE_ENABLE_I18N
-build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
- re_charset_t *mbcset;
- int *coll_sym_alloc;
-# else /* not RE_ENABLE_I18N */
-build_collating_symbol (sbcset, name)
-# endif /* not RE_ENABLE_I18N */
- re_bitset_ptr_t sbcset;
- const unsigned char *name;
-{
- size_t name_len = strlen ((const char *) name);
- if (BE (name_len != 1, 0))
- return REG_ECOLLATE;
- else
- {
- bitset_set (sbcset, name[0]);
- return REG_NOERROR;
- }
-}
-#endif /* not _LIBC */
-
-/* This function parse bracket expression like "[abc]", "[a-c]",
- "[[.a-a.]]" etc. */
-
-static bin_tree_t *
-parse_bracket_exp (regexp, dfa, token, syntax, err)
- re_string_t *regexp;
- re_dfa_t *dfa;
- re_token_t *token;
- reg_syntax_t syntax;
- reg_errcode_t *err;
-{
-#ifdef _LIBC
- const unsigned char *collseqmb;
- const char *collseqwc;
- uint32_t nrules;
- int32_t table_size;
- const int32_t *symb_table;
- const unsigned char *extra;
-
- /* Local function for parse_bracket_exp used in _LIBC environement.
- Seek the collating symbol entry correspondings to NAME.
- Return the index of the symbol in the SYMB_TABLE. */
-
- static inline int32_t
- seek_collating_symbol_entry (name, name_len)
- const unsigned char *name;
- size_t name_len;
- {
- int32_t hash = elem_hash ((const char *) name, name_len);
- int32_t elem = hash % table_size;
- int32_t second = hash % (table_size - 2);
- while (symb_table[2 * elem] != 0)
- {
- /* First compare the hashing value. */
- if (symb_table[2 * elem] == hash
- /* Compare the length of the name. */
- && name_len == extra[symb_table[2 * elem + 1]]
- /* Compare the name. */
- && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
- name_len) == 0)
- {
- /* Yep, this is the entry. */
- break;
- }
-
- /* Next entry. */
- elem += second;
- }
- return elem;
- }
-
- /* Local function for parse_bracket_exp used in _LIBC environement.
- Look up the collation sequence value of BR_ELEM.
- Return the value if succeeded, UINT_MAX otherwise. */
-
- static inline unsigned int
- lookup_collation_sequence_value (br_elem)
- bracket_elem_t *br_elem;
- {
- if (br_elem->type == SB_CHAR)
- {
- /*
- if (MB_CUR_MAX == 1)
- */
- if (nrules == 0)
- return collseqmb[br_elem->opr.ch];
- else
- {
- wint_t wc = __btowc (br_elem->opr.ch);
- return collseq_table_lookup (collseqwc, wc);
- }
- }
- else if (br_elem->type == MB_CHAR)
- {
- return collseq_table_lookup (collseqwc, br_elem->opr.wch);
- }
- else if (br_elem->type == COLL_SYM)
- {
- size_t sym_name_len = strlen ((char *) br_elem->opr.name);
- if (nrules != 0)
- {
- int32_t elem, idx;
- elem = seek_collating_symbol_entry (br_elem->opr.name,
- sym_name_len);
- if (symb_table[2 * elem] != 0)
- {
- /* We found the entry. */
- idx = symb_table[2 * elem + 1];
- /* Skip the name of collating element name. */
- idx += 1 + extra[idx];
- /* Skip the byte sequence of the collating element. */
- idx += 1 + extra[idx];
- /* Adjust for the alignment. */
- idx = (idx + 3) & ~3;
- /* Skip the multibyte collation sequence value. */
- idx += sizeof (unsigned int);
- /* Skip the wide char sequence of the collating element. */
- idx += sizeof (unsigned int) *
- (1 + *(unsigned int *) (extra + idx));
- /* Return the collation sequence value. */
- return *(unsigned int *) (extra + idx);
- }
- else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
- {
- /* No valid character. Match it as a single byte
- character. */
- return collseqmb[br_elem->opr.name[0]];
- }
- }
- else if (sym_name_len == 1)
- return collseqmb[br_elem->opr.name[0]];
- }
- return UINT_MAX;
- }
-
- /* Local function for parse_bracket_exp used in _LIBC environement.
- Build the range expression which starts from START_ELEM, and ends
- at END_ELEM. The result are written to MBCSET and SBCSET.
- RANGE_ALLOC is the allocated size of mbcset->range_starts, and
- mbcset->range_ends, is a pointer argument sinse we may
- update it. */
-
- static inline reg_errcode_t
-# ifdef RE_ENABLE_I18N
- build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
- re_charset_t *mbcset;
- int *range_alloc;
-# else /* not RE_ENABLE_I18N */
- build_range_exp (sbcset, start_elem, end_elem)
-# endif /* not RE_ENABLE_I18N */
- re_bitset_ptr_t sbcset;
- bracket_elem_t *start_elem, *end_elem;
- {
- unsigned int ch;
- uint32_t start_collseq;
- uint32_t end_collseq;
-
-# ifdef RE_ENABLE_I18N
- /* Check the space of the arrays. */
- if (*range_alloc == mbcset->nranges)
- {
- /* There are not enough space, need realloc. */
- uint32_t *new_array_start;
- uint32_t *new_array_end;
- int new_nranges;
-
- /* +1 in case of mbcset->nranges is 0. */
- new_nranges = 2 * mbcset->nranges + 1;
- /* Use realloc since mbcset->range_starts and mbcset->range_ends
- are NULL if *range_alloc == 0. */
- new_array_start = re_realloc (mbcset->range_starts, uint32_t,
- new_nranges);
- new_array_end = re_realloc (mbcset->range_ends, uint32_t,
- new_nranges);
-
- if (BE (new_array_start == NULL || new_array_end == NULL, 0))
- return REG_ESPACE;
-
- mbcset->range_starts = new_array_start;
- mbcset->range_ends = new_array_end;
- *range_alloc = new_nranges;
- }
-# endif /* RE_ENABLE_I18N */
-
- /* Equivalence Classes and Character Classes can't be a range
- start/end. */
- if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
- || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
- 0))
- return REG_ERANGE;
-
- start_collseq = lookup_collation_sequence_value (start_elem);
- end_collseq = lookup_collation_sequence_value (end_elem);
- /* Check start/end collation sequence values. */
- if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
- return REG_ECOLLATE;
- if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
- return REG_ERANGE;
-
-# ifdef RE_ENABLE_I18N
- /* Got valid collation sequence values, add them as a new entry. */
- mbcset->range_starts[mbcset->nranges] = start_collseq;
- mbcset->range_ends[mbcset->nranges++] = end_collseq;
-# endif /* RE_ENABLE_I18N */
-
- /* Build the table for single byte characters. */
- for (ch = 0; ch <= SBC_MAX; ch++)
- {
- uint32_t ch_collseq;
- /*
- if (MB_CUR_MAX == 1)
- */
- if (nrules == 0)
- ch_collseq = collseqmb[ch];
- else
- ch_collseq = collseq_table_lookup (collseqwc, __btowc (ch));
- if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
- bitset_set (sbcset, ch);
- }
- return REG_NOERROR;
- }
-
- /* Local function for parse_bracket_exp used in _LIBC environement.
- Build the collating element which is represented by NAME.
- The result are written to MBCSET and SBCSET.
- COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
- pointer argument sinse we may update it. */
-
- static inline reg_errcode_t
-# ifdef RE_ENABLE_I18N
- build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
- re_charset_t *mbcset;
- int *coll_sym_alloc;
-# else /* not RE_ENABLE_I18N */
- build_collating_symbol (sbcset, name)
-# endif /* not RE_ENABLE_I18N */
- re_bitset_ptr_t sbcset;
- const unsigned char *name;
- {
- int32_t elem, idx;
- size_t name_len = strlen ((const char *) name);
- if (nrules != 0)
- {
- elem = seek_collating_symbol_entry (name, name_len);
- if (symb_table[2 * elem] != 0)
- {
- /* We found the entry. */
- idx = symb_table[2 * elem + 1];
- /* Skip the name of collating element name. */
- idx += 1 + extra[idx];
- }
- else if (symb_table[2 * elem] == 0 && name_len == 1)
- {
- /* No valid character, treat it as a normal
- character. */
- bitset_set (sbcset, name[0]);
- return REG_NOERROR;
- }
- else
- return REG_ECOLLATE;
-
-# ifdef RE_ENABLE_I18N
- /* Got valid collation sequence, add it as a new entry. */
- /* Check the space of the arrays. */
- if (*coll_sym_alloc == mbcset->ncoll_syms)
- {
- /* Not enough, realloc it. */
- /* +1 in case of mbcset->ncoll_syms is 0. */
- *coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
- /* Use realloc since mbcset->coll_syms is NULL
- if *alloc == 0. */
- mbcset->coll_syms = re_realloc (mbcset->coll_syms, int32_t,
- *coll_sym_alloc);
- if (BE (mbcset->coll_syms == NULL, 0))
- return REG_ESPACE;
- }
- mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
-# endif /* RE_ENABLE_I18N */
- return REG_NOERROR;
- }
- else
- {
- if (BE (name_len != 1, 0))
- return REG_ECOLLATE;
- else
- {
- bitset_set (sbcset, name[0]);
- return REG_NOERROR;
- }
- }
- }
-#endif
-
- re_token_t br_token;
- re_bitset_ptr_t sbcset;
-#ifdef RE_ENABLE_I18N
- re_charset_t *mbcset;
- int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
- int equiv_class_alloc = 0, char_class_alloc = 0;
-#else /* not RE_ENABLE_I18N */
- int non_match = 0;
-#endif /* not RE_ENABLE_I18N */
- bin_tree_t *work_tree;
- int token_len, new_idx;
-#ifdef _LIBC
- collseqmb = (const unsigned char *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
- nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
- if (nrules)
- {
- /*
- if (MB_CUR_MAX > 1)
- */
- collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
- table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
- symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
- _NL_COLLATE_SYMB_TABLEMB);
- extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
- _NL_COLLATE_SYMB_EXTRAMB);
- }
-#endif
- sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
-#ifdef RE_ENABLE_I18N
- mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
-#endif /* RE_ENABLE_I18N */
-#ifdef RE_ENABLE_I18N
- if (BE (sbcset == NULL || mbcset == NULL, 0))
-#else
- if (BE (sbcset == NULL, 0))
-#endif /* RE_ENABLE_I18N */
- {
- *err = REG_ESPACE;
- return NULL;
- }
-
- token_len = peek_token_bracket (token, regexp, syntax);
- if (BE (token->type == END_OF_RE, 0))
- {
- *err = REG_BADPAT;
- goto parse_bracket_exp_free_return;
- }
- if (token->type == OP_NON_MATCH_LIST)
- {
-#ifdef RE_ENABLE_I18N
- int i;
- mbcset->non_match = 1;
-#else /* not RE_ENABLE_I18N */
- non_match = 1;
-#endif /* not RE_ENABLE_I18N */
- if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
- bitset_set (sbcset, '\0');
- re_string_skip_bytes (regexp, token_len); /* Skip a token. */
- token_len = peek_token_bracket (token, regexp, syntax);
- if (BE (token->type == END_OF_RE, 0))
- {
- *err = REG_BADPAT;
- goto parse_bracket_exp_free_return;
- }
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- for (i = 0; i < SBC_MAX; ++i)
- if (__btowc (i) == WEOF)
- bitset_set (sbcset, i);
-#endif /* RE_ENABLE_I18N */
- }
-
- /* We treat the first ']' as a normal character. */
- if (token->type == OP_CLOSE_BRACKET)
- token->type = CHARACTER;
-
- while (1)
- {
- bracket_elem_t start_elem, end_elem;
- unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
- unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
- reg_errcode_t ret;
- int token_len2 = 0, is_range_exp = 0;
- re_token_t token2;
-
- start_elem.opr.name = start_name_buf;
- ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
- syntax);
- if (BE (ret != REG_NOERROR, 0))
- {
- *err = ret;
- goto parse_bracket_exp_free_return;
- }
-
- token_len = peek_token_bracket (token, regexp, syntax);
- if (BE (token->type == END_OF_RE, 0))
- {
- *err = REG_BADPAT;
- goto parse_bracket_exp_free_return;
- }
- if (token->type == OP_CHARSET_RANGE)
- {
- re_string_skip_bytes (regexp, token_len); /* Skip '-'. */
- token_len2 = peek_token_bracket (&token2, regexp, syntax);
- if (BE (token->type == END_OF_RE, 0))
- {
- *err = REG_BADPAT;
- goto parse_bracket_exp_free_return;
- }
- if (token2.type == OP_CLOSE_BRACKET)
- {
- /* We treat the last '-' as a normal character. */
- re_string_skip_bytes (regexp, -token_len);
- token->type = CHARACTER;
- }
- else
- is_range_exp = 1;
- }
-
- if (is_range_exp == 1)
- {
- end_elem.opr.name = end_name_buf;
- ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
- dfa, syntax);
- if (BE (ret != REG_NOERROR, 0))
- {
- *err = ret;
- goto parse_bracket_exp_free_return;
- }
-
- token_len = peek_token_bracket (token, regexp, syntax);
- if (BE (token->type == END_OF_RE, 0))
- {
- *err = REG_BADPAT;
- goto parse_bracket_exp_free_return;
- }
- *err = build_range_exp (sbcset,
-#ifdef RE_ENABLE_I18N
- mbcset, &range_alloc,
-#endif /* RE_ENABLE_I18N */
- &start_elem, &end_elem);
- if (BE (*err != REG_NOERROR, 0))
- goto parse_bracket_exp_free_return;
- }
- else
- {
- switch (start_elem.type)
- {
- case SB_CHAR:
- bitset_set (sbcset, start_elem.opr.ch);
- break;
-#ifdef RE_ENABLE_I18N
- case MB_CHAR:
- /* Check whether the array has enough space. */
- if (mbchar_alloc == mbcset->nmbchars)
- {
- /* Not enough, realloc it. */
- /* +1 in case of mbcset->nmbchars is 0. */
- mbchar_alloc = 2 * mbcset->nmbchars + 1;
- /* Use realloc since array is NULL if *alloc == 0. */
- mbcset->mbchars = re_realloc (mbcset->mbchars, wchar_t,
- mbchar_alloc);
- if (BE (mbcset->mbchars == NULL, 0))
- goto parse_bracket_exp_espace;
- }
- mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
- break;
-#endif /* RE_ENABLE_I18N */
- case EQUIV_CLASS:
- *err = build_equiv_class (sbcset,
-#ifdef RE_ENABLE_I18N
- mbcset, &equiv_class_alloc,
-#endif /* RE_ENABLE_I18N */
- start_elem.opr.name);
- if (BE (*err != REG_NOERROR, 0))
- goto parse_bracket_exp_free_return;
- break;
- case COLL_SYM:
- *err = build_collating_symbol (sbcset,
-#ifdef RE_ENABLE_I18N
- mbcset, &coll_sym_alloc,
-#endif /* RE_ENABLE_I18N */
- start_elem.opr.name);
- if (BE (*err != REG_NOERROR, 0))
- goto parse_bracket_exp_free_return;
- break;
- case CHAR_CLASS:
- *err = build_charclass (sbcset,
-#ifdef RE_ENABLE_I18N
- mbcset, &char_class_alloc,
-#endif /* RE_ENABLE_I18N */
- start_elem.opr.name, syntax);
- if (BE (*err != REG_NOERROR, 0))
- goto parse_bracket_exp_free_return;
- break;
- default:
- assert (0);
- break;
- }
- }
- if (token->type == OP_CLOSE_BRACKET)
- break;
- }
-
- re_string_skip_bytes (regexp, token_len); /* Skip a token. */
-
- /* If it is non-matching list. */
-#ifdef RE_ENABLE_I18N
- if (mbcset->non_match)
-#else /* not RE_ENABLE_I18N */
- if (non_match)
-#endif /* not RE_ENABLE_I18N */
- bitset_not (sbcset);
-
- /* Build a tree for simple bracket. */
- br_token.type = SIMPLE_BRACKET;
- br_token.opr.sbcset = sbcset;
- new_idx = re_dfa_add_node (dfa, br_token, 0);
- work_tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || work_tree == NULL, 0))
- goto parse_bracket_exp_espace;
-
-#ifdef RE_ENABLE_I18N
- if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
- || mbcset->nranges || (MB_CUR_MAX > 1 && (mbcset->nchar_classes
- || mbcset->non_match)))
- {
- re_token_t alt_token;
- bin_tree_t *mbc_tree;
- /* Build a tree for complex bracket. */
- br_token.type = COMPLEX_BRACKET;
- br_token.opr.mbcset = mbcset;
- dfa->has_mb_node = 1;
- new_idx = re_dfa_add_node (dfa, br_token, 0);
- mbc_tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || mbc_tree == NULL, 0))
- goto parse_bracket_exp_espace;
- /* Then join them by ALT node. */
- dfa->has_plural_match = 1;
- alt_token.type = OP_ALT;
- new_idx = re_dfa_add_node (dfa, alt_token, 0);
- work_tree = create_tree (work_tree, mbc_tree, 0, new_idx);
- if (BE (new_idx != -1 && mbc_tree != NULL, 1))
- return work_tree;
- }
- else
- {
- free_charset (mbcset);
- return work_tree;
- }
-#else /* not RE_ENABLE_I18N */
- return work_tree;
-#endif /* not RE_ENABLE_I18N */
-
- parse_bracket_exp_espace:
- *err = REG_ESPACE;
- parse_bracket_exp_free_return:
- re_free (sbcset);
-#ifdef RE_ENABLE_I18N
- free_charset (mbcset);
-#endif /* RE_ENABLE_I18N */
- return NULL;
-}
-
-/* Parse an element in the bracket expression. */
-
-static reg_errcode_t
-parse_bracket_element (elem, regexp, token, token_len, dfa, syntax)
- bracket_elem_t *elem;
- re_string_t *regexp;
- re_token_t *token;
- int token_len;
- re_dfa_t *dfa;
- reg_syntax_t syntax;
-{
-#ifdef RE_ENABLE_I18N
- int cur_char_size;
- cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
- if (cur_char_size > 1)
- {
- elem->type = MB_CHAR;
- elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
- re_string_skip_bytes (regexp, cur_char_size);
- return REG_NOERROR;
- }
-#endif /* RE_ENABLE_I18N */
- re_string_skip_bytes (regexp, token_len); /* Skip a token. */
- if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
- || token->type == OP_OPEN_EQUIV_CLASS)
- return parse_bracket_symbol (elem, regexp, token);
- elem->type = SB_CHAR;
- elem->opr.ch = token->opr.c;
- return REG_NOERROR;
-}
-
-/* Parse a bracket symbol in the bracket expression. Bracket symbols are
- such as [:<character_class>:], [.<collating_element>.], and
- [=<equivalent_class>=]. */
-
-static reg_errcode_t
-parse_bracket_symbol (elem, regexp, token)
- bracket_elem_t *elem;
- re_string_t *regexp;
- re_token_t *token;
-{
- unsigned char ch, delim = token->opr.c;
- int i = 0;
- for (;; ++i)
- {
- if (re_string_eoi(regexp) || i >= BRACKET_NAME_BUF_SIZE)
- return REG_EBRACK;
- if (token->type == OP_OPEN_CHAR_CLASS)
- ch = re_string_fetch_byte_case (regexp);
- else
- ch = re_string_fetch_byte (regexp);
- if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
- break;
- elem->opr.name[i] = ch;
- }
- re_string_skip_bytes (regexp, 1);
- elem->opr.name[i] = '\0';
- switch (token->type)
- {
- case OP_OPEN_COLL_ELEM:
- elem->type = COLL_SYM;
- break;
- case OP_OPEN_EQUIV_CLASS:
- elem->type = EQUIV_CLASS;
- break;
- case OP_OPEN_CHAR_CLASS:
- elem->type = CHAR_CLASS;
- break;
- default:
- break;
- }
- return REG_NOERROR;
-}
-
- /* Helper function for parse_bracket_exp.
- Build the equivalence class which is represented by NAME.
- The result are written to MBCSET and SBCSET.
- EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
- is a pointer argument sinse we may update it. */
-
-static reg_errcode_t
-#ifdef RE_ENABLE_I18N
-build_equiv_class (sbcset, mbcset, equiv_class_alloc, name)
- re_charset_t *mbcset;
- int *equiv_class_alloc;
-#else /* not RE_ENABLE_I18N */
-build_equiv_class (sbcset, name)
-#endif /* not RE_ENABLE_I18N */
- re_bitset_ptr_t sbcset;
- const unsigned char *name;
-{
-#if defined _LIBC && defined RE_ENABLE_I18N
- uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
- if (nrules != 0)
- {
- const int32_t *table, *indirect;
- const unsigned char *weights, *extra, *cp;
- unsigned char char_buf[2];
- int32_t idx1, idx2;
- unsigned int ch;
- size_t len;
- /* This #include defines a local function! */
-# include <locale/weight.h>
- /* Calculate the index for equivalence class. */
- cp = name;
- table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
- weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
- _NL_COLLATE_WEIGHTMB);
- extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
- _NL_COLLATE_EXTRAMB);
- indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
- _NL_COLLATE_INDIRECTMB);
- idx1 = findidx (&cp);
- if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
- /* This isn't a valid character. */
- return REG_ECOLLATE;
-
- /* Build single byte matcing table for this equivalence class. */
- char_buf[1] = (unsigned char) '\0';
- len = weights[idx1];
- for (ch = 0; ch < SBC_MAX; ++ch)
- {
- char_buf[0] = ch;
- cp = char_buf;
- idx2 = findidx (&cp);
-/*
- idx2 = table[ch];
-*/
- if (idx2 == 0)
- /* This isn't a valid character. */
- continue;
- if (len == weights[idx2])
- {
- int cnt = 0;
- while (cnt <= len &&
- weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt])
- ++cnt;
-
- if (cnt > len)
- bitset_set (sbcset, ch);
- }
- }
- /* Check whether the array has enough space. */
- if (*equiv_class_alloc == mbcset->nequiv_classes)
- {
- /* Not enough, realloc it. */
- /* +1 in case of mbcset->nequiv_classes is 0. */
- *equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
- /* Use realloc since the array is NULL if *alloc == 0. */
- mbcset->equiv_classes = re_realloc (mbcset->equiv_classes, int32_t,
- *equiv_class_alloc);
- if (BE (mbcset->equiv_classes == NULL, 0))
- return REG_ESPACE;
- }
- mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
- }
- else
-#endif /* _LIBC && RE_ENABLE_I18N */
- {
- if (BE (strlen ((const char *) name) != 1, 0))
- return REG_ECOLLATE;
- bitset_set (sbcset, *name);
- }
- return REG_NOERROR;
-}
-
- /* Helper function for parse_bracket_exp.
- Build the character class which is represented by NAME.
- The result are written to MBCSET and SBCSET.
- CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
- is a pointer argument sinse we may update it. */
-
-static reg_errcode_t
-#ifdef RE_ENABLE_I18N
-build_charclass (sbcset, mbcset, char_class_alloc, class_name, syntax)
- re_charset_t *mbcset;
- int *char_class_alloc;
-#else /* not RE_ENABLE_I18N */
-build_charclass (sbcset, class_name, syntax)
-#endif /* not RE_ENABLE_I18N */
- re_bitset_ptr_t sbcset;
- const unsigned char *class_name;
- reg_syntax_t syntax;
-{
- int i;
- const char *name = (const char *) class_name;
-
- /* In case of REG_ICASE "upper" and "lower" match the both of
- upper and lower cases. */
- if ((syntax & RE_ICASE)
- && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0))
- name = "alpha";
-
-#ifdef RE_ENABLE_I18N
- /* Check the space of the arrays. */
- if (*char_class_alloc == mbcset->nchar_classes)
- {
- /* Not enough, realloc it. */
- /* +1 in case of mbcset->nchar_classes is 0. */
- *char_class_alloc = 2 * mbcset->nchar_classes + 1;
- /* Use realloc since array is NULL if *alloc == 0. */
- mbcset->char_classes = re_realloc (mbcset->char_classes, wctype_t,
- *char_class_alloc);
- if (BE (mbcset->char_classes == NULL, 0))
- return REG_ESPACE;
- }
- mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
-#endif /* RE_ENABLE_I18N */
-
-#define BUILD_CHARCLASS_LOOP(ctype_func)\
- for (i = 0; i < SBC_MAX; ++i) \
- { \
- if (ctype_func (i)) \
- bitset_set (sbcset, i); \
- }
-
- if (strcmp (name, "alnum") == 0)
- BUILD_CHARCLASS_LOOP (isalnum)
- else if (strcmp (name, "cntrl") == 0)
- BUILD_CHARCLASS_LOOP (iscntrl)
- else if (strcmp (name, "lower") == 0)
- BUILD_CHARCLASS_LOOP (islower)
- else if (strcmp (name, "space") == 0)
- BUILD_CHARCLASS_LOOP (isspace)
- else if (strcmp (name, "alpha") == 0)
- BUILD_CHARCLASS_LOOP (isalpha)
- else if (strcmp (name, "digit") == 0)
- BUILD_CHARCLASS_LOOP (isdigit)
- else if (strcmp (name, "print") == 0)
- BUILD_CHARCLASS_LOOP (isprint)
- else if (strcmp (name, "upper") == 0)
- BUILD_CHARCLASS_LOOP (isupper)
- else if (strcmp (name, "blank") == 0)
- BUILD_CHARCLASS_LOOP (isblank)
- else if (strcmp (name, "graph") == 0)
- BUILD_CHARCLASS_LOOP (isgraph)
- else if (strcmp (name, "punct") == 0)
- BUILD_CHARCLASS_LOOP (ispunct)
- else if (strcmp (name, "xdigit") == 0)
- BUILD_CHARCLASS_LOOP (isxdigit)
- else
- return REG_ECTYPE;
-
- return REG_NOERROR;
-}
-
-static bin_tree_t *
-build_word_op (dfa, not, err)
- re_dfa_t *dfa;
- int not;
- reg_errcode_t *err;
-{
- re_bitset_ptr_t sbcset;
-#ifdef RE_ENABLE_I18N
- re_charset_t *mbcset;
- int alloc = 0;
-#else /* not RE_ENABLE_I18N */
- int non_match = 0;
-#endif /* not RE_ENABLE_I18N */
- reg_errcode_t ret;
- re_token_t br_token;
- bin_tree_t *tree;
- int new_idx;
-
- sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
-#ifdef RE_ENABLE_I18N
- mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
-#endif /* RE_ENABLE_I18N */
-
-#ifdef RE_ENABLE_I18N
- if (BE (sbcset == NULL || mbcset == NULL, 0))
-#else /* not RE_ENABLE_I18N */
- if (BE (sbcset == NULL, 0))
-#endif /* not RE_ENABLE_I18N */
- {
- *err = REG_ESPACE;
- return NULL;
- }
-
- if (not)
- {
-#ifdef RE_ENABLE_I18N
- int i;
- /*
- if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
- bitset_set(cset->sbcset, '\0');
- */
- mbcset->non_match = 1;
- if (MB_CUR_MAX > 1)
- for (i = 0; i < SBC_MAX; ++i)
- if (__btowc (i) == WEOF)
- bitset_set (sbcset, i);
-#else /* not RE_ENABLE_I18N */
- non_match = 1;
-#endif /* not RE_ENABLE_I18N */
- }
-
- /* We don't care the syntax in this case. */
- ret = build_charclass (sbcset,
-#ifdef RE_ENABLE_I18N
- mbcset, &alloc,
-#endif /* RE_ENABLE_I18N */
- (const unsigned char *) "alpha", 0);
-
- if (BE (ret != REG_NOERROR, 0))
- {
- re_free (sbcset);
-#ifdef RE_ENABLE_I18N
- free_charset (mbcset);
-#endif /* RE_ENABLE_I18N */
- *err = ret;
- return NULL;
- }
- /* \w match '_' also. */
- bitset_set (sbcset, '_');
-
- /* If it is non-matching list. */
-#ifdef RE_ENABLE_I18N
- if (mbcset->non_match)
-#else /* not RE_ENABLE_I18N */
- if (non_match)
-#endif /* not RE_ENABLE_I18N */
- bitset_not (sbcset);
-
- /* Build a tree for simple bracket. */
- br_token.type = SIMPLE_BRACKET;
- br_token.opr.sbcset = sbcset;
- new_idx = re_dfa_add_node (dfa, br_token, 0);
- tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || tree == NULL, 0))
- goto build_word_op_espace;
-
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- {
- re_token_t alt_token;
- bin_tree_t *mbc_tree;
- /* Build a tree for complex bracket. */
- br_token.type = COMPLEX_BRACKET;
- br_token.opr.mbcset = mbcset;
- dfa->has_mb_node = 1;
- new_idx = re_dfa_add_node (dfa, br_token, 0);
- mbc_tree = create_tree (NULL, NULL, 0, new_idx);
- if (BE (new_idx == -1 || mbc_tree == NULL, 0))
- goto build_word_op_espace;
- /* Then join them by ALT node. */
- alt_token.type = OP_ALT;
- new_idx = re_dfa_add_node (dfa, alt_token, 0);
- tree = create_tree (tree, mbc_tree, 0, new_idx);
- if (BE (new_idx != -1 && mbc_tree != NULL, 1))
- return tree;
- }
- else
- {
- free_charset (mbcset);
- return tree;
- }
-#else /* not RE_ENABLE_I18N */
- return tree;
-#endif /* not RE_ENABLE_I18N */
-
- build_word_op_espace:
- re_free (sbcset);
-#ifdef RE_ENABLE_I18N
- free_charset (mbcset);
-#endif /* RE_ENABLE_I18N */
- *err = REG_ESPACE;
- return NULL;
-}
-
-/* This is intended for the expressions like "a{1,3}".
- Fetch a number from `input', and return the number.
- Return -1, if the number field is empty like "{,1}".
- Return -2, If an error is occured. */
-
-static int
-fetch_number (input, token, syntax)
- re_string_t *input;
- re_token_t *token;
- reg_syntax_t syntax;
-{
- int num = -1;
- unsigned char c;
- while (1)
- {
- *token = fetch_token (input, syntax);
- c = token->opr.c;
- if (BE (token->type == END_OF_RE, 0))
- return -2;
- if (token->type == OP_CLOSE_DUP_NUM || c == ',')
- break;
- num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
- ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
- num = (num > RE_DUP_MAX) ? -2 : num;
- }
- return num;
-}
-\f
-#ifdef RE_ENABLE_I18N
-static void
-free_charset (re_charset_t *cset)
-{
- re_free (cset->mbchars);
-# ifdef _LIBC
- re_free (cset->coll_syms);
- re_free (cset->equiv_classes);
- re_free (cset->range_starts);
- re_free (cset->range_ends);
-# endif
- re_free (cset->char_classes);
- re_free (cset);
-}
-#endif /* RE_ENABLE_I18N */
-\f
-/* Functions for binary tree operation. */
-
-/* Create a node of tree.
- Note: This function automatically free left and right if malloc fails. */
-
-static bin_tree_t *
-create_tree (left, right, type, index)
- bin_tree_t *left;
- bin_tree_t *right;
- re_token_type_t type;
- int index;
-{
- bin_tree_t *tree;
- tree = re_malloc (bin_tree_t, 1);
- if (BE (tree == NULL, 0))
- {
- free_bin_tree (left);
- free_bin_tree (right);
- return NULL;
- }
- tree->parent = NULL;
- tree->left = left;
- tree->right = right;
- tree->type = type;
- tree->node_idx = index;
- tree->first = -1;
- tree->next = -1;
- re_node_set_init_empty (&tree->eclosure);
-
- if (left != NULL)
- left->parent = tree;
- if (right != NULL)
- right->parent = tree;
- return tree;
-}
-
-/* Free the sub tree pointed by TREE. */
-
-static void
-free_bin_tree (tree)
- bin_tree_t *tree;
-{
- if (tree == NULL)
- return;
- /*re_node_set_free (&tree->eclosure);*/
- free_bin_tree (tree->left);
- free_bin_tree (tree->right);
- re_free (tree);
-}
-
-/* Duplicate the node SRC, and return new node. */
-
-static bin_tree_t *
-duplicate_tree (src, dfa)
- const bin_tree_t *src;
- re_dfa_t *dfa;
-{
- bin_tree_t *left = NULL, *right = NULL, *new_tree;
- int new_node_idx;
- /* Since node indies must be according to Post-order of the tree,
- we must duplicate the left at first. */
- if (src->left != NULL)
- {
- left = duplicate_tree (src->left, dfa);
- if (left == NULL)
- return NULL;
- }
-
- /* Secondaly, duplicate the right. */
- if (src->right != NULL)
- {
- right = duplicate_tree (src->right, dfa);
- if (right == NULL)
- {
- free_bin_tree (left);
- return NULL;
- }
- }
-
- /* At last, duplicate itself. */
- if (src->type == NON_TYPE)
- {
- new_node_idx = re_dfa_add_node (dfa, dfa->nodes[src->node_idx], 0);
- dfa->nodes[new_node_idx].duplicated = 1;
- if (BE (new_node_idx == -1, 0))
- {
- free_bin_tree (left);
- free_bin_tree (right);
- return NULL;
- }
- }
- else
- new_node_idx = src->type;
-
- new_tree = create_tree (left, right, src->type, new_node_idx);
- if (BE (new_tree == NULL, 0))
- {
- free_bin_tree (left);
- free_bin_tree (right);
- }
- return new_tree;
-}
+++ /dev/null
-/*
- * Regular Expression Functions from glibc 2.3.2
- * (renamed to sh_* to avoid clashes with the system libraries)
- */
-
-#ifndef _UCW_REGEX_H
-#define _UCW_REGEX_H
-
-#define regfree sh_regfree
-#define regexec sh_regexec
-#define regcomp sh_regcomp
-#define regerror sh_regerror
-#define re_set_registers sh_re_set_registers
-#define re_match_2 sh_re_match2
-#define re_match sh_re_match
-#define re_search sh_re_search
-#define re_compile_pattern sh_re_compile_pattern
-#define re_set_syntax sh_re_set_syntax
-#define re_search_2 sh_re_search_2
-#define re_compile_fastmap sh_re_compile_fastmap
-
-#include "lib/regex/regex.h"
-
-#endif
+++ /dev/null
-/*
- * Regular Expression Functions from glibc 2.3.2
- */
-
-#include <sys/types.h>
-#include "regex-sh.h"
-#include "regex_internal.h"
-#include "regex_internal.c"
-#include "regcomp.c"
-#include "regexec.c"
+++ /dev/null
-/* Definitions for data structures and routines for the regular
- expression library.
- Copyright (C) 1985,1989-93,1995-98,2000,2001,2002
- Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
-
-#ifndef _REGEX_H
-#define _REGEX_H 1
-
-/* Allow the use in C++ code. */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* POSIX says that <sys/types.h> must be included (by the caller) before
- <regex.h>. */
-
-#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS
-/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
- should be there. */
-# include <stddef.h>
-#endif
-
-/* The following two types have to be signed and unsigned integer type
- wide enough to hold a value of a pointer. For most ANSI compilers
- ptrdiff_t and size_t should be likely OK. Still size of these two
- types is 2 for Microsoft C. Ugh... */
-typedef long int s_reg_t;
-typedef unsigned long int active_reg_t;
-
-/* The following bits are used to determine the regexp syntax we
- recognize. The set/not-set meanings are chosen so that Emacs syntax
- remains the value 0. The bits are given in alphabetical order, and
- the definitions shifted by one from the previous bit; thus, when we
- add or remove a bit, only one other definition need change. */
-typedef unsigned long int reg_syntax_t;
-
-/* If this bit is not set, then \ inside a bracket expression is literal.
- If set, then such a \ quotes the following character. */
-#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
-
-/* If this bit is not set, then + and ? are operators, and \+ and \? are
- literals.
- If set, then \+ and \? are operators and + and ? are literals. */
-#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
-
-/* If this bit is set, then character classes are supported. They are:
- [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
- [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
- If not set, then character classes are not supported. */
-#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
-
-/* If this bit is set, then ^ and $ are always anchors (outside bracket
- expressions, of course).
- If this bit is not set, then it depends:
- ^ is an anchor if it is at the beginning of a regular
- expression or after an open-group or an alternation operator;
- $ is an anchor if it is at the end of a regular expression, or
- before a close-group or an alternation operator.
-
- This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
- POSIX draft 11.2 says that * etc. in leading positions is undefined.
- We already implemented a previous draft which made those constructs
- invalid, though, so we haven't changed the code back. */
-#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
-
-/* If this bit is set, then special characters are always special
- regardless of where they are in the pattern.
- If this bit is not set, then special characters are special only in
- some contexts; otherwise they are ordinary. Specifically,
- * + ? and intervals are only special when not after the beginning,
- open-group, or alternation operator. */
-#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
-
-/* If this bit is set, then *, +, ?, and { cannot be first in an re or
- immediately after an alternation or begin-group operator. */
-#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
-
-/* If this bit is set, then . matches newline.
- If not set, then it doesn't. */
-#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
-
-/* If this bit is set, then . doesn't match NUL.
- If not set, then it does. */
-#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
-
-/* If this bit is set, nonmatching lists [^...] do not match newline.
- If not set, they do. */
-#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
-
-/* If this bit is set, either \{...\} or {...} defines an
- interval, depending on RE_NO_BK_BRACES.
- If not set, \{, \}, {, and } are literals. */
-#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
-
-/* If this bit is set, +, ? and | aren't recognized as operators.
- If not set, they are. */
-#define RE_LIMITED_OPS (RE_INTERVALS << 1)
-
-/* If this bit is set, newline is an alternation operator.
- If not set, newline is literal. */
-#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
-
-/* If this bit is set, then `{...}' defines an interval, and \{ and \}
- are literals.
- If not set, then `\{...\}' defines an interval. */
-#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
-
-/* If this bit is set, (...) defines a group, and \( and \) are literals.
- If not set, \(...\) defines a group, and ( and ) are literals. */
-#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
-
-/* If this bit is set, then \<digit> matches <digit>.
- If not set, then \<digit> is a back-reference. */
-#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
-
-/* If this bit is set, then | is an alternation operator, and \| is literal.
- If not set, then \| is an alternation operator, and | is literal. */
-#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
-
-/* If this bit is set, then an ending range point collating higher
- than the starting range point, as in [z-a], is invalid.
- If not set, then when ending range point collates higher than the
- starting range point, the range is ignored. */
-#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
-
-/* If this bit is set, then an unmatched ) is ordinary.
- If not set, then an unmatched ) is invalid. */
-#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
-
-/* If this bit is set, succeed as soon as we match the whole pattern,
- without further backtracking. */
-#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
-
-/* If this bit is set, do not process the GNU regex operators.
- If not set, then the GNU regex operators are recognized. */
-#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
-
-/* If this bit is set, turn on internal regex debugging.
- If not set, and debugging was on, turn it off.
- This only works if regex.c is compiled -DDEBUG.
- We define this bit always, so that all that's needed to turn on
- debugging is to recompile regex.c; the calling code can always have
- this bit set, and it won't affect anything in the normal case. */
-#define RE_DEBUG (RE_NO_GNU_OPS << 1)
-
-/* If this bit is set, a syntactically invalid interval is treated as
- a string of ordinary characters. For example, the ERE 'a{1' is
- treated as 'a\{1'. */
-#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)
-
-/* If this bit is set, then ignore case when matching.
- If not set, then case is significant. */
-#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
-
-/* This global variable defines the particular regexp syntax to use (for
- some interfaces). When a regexp is compiled, the syntax used is
- stored in the pattern buffer, so changing this does not affect
- already-compiled regexps. */
-extern reg_syntax_t re_syntax_options;
-\f
-/* Define combinations of the above bits for the standard possibilities.
- (The [[[ comments delimit what gets put into the Texinfo file, so
- don't delete them!) */
-/* [[[begin syntaxes]]] */
-#define RE_SYNTAX_EMACS 0
-
-#define RE_SYNTAX_AWK \
- (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
- | RE_NO_BK_PARENS | RE_NO_BK_REFS \
- | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
- | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \
- | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
-
-#define RE_SYNTAX_GNU_AWK \
- ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \
- & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \
- | RE_CONTEXT_INVALID_OPS ))
-
-#define RE_SYNTAX_POSIX_AWK \
- (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
- | RE_INTERVALS | RE_NO_GNU_OPS)
-
-#define RE_SYNTAX_GREP \
- (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
- | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
- | RE_NEWLINE_ALT)
-
-#define RE_SYNTAX_EGREP \
- (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
- | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
- | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
- | RE_NO_BK_VBAR)
-
-#define RE_SYNTAX_POSIX_EGREP \
- (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \
- | RE_INVALID_INTERVAL_ORD)
-
-/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
-#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
-
-#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
-
-/* Syntax bits common to both basic and extended POSIX regex syntax. */
-#define _RE_SYNTAX_POSIX_COMMON \
- (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
- | RE_INTERVALS | RE_NO_EMPTY_RANGES)
-
-#define RE_SYNTAX_POSIX_BASIC \
- (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
-
-/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
- RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
- isn't minimal, since other operators, such as \`, aren't disabled. */
-#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
- (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
-
-#define RE_SYNTAX_POSIX_EXTENDED \
- (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
- | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
- | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
- | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
-
-/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
- removed and RE_NO_BK_REFS is added. */
-#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
- (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
- | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
- | RE_NO_BK_PARENS | RE_NO_BK_REFS \
- | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
-/* [[[end syntaxes]]] */
-\f
-/* Maximum number of duplicates an interval can allow. Some systems
- (erroneously) define this in other header files, but we want our
- value, so remove any previous define. */
-#ifdef RE_DUP_MAX
-# undef RE_DUP_MAX
-#endif
-/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */
-#define RE_DUP_MAX (0x7fff)
-
-
-/* POSIX `cflags' bits (i.e., information for `regcomp'). */
-
-/* If this bit is set, then use extended regular expression syntax.
- If not set, then use basic regular expression syntax. */
-#define REG_EXTENDED 1
-
-/* If this bit is set, then ignore case when matching.
- If not set, then case is significant. */
-#define REG_ICASE (REG_EXTENDED << 1)
-
-/* If this bit is set, then anchors do not match at newline
- characters in the string.
- If not set, then anchors do match at newlines. */
-#define REG_NEWLINE (REG_ICASE << 1)
-
-/* If this bit is set, then report only success or fail in regexec.
- If not set, then returns differ between not matching and errors. */
-#define REG_NOSUB (REG_NEWLINE << 1)
-
-
-/* POSIX `eflags' bits (i.e., information for regexec). */
-
-/* If this bit is set, then the beginning-of-line operator doesn't match
- the beginning of the string (presumably because it's not the
- beginning of a line).
- If not set, then the beginning-of-line operator does match the
- beginning of the string. */
-#define REG_NOTBOL 1
-
-/* Like REG_NOTBOL, except for the end-of-line. */
-#define REG_NOTEOL (1 << 1)
-
-
-/* If any error codes are removed, changed, or added, update the
- `re_error_msg' table in regex.c. */
-typedef enum
-{
-#ifdef _XOPEN_SOURCE
- REG_ENOSYS = -1, /* This will never happen for this implementation. */
-#endif
-
- REG_NOERROR = 0, /* Success. */
- REG_NOMATCH, /* Didn't find a match (for regexec). */
-
- /* POSIX regcomp return error codes. (In the order listed in the
- standard.) */
- REG_BADPAT, /* Invalid pattern. */
- REG_ECOLLATE, /* Not implemented. */
- REG_ECTYPE, /* Invalid character class name. */
- REG_EESCAPE, /* Trailing backslash. */
- REG_ESUBREG, /* Invalid back reference. */
- REG_EBRACK, /* Unmatched left bracket. */
- REG_EPAREN, /* Parenthesis imbalance. */
- REG_EBRACE, /* Unmatched \{. */
- REG_BADBR, /* Invalid contents of \{\}. */
- REG_ERANGE, /* Invalid range end. */
- REG_ESPACE, /* Ran out of memory. */
- REG_BADRPT, /* No preceding re for repetition op. */
-
- /* Error codes we've added. */
- REG_EEND, /* Premature end. */
- REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
- REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
-} reg_errcode_t;
-\f
-/* This data structure represents a compiled pattern. Before calling
- the pattern compiler, the fields `buffer', `allocated', `fastmap',
- `translate', and `no_sub' can be set. After the pattern has been
- compiled, the `re_nsub' field is available. All other fields are
- private to the regex routines. */
-
-#ifndef RE_TRANSLATE_TYPE
-# define RE_TRANSLATE_TYPE char *
-#endif
-
-struct re_pattern_buffer
-{
-/* [[[begin pattern_buffer]]] */
- /* Space that holds the compiled pattern. It is declared as
- `unsigned char *' because its elements are
- sometimes used as array indexes. */
- unsigned char *buffer;
-
- /* Number of bytes to which `buffer' points. */
- unsigned long int allocated;
-
- /* Number of bytes actually used in `buffer'. */
- unsigned long int used;
-
- /* Syntax setting with which the pattern was compiled. */
- reg_syntax_t syntax;
-
- /* Pointer to a fastmap, if any, otherwise zero. re_search uses
- the fastmap, if there is one, to skip over impossible
- starting points for matches. */
- char *fastmap;
-
- /* Either a translate table to apply to all characters before
- comparing them, or zero for no translation. The translation
- is applied to a pattern when it is compiled and to a string
- when it is matched. */
- RE_TRANSLATE_TYPE translate;
-
- /* Number of subexpressions found by the compiler. */
- size_t re_nsub;
-
- /* Zero if this pattern cannot match the empty string, one else.
- Well, in truth it's used only in `re_search_2', to see
- whether or not we should use the fastmap, so we don't set
- this absolutely perfectly; see `re_compile_fastmap' (the
- `duplicate' case). */
- unsigned can_be_null : 1;
-
- /* If REGS_UNALLOCATED, allocate space in the `regs' structure
- for `max (RE_NREGS, re_nsub + 1)' groups.
- If REGS_REALLOCATE, reallocate space if necessary.
- If REGS_FIXED, use what's there. */
-#define REGS_UNALLOCATED 0
-#define REGS_REALLOCATE 1
-#define REGS_FIXED 2
- unsigned regs_allocated : 2;
-
- /* Set to zero when `regex_compile' compiles a pattern; set to one
- by `re_compile_fastmap' if it updates the fastmap. */
- unsigned fastmap_accurate : 1;
-
- /* If set, `re_match_2' does not return information about
- subexpressions. */
- unsigned no_sub : 1;
-
- /* If set, a beginning-of-line anchor doesn't match at the
- beginning of the string. */
- unsigned not_bol : 1;
-
- /* Similarly for an end-of-line anchor. */
- unsigned not_eol : 1;
-
- /* If true, an anchor at a newline matches. */
- unsigned newline_anchor : 1;
-
-/* [[[end pattern_buffer]]] */
-};
-
-typedef struct re_pattern_buffer regex_t;
-\f
-/* Type for byte offsets within the string. POSIX mandates this. */
-typedef int regoff_t;
-
-
-/* This is the structure we store register match data in. See
- regex.texinfo for a full description of what registers match. */
-struct re_registers
-{
- unsigned num_regs;
- regoff_t *start;
- regoff_t *end;
-};
-
-
-/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
- `re_match_2' returns information about at least this many registers
- the first time a `regs' structure is passed. */
-#ifndef RE_NREGS
-# define RE_NREGS 30
-#endif
-
-
-/* POSIX specification for registers. Aside from the different names than
- `re_registers', POSIX uses an array of structures, instead of a
- structure of arrays. */
-typedef struct
-{
- regoff_t rm_so; /* Byte offset from string's start to substring's start. */
- regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
-} regmatch_t;
-\f
-/* Declarations for routines. */
-
-/* To avoid duplicating every routine declaration -- once with a
- prototype (if we are ANSI), and once without (if we aren't) -- we
- use the following macro to declare argument types. This
- unfortunately clutters up the declarations a bit, but I think it's
- worth it. */
-
-#if __STDC__
-
-# define _RE_ARGS(args) args
-
-#else /* not __STDC__ */
-
-# define _RE_ARGS(args) ()
-
-#endif /* not __STDC__ */
-
-/* Sets the current default syntax to SYNTAX, and return the old syntax.
- You can also simply assign to the `re_syntax_options' variable. */
-extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
-
-/* Compile the regular expression PATTERN, with length LENGTH
- and syntax given by the global `re_syntax_options', into the buffer
- BUFFER. Return NULL if successful, and an error string if not. */
-extern const char *re_compile_pattern
- _RE_ARGS ((const char *pattern, size_t length,
- struct re_pattern_buffer *buffer));
-
-
-/* Compile a fastmap for the compiled pattern in BUFFER; used to
- accelerate searches. Return 0 if successful and -2 if was an
- internal error. */
-extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
-
-
-/* Search in the string STRING (with length LENGTH) for the pattern
- compiled into BUFFER. Start searching at position START, for RANGE
- characters. Return the starting position of the match, -1 for no
- match, or -2 for an internal error. Also return register
- information in REGS (if REGS and BUFFER->no_sub are nonzero). */
-extern int re_search
- _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
- int length, int start, int range, struct re_registers *regs));
-
-
-/* Like `re_search', but search in the concatenation of STRING1 and
- STRING2. Also, stop searching at index START + STOP. */
-extern int re_search_2
- _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
- int length1, const char *string2, int length2,
- int start, int range, struct re_registers *regs, int stop));
-
-
-/* Like `re_search', but return how many characters in STRING the regexp
- in BUFFER matched, starting at position START. */
-extern int re_match
- _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
- int length, int start, struct re_registers *regs));
-
-
-/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
-extern int re_match_2
- _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
- int length1, const char *string2, int length2,
- int start, struct re_registers *regs, int stop));
-
-
-/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
- ENDS. Subsequent matches using BUFFER and REGS will use this memory
- for recording register information. STARTS and ENDS must be
- allocated with malloc, and must each be at least `NUM_REGS * sizeof
- (regoff_t)' bytes long.
-
- If NUM_REGS == 0, then subsequent matches should allocate their own
- register data.
-
- Unless this function is called, the first search or match using
- PATTERN_BUFFER will allocate its own register data, without
- freeing the old data. */
-extern void re_set_registers
- _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
- unsigned num_regs, regoff_t *starts, regoff_t *ends));
-
-#if defined _REGEX_RE_COMP || defined _LIBC
-# ifndef _CRAY
-/* 4.2 bsd compatibility. */
-extern char *re_comp _RE_ARGS ((const char *));
-extern int re_exec _RE_ARGS ((const char *));
-# endif
-#endif
-
-/* GCC 2.95 and later have "__restrict"; C99 compilers have
- "restrict", and "configure" may have defined "restrict". */
-#ifndef __restrict
-# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
-# if defined restrict || 199901L <= __STDC_VERSION__
-# define __restrict restrict
-# else
-# define __restrict
-# endif
-# endif
-#endif
-/* gcc 3.1 and up support the [restrict] syntax. */
-#ifndef __restrict_arr
-# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
-# define __restrict_arr __restrict
-# else
-# define __restrict_arr
-# endif
-#endif
-
-/* POSIX compatibility. */
-extern int regcomp _RE_ARGS ((regex_t *__restrict __preg,
- const char *__restrict __pattern,
- int __cflags));
-
-extern int regexec _RE_ARGS ((const regex_t *__restrict __preg,
- const char *__restrict __string, size_t __nmatch,
- regmatch_t __pmatch[__restrict_arr],
- int __eflags));
-
-extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg,
- char *__errbuf, size_t __errbuf_size));
-
-extern void regfree _RE_ARGS ((regex_t *__preg));
-
-
-#ifdef __cplusplus
-}
-#endif /* C++ */
-
-#endif /* regex.h */
-\f
-/*
-Local variables:
-make-backup-files: t
-version-control: t
-trim-versions-without-asking: nil
-End:
-*/
+++ /dev/null
-/* Extended regular expression matching and search library.
- Copyright (C) 2002, 2003 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
-
-static void re_string_construct_common (const char *str, int len,
- re_string_t *pstr,
- RE_TRANSLATE_TYPE trans, int icase);
-#ifdef RE_ENABLE_I18N
-static int re_string_skip_chars (re_string_t *pstr, int new_raw_idx,
- wint_t *last_wc);
-#endif /* RE_ENABLE_I18N */
-static re_dfastate_t *create_newstate_common (re_dfa_t *dfa,
- const re_node_set *nodes,
- unsigned int hash);
-static reg_errcode_t register_state (re_dfa_t *dfa, re_dfastate_t *newstate,
- unsigned int hash);
-static re_dfastate_t *create_ci_newstate (re_dfa_t *dfa,
- const re_node_set *nodes,
- unsigned int hash);
-static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa,
- const re_node_set *nodes,
- unsigned int context,
- unsigned int hash);
-static unsigned int inline calc_state_hash (const re_node_set *nodes,
- unsigned int context);
-\f
-/* Functions for string operation. */
-
-/* This function allocate the buffers. It is necessary to call
- re_string_reconstruct before using the object. */
-
-static reg_errcode_t
-re_string_allocate (pstr, str, len, init_len, trans, icase)
- re_string_t *pstr;
- const char *str;
- int len, init_len, icase;
- RE_TRANSLATE_TYPE trans;
-{
- reg_errcode_t ret;
- int init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
- re_string_construct_common (str, len, pstr, trans, icase);
- pstr->stop = pstr->len;
-
- ret = re_string_realloc_buffers (pstr, init_buf_len);
- if (BE (ret != REG_NOERROR, 0))
- return ret;
-
- pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case
- : (unsigned char *) str);
- pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case;
- pstr->valid_len = (MBS_CASE_ALLOCATED (pstr) || MBS_ALLOCATED (pstr)
- || MB_CUR_MAX > 1) ? pstr->valid_len : len;
- return REG_NOERROR;
-}
-
-/* This function allocate the buffers, and initialize them. */
-
-static reg_errcode_t
-re_string_construct (pstr, str, len, trans, icase)
- re_string_t *pstr;
- const char *str;
- int len, icase;
- RE_TRANSLATE_TYPE trans;
-{
- reg_errcode_t ret;
- re_string_construct_common (str, len, pstr, trans, icase);
- pstr->stop = pstr->len;
- /* Set 0 so that this function can initialize whole buffers. */
- pstr->valid_len = 0;
-
- if (len > 0)
- {
- ret = re_string_realloc_buffers (pstr, len + 1);
- if (BE (ret != REG_NOERROR, 0))
- return ret;
- }
- pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case
- : (unsigned char *) str);
- pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case;
-
- if (icase)
- {
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- build_wcs_upper_buffer (pstr);
- else
-#endif /* RE_ENABLE_I18N */
- build_upper_buffer (pstr);
- }
- else
- {
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- build_wcs_buffer (pstr);
- else
-#endif /* RE_ENABLE_I18N */
- {
- if (trans != NULL)
- re_string_translate_buffer (pstr);
- else
- pstr->valid_len = len;
- }
- }
-
- /* Initialized whole buffers, then valid_len == bufs_len. */
- pstr->valid_len = pstr->bufs_len;
- return REG_NOERROR;
-}
-
-/* Helper functions for re_string_allocate, and re_string_construct. */
-
-static reg_errcode_t
-re_string_realloc_buffers (pstr, new_buf_len)
- re_string_t *pstr;
- int new_buf_len;
-{
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- {
- wint_t *new_array = re_realloc (pstr->wcs, wint_t, new_buf_len);
- if (BE (new_array == NULL, 0))
- return REG_ESPACE;
- pstr->wcs = new_array;
- }
-#endif /* RE_ENABLE_I18N */
- if (MBS_ALLOCATED (pstr))
- {
- unsigned char *new_array = re_realloc (pstr->mbs, unsigned char,
- new_buf_len);
- if (BE (new_array == NULL, 0))
- return REG_ESPACE;
- pstr->mbs = new_array;
- }
- if (MBS_CASE_ALLOCATED (pstr))
- {
- unsigned char *new_array = re_realloc (pstr->mbs_case, unsigned char,
- new_buf_len);
- if (BE (new_array == NULL, 0))
- return REG_ESPACE;
- pstr->mbs_case = new_array;
- if (!MBS_ALLOCATED (pstr))
- pstr->mbs = pstr->mbs_case;
- }
- pstr->bufs_len = new_buf_len;
- return REG_NOERROR;
-}
-
-
-static void
-re_string_construct_common (str, len, pstr, trans, icase)
- const char *str;
- int len;
- re_string_t *pstr;
- RE_TRANSLATE_TYPE trans;
- int icase;
-{
- memset (pstr, '\0', sizeof (re_string_t));
- pstr->raw_mbs = (const unsigned char *) str;
- pstr->len = len;
- pstr->trans = trans;
- pstr->icase = icase ? 1 : 0;
-}
-
-#ifdef RE_ENABLE_I18N
-
-/* Build wide character buffer PSTR->WCS.
- If the byte sequence of the string are:
- <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
- Then wide character buffer will be:
- <wc1> , WEOF , <wc2> , WEOF , <wc3>
- We use WEOF for padding, they indicate that the position isn't
- a first byte of a multibyte character.
-
- Note that this function assumes PSTR->VALID_LEN elements are already
- built and starts from PSTR->VALID_LEN. */
-
-static void
-build_wcs_buffer (pstr)
- re_string_t *pstr;
-{
- mbstate_t prev_st;
- int byte_idx, end_idx, mbclen, remain_len;
- /* Build the buffers from pstr->valid_len to either pstr->len or
- pstr->bufs_len. */
- end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len;
- for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
- {
- wchar_t wc;
- remain_len = end_idx - byte_idx;
- prev_st = pstr->cur_state;
- mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
- + byte_idx), remain_len, &pstr->cur_state);
- if (BE (mbclen == (size_t) -2, 0))
- {
- /* The buffer doesn't have enough space, finish to build. */
- pstr->cur_state = prev_st;
- break;
- }
- else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
- {
- /* We treat these cases as a singlebyte character. */
- mbclen = 1;
- wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
- pstr->cur_state = prev_st;
- }
-
- /* Apply the translateion if we need. */
- if (pstr->trans != NULL && mbclen == 1)
- {
- int ch = pstr->trans[pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]];
- pstr->mbs_case[byte_idx] = ch;
- }
- /* Write wide character and padding. */
- pstr->wcs[byte_idx++] = wc;
- /* Write paddings. */
- for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
- pstr->wcs[byte_idx++] = WEOF;
- }
- pstr->valid_len = byte_idx;
-}
-
-/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
- but for REG_ICASE. */
-
-static void
-build_wcs_upper_buffer (pstr)
- re_string_t *pstr;
-{
- mbstate_t prev_st;
- int byte_idx, end_idx, mbclen, remain_len;
- /* Build the buffers from pstr->valid_len to either pstr->len or
- pstr->bufs_len. */
- end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len;
- for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
- {
- wchar_t wc;
- remain_len = end_idx - byte_idx;
- prev_st = pstr->cur_state;
- mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
- + byte_idx), remain_len, &pstr->cur_state);
- if (BE (mbclen == (size_t) -2, 0))
- {
- /* The buffer doesn't have enough space, finish to build. */
- pstr->cur_state = prev_st;
- break;
- }
- else if (mbclen == 1 || mbclen == (size_t) -1 || mbclen == 0)
- {
- /* In case of a singlebyte character. */
- int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
- /* Apply the translateion if we need. */
- if (pstr->trans != NULL && mbclen == 1)
- {
- ch = pstr->trans[ch];
- pstr->mbs_case[byte_idx] = ch;
- }
- pstr->wcs[byte_idx] = iswlower (wc) ? toupper (wc) : wc;
- pstr->mbs[byte_idx++] = islower (ch) ? toupper (ch) : ch;
- if (BE (mbclen == (size_t) -1, 0))
- pstr->cur_state = prev_st;
- }
- else /* mbclen > 1 */
- {
- if (iswlower (wc))
- wcrtomb ((char *) pstr->mbs + byte_idx, towupper (wc), &prev_st);
- else
- memcpy (pstr->mbs + byte_idx,
- pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
- pstr->wcs[byte_idx++] = iswlower (wc) ? toupper (wc) : wc;
- /* Write paddings. */
- for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
- pstr->wcs[byte_idx++] = WEOF;
- }
- }
- pstr->valid_len = byte_idx;
-}
-
-/* Skip characters until the index becomes greater than NEW_RAW_IDX.
- Return the index. */
-
-static int
-re_string_skip_chars (pstr, new_raw_idx, last_wc)
- re_string_t *pstr;
- int new_raw_idx;
- wint_t *last_wc;
-{
- mbstate_t prev_st;
- int rawbuf_idx, mbclen;
- wchar_t wc = 0;
-
- /* Skip the characters which are not necessary to check. */
- for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_len;
- rawbuf_idx < new_raw_idx;)
- {
- int remain_len;
- remain_len = pstr->len - rawbuf_idx;
- prev_st = pstr->cur_state;
- mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx,
- remain_len, &pstr->cur_state);
- if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
- {
- /* We treat these cases as a singlebyte character. */
- mbclen = 1;
- pstr->cur_state = prev_st;
- }
- /* Then proceed the next character. */
- rawbuf_idx += mbclen;
- }
- *last_wc = (wint_t) wc;
- return rawbuf_idx;
-}
-#endif /* RE_ENABLE_I18N */
-
-/* Build the buffer PSTR->MBS, and apply the translation if we need.
- This function is used in case of REG_ICASE. */
-
-static void
-build_upper_buffer (pstr)
- re_string_t *pstr;
-{
- int char_idx, end_idx;
- end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
-
- for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
- {
- int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
- if (pstr->trans != NULL)
- {
- ch = pstr->trans[ch];
- pstr->mbs_case[char_idx] = ch;
- }
- if (islower (ch))
- pstr->mbs[char_idx] = toupper (ch);
- else
- pstr->mbs[char_idx] = ch;
- }
- pstr->valid_len = char_idx;
-}
-
-/* Apply TRANS to the buffer in PSTR. */
-
-static void
-re_string_translate_buffer (pstr)
- re_string_t *pstr;
-{
- int buf_idx, end_idx;
- end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
-
- for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
- {
- int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
- pstr->mbs_case[buf_idx] = pstr->trans[ch];
- }
-
- pstr->valid_len = buf_idx;
-}
-
-/* This function re-construct the buffers.
- Concretely, convert to wide character in case of MB_CUR_MAX > 1,
- convert to upper case in case of REG_ICASE, apply translation. */
-
-static reg_errcode_t
-re_string_reconstruct (pstr, idx, eflags, newline)
- re_string_t *pstr;
- int idx, eflags, newline;
-{
- int offset = idx - pstr->raw_mbs_idx;
- if (offset < 0)
- {
- /* Reset buffer. */
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
-#endif /* RE_ENABLE_I18N */
- pstr->len += pstr->raw_mbs_idx;
- pstr->stop += pstr->raw_mbs_idx;
- pstr->valid_len = pstr->raw_mbs_idx = 0;
- pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
- : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
- if (!MBS_CASE_ALLOCATED (pstr))
- pstr->mbs_case = (unsigned char *) pstr->raw_mbs;
- if (!MBS_ALLOCATED (pstr) && !MBS_CASE_ALLOCATED (pstr))
- pstr->mbs = (unsigned char *) pstr->raw_mbs;
- offset = idx;
- }
-
- if (offset != 0)
- {
- /* Are the characters which are already checked remain? */
- if (offset < pstr->valid_len)
- {
- /* Yes, move them to the front of the buffer. */
- pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags,
- newline);
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- memmove (pstr->wcs, pstr->wcs + offset,
- (pstr->valid_len - offset) * sizeof (wint_t));
-#endif /* RE_ENABLE_I18N */
- if (MBS_ALLOCATED (pstr))
- memmove (pstr->mbs, pstr->mbs + offset,
- pstr->valid_len - offset);
- if (MBS_CASE_ALLOCATED (pstr))
- memmove (pstr->mbs_case, pstr->mbs_case + offset,
- pstr->valid_len - offset);
- pstr->valid_len -= offset;
-#if DEBUG
- assert (pstr->valid_len > 0);
-#endif
- }
- else
- {
- /* No, skip all characters until IDX. */
- pstr->valid_len = 0;
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- {
- int wcs_idx;
- wint_t wc;
- pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
- for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
- pstr->wcs[wcs_idx] = WEOF;
- if (pstr->trans && wc <= 0xff)
- wc = pstr->trans[wc];
- pstr->tip_context = (IS_WIDE_WORD_CHAR (wc) ? CONTEXT_WORD
- : ((newline && IS_WIDE_NEWLINE (wc))
- ? CONTEXT_NEWLINE : 0));
- }
- else
-#endif /* RE_ENABLE_I18N */
- {
- int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
- if (pstr->trans)
- c = pstr->trans[c];
- pstr->tip_context = (IS_WORD_CHAR (c) ? CONTEXT_WORD
- : ((newline && IS_NEWLINE (c))
- ? CONTEXT_NEWLINE : 0));
- }
- }
- if (!MBS_CASE_ALLOCATED (pstr))
- {
- pstr->mbs_case += offset;
- /* In case of !MBS_ALLOCATED && !MBS_CASE_ALLOCATED. */
- if (!MBS_ALLOCATED (pstr))
- pstr->mbs += offset;
- }
- }
- pstr->raw_mbs_idx = idx;
- pstr->len -= offset;
- pstr->stop -= offset;
-
- /* Then build the buffers. */
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- {
- if (pstr->icase)
- build_wcs_upper_buffer (pstr);
- else
- build_wcs_buffer (pstr);
- }
- else
-#endif /* RE_ENABLE_I18N */
- {
- if (pstr->icase)
- build_upper_buffer (pstr);
- else if (pstr->trans != NULL)
- re_string_translate_buffer (pstr);
- }
- pstr->cur_idx = 0;
-
- return REG_NOERROR;
-}
-
-static void
-re_string_destruct (pstr)
- re_string_t *pstr;
-{
-#ifdef RE_ENABLE_I18N
- re_free (pstr->wcs);
-#endif /* RE_ENABLE_I18N */
- if (MBS_ALLOCATED (pstr))
- re_free (pstr->mbs);
- if (MBS_CASE_ALLOCATED (pstr))
- re_free (pstr->mbs_case);
-}
-
-/* Return the context at IDX in INPUT. */
-
-static unsigned int
-re_string_context_at (input, idx, eflags, newline_anchor)
- const re_string_t *input;
- int idx, eflags, newline_anchor;
-{
- int c;
- if (idx < 0 || idx == input->len)
- {
- if (idx < 0)
- /* In this case, we use the value stored in input->tip_context,
- since we can't know the character in input->mbs[-1] here. */
- return input->tip_context;
- else /* (idx == input->len) */
- return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
- : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
- }
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- {
- wint_t wc;
- int wc_idx = idx;
- while(input->wcs[wc_idx] == WEOF)
- {
-#ifdef DEBUG
- /* It must not happen. */
- assert (wc_idx >= 0);
-#endif
- --wc_idx;
- if (wc_idx < 0)
- return input->tip_context;
- }
- wc = input->wcs[wc_idx];
- if (IS_WIDE_WORD_CHAR (wc))
- return CONTEXT_WORD;
- return (newline_anchor && IS_WIDE_NEWLINE (wc)) ? CONTEXT_NEWLINE : 0;
- }
- else
-#endif
- {
- c = re_string_byte_at (input, idx);
- if (IS_WORD_CHAR (c))
- return CONTEXT_WORD;
- return (newline_anchor && IS_NEWLINE (c)) ? CONTEXT_NEWLINE : 0;
- }
-}
-\f
-/* Functions for set operation. */
-
-static reg_errcode_t
-re_node_set_alloc (set, size)
- re_node_set *set;
- int size;
-{
- set->alloc = size;
- set->nelem = 0;
- set->elems = re_malloc (int, size);
- if (BE (set->elems == NULL, 0))
- return REG_ESPACE;
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-re_node_set_init_1 (set, elem)
- re_node_set *set;
- int elem;
-{
- set->alloc = 1;
- set->nelem = 1;
- set->elems = re_malloc (int, 1);
- if (BE (set->elems == NULL, 0))
- {
- set->alloc = set->nelem = 0;
- return REG_ESPACE;
- }
- set->elems[0] = elem;
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-re_node_set_init_2 (set, elem1, elem2)
- re_node_set *set;
- int elem1, elem2;
-{
- set->alloc = 2;
- set->elems = re_malloc (int, 2);
- if (BE (set->elems == NULL, 0))
- return REG_ESPACE;
- if (elem1 == elem2)
- {
- set->nelem = 1;
- set->elems[0] = elem1;
- }
- else
- {
- set->nelem = 2;
- if (elem1 < elem2)
- {
- set->elems[0] = elem1;
- set->elems[1] = elem2;
- }
- else
- {
- set->elems[0] = elem2;
- set->elems[1] = elem1;
- }
- }
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-re_node_set_init_copy (dest, src)
- re_node_set *dest;
- const re_node_set *src;
-{
- dest->nelem = src->nelem;
- if (src->nelem > 0)
- {
- dest->alloc = dest->nelem;
- dest->elems = re_malloc (int, dest->alloc);
- if (BE (dest->elems == NULL, 0))
- {
- dest->alloc = dest->nelem = 0;
- return REG_ESPACE;
- }
- memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
- }
- else
- re_node_set_init_empty (dest);
- return REG_NOERROR;
-}
-
-/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
- DEST. Return value indicate the error code or REG_NOERROR if succeeded.
- Note: We assume dest->elems is NULL, when dest->alloc is 0. */
-
-static reg_errcode_t
-re_node_set_add_intersect (dest, src1, src2)
- re_node_set *dest;
- const re_node_set *src1, *src2;
-{
- int i1, i2, id;
- if (src1->nelem > 0 && src2->nelem > 0)
- {
- if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
- {
- dest->alloc = src1->nelem + src2->nelem + dest->nelem;
- dest->elems = re_realloc (dest->elems, int, dest->alloc);
- if (BE (dest->elems == NULL, 0))
- return REG_ESPACE;
- }
- }
- else
- return REG_NOERROR;
-
- for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
- {
- if (src1->elems[i1] > src2->elems[i2])
- {
- ++i2;
- continue;
- }
- if (src1->elems[i1] == src2->elems[i2])
- {
- while (id < dest->nelem && dest->elems[id] < src2->elems[i2])
- ++id;
- if (id < dest->nelem && dest->elems[id] == src2->elems[i2])
- ++id;
- else
- {
- memmove (dest->elems + id + 1, dest->elems + id,
- sizeof (int) * (dest->nelem - id));
- dest->elems[id++] = src2->elems[i2++];
- ++dest->nelem;
- }
- }
- ++i1;
- }
- return REG_NOERROR;
-}
-
-/* Calculate the union set of the sets SRC1 and SRC2. And store it to
- DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
-
-static reg_errcode_t
-re_node_set_init_union (dest, src1, src2)
- re_node_set *dest;
- const re_node_set *src1, *src2;
-{
- int i1, i2, id;
- if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
- {
- dest->alloc = src1->nelem + src2->nelem;
- dest->elems = re_malloc (int, dest->alloc);
- if (BE (dest->elems == NULL, 0))
- return REG_ESPACE;
- }
- else
- {
- if (src1 != NULL && src1->nelem > 0)
- return re_node_set_init_copy (dest, src1);
- else if (src2 != NULL && src2->nelem > 0)
- return re_node_set_init_copy (dest, src2);
- else
- re_node_set_init_empty (dest);
- return REG_NOERROR;
- }
- for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
- {
- if (src1->elems[i1] > src2->elems[i2])
- {
- dest->elems[id++] = src2->elems[i2++];
- continue;
- }
- if (src1->elems[i1] == src2->elems[i2])
- ++i2;
- dest->elems[id++] = src1->elems[i1++];
- }
- if (i1 < src1->nelem)
- {
- memcpy (dest->elems + id, src1->elems + i1,
- (src1->nelem - i1) * sizeof (int));
- id += src1->nelem - i1;
- }
- else if (i2 < src2->nelem)
- {
- memcpy (dest->elems + id, src2->elems + i2,
- (src2->nelem - i2) * sizeof (int));
- id += src2->nelem - i2;
- }
- dest->nelem = id;
- return REG_NOERROR;
-}
-
-/* Calculate the union set of the sets DEST and SRC. And store it to
- DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
-
-static reg_errcode_t
-re_node_set_merge (dest, src)
- re_node_set *dest;
- const re_node_set *src;
-{
- int si, di;
- if (src == NULL || src->nelem == 0)
- return REG_NOERROR;
- if (dest->alloc < src->nelem + dest->nelem)
- {
- int *new_buffer;
- dest->alloc = 2 * (src->nelem + dest->alloc);
- new_buffer = re_realloc (dest->elems, int, dest->alloc);
- if (BE (new_buffer == NULL, 0))
- return REG_ESPACE;
- dest->elems = new_buffer;
- }
-
- for (si = 0, di = 0 ; si < src->nelem && di < dest->nelem ;)
- {
- int cp_from, ncp, mid, right, src_elem = src->elems[si];
- /* Binary search the spot we will add the new element. */
- right = dest->nelem;
- while (di < right)
- {
- mid = (di + right) / 2;
- if (dest->elems[mid] < src_elem)
- di = mid + 1;
- else
- right = mid;
- }
- if (di >= dest->nelem)
- break;
-
- if (dest->elems[di] == src_elem)
- {
- /* Skip since, DEST already has the element. */
- ++di;
- ++si;
- continue;
- }
-
- /* Skip the src elements which are less than dest->elems[di]. */
- cp_from = si;
- while (si < src->nelem && src->elems[si] < dest->elems[di])
- ++si;
- /* Copy these src elements. */
- ncp = si - cp_from;
- memmove (dest->elems + di + ncp, dest->elems + di,
- sizeof (int) * (dest->nelem - di));
- memcpy (dest->elems + di, src->elems + cp_from,
- sizeof (int) * ncp);
- /* Update counters. */
- di += ncp;
- dest->nelem += ncp;
- }
-
- /* Copy remaining src elements. */
- if (si < src->nelem)
- {
- memcpy (dest->elems + di, src->elems + si,
- sizeof (int) * (src->nelem - si));
- dest->nelem += src->nelem - si;
- }
- return REG_NOERROR;
-}
-
-/* Insert the new element ELEM to the re_node_set* SET.
- return 0 if SET already has ELEM,
- return -1 if an error is occured, return 1 otherwise. */
-
-static int
-re_node_set_insert (set, elem)
- re_node_set *set;
- int elem;
-{
- int idx, right, mid;
- /* In case of the set is empty. */
- if (set->elems == NULL || set->alloc == 0)
- {
- if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
- return 1;
- else
- return -1;
- }
-
- /* Binary search the spot we will add the new element. */
- idx = 0;
- right = set->nelem;
- while (idx < right)
- {
- mid = (idx + right) / 2;
- if (set->elems[mid] < elem)
- idx = mid + 1;
- else
- right = mid;
- }
-
- /* Realloc if we need. */
- if (set->alloc < set->nelem + 1)
- {
- int *new_array;
- set->alloc = set->alloc * 2;
- new_array = re_malloc (int, set->alloc);
- if (BE (new_array == NULL, 0))
- return -1;
- /* Copy the elements they are followed by the new element. */
- if (idx > 0)
- memcpy (new_array, set->elems, sizeof (int) * (idx));
- /* Copy the elements which follows the new element. */
- if (set->nelem - idx > 0)
- memcpy (new_array + idx + 1, set->elems + idx,
- sizeof (int) * (set->nelem - idx));
- re_free (set->elems);
- set->elems = new_array;
- }
- else
- {
- /* Move the elements which follows the new element. */
- if (set->nelem - idx > 0)
- memmove (set->elems + idx + 1, set->elems + idx,
- sizeof (int) * (set->nelem - idx));
- }
- /* Insert the new element. */
- set->elems[idx] = elem;
- ++set->nelem;
- return 1;
-}
-
-/* Compare two node sets SET1 and SET2.
- return 1 if SET1 and SET2 are equivalent, retrun 0 otherwise. */
-
-static int
-re_node_set_compare (set1, set2)
- const re_node_set *set1, *set2;
-{
- int i;
- if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
- return 0;
- for (i = 0 ; i < set1->nelem ; i++)
- if (set1->elems[i] != set2->elems[i])
- return 0;
- return 1;
-}
-
-/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */
-
-static int
-re_node_set_contains (set, elem)
- const re_node_set *set;
- int elem;
-{
- int idx, right, mid;
- if (set->nelem <= 0)
- return 0;
-
- /* Binary search the element. */
- idx = 0;
- right = set->nelem - 1;
- while (idx < right)
- {
- mid = (idx + right) / 2;
- if (set->elems[mid] < elem)
- idx = mid + 1;
- else
- right = mid;
- }
- return set->elems[idx] == elem ? idx + 1 : 0;
-}
-
-static void
-re_node_set_remove_at (set, idx)
- re_node_set *set;
- int idx;
-{
- if (idx < 0 || idx >= set->nelem)
- return;
- if (idx < set->nelem - 1)
- memmove (set->elems + idx, set->elems + idx + 1,
- sizeof (int) * (set->nelem - idx - 1));
- --set->nelem;
-}
-\f
-
-/* Add the token TOKEN to dfa->nodes, and return the index of the token.
- Or return -1, if an error will be occured. */
-
-static int
-re_dfa_add_node (dfa, token, mode)
- re_dfa_t *dfa;
- re_token_t token;
- int mode;
-{
- if (dfa->nodes_len >= dfa->nodes_alloc)
- {
- re_token_t *new_array;
- dfa->nodes_alloc *= 2;
- new_array = re_realloc (dfa->nodes, re_token_t, dfa->nodes_alloc);
- if (BE (new_array == NULL, 0))
- return -1;
- else
- dfa->nodes = new_array;
- if (mode)
- {
- int *new_nexts, *new_indices;
- re_node_set *new_edests, *new_eclosures, *new_inveclosures;
-
- new_nexts = re_realloc (dfa->nexts, int, dfa->nodes_alloc);
- new_indices = re_realloc (dfa->org_indices, int, dfa->nodes_alloc);
- new_edests = re_realloc (dfa->edests, re_node_set, dfa->nodes_alloc);
- new_eclosures = re_realloc (dfa->eclosures, re_node_set,
- dfa->nodes_alloc);
- new_inveclosures = re_realloc (dfa->inveclosures, re_node_set,
- dfa->nodes_alloc);
- if (BE (new_nexts == NULL || new_indices == NULL
- || new_edests == NULL || new_eclosures == NULL
- || new_inveclosures == NULL, 0))
- return -1;
- dfa->nexts = new_nexts;
- dfa->org_indices = new_indices;
- dfa->edests = new_edests;
- dfa->eclosures = new_eclosures;
- dfa->inveclosures = new_inveclosures;
- }
- }
- dfa->nodes[dfa->nodes_len] = token;
- dfa->nodes[dfa->nodes_len].duplicated = 0;
- dfa->nodes[dfa->nodes_len].constraint = 0;
- return dfa->nodes_len++;
-}
-
-static unsigned int inline
-calc_state_hash (nodes, context)
- const re_node_set *nodes;
- unsigned int context;
-{
- unsigned int hash = nodes->nelem + context;
- int i;
- for (i = 0 ; i < nodes->nelem ; i++)
- hash += nodes->elems[i];
- return hash;
-}
-
-/* Search for the state whose node_set is equivalent to NODES.
- Return the pointer to the state, if we found it in the DFA.
- Otherwise create the new one and return it. In case of an error
- return NULL and set the error code in ERR.
- Note: - We assume NULL as the invalid state, then it is possible that
- return value is NULL and ERR is REG_NOERROR.
- - We never return non-NULL value in case of any errors, it is for
- optimization. */
-
-static re_dfastate_t*
-re_acquire_state (err, dfa, nodes)
- reg_errcode_t *err;
- re_dfa_t *dfa;
- const re_node_set *nodes;
-{
- unsigned int hash;
- re_dfastate_t *new_state;
- struct re_state_table_entry *spot;
- int i;
- if (BE (nodes->nelem == 0, 0))
- {
- *err = REG_NOERROR;
- return NULL;
- }
- hash = calc_state_hash (nodes, 0);
- spot = dfa->state_table + (hash & dfa->state_hash_mask);
-
- for (i = 0 ; i < spot->num ; i++)
- {
- re_dfastate_t *state = spot->array[i];
- if (hash != state->hash)
- continue;
- if (re_node_set_compare (&state->nodes, nodes))
- return state;
- }
-
- /* There are no appropriate state in the dfa, create the new one. */
- new_state = create_ci_newstate (dfa, nodes, hash);
- if (BE (new_state != NULL, 1))
- return new_state;
- else
- {
- *err = REG_ESPACE;
- return NULL;
- }
-}
-
-/* Search for the state whose node_set is equivalent to NODES and
- whose context is equivalent to CONTEXT.
- Return the pointer to the state, if we found it in the DFA.
- Otherwise create the new one and return it. In case of an error
- return NULL and set the error code in ERR.
- Note: - We assume NULL as the invalid state, then it is possible that
- return value is NULL and ERR is REG_NOERROR.
- - We never return non-NULL value in case of any errors, it is for
- optimization. */
-
-static re_dfastate_t*
-re_acquire_state_context (err, dfa, nodes, context)
- reg_errcode_t *err;
- re_dfa_t *dfa;
- const re_node_set *nodes;
- unsigned int context;
-{
- unsigned int hash;
- re_dfastate_t *new_state;
- struct re_state_table_entry *spot;
- int i;
- if (nodes->nelem == 0)
- {
- *err = REG_NOERROR;
- return NULL;
- }
- hash = calc_state_hash (nodes, context);
- spot = dfa->state_table + (hash & dfa->state_hash_mask);
-
- for (i = 0 ; i < spot->num ; i++)
- {
- re_dfastate_t *state = spot->array[i];
- if (hash != state->hash)
- continue;
- if (re_node_set_compare (state->entrance_nodes, nodes)
- && state->context == context)
- return state;
- }
- /* There are no appropriate state in `dfa', create the new one. */
- new_state = create_cd_newstate (dfa, nodes, context, hash);
- if (BE (new_state != NULL, 1))
- return new_state;
- else
- {
- *err = REG_ESPACE;
- return NULL;
- }
-}
-
-/* Allocate memory for DFA state and initialize common properties.
- Return the new state if succeeded, otherwise return NULL. */
-
-static re_dfastate_t *
-create_newstate_common (dfa, nodes, hash)
- re_dfa_t *dfa;
- const re_node_set *nodes;
- unsigned int hash;
-{
- re_dfastate_t *newstate;
- reg_errcode_t err;
- newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
- if (BE (newstate == NULL, 0))
- return NULL;
- err = re_node_set_init_copy (&newstate->nodes, nodes);
- if (BE (err != REG_NOERROR, 0))
- {
- re_free (newstate);
- return NULL;
- }
- newstate->trtable = NULL;
- newstate->trtable_search = NULL;
- newstate->hash = hash;
- return newstate;
-}
-
-/* Store the new state NEWSTATE whose hash value is HASH in appropriate
- position. Return value indicate the error code if failed. */
-
-static reg_errcode_t
-register_state (dfa, newstate, hash)
- re_dfa_t *dfa;
- re_dfastate_t *newstate;
- unsigned int hash;
-{
- struct re_state_table_entry *spot;
- spot = dfa->state_table + (hash & dfa->state_hash_mask);
-
- if (spot->alloc <= spot->num)
- {
- re_dfastate_t **new_array;
- spot->alloc = 2 * spot->num + 2;
- new_array = re_realloc (spot->array, re_dfastate_t *, spot->alloc);
- if (BE (new_array == NULL, 0))
- return REG_ESPACE;
- spot->array = new_array;
- }
- spot->array[spot->num++] = newstate;
- return REG_NOERROR;
-}
-
-/* Create the new state which is independ of contexts.
- Return the new state if succeeded, otherwise return NULL. */
-
-static re_dfastate_t *
-create_ci_newstate (dfa, nodes, hash)
- re_dfa_t *dfa;
- const re_node_set *nodes;
- unsigned int hash;
-{
- int i;
- reg_errcode_t err;
- re_dfastate_t *newstate;
- newstate = create_newstate_common (dfa, nodes, hash);
- if (BE (newstate == NULL, 0))
- return NULL;
- newstate->entrance_nodes = &newstate->nodes;
-
- for (i = 0 ; i < nodes->nelem ; i++)
- {
- re_token_t *node = dfa->nodes + nodes->elems[i];
- re_token_type_t type = node->type;
- if (type == CHARACTER && !node->constraint)
- continue;
-
- /* If the state has the halt node, the state is a halt state. */
- else if (type == END_OF_RE)
- newstate->halt = 1;
-#ifdef RE_ENABLE_I18N
- else if (type == COMPLEX_BRACKET
- || (type == OP_PERIOD && MB_CUR_MAX > 1))
- newstate->accept_mb = 1;
-#endif /* RE_ENABLE_I18N */
- else if (type == OP_BACK_REF)
- newstate->has_backref = 1;
- else if (type == ANCHOR || node->constraint)
- newstate->has_constraint = 1;
- }
- err = register_state (dfa, newstate, hash);
- if (BE (err != REG_NOERROR, 0))
- {
- free_state (newstate);
- newstate = NULL;
- }
- return newstate;
-}
-
-/* Create the new state which is depend on the context CONTEXT.
- Return the new state if succeeded, otherwise return NULL. */
-
-static re_dfastate_t *
-create_cd_newstate (dfa, nodes, context, hash)
- re_dfa_t *dfa;
- const re_node_set *nodes;
- unsigned int context, hash;
-{
- int i, nctx_nodes = 0;
- reg_errcode_t err;
- re_dfastate_t *newstate;
-
- newstate = create_newstate_common (dfa, nodes, hash);
- if (BE (newstate == NULL, 0))
- return NULL;
- newstate->context = context;
- newstate->entrance_nodes = &newstate->nodes;
-
- for (i = 0 ; i < nodes->nelem ; i++)
- {
- unsigned int constraint = 0;
- re_token_t *node = dfa->nodes + nodes->elems[i];
- re_token_type_t type = node->type;
- if (node->constraint)
- constraint = node->constraint;
-
- if (type == CHARACTER && !constraint)
- continue;
- /* If the state has the halt node, the state is a halt state. */
- else if (type == END_OF_RE)
- newstate->halt = 1;
-#ifdef RE_ENABLE_I18N
- else if (type == COMPLEX_BRACKET
- || (type == OP_PERIOD && MB_CUR_MAX > 1))
- newstate->accept_mb = 1;
-#endif /* RE_ENABLE_I18N */
- else if (type == OP_BACK_REF)
- newstate->has_backref = 1;
- else if (type == ANCHOR)
- constraint = node->opr.ctx_type;
-
- if (constraint)
- {
- if (newstate->entrance_nodes == &newstate->nodes)
- {
- newstate->entrance_nodes = re_malloc (re_node_set, 1);
- if (BE (newstate->entrance_nodes == NULL, 0))
- {
- free_state (newstate);
- return NULL;
- }
- re_node_set_init_copy (newstate->entrance_nodes, nodes);
- nctx_nodes = 0;
- newstate->has_constraint = 1;
- }
-
- if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
- {
- re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
- ++nctx_nodes;
- }
- }
- }
- err = register_state (dfa, newstate, hash);
- if (BE (err != REG_NOERROR, 0))
- {
- free_state (newstate);
- newstate = NULL;
- }
- return newstate;
-}
-
-static void
-free_state (state)
- re_dfastate_t *state;
-{
- if (state->entrance_nodes != &state->nodes)
- {
- re_node_set_free (state->entrance_nodes);
- re_free (state->entrance_nodes);
- }
- re_node_set_free (&state->nodes);
- re_free (state->trtable);
- re_free (state->trtable_search);
- re_free (state);
-}
+++ /dev/null
-/* Extended regular expression matching and search library.
- Copyright (C) 2002, 2003 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
-
-#ifndef _REGEX_INTERNAL_H
-#define _REGEX_INTERNAL_H 1
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <assert.h>
-#include <ctype.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#if defined HAVE_LOCALE_H || defined _LIBC
-# include <locale.h>
-#endif
-#if defined HAVE_WCHAR_H || defined _LIBC
-# include <wchar.h>
-#endif /* HAVE_WCHAR_H || _LIBC */
-#if defined HAVE_WCTYPE_H || defined _LIBC
-# include <wctype.h>
-#endif /* HAVE_WCTYPE_H || _LIBC */
-
-/* In case that the system doesn't have isblank(). */
-#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
-# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
-#endif
-
-#ifdef _LIBC
-# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
-# define _RE_DEFINE_LOCALE_FUNCTIONS 1
-# include <locale/localeinfo.h>
-# include <locale/elem-hash.h>
-# include <locale/coll-lookup.h>
-# endif
-#endif
-
-/* This is for other GNU distributions with internationalized messages. */
-#if HAVE_LIBINTL_H || defined _LIBC
-# include <libintl.h>
-# ifdef _LIBC
-# undef gettext
-# define gettext(msgid) \
- INTUSE(__dcgettext) (INTUSE(_libc_intl_domainname), msgid, LC_MESSAGES)
-# endif
-#else
-# define gettext(msgid) (msgid)
-#endif
-
-#ifndef gettext_noop
-/* This define is so xgettext can find the internationalizable
- strings. */
-# define gettext_noop(String) String
-#endif
-
-#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC
-# define RE_ENABLE_I18N
-#endif
-
-#if __GNUC__ >= 3
-# define BE(expr, val) __builtin_expect (expr, val)
-#else
-# define BE(expr, val) (expr)
-# define inline
-#endif
-
-/* Number of bits in a byte. */
-#define BYTE_BITS 8
-/* Number of single byte character. */
-#define SBC_MAX 256
-
-#define COLL_ELEM_LEN_MAX 8
-
-/* The character which represents newline. */
-#define NEWLINE_CHAR '\n'
-#define WIDE_NEWLINE_CHAR L'\n'
-
-/* Rename to standard API for using out of glibc. */
-#ifndef _LIBC
-# define __wctype wctype
-# define __iswctype iswctype
-# define __btowc btowc
-# define __mempcpy mempcpy
-# define __wcrtomb wcrtomb
-# define attribute_hidden
-#endif /* not _LIBC */
-
-extern const char __re_error_msgid[] attribute_hidden;
-extern const size_t __re_error_msgid_idx[] attribute_hidden;
-
-/* Number of bits in an unsinged int. */
-#define UINT_BITS (sizeof (unsigned int) * BYTE_BITS)
-/* Number of unsigned int in an bit_set. */
-#define BITSET_UINTS ((SBC_MAX + UINT_BITS - 1) / UINT_BITS)
-typedef unsigned int bitset[BITSET_UINTS];
-typedef unsigned int *re_bitset_ptr_t;
-
-#define bitset_set(set,i) (set[i / UINT_BITS] |= 1 << i % UINT_BITS)
-#define bitset_clear(set,i) (set[i / UINT_BITS] &= ~(1 << i % UINT_BITS))
-#define bitset_contain(set,i) (set[i / UINT_BITS] & (1 << i % UINT_BITS))
-#define bitset_empty(set) memset (set, 0, sizeof (unsigned int) * BITSET_UINTS)
-#define bitset_set_all(set) \
- memset (set, 255, sizeof (unsigned int) * BITSET_UINTS)
-#define bitset_copy(dest,src) \
- memcpy (dest, src, sizeof (unsigned int) * BITSET_UINTS)
-static inline void bitset_not (bitset set);
-static inline void bitset_merge (bitset dest, const bitset src);
-static inline void bitset_not_merge (bitset dest, const bitset src);
-
-#define PREV_WORD_CONSTRAINT 0x0001
-#define PREV_NOTWORD_CONSTRAINT 0x0002
-#define NEXT_WORD_CONSTRAINT 0x0004
-#define NEXT_NOTWORD_CONSTRAINT 0x0008
-#define PREV_NEWLINE_CONSTRAINT 0x0010
-#define NEXT_NEWLINE_CONSTRAINT 0x0020
-#define PREV_BEGBUF_CONSTRAINT 0x0040
-#define NEXT_ENDBUF_CONSTRAINT 0x0080
-#define DUMMY_CONSTRAINT 0x0100
-
-typedef enum
-{
- INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
- WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
- WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
- LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
- LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
- BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
- BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
- WORD_DELIM = DUMMY_CONSTRAINT
-} re_context_type;
-
-typedef struct
-{
- int alloc;
- int nelem;
- int *elems;
-} re_node_set;
-
-typedef enum
-{
- NON_TYPE = 0,
-
- /* Token type, these are used only by token. */
- OP_OPEN_BRACKET,
- OP_CLOSE_BRACKET,
- OP_CHARSET_RANGE,
- OP_OPEN_DUP_NUM,
- OP_CLOSE_DUP_NUM,
- OP_NON_MATCH_LIST,
- OP_OPEN_COLL_ELEM,
- OP_CLOSE_COLL_ELEM,
- OP_OPEN_EQUIV_CLASS,
- OP_CLOSE_EQUIV_CLASS,
- OP_OPEN_CHAR_CLASS,
- OP_CLOSE_CHAR_CLASS,
- OP_WORD,
- OP_NOTWORD,
- BACK_SLASH,
-
- /* Tree type, these are used only by tree. */
- CONCAT,
- ALT,
- SUBEXP,
- SIMPLE_BRACKET,
-#ifdef RE_ENABLE_I18N
- COMPLEX_BRACKET,
-#endif /* RE_ENABLE_I18N */
-
- /* Node type, These are used by token, node, tree. */
- OP_OPEN_SUBEXP,
- OP_CLOSE_SUBEXP,
- OP_PERIOD,
- CHARACTER,
- END_OF_RE,
- OP_ALT,
- OP_DUP_ASTERISK,
- OP_DUP_PLUS,
- OP_DUP_QUESTION,
- OP_BACK_REF,
- ANCHOR,
-
- /* Dummy marker. */
- END_OF_RE_TOKEN_T
-} re_token_type_t;
-
-#ifdef RE_ENABLE_I18N
-typedef struct
-{
- /* Multibyte characters. */
- wchar_t *mbchars;
-
- /* Collating symbols. */
-# ifdef _LIBC
- int32_t *coll_syms;
-# endif
-
- /* Equivalence classes. */
-# ifdef _LIBC
- int32_t *equiv_classes;
-# endif
-
- /* Range expressions. */
-# ifdef _LIBC
- uint32_t *range_starts;
- uint32_t *range_ends;
-# else /* not _LIBC */
- wchar_t *range_starts;
- wchar_t *range_ends;
-# endif /* not _LIBC */
-
- /* Character classes. */
- wctype_t *char_classes;
-
- /* If this character set is the non-matching list. */
- unsigned int non_match : 1;
-
- /* # of multibyte characters. */
- int nmbchars;
-
- /* # of collating symbols. */
- int ncoll_syms;
-
- /* # of equivalence classes. */
- int nequiv_classes;
-
- /* # of range expressions. */
- int nranges;
-
- /* # of character classes. */
- int nchar_classes;
-} re_charset_t;
-#endif /* RE_ENABLE_I18N */
-
-typedef struct
-{
- union
- {
- unsigned char c; /* for CHARACTER */
- re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
-#ifdef RE_ENABLE_I18N
- re_charset_t *mbcset; /* for COMPLEX_BRACKET */
-#endif /* RE_ENABLE_I18N */
- int idx; /* for BACK_REF */
- re_context_type ctx_type; /* for ANCHOR */
- } opr;
-#if __GNUC__ >= 2
- re_token_type_t type : 8;
-#else
- re_token_type_t type;
-#endif
- unsigned int constraint : 10; /* context constraint */
- unsigned int duplicated : 1;
-#ifdef RE_ENABLE_I18N
- unsigned int mb_partial : 1;
-#endif
-} re_token_t;
-
-#define IS_EPSILON_NODE(type) \
- ((type) == OP_ALT || (type) == OP_DUP_ASTERISK || (type) == OP_DUP_PLUS \
- || (type) == OP_DUP_QUESTION || (type) == ANCHOR \
- || (type) == OP_OPEN_SUBEXP || (type) == OP_CLOSE_SUBEXP)
-
-#define ACCEPT_MB_NODE(type) \
- ((type) == COMPLEX_BRACKET || (type) == OP_PERIOD)
-
-struct re_string_t
-{
- /* Indicate the raw buffer which is the original string passed as an
- argument of regexec(), re_search(), etc.. */
- const unsigned char *raw_mbs;
- /* Store the multibyte string. In case of "case insensitive mode" like
- REG_ICASE, upper cases of the string are stored, otherwise MBS points
- the same address that RAW_MBS points. */
- unsigned char *mbs;
- /* Store the case sensitive multibyte string. In case of
- "case insensitive mode", the original string are stored,
- otherwise MBS_CASE points the same address that MBS points. */
- unsigned char *mbs_case;
-#ifdef RE_ENABLE_I18N
- /* Store the wide character string which is corresponding to MBS. */
- wint_t *wcs;
- mbstate_t cur_state;
-#endif
- /* Index in RAW_MBS. Each character mbs[i] corresponds to
- raw_mbs[raw_mbs_idx + i]. */
- int raw_mbs_idx;
- /* The length of the valid characters in the buffers. */
- int valid_len;
- /* The length of the buffers MBS, MBS_CASE, and WCS. */
- int bufs_len;
- /* The index in MBS, which is updated by re_string_fetch_byte. */
- int cur_idx;
- /* This is length_of_RAW_MBS - RAW_MBS_IDX. */
- int len;
- /* End of the buffer may be shorter than its length in the cases such
- as re_match_2, re_search_2. Then, we use STOP for end of the buffer
- instead of LEN. */
- int stop;
-
- /* The context of mbs[0]. We store the context independently, since
- the context of mbs[0] may be different from raw_mbs[0], which is
- the beginning of the input string. */
- unsigned int tip_context;
- /* The translation passed as a part of an argument of re_compile_pattern. */
- RE_TRANSLATE_TYPE trans;
- /* 1 if REG_ICASE. */
- unsigned int icase : 1;
-};
-typedef struct re_string_t re_string_t;
-/* In case of REG_ICASE, we allocate the buffer dynamically for mbs. */
-#define MBS_ALLOCATED(pstr) (pstr->icase)
-/* In case that we need translation, we allocate the buffer dynamically
- for mbs_case. Note that mbs == mbs_case if not REG_ICASE. */
-#define MBS_CASE_ALLOCATED(pstr) (pstr->trans != NULL)
-
-
-static reg_errcode_t re_string_allocate (re_string_t *pstr, const char *str,
- int len, int init_len,
- RE_TRANSLATE_TYPE trans, int icase);
-static reg_errcode_t re_string_construct (re_string_t *pstr, const char *str,
- int len, RE_TRANSLATE_TYPE trans,
- int icase);
-static reg_errcode_t re_string_reconstruct (re_string_t *pstr, int idx,
- int eflags, int newline);
-static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
- int new_buf_len);
-#ifdef RE_ENABLE_I18N
-static void build_wcs_buffer (re_string_t *pstr);
-static void build_wcs_upper_buffer (re_string_t *pstr);
-#endif /* RE_ENABLE_I18N */
-static void build_upper_buffer (re_string_t *pstr);
-static void re_string_translate_buffer (re_string_t *pstr);
-static void re_string_destruct (re_string_t *pstr);
-#ifdef RE_ENABLE_I18N
-static int re_string_elem_size_at (const re_string_t *pstr, int idx);
-static inline int re_string_char_size_at (const re_string_t *pstr, int idx);
-static inline wint_t re_string_wchar_at (const re_string_t *pstr, int idx);
-#endif /* RE_ENABLE_I18N */
-static unsigned int re_string_context_at (const re_string_t *input, int idx,
- int eflags, int newline_anchor);
-#define re_string_peek_byte(pstr, offset) \
- ((pstr)->mbs[(pstr)->cur_idx + offset])
-#define re_string_peek_byte_case(pstr, offset) \
- ((pstr)->mbs_case[(pstr)->cur_idx + offset])
-#define re_string_fetch_byte(pstr) \
- ((pstr)->mbs[(pstr)->cur_idx++])
-#define re_string_fetch_byte_case(pstr) \
- ((pstr)->mbs_case[(pstr)->cur_idx++])
-#define re_string_first_byte(pstr, idx) \
- ((idx) == (pstr)->len || (pstr)->wcs[idx] != WEOF)
-#define re_string_is_single_byte_char(pstr, idx) \
- ((pstr)->wcs[idx] != WEOF && ((pstr)->len == (idx) \
- || (pstr)->wcs[(idx) + 1] != WEOF))
-#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
-#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
-#define re_string_get_buffer(pstr) ((pstr)->mbs)
-#define re_string_length(pstr) ((pstr)->len)
-#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
-#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
-#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
-
-#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
-#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
-#define re_free(p) free (p)
-
-struct bin_tree_t
-{
- struct bin_tree_t *parent;
- struct bin_tree_t *left;
- struct bin_tree_t *right;
-
- /* `node_idx' is the index in dfa->nodes, if `type' == 0.
- Otherwise `type' indicate the type of this node. */
- re_token_type_t type;
- int node_idx;
-
- int first;
- int next;
- re_node_set eclosure;
-};
-typedef struct bin_tree_t bin_tree_t;
-
-
-#define CONTEXT_WORD 1
-#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
-#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
-#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
-
-#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
-#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
-#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
-#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
-#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
-
-#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
-#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
-#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
-#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
-
-#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
- ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
- || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
- || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
- || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
-
-#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
- ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
- || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
- || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
- || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
-
-struct re_dfastate_t
-{
- unsigned int hash;
- re_node_set nodes;
- re_node_set *entrance_nodes;
- struct re_dfastate_t **trtable;
- struct re_dfastate_t **trtable_search;
- /* If this state is a special state.
- A state is a special state if the state is the halt state, or
- a anchor. */
- unsigned int context : 2;
- unsigned int halt : 1;
- /* If this state can accept `multi byte'.
- Note that we refer to multibyte characters, and multi character
- collating elements as `multi byte'. */
- unsigned int accept_mb : 1;
- /* If this state has backreference node(s). */
- unsigned int has_backref : 1;
- unsigned int has_constraint : 1;
-};
-typedef struct re_dfastate_t re_dfastate_t;
-
-typedef struct
-{
- /* start <= node < end */
- int start;
- int end;
-} re_subexp_t;
-
-struct re_state_table_entry
-{
- int num;
- int alloc;
- re_dfastate_t **array;
-};
-
-/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */
-
-typedef struct
-{
- int next_idx;
- int alloc;
- re_dfastate_t **array;
-} state_array_t;
-
-/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */
-
-typedef struct
-{
- int node;
- int str_idx; /* The position NODE match at. */
- state_array_t path;
-} re_sub_match_last_t;
-
-/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
- And information about the node, whose type is OP_CLOSE_SUBEXP,
- corresponding to NODE is stored in LASTS. */
-
-typedef struct
-{
- int str_idx;
- int node;
- int next_last_offset;
- state_array_t *path;
- int alasts; /* Allocation size of LASTS. */
- int nlasts; /* The number of LASTS. */
- re_sub_match_last_t **lasts;
-} re_sub_match_top_t;
-
-struct re_backref_cache_entry
-{
- int node;
- int str_idx;
- int subexp_from;
- int subexp_to;
- int flag;
-};
-
-typedef struct
-{
- /* EFLAGS of the argument of regexec. */
- int eflags;
- /* Where the matching ends. */
- int match_last;
- int last_node;
- /* The string object corresponding to the input string. */
- re_string_t *input;
- /* The state log used by the matcher. */
- re_dfastate_t **state_log;
- int state_log_top;
- /* Back reference cache. */
- int nbkref_ents;
- int abkref_ents;
- struct re_backref_cache_entry *bkref_ents;
- int max_mb_elem_len;
- int nsub_tops;
- int asub_tops;
- re_sub_match_top_t **sub_tops;
-} re_match_context_t;
-
-typedef struct
-{
- int cur_bkref;
- int cls_subexp_idx;
-
- re_dfastate_t **sifted_states;
- re_dfastate_t **limited_states;
-
- re_node_set limits;
-
- int last_node;
- int last_str_idx;
- int check_subexp;
-} re_sift_context_t;
-
-struct re_fail_stack_ent_t
-{
- int idx;
- int node;
- regmatch_t *regs;
- re_node_set eps_via_nodes;
-};
-
-struct re_fail_stack_t
-{
- int num;
- int alloc;
- struct re_fail_stack_ent_t *stack;
-};
-
-struct re_dfa_t
-{
- re_bitset_ptr_t word_char;
-
- /* number of subexpressions `re_nsub' is in regex_t. */
- int subexps_alloc;
- re_subexp_t *subexps;
-
- re_token_t *nodes;
- int nodes_alloc;
- int nodes_len;
- bin_tree_t *str_tree;
- int *nexts;
- int *org_indices;
- re_node_set *edests;
- re_node_set *eclosures;
- re_node_set *inveclosures;
- struct re_state_table_entry *state_table;
- unsigned int state_hash_mask;
- re_dfastate_t *init_state;
- re_dfastate_t *init_state_word;
- re_dfastate_t *init_state_nl;
- re_dfastate_t *init_state_begbuf;
- int states_alloc;
- int init_node;
- int nbackref; /* The number of backreference in this dfa. */
- /* Bitmap expressing which backreference is used. */
- unsigned int used_bkref_map;
-#ifdef DEBUG
- char* re_str;
-#endif
- unsigned int has_plural_match : 1;
- /* If this dfa has "multibyte node", which is a backreference or
- a node which can accept multibyte character or multi character
- collating element. */
- unsigned int has_mb_node : 1;
-};
-typedef struct re_dfa_t re_dfa_t;
-
-static reg_errcode_t re_node_set_alloc (re_node_set *set, int size);
-static reg_errcode_t re_node_set_init_1 (re_node_set *set, int elem);
-static reg_errcode_t re_node_set_init_2 (re_node_set *set, int elem1,
- int elem2);
-#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
-static reg_errcode_t re_node_set_init_copy (re_node_set *dest,
- const re_node_set *src);
-static reg_errcode_t re_node_set_add_intersect (re_node_set *dest,
- const re_node_set *src1,
- const re_node_set *src2);
-static reg_errcode_t re_node_set_init_union (re_node_set *dest,
- const re_node_set *src1,
- const re_node_set *src2);
-static reg_errcode_t re_node_set_merge (re_node_set *dest,
- const re_node_set *src);
-static int re_node_set_insert (re_node_set *set, int elem);
-static int re_node_set_compare (const re_node_set *set1,
- const re_node_set *set2);
-static int re_node_set_contains (const re_node_set *set, int elem);
-static void re_node_set_remove_at (re_node_set *set, int idx);
-#define re_node_set_remove(set,id) \
- (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
-#define re_node_set_empty(p) ((p)->nelem = 0)
-#define re_node_set_free(set) re_free ((set)->elems)
-static int re_dfa_add_node (re_dfa_t *dfa, re_token_t token, int mode);
-static re_dfastate_t *re_acquire_state (reg_errcode_t *err, re_dfa_t *dfa,
- const re_node_set *nodes);
-static re_dfastate_t *re_acquire_state_context (reg_errcode_t *err,
- re_dfa_t *dfa,
- const re_node_set *nodes,
- unsigned int context);
-static void free_state (re_dfastate_t *state);
-\f
-
-typedef enum
-{
- SB_CHAR,
- MB_CHAR,
- EQUIV_CLASS,
- COLL_SYM,
- CHAR_CLASS
-} bracket_elem_type;
-
-typedef struct
-{
- bracket_elem_type type;
- union
- {
- unsigned char ch;
- unsigned char *name;
- wchar_t wch;
- } opr;
-} bracket_elem_t;
-
-
-/* Inline functions for bitset operation. */
-static inline void
-bitset_not (set)
- bitset set;
-{
- int bitset_i;
- for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
- set[bitset_i] = ~set[bitset_i];
-}
-
-static inline void
-bitset_merge (dest, src)
- bitset dest;
- const bitset src;
-{
- int bitset_i;
- for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
- dest[bitset_i] |= src[bitset_i];
-}
-
-static inline void
-bitset_not_merge (dest, src)
- bitset dest;
- const bitset src;
-{
- int i;
- for (i = 0; i < BITSET_UINTS; ++i)
- dest[i] |= ~src[i];
-}
-
-#ifdef RE_ENABLE_I18N
-/* Inline functions for re_string. */
-static inline int
-re_string_char_size_at (pstr, idx)
- const re_string_t *pstr;
- int idx;
-{
- int byte_idx;
- if (MB_CUR_MAX == 1)
- return 1;
- for (byte_idx = 1; idx + byte_idx < pstr->len; ++byte_idx)
- if (pstr->wcs[idx + byte_idx] != WEOF)
- break;
- return byte_idx;
-}
-
-static inline wint_t
-re_string_wchar_at (pstr, idx)
- const re_string_t *pstr;
- int idx;
-{
- if (MB_CUR_MAX == 1)
- return (wint_t) pstr->mbs[idx];
- return (wint_t) pstr->wcs[idx];
-}
-
-static int
-re_string_elem_size_at (pstr, idx)
- const re_string_t *pstr;
- int idx;
-{
-#ifdef _LIBC
- const unsigned char *p, *extra;
- const int32_t *table, *indirect;
- int32_t tmp;
-# include <locale/weight.h>
- uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
-
- if (nrules != 0)
- {
- table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
- extra = (const unsigned char *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
- indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
- _NL_COLLATE_INDIRECTMB);
- p = pstr->mbs + idx;
- tmp = findidx (&p);
- return p - pstr->mbs - idx;
- }
- else
-#endif /* _LIBC */
- return 1;
-}
-#endif /* RE_ENABLE_I18N */
-
-#endif /* _REGEX_INTERNAL_H */
+++ /dev/null
-/* Extended regular expression matching and search library.
- Copyright (C) 2002, 2003 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
-
-static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
- re_string_t *input, int n);
-static void match_ctx_clean (re_match_context_t *mctx);
-static void match_ctx_free (re_match_context_t *cache);
-static void match_ctx_free_subtops (re_match_context_t *mctx);
-static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
- int str_idx, int from, int to);
-static int search_cur_bkref_entry (re_match_context_t *mctx, int str_idx);
-static void match_ctx_clear_flag (re_match_context_t *mctx);
-static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
- int str_idx);
-static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
- int node, int str_idx);
-static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
- re_dfastate_t **limited_sts, int last_node,
- int last_str_idx, int check_subexp);
-static reg_errcode_t re_search_internal (const regex_t *preg,
- const char *string, int length,
- int start, int range, int stop,
- size_t nmatch, regmatch_t pmatch[],
- int eflags);
-static int re_search_2_stub (struct re_pattern_buffer *bufp,
- const char *string1, int length1,
- const char *string2, int length2,
- int start, int range, struct re_registers *regs,
- int stop, int ret_len);
-static int re_search_stub (struct re_pattern_buffer *bufp,
- const char *string, int length, int start,
- int range, int stop, struct re_registers *regs,
- int ret_len);
-static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
- int nregs, int regs_allocated);
-static inline re_dfastate_t *acquire_init_state_context (reg_errcode_t *err,
- const regex_t *preg,
- const re_match_context_t *mctx,
- int idx);
-static reg_errcode_t prune_impossible_nodes (const regex_t *preg,
- re_match_context_t *mctx);
-static int check_matching (const regex_t *preg, re_match_context_t *mctx,
- int fl_search, int fl_longest_match);
-static int check_halt_node_context (const re_dfa_t *dfa, int node,
- unsigned int context);
-static int check_halt_state_context (const regex_t *preg,
- const re_dfastate_t *state,
- const re_match_context_t *mctx, int idx);
-static void update_regs (re_dfa_t *dfa, regmatch_t *pmatch, int cur_node,
- int cur_idx, int nmatch);
-static int proceed_next_node (const regex_t *preg, int nregs, regmatch_t *regs,
- const re_match_context_t *mctx,
- int *pidx, int node, re_node_set *eps_via_nodes,
- struct re_fail_stack_t *fs);
-static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
- int str_idx, int *dests, int nregs,
- regmatch_t *regs,
- re_node_set *eps_via_nodes);
-static int pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
- regmatch_t *regs, re_node_set *eps_via_nodes);
-static reg_errcode_t set_regs (const regex_t *preg,
- const re_match_context_t *mctx,
- size_t nmatch, regmatch_t *pmatch,
- int fl_backtrack);
-static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs);
-
-#ifdef RE_ENABLE_I18N
-static int sift_states_iter_mb (const regex_t *preg,
- const re_match_context_t *mctx,
- re_sift_context_t *sctx,
- int node_idx, int str_idx, int max_str_idx);
-#endif /* RE_ENABLE_I18N */
-static reg_errcode_t sift_states_backward (const regex_t *preg,
- re_match_context_t *mctx,
- re_sift_context_t *sctx);
-static reg_errcode_t update_cur_sifted_state (const regex_t *preg,
- re_match_context_t *mctx,
- re_sift_context_t *sctx,
- int str_idx,
- re_node_set *dest_nodes);
-static reg_errcode_t add_epsilon_src_nodes (re_dfa_t *dfa,
- re_node_set *dest_nodes,
- const re_node_set *candidates);
-static reg_errcode_t sub_epsilon_src_nodes (re_dfa_t *dfa, int node,
- re_node_set *dest_nodes,
- const re_node_set *and_nodes);
-static int check_dst_limits (re_dfa_t *dfa, re_node_set *limits,
- re_match_context_t *mctx, int dst_node,
- int dst_idx, int src_node, int src_idx);
-static int check_dst_limits_calc_pos (re_dfa_t *dfa, re_match_context_t *mctx,
- int limit, re_node_set *eclosures,
- int subexp_idx, int node, int str_idx);
-static reg_errcode_t check_subexp_limits (re_dfa_t *dfa,
- re_node_set *dest_nodes,
- const re_node_set *candidates,
- re_node_set *limits,
- struct re_backref_cache_entry *bkref_ents,
- int str_idx);
-static reg_errcode_t sift_states_bkref (const regex_t *preg,
- re_match_context_t *mctx,
- re_sift_context_t *sctx,
- int str_idx, re_node_set *dest_nodes);
-static reg_errcode_t clean_state_log_if_need (re_match_context_t *mctx,
- int next_state_log_idx);
-static reg_errcode_t merge_state_array (re_dfa_t *dfa, re_dfastate_t **dst,
- re_dfastate_t **src, int num);
-static re_dfastate_t *transit_state (reg_errcode_t *err, const regex_t *preg,
- re_match_context_t *mctx,
- re_dfastate_t *state, int fl_search);
-static reg_errcode_t check_subexp_matching_top (re_dfa_t *dfa,
- re_match_context_t *mctx,
- re_node_set *cur_nodes,
- int str_idx);
-static re_dfastate_t *transit_state_sb (reg_errcode_t *err, const regex_t *preg,
- re_dfastate_t *pstate,
- int fl_search,
- re_match_context_t *mctx);
-#ifdef RE_ENABLE_I18N
-static reg_errcode_t transit_state_mb (const regex_t *preg,
- re_dfastate_t *pstate,
- re_match_context_t *mctx);
-#endif /* RE_ENABLE_I18N */
-static reg_errcode_t transit_state_bkref (const regex_t *preg,
- re_node_set *nodes,
- re_match_context_t *mctx);
-static reg_errcode_t get_subexp (const regex_t *preg, re_match_context_t *mctx,
- int bkref_node, int bkref_str_idx);
-static reg_errcode_t get_subexp_sub (const regex_t *preg,
- re_match_context_t *mctx,
- re_sub_match_top_t *sub_top,
- re_sub_match_last_t *sub_last,
- int bkref_node, int bkref_str);
-static int find_subexp_node (re_dfa_t *dfa, re_node_set *nodes,
- int subexp_idx, int fl_open);
-static reg_errcode_t check_arrival (const regex_t *preg,
- re_match_context_t *mctx,
- state_array_t *path, int top_node,
- int top_str, int last_node, int last_str,
- int fl_open);
-static reg_errcode_t check_arrival_add_next_nodes (const regex_t *preg,
- re_dfa_t *dfa,
- re_match_context_t *mctx,
- int str_idx,
- re_node_set *cur_nodes,
- re_node_set *next_nodes);
-static reg_errcode_t check_arrival_expand_ecl (re_dfa_t *dfa,
- re_node_set *cur_nodes,
- int ex_subexp, int fl_open);
-static reg_errcode_t check_arrival_expand_ecl_sub (re_dfa_t *dfa,
- re_node_set *dst_nodes,
- int target, int ex_subexp,
- int fl_open);
-static reg_errcode_t expand_bkref_cache (const regex_t *preg,
- re_match_context_t *mctx,
- re_node_set *cur_nodes, int cur_str,
- int last_str, int subexp_num,
- int fl_open);
-static re_dfastate_t **build_trtable (const regex_t *dfa,
- const re_dfastate_t *state,
- int fl_search);
-#ifdef RE_ENABLE_I18N
-static int check_node_accept_bytes (const regex_t *preg, int node_idx,
- const re_string_t *input, int idx);
-# ifdef _LIBC
-static unsigned int find_collation_sequence_value (const unsigned char *mbs,
- size_t name_len);
-# endif /* _LIBC */
-#endif /* RE_ENABLE_I18N */
-static int group_nodes_into_DFAstates (const regex_t *dfa,
- const re_dfastate_t *state,
- re_node_set *states_node,
- bitset *states_ch);
-static int check_node_accept (const regex_t *preg, const re_token_t *node,
- const re_match_context_t *mctx, int idx);
-static reg_errcode_t extend_buffers (re_match_context_t *mctx);
-\f
-/* Entry point for POSIX code. */
-
-/* regexec searches for a given pattern, specified by PREG, in the
- string STRING.
-
- If NMATCH is zero or REG_NOSUB was set in the cflags argument to
- `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
- least NMATCH elements, and we set them to the offsets of the
- corresponding matched substrings.
-
- EFLAGS specifies `execution flags' which affect matching: if
- REG_NOTBOL is set, then ^ does not match at the beginning of the
- string; if REG_NOTEOL is set, then $ does not match at the end.
-
- We return 0 if we find a match and REG_NOMATCH if not. */
-
-int
-regexec (preg, string, nmatch, pmatch, eflags)
- const regex_t *__restrict preg;
- const char *__restrict string;
- size_t nmatch;
- regmatch_t pmatch[];
- int eflags;
-{
- reg_errcode_t err;
- int length = strlen (string);
- if (preg->no_sub)
- err = re_search_internal (preg, string, length, 0, length, length, 0,
- NULL, eflags);
- else
- err = re_search_internal (preg, string, length, 0, length, length, nmatch,
- pmatch, eflags);
- return err != REG_NOERROR;
-}
-#ifdef _LIBC
-weak_alias (__regexec, regexec)
-#endif
-
-/* Entry points for GNU code. */
-
-/* re_match, re_search, re_match_2, re_search_2
-
- The former two functions operate on STRING with length LENGTH,
- while the later two operate on concatenation of STRING1 and STRING2
- with lengths LENGTH1 and LENGTH2, respectively.
-
- re_match() matches the compiled pattern in BUFP against the string,
- starting at index START.
-
- re_search() first tries matching at index START, then it tries to match
- starting from index START + 1, and so on. The last start position tried
- is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same
- way as re_match().)
-
- The parameter STOP of re_{match,search}_2 specifies that no match exceeding
- the first STOP characters of the concatenation of the strings should be
- concerned.
-
- If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
- and all groups is stroed in REGS. (For the "_2" variants, the offsets are
- computed relative to the concatenation, not relative to the individual
- strings.)
-
- On success, re_match* functions return the length of the match, re_search*
- return the position of the start of the match. Return value -1 means no
- match was found and -2 indicates an internal error. */
-
-int
-re_match (bufp, string, length, start, regs)
- struct re_pattern_buffer *bufp;
- const char *string;
- int length, start;
- struct re_registers *regs;
-{
- return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
-}
-#ifdef _LIBC
-weak_alias (__re_match, re_match)
-#endif
-
-int
-re_search (bufp, string, length, start, range, regs)
- struct re_pattern_buffer *bufp;
- const char *string;
- int length, start, range;
- struct re_registers *regs;
-{
- return re_search_stub (bufp, string, length, start, range, length, regs, 0);
-}
-#ifdef _LIBC
-weak_alias (__re_search, re_search)
-#endif
-
-int
-re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop)
- struct re_pattern_buffer *bufp;
- const char *string1, *string2;
- int length1, length2, start, stop;
- struct re_registers *regs;
-{
- return re_search_2_stub (bufp, string1, length1, string2, length2,
- start, 0, regs, stop, 1);
-}
-#ifdef _LIBC
-weak_alias (__re_match_2, re_match_2)
-#endif
-
-int
-re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop)
- struct re_pattern_buffer *bufp;
- const char *string1, *string2;
- int length1, length2, start, range, stop;
- struct re_registers *regs;
-{
- return re_search_2_stub (bufp, string1, length1, string2, length2,
- start, range, regs, stop, 0);
-}
-#ifdef _LIBC
-weak_alias (__re_search_2, re_search_2)
-#endif
-
-static int
-re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs,
- stop, ret_len)
- struct re_pattern_buffer *bufp;
- const char *string1, *string2;
- int length1, length2, start, range, stop, ret_len;
- struct re_registers *regs;
-{
- const char *str;
- int rval;
- int len = length1 + length2;
- int free_str = 0;
-
- if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
- return -2;
-
- /* Concatenate the strings. */
- if (length2 > 0)
- if (length1 > 0)
- {
- char *s = re_malloc (char, len);
-
- if (BE (s == NULL, 0))
- return -2;
- memcpy (s, string1, length1);
- memcpy (s + length1, string2, length2);
- str = s;
- free_str = 1;
- }
- else
- str = string2;
- else
- str = string1;
-
- rval = re_search_stub (bufp, str, len, start, range, stop, regs,
- ret_len);
- if (free_str)
- re_free ((char *) str);
- return rval;
-}
-
-/* The parameters have the same meaning as those of re_search.
- Additional parameters:
- If RET_LEN is nonzero the length of the match is returned (re_match style);
- otherwise the position of the match is returned. */
-
-static int
-re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
- struct re_pattern_buffer *bufp;
- const char *string;
- int length, start, range, stop, ret_len;
- struct re_registers *regs;
-{
- reg_errcode_t result;
- regmatch_t *pmatch;
- int nregs, rval;
- int eflags = 0;
-
- /* Check for out-of-range. */
- if (BE (start < 0 || start > length, 0))
- return -1;
- if (BE (start + range > length, 0))
- range = length - start;
- else if (BE (start + range < 0, 0))
- range = -start;
-
- eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
- eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
-
- /* Compile fastmap if we haven't yet. */
- if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
- re_compile_fastmap (bufp);
-
- if (BE (bufp->no_sub, 0))
- regs = NULL;
-
- /* We need at least 1 register. */
- if (regs == NULL)
- nregs = 1;
- else if (BE (bufp->regs_allocated == REGS_FIXED &&
- regs->num_regs < bufp->re_nsub + 1, 0))
- {
- nregs = regs->num_regs;
- if (BE (nregs < 1, 0))
- {
- /* Nothing can be copied to regs. */
- regs = NULL;
- nregs = 1;
- }
- }
- else
- nregs = bufp->re_nsub + 1;
- pmatch = re_malloc (regmatch_t, nregs);
- if (BE (pmatch == NULL, 0))
- return -2;
-
- result = re_search_internal (bufp, string, length, start, range, stop,
- nregs, pmatch, eflags);
-
- rval = 0;
-
- /* I hope we needn't fill ther regs with -1's when no match was found. */
- if (result != REG_NOERROR)
- rval = -1;
- else if (regs != NULL)
- {
- /* If caller wants register contents data back, copy them. */
- bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
- bufp->regs_allocated);
- if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
- rval = -2;
- }
-
- if (BE (rval == 0, 1))
- {
- if (ret_len)
- {
- assert (pmatch[0].rm_so == start);
- rval = pmatch[0].rm_eo - start;
- }
- else
- rval = pmatch[0].rm_so;
- }
- re_free (pmatch);
- return rval;
-}
-
-static unsigned
-re_copy_regs (regs, pmatch, nregs, regs_allocated)
- struct re_registers *regs;
- regmatch_t *pmatch;
- int nregs, regs_allocated;
-{
- int rval = REGS_REALLOCATE;
- int i;
- int need_regs = nregs + 1;
- /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
- uses. */
-
- /* Have the register data arrays been allocated? */
- if (regs_allocated == REGS_UNALLOCATED)
- { /* No. So allocate them with malloc. */
- regs->start = re_malloc (regoff_t, need_regs);
- if (BE (regs->start == NULL, 0))
- return REGS_UNALLOCATED;
- regs->end = re_malloc (regoff_t, need_regs);
- if (BE (regs->end == NULL, 0))
- {
- re_free (regs->start);
- return REGS_UNALLOCATED;
- }
- regs->num_regs = need_regs;
- }
- else if (regs_allocated == REGS_REALLOCATE)
- { /* Yes. If we need more elements than were already
- allocated, reallocate them. If we need fewer, just
- leave it alone. */
- if (need_regs > regs->num_regs)
- {
- regs->start = re_realloc (regs->start, regoff_t, need_regs);
- if (BE (regs->start == NULL, 0))
- {
- if (regs->end != NULL)
- re_free (regs->end);
- return REGS_UNALLOCATED;
- }
- regs->end = re_realloc (regs->end, regoff_t, need_regs);
- if (BE (regs->end == NULL, 0))
- {
- re_free (regs->start);
- return REGS_UNALLOCATED;
- }
- regs->num_regs = need_regs;
- }
- }
- else
- {
- assert (regs_allocated == REGS_FIXED);
- /* This function may not be called with REGS_FIXED and nregs too big. */
- assert (regs->num_regs >= nregs);
- rval = REGS_FIXED;
- }
-
- /* Copy the regs. */
- for (i = 0; i < nregs; ++i)
- {
- regs->start[i] = pmatch[i].rm_so;
- regs->end[i] = pmatch[i].rm_eo;
- }
- for ( ; i < regs->num_regs; ++i)
- regs->start[i] = regs->end[i] = -1;
-
- return rval;
-}
-
-/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
- ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
- this memory for recording register information. STARTS and ENDS
- must be allocated using the malloc library routine, and must each
- be at least NUM_REGS * sizeof (regoff_t) bytes long.
-
- If NUM_REGS == 0, then subsequent matches should allocate their own
- register data.
-
- Unless this function is called, the first search or match using
- PATTERN_BUFFER will allocate its own register data, without
- freeing the old data. */
-
-void
-re_set_registers (bufp, regs, num_regs, starts, ends)
- struct re_pattern_buffer *bufp;
- struct re_registers *regs;
- unsigned num_regs;
- regoff_t *starts, *ends;
-{
- if (num_regs)
- {
- bufp->regs_allocated = REGS_REALLOCATE;
- regs->num_regs = num_regs;
- regs->start = starts;
- regs->end = ends;
- }
- else
- {
- bufp->regs_allocated = REGS_UNALLOCATED;
- regs->num_regs = 0;
- regs->start = regs->end = (regoff_t *) 0;
- }
-}
-#ifdef _LIBC
-weak_alias (__re_set_registers, re_set_registers)
-#endif
-\f
-/* Entry points compatible with 4.2 BSD regex library. We don't define
- them unless specifically requested. */
-
-#if defined _REGEX_RE_COMP || defined _LIBC
-int
-# ifdef _LIBC
-weak_function
-# endif
-re_exec (s)
- const char *s;
-{
- return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
-}
-#endif /* _REGEX_RE_COMP */
-\f
-static re_node_set empty_set;
-
-/* Internal entry point. */
-
-/* Searches for a compiled pattern PREG in the string STRING, whose
- length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same
- mingings with regexec. START, and RANGE have the same meanings
- with re_search.
- Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
- otherwise return the error code.
- Note: We assume front end functions already check ranges.
- (START + RANGE >= 0 && START + RANGE <= LENGTH) */
-
-static reg_errcode_t
-re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
- eflags)
- const regex_t *preg;
- const char *string;
- int length, start, range, stop, eflags;
- size_t nmatch;
- regmatch_t pmatch[];
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
- re_string_t input;
- int left_lim, right_lim, incr;
- int fl_longest_match, match_first, match_last = -1;
- int fast_translate, sb;
- re_match_context_t mctx;
- char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate
- && range && !preg->can_be_null) ? preg->fastmap : NULL);
-
- /* Check if the DFA haven't been compiled. */
- if (BE (preg->used == 0 || dfa->init_state == NULL
- || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
- || dfa->init_state_begbuf == NULL, 0))
- return REG_NOMATCH;
-
- re_node_set_init_empty (&empty_set);
- memset (&mctx, '\0', sizeof (re_match_context_t));
-
- /* We must check the longest matching, if nmatch > 0. */
- fl_longest_match = (nmatch != 0 || dfa->nbackref);
-
- err = re_string_allocate (&input, string, length, dfa->nodes_len + 1,
- preg->translate, preg->syntax & RE_ICASE);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- input.stop = stop;
-
- err = match_ctx_init (&mctx, eflags, &input, dfa->nbackref * 2);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
-
- /* We will log all the DFA states through which the dfa pass,
- if nmatch > 1, or this dfa has "multibyte node", which is a
- back-reference or a node which can accept multibyte character or
- multi character collating element. */
- if (nmatch > 1 || dfa->has_mb_node)
- {
- mctx.state_log = re_malloc (re_dfastate_t *, dfa->nodes_len + 1);
- if (BE (mctx.state_log == NULL, 0))
- {
- err = REG_ESPACE;
- goto free_return;
- }
- }
- else
- mctx.state_log = NULL;
-
-#ifdef DEBUG
- /* We assume front-end functions already check them. */
- assert (start + range >= 0 && start + range <= length);
-#endif
-
- match_first = start;
- input.tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
- : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
-
- /* Check incrementally whether of not the input string match. */
- incr = (range < 0) ? -1 : 1;
- left_lim = (range < 0) ? start + range : start;
- right_lim = (range < 0) ? start : start + range;
- sb = MB_CUR_MAX == 1;
- fast_translate = sb || !(preg->syntax & RE_ICASE || preg->translate);
-
- for (;;)
- {
- /* At first get the current byte from input string. */
- if (fastmap)
- {
- if (BE (fast_translate, 1))
- {
- unsigned RE_TRANSLATE_TYPE t
- = (unsigned RE_TRANSLATE_TYPE) preg->translate;
- if (BE (range >= 0, 1))
- {
- if (BE (t != NULL, 0))
- {
- while (BE (match_first < right_lim, 1)
- && !fastmap[t[(unsigned char) string[match_first]]])
- ++match_first;
- }
- else
- {
- while (BE (match_first < right_lim, 1)
- && !fastmap[(unsigned char) string[match_first]])
- ++match_first;
- }
- if (BE (match_first == right_lim, 0))
- {
- int ch = match_first >= length
- ? 0 : (unsigned char) string[match_first];
- if (!fastmap[t ? t[ch] : ch])
- break;
- }
- }
- else
- {
- while (match_first >= left_lim)
- {
- int ch = match_first >= length
- ? 0 : (unsigned char) string[match_first];
- if (fastmap[t ? t[ch] : ch])
- break;
- --match_first;
- }
- if (match_first < left_lim)
- break;
- }
- }
- else
- {
- int ch;
-
- do
- {
- /* In this case, we can't determine easily the current byte,
- since it might be a component byte of a multibyte
- character. Then we use the constructed buffer
- instead. */
- /* If MATCH_FIRST is out of the valid range, reconstruct the
- buffers. */
- if (input.raw_mbs_idx + input.valid_len <= match_first
- || match_first < input.raw_mbs_idx)
- {
- err = re_string_reconstruct (&input, match_first, eflags,
- preg->newline_anchor);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- }
- /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
- Note that MATCH_FIRST must not be smaller than 0. */
- ch = ((match_first >= length) ? 0
- : re_string_byte_at (&input,
- match_first - input.raw_mbs_idx));
- if (fastmap[ch])
- break;
- match_first += incr;
- }
- while (match_first >= left_lim && match_first <= right_lim);
- if (! fastmap[ch])
- break;
- }
- }
-
- /* Reconstruct the buffers so that the matcher can assume that
- the matching starts from the begining of the buffer. */
- err = re_string_reconstruct (&input, match_first, eflags,
- preg->newline_anchor);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
-#ifdef RE_ENABLE_I18N
- /* Eliminate it when it is a component of a multibyte character
- and isn't the head of a multibyte character. */
- if (sb || re_string_first_byte (&input, 0))
-#endif
- {
- /* It seems to be appropriate one, then use the matcher. */
- /* We assume that the matching starts from 0. */
- mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
- match_last = check_matching (preg, &mctx, 0, fl_longest_match);
- if (match_last != -1)
- {
- if (BE (match_last == -2, 0))
- {
- err = REG_ESPACE;
- goto free_return;
- }
- else
- {
- mctx.match_last = match_last;
- if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
- {
- re_dfastate_t *pstate = mctx.state_log[match_last];
- mctx.last_node = check_halt_state_context (preg, pstate,
- &mctx, match_last);
- }
- if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
- || dfa->nbackref)
- {
- err = prune_impossible_nodes (preg, &mctx);
- if (err == REG_NOERROR)
- break;
- if (BE (err != REG_NOMATCH, 0))
- goto free_return;
- }
- else
- break; /* We found a matching. */
- }
- }
- match_ctx_clean (&mctx);
- }
- /* Update counter. */
- match_first += incr;
- if (match_first < left_lim || right_lim < match_first)
- break;
- }
-
- /* Set pmatch[] if we need. */
- if (match_last != -1 && nmatch > 0)
- {
- int reg_idx;
-
- /* Initialize registers. */
- for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
- pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
-
- /* Set the points where matching start/end. */
- pmatch[0].rm_so = 0;
- pmatch[0].rm_eo = mctx.match_last;
-
- if (!preg->no_sub && nmatch > 1)
- {
- err = set_regs (preg, &mctx, nmatch, pmatch,
- dfa->has_plural_match && dfa->nbackref > 0);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- }
-
- /* At last, add the offset to the each registers, since we slided
- the buffers so that We can assume that the matching starts from 0. */
- for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
- if (pmatch[reg_idx].rm_so != -1)
- {
- pmatch[reg_idx].rm_so += match_first;
- pmatch[reg_idx].rm_eo += match_first;
- }
- }
- err = (match_last == -1) ? REG_NOMATCH : REG_NOERROR;
- free_return:
- re_free (mctx.state_log);
- if (dfa->nbackref)
- match_ctx_free (&mctx);
- re_string_destruct (&input);
- return err;
-}
-
-static reg_errcode_t
-prune_impossible_nodes (preg, mctx)
- const regex_t *preg;
- re_match_context_t *mctx;
-{
- int halt_node, match_last;
- reg_errcode_t ret;
- re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
- re_dfastate_t **sifted_states;
- re_dfastate_t **lim_states = NULL;
- re_sift_context_t sctx;
-#ifdef DEBUG
- assert (mctx->state_log != NULL);
-#endif
- match_last = mctx->match_last;
- halt_node = mctx->last_node;
- sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
- if (BE (sifted_states == NULL, 0))
- {
- ret = REG_ESPACE;
- goto free_return;
- }
- if (dfa->nbackref)
- {
- lim_states = re_malloc (re_dfastate_t *, match_last + 1);
- if (BE (lim_states == NULL, 0))
- {
- ret = REG_ESPACE;
- goto free_return;
- }
- while (1)
- {
- memset (lim_states, '\0',
- sizeof (re_dfastate_t *) * (match_last + 1));
- match_ctx_clear_flag (mctx);
- sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
- match_last, 0);
- ret = sift_states_backward (preg, mctx, &sctx);
- re_node_set_free (&sctx.limits);
- if (BE (ret != REG_NOERROR, 0))
- goto free_return;
- if (sifted_states[0] != NULL || lim_states[0] != NULL)
- break;
- do
- {
- --match_last;
- if (match_last < 0)
- {
- ret = REG_NOMATCH;
- goto free_return;
- }
- } while (!mctx->state_log[match_last]->halt);
- halt_node = check_halt_state_context (preg,
- mctx->state_log[match_last],
- mctx, match_last);
- }
- ret = merge_state_array (dfa, sifted_states, lim_states,
- match_last + 1);
- re_free (lim_states);
- lim_states = NULL;
- if (BE (ret != REG_NOERROR, 0))
- goto free_return;
- }
- else
- {
- sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
- match_last, 0);
- ret = sift_states_backward (preg, mctx, &sctx);
- re_node_set_free (&sctx.limits);
- if (BE (ret != REG_NOERROR, 0))
- goto free_return;
- }
- re_free (mctx->state_log);
- mctx->state_log = sifted_states;
- sifted_states = NULL;
- mctx->last_node = halt_node;
- mctx->match_last = match_last;
- ret = REG_NOERROR;
- free_return:
- re_free (sifted_states);
- re_free (lim_states);
- return ret;
-}
-
-/* Acquire an initial state and return it.
- We must select appropriate initial state depending on the context,
- since initial states may have constraints like "\<", "^", etc.. */
-
-static inline re_dfastate_t *
-acquire_init_state_context (err, preg, mctx, idx)
- reg_errcode_t *err;
- const regex_t *preg;
- const re_match_context_t *mctx;
- int idx;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
-
- *err = REG_NOERROR;
- if (dfa->init_state->has_constraint)
- {
- unsigned int context;
- context = re_string_context_at (mctx->input, idx - 1, mctx->eflags,
- preg->newline_anchor);
- if (IS_WORD_CONTEXT (context))
- return dfa->init_state_word;
- else if (IS_ORDINARY_CONTEXT (context))
- return dfa->init_state;
- else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
- return dfa->init_state_begbuf;
- else if (IS_NEWLINE_CONTEXT (context))
- return dfa->init_state_nl;
- else if (IS_BEGBUF_CONTEXT (context))
- {
- /* It is relatively rare case, then calculate on demand. */
- return re_acquire_state_context (err, dfa,
- dfa->init_state->entrance_nodes,
- context);
- }
- else
- /* Must not happen? */
- return dfa->init_state;
- }
- else
- return dfa->init_state;
-}
-
-/* Check whether the regular expression match input string INPUT or not,
- and return the index where the matching end, return -1 if not match,
- or return -2 in case of an error.
- FL_SEARCH means we must search where the matching starts,
- FL_LONGEST_MATCH means we want the POSIX longest matching.
- Note that the matcher assume that the maching starts from the current
- index of the buffer. */
-
-static int
-check_matching (preg, mctx, fl_search, fl_longest_match)
- const regex_t *preg;
- re_match_context_t *mctx;
- int fl_search, fl_longest_match;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- reg_errcode_t err;
- int match = 0;
- int match_last = -1;
- int cur_str_idx = re_string_cur_idx (mctx->input);
- re_dfastate_t *cur_state;
-
- cur_state = acquire_init_state_context (&err, preg, mctx, cur_str_idx);
- /* An initial state must not be NULL(invalid state). */
- if (BE (cur_state == NULL, 0))
- return -2;
- if (mctx->state_log != NULL)
- mctx->state_log[cur_str_idx] = cur_state;
-
- /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
- later. E.g. Processing back references. */
- if (dfa->nbackref)
- {
- err = check_subexp_matching_top (dfa, mctx, &cur_state->nodes, 0);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
-
- if (cur_state->has_backref)
- {
- err = transit_state_bkref (preg, &cur_state->nodes, mctx);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
-
- /* If the RE accepts NULL string. */
- if (cur_state->halt)
- {
- if (!cur_state->has_constraint
- || check_halt_state_context (preg, cur_state, mctx, cur_str_idx))
- {
- if (!fl_longest_match)
- return cur_str_idx;
- else
- {
- match_last = cur_str_idx;
- match = 1;
- }
- }
- }
-
- while (!re_string_eoi (mctx->input))
- {
- cur_state = transit_state (&err, preg, mctx, cur_state,
- fl_search && !match);
- if (cur_state == NULL) /* Reached at the invalid state or an error. */
- {
- cur_str_idx = re_string_cur_idx (mctx->input);
- if (BE (err != REG_NOERROR, 0))
- return -2;
- if (fl_search && !match)
- {
- /* Restart from initial state, since we are searching
- the point from where matching start. */
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX == 1
- || re_string_first_byte (mctx->input, cur_str_idx))
-#endif /* RE_ENABLE_I18N */
- cur_state = acquire_init_state_context (&err, preg, mctx,
- cur_str_idx);
- if (BE (cur_state == NULL && err != REG_NOERROR, 0))
- return -2;
- if (mctx->state_log != NULL)
- mctx->state_log[cur_str_idx] = cur_state;
- }
- else if (!fl_longest_match && match)
- break;
- else /* (fl_longest_match && match) || (!fl_search && !match) */
- {
- if (mctx->state_log == NULL)
- break;
- else
- {
- int max = mctx->state_log_top;
- for (; cur_str_idx <= max; ++cur_str_idx)
- if (mctx->state_log[cur_str_idx] != NULL)
- break;
- if (cur_str_idx > max)
- break;
- }
- }
- }
-
- if (cur_state != NULL && cur_state->halt)
- {
- /* Reached at a halt state.
- Check the halt state can satisfy the current context. */
- if (!cur_state->has_constraint
- || check_halt_state_context (preg, cur_state, mctx,
- re_string_cur_idx (mctx->input)))
- {
- /* We found an appropriate halt state. */
- match_last = re_string_cur_idx (mctx->input);
- match = 1;
- if (!fl_longest_match)
- break;
- }
- }
- }
- return match_last;
-}
-
-/* Check NODE match the current context. */
-
-static int check_halt_node_context (dfa, node, context)
- const re_dfa_t *dfa;
- int node;
- unsigned int context;
-{
- re_token_type_t type = dfa->nodes[node].type;
- unsigned int constraint = dfa->nodes[node].constraint;
- if (type != END_OF_RE)
- return 0;
- if (!constraint)
- return 1;
- if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
- return 0;
- return 1;
-}
-
-/* Check the halt state STATE match the current context.
- Return 0 if not match, if the node, STATE has, is a halt node and
- match the context, return the node. */
-
-static int
-check_halt_state_context (preg, state, mctx, idx)
- const regex_t *preg;
- const re_dfastate_t *state;
- const re_match_context_t *mctx;
- int idx;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- int i;
- unsigned int context;
-#ifdef DEBUG
- assert (state->halt);
-#endif
- context = re_string_context_at (mctx->input, idx, mctx->eflags,
- preg->newline_anchor);
- for (i = 0; i < state->nodes.nelem; ++i)
- if (check_halt_node_context (dfa, state->nodes.elems[i], context))
- return state->nodes.elems[i];
- return 0;
-}
-
-/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
- corresponding to the DFA).
- Return the destination node, and update EPS_VIA_NODES, return -1 in case
- of errors. */
-
-static int
-proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
- const regex_t *preg;
- regmatch_t *regs;
- const re_match_context_t *mctx;
- int nregs, *pidx, node;
- re_node_set *eps_via_nodes;
- struct re_fail_stack_t *fs;
-{
- re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
- int i, err, dest_node;
- dest_node = -1;
- if (IS_EPSILON_NODE (dfa->nodes[node].type))
- {
- re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
- int ndest, dest_nodes[2];
- err = re_node_set_insert (eps_via_nodes, node);
- if (BE (err < 0, 0))
- return -1;
- /* Pick up valid destinations. */
- for (ndest = 0, i = 0; i < dfa->edests[node].nelem; ++i)
- {
- int candidate = dfa->edests[node].elems[i];
- if (!re_node_set_contains (cur_nodes, candidate))
- continue;
- dest_nodes[0] = (ndest == 0) ? candidate : dest_nodes[0];
- dest_nodes[1] = (ndest == 1) ? candidate : dest_nodes[1];
- ++ndest;
- }
- if (ndest <= 1)
- return ndest == 0 ? -1 : (ndest == 1 ? dest_nodes[0] : 0);
- /* In order to avoid infinite loop like "(a*)*". */
- if (re_node_set_contains (eps_via_nodes, dest_nodes[0]))
- return dest_nodes[1];
- if (fs != NULL)
- push_fail_stack (fs, *pidx, dest_nodes, nregs, regs, eps_via_nodes);
- return dest_nodes[0];
- }
- else
- {
- int naccepted = 0;
- re_token_type_t type = dfa->nodes[node].type;
-
-#ifdef RE_ENABLE_I18N
- if (ACCEPT_MB_NODE (type))
- naccepted = check_node_accept_bytes (preg, node, mctx->input, *pidx);
- else
-#endif /* RE_ENABLE_I18N */
- if (type == OP_BACK_REF)
- {
- int subexp_idx = dfa->nodes[node].opr.idx;
- naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
- if (fs != NULL)
- {
- if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
- return -1;
- else if (naccepted)
- {
- char *buf = (char *) re_string_get_buffer (mctx->input);
- if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
- naccepted) != 0)
- return -1;
- }
- }
-
- if (naccepted == 0)
- {
- err = re_node_set_insert (eps_via_nodes, node);
- if (BE (err < 0, 0))
- return -2;
- dest_node = dfa->edests[node].elems[0];
- if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
- dest_node))
- return dest_node;
- }
- }
-
- if (naccepted != 0
- || check_node_accept (preg, dfa->nodes + node, mctx, *pidx))
- {
- dest_node = dfa->nexts[node];
- *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
- if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
- || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
- dest_node)))
- return -1;
- re_node_set_empty (eps_via_nodes);
- return dest_node;
- }
- }
- return -1;
-}
-
-static reg_errcode_t
-push_fail_stack (fs, str_idx, dests, nregs, regs, eps_via_nodes)
- struct re_fail_stack_t *fs;
- int str_idx, *dests, nregs;
- regmatch_t *regs;
- re_node_set *eps_via_nodes;
-{
- reg_errcode_t err;
- int num = fs->num++;
- if (fs->num == fs->alloc)
- {
- struct re_fail_stack_ent_t *new_array;
- fs->alloc *= 2;
- new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
- * fs->alloc));
- if (new_array == NULL)
- return REG_ESPACE;
- fs->stack = new_array;
- }
- fs->stack[num].idx = str_idx;
- fs->stack[num].node = dests[1];
- fs->stack[num].regs = re_malloc (regmatch_t, nregs);
- memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
- err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
- return err;
-}
-
-static int
-pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes)
- struct re_fail_stack_t *fs;
- int *pidx, nregs;
- regmatch_t *regs;
- re_node_set *eps_via_nodes;
-{
- int num = --fs->num;
- assert (num >= 0);
- *pidx = fs->stack[num].idx;
- memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
- re_node_set_free (eps_via_nodes);
- re_free (fs->stack[num].regs);
- *eps_via_nodes = fs->stack[num].eps_via_nodes;
- return fs->stack[num].node;
-}
-
-/* Set the positions where the subexpressions are starts/ends to registers
- PMATCH.
- Note: We assume that pmatch[0] is already set, and
- pmatch[i].rm_so == pmatch[i].rm_eo == -1 (i > 1). */
-
-static reg_errcode_t
-set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
- const regex_t *preg;
- const re_match_context_t *mctx;
- size_t nmatch;
- regmatch_t *pmatch;
- int fl_backtrack;
-{
- re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
- int idx, cur_node, real_nmatch;
- re_node_set eps_via_nodes;
- struct re_fail_stack_t *fs;
- struct re_fail_stack_t fs_body = {0, 2, NULL};
-#ifdef DEBUG
- assert (nmatch > 1);
- assert (mctx->state_log != NULL);
-#endif
- if (fl_backtrack)
- {
- fs = &fs_body;
- fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
- }
- else
- fs = NULL;
- cur_node = dfa->init_node;
- real_nmatch = (nmatch <= preg->re_nsub) ? nmatch : preg->re_nsub + 1;
- re_node_set_init_empty (&eps_via_nodes);
- for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
- {
- update_regs (dfa, pmatch, cur_node, idx, real_nmatch);
- if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
- {
- int reg_idx;
- if (fs)
- {
- for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
- if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
- break;
- if (reg_idx == nmatch)
- {
- re_node_set_free (&eps_via_nodes);
- return free_fail_stack_return (fs);
- }
- cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
- &eps_via_nodes);
- }
- else
- {
- re_node_set_free (&eps_via_nodes);
- return REG_NOERROR;
- }
- }
-
- /* Proceed to next node. */
- cur_node = proceed_next_node (preg, nmatch, pmatch, mctx, &idx, cur_node,
- &eps_via_nodes, fs);
-
- if (BE (cur_node < 0, 0))
- {
- if (cur_node == -2)
- return REG_ESPACE;
- if (fs)
- cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
- &eps_via_nodes);
- else
- {
- re_node_set_free (&eps_via_nodes);
- return REG_NOMATCH;
- }
- }
- }
- re_node_set_free (&eps_via_nodes);
- return free_fail_stack_return (fs);
-}
-
-static reg_errcode_t
-free_fail_stack_return (fs)
- struct re_fail_stack_t *fs;
-{
- if (fs)
- {
- int fs_idx;
- for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
- {
- re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
- re_free (fs->stack[fs_idx].regs);
- }
- re_free (fs->stack);
- }
- return REG_NOERROR;
-}
-
-static void
-update_regs (dfa, pmatch, cur_node, cur_idx, nmatch)
- re_dfa_t *dfa;
- regmatch_t *pmatch;
- int cur_node, cur_idx, nmatch;
-{
- int type = dfa->nodes[cur_node].type;
- int reg_num;
- if (type != OP_OPEN_SUBEXP && type != OP_CLOSE_SUBEXP)
- return;
- reg_num = dfa->nodes[cur_node].opr.idx + 1;
- if (reg_num >= nmatch)
- return;
- if (type == OP_OPEN_SUBEXP)
- {
- /* We are at the first node of this sub expression. */
- pmatch[reg_num].rm_so = cur_idx;
- pmatch[reg_num].rm_eo = -1;
- }
- else if (type == OP_CLOSE_SUBEXP)
- /* We are at the first node of this sub expression. */
- pmatch[reg_num].rm_eo = cur_idx;
-}
-
-#define NUMBER_OF_STATE 1
-
-/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
- and sift the nodes in each states according to the following rules.
- Updated state_log will be wrote to STATE_LOG.
-
- Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
- 1. When STR_IDX == MATCH_LAST(the last index in the state_log):
- If `a' isn't the LAST_NODE and `a' can't epsilon transit to
- the LAST_NODE, we throw away the node `a'.
- 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
- string `s' and transit to `b':
- i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
- away the node `a'.
- ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
- throwed away, we throw away the node `a'.
- 3. When 0 <= STR_IDX < n and 'a' epsilon transit to 'b':
- i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
- node `a'.
- ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is throwed away,
- we throw away the node `a'. */
-
-#define STATE_NODE_CONTAINS(state,node) \
- ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
-
-static reg_errcode_t
-sift_states_backward (preg, mctx, sctx)
- const regex_t *preg;
- re_match_context_t *mctx;
- re_sift_context_t *sctx;
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
- int null_cnt = 0;
- int str_idx = sctx->last_str_idx;
- re_node_set cur_dest;
- re_node_set *cur_src; /* Points the state_log[str_idx]->nodes */
-
-#ifdef DEBUG
- assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
-#endif
- cur_src = &mctx->state_log[str_idx]->nodes;
-
- /* Build sifted state_log[str_idx]. It has the nodes which can epsilon
- transit to the last_node and the last_node itself. */
- err = re_node_set_init_1 (&cur_dest, sctx->last_node);
- if (BE (err != REG_NOERROR, 0))
- return err;
- err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
-
- /* Then check each states in the state_log. */
- while (str_idx > 0)
- {
- int i, ret;
- /* Update counters. */
- null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
- if (null_cnt > mctx->max_mb_elem_len)
- {
- memset (sctx->sifted_states, '\0',
- sizeof (re_dfastate_t *) * str_idx);
- re_node_set_free (&cur_dest);
- return REG_NOERROR;
- }
- re_node_set_empty (&cur_dest);
- --str_idx;
- cur_src = ((mctx->state_log[str_idx] == NULL) ? &empty_set
- : &mctx->state_log[str_idx]->nodes);
-
- /* Then build the next sifted state.
- We build the next sifted state on `cur_dest', and update
- `sifted_states[str_idx]' with `cur_dest'.
- Note:
- `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
- `cur_src' points the node_set of the old `state_log[str_idx]'. */
- for (i = 0; i < cur_src->nelem; i++)
- {
- int prev_node = cur_src->elems[i];
- int naccepted = 0;
- re_token_type_t type = dfa->nodes[prev_node].type;
-
- if (IS_EPSILON_NODE(type))
- continue;
-#ifdef RE_ENABLE_I18N
- /* If the node may accept `multi byte'. */
- if (ACCEPT_MB_NODE (type))
- naccepted = sift_states_iter_mb (preg, mctx, sctx, prev_node,
- str_idx, sctx->last_str_idx);
-
-#endif /* RE_ENABLE_I18N */
- /* We don't check backreferences here.
- See update_cur_sifted_state(). */
-
- if (!naccepted
- && check_node_accept (preg, dfa->nodes + prev_node, mctx,
- str_idx)
- && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
- dfa->nexts[prev_node]))
- naccepted = 1;
-
- if (naccepted == 0)
- continue;
-
- if (sctx->limits.nelem)
- {
- int to_idx = str_idx + naccepted;
- if (check_dst_limits (dfa, &sctx->limits, mctx,
- dfa->nexts[prev_node], to_idx,
- prev_node, str_idx))
- continue;
- }
- ret = re_node_set_insert (&cur_dest, prev_node);
- if (BE (ret == -1, 0))
- {
- err = REG_ESPACE;
- goto free_return;
- }
- }
-
- /* Add all the nodes which satisfy the following conditions:
- - It can epsilon transit to a node in CUR_DEST.
- - It is in CUR_SRC.
- And update state_log. */
- err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- }
- err = REG_NOERROR;
- free_return:
- re_node_set_free (&cur_dest);
- return err;
-}
-
-/* Helper functions. */
-
-static inline reg_errcode_t
-clean_state_log_if_need (mctx, next_state_log_idx)
- re_match_context_t *mctx;
- int next_state_log_idx;
-{
- int top = mctx->state_log_top;
-
- if (next_state_log_idx >= mctx->input->bufs_len
- || (next_state_log_idx >= mctx->input->valid_len
- && mctx->input->valid_len < mctx->input->len))
- {
- reg_errcode_t err;
- err = extend_buffers (mctx);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
-
- if (top < next_state_log_idx)
- {
- memset (mctx->state_log + top + 1, '\0',
- sizeof (re_dfastate_t *) * (next_state_log_idx - top));
- mctx->state_log_top = next_state_log_idx;
- }
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-merge_state_array (dfa, dst, src, num)
- re_dfa_t *dfa;
- re_dfastate_t **dst;
- re_dfastate_t **src;
- int num;
-{
- int st_idx;
- reg_errcode_t err;
- for (st_idx = 0; st_idx < num; ++st_idx)
- {
- if (dst[st_idx] == NULL)
- dst[st_idx] = src[st_idx];
- else if (src[st_idx] != NULL)
- {
- re_node_set merged_set;
- err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
- &src[st_idx]->nodes);
- if (BE (err != REG_NOERROR, 0))
- return err;
- dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
- re_node_set_free (&merged_set);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- }
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-update_cur_sifted_state (preg, mctx, sctx, str_idx, dest_nodes)
- const regex_t *preg;
- re_match_context_t *mctx;
- re_sift_context_t *sctx;
- int str_idx;
- re_node_set *dest_nodes;
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
- const re_node_set *candidates;
- candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
- : &mctx->state_log[str_idx]->nodes);
-
- /* At first, add the nodes which can epsilon transit to a node in
- DEST_NODE. */
- if (dest_nodes->nelem)
- {
- err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
-
- /* Then, check the limitations in the current sift_context. */
- if (dest_nodes->nelem && sctx->limits.nelem)
- {
- err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
- mctx->bkref_ents, str_idx);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
-
- /* Update state_log. */
- sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
- if (BE (sctx->sifted_states[str_idx] == NULL && err != REG_NOERROR, 0))
- return err;
-
- if ((mctx->state_log[str_idx] != NULL
- && mctx->state_log[str_idx]->has_backref))
- {
- err = sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-add_epsilon_src_nodes (dfa, dest_nodes, candidates)
- re_dfa_t *dfa;
- re_node_set *dest_nodes;
- const re_node_set *candidates;
-{
- reg_errcode_t err;
- int src_idx;
- re_node_set src_copy;
-
- err = re_node_set_init_copy (&src_copy, dest_nodes);
- if (BE (err != REG_NOERROR, 0))
- return err;
- for (src_idx = 0; src_idx < src_copy.nelem; ++src_idx)
- {
- err = re_node_set_add_intersect (dest_nodes, candidates,
- dfa->inveclosures
- + src_copy.elems[src_idx]);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&src_copy);
- return err;
- }
- }
- re_node_set_free (&src_copy);
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates)
- re_dfa_t *dfa;
- int node;
- re_node_set *dest_nodes;
- const re_node_set *candidates;
-{
- int ecl_idx;
- reg_errcode_t err;
- re_node_set *inv_eclosure = dfa->inveclosures + node;
- re_node_set except_nodes;
- re_node_set_init_empty (&except_nodes);
- for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
- {
- int cur_node = inv_eclosure->elems[ecl_idx];
- if (cur_node == node)
- continue;
- if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
- {
- int edst1 = dfa->edests[cur_node].elems[0];
- int edst2 = ((dfa->edests[cur_node].nelem > 1)
- ? dfa->edests[cur_node].elems[1] : -1);
- if ((!re_node_set_contains (inv_eclosure, edst1)
- && re_node_set_contains (dest_nodes, edst1))
- || (edst2 > 0
- && !re_node_set_contains (inv_eclosure, edst2)
- && re_node_set_contains (dest_nodes, edst2)))
- {
- err = re_node_set_add_intersect (&except_nodes, candidates,
- dfa->inveclosures + cur_node);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&except_nodes);
- return err;
- }
- }
- }
- }
- for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
- {
- int cur_node = inv_eclosure->elems[ecl_idx];
- if (!re_node_set_contains (&except_nodes, cur_node))
- {
- int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
- re_node_set_remove_at (dest_nodes, idx);
- }
- }
- re_node_set_free (&except_nodes);
- return REG_NOERROR;
-}
-
-static int
-check_dst_limits (dfa, limits, mctx, dst_node, dst_idx, src_node, src_idx)
- re_dfa_t *dfa;
- re_node_set *limits;
- re_match_context_t *mctx;
- int dst_node, dst_idx, src_node, src_idx;
-{
- int lim_idx, src_pos, dst_pos;
-
- for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
- {
- int subexp_idx;
- struct re_backref_cache_entry *ent;
- ent = mctx->bkref_ents + limits->elems[lim_idx];
- subexp_idx = dfa->nodes[ent->node].opr.idx - 1;
-
- dst_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
- dfa->eclosures + dst_node,
- subexp_idx, dst_node, dst_idx);
- src_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
- dfa->eclosures + src_node,
- subexp_idx, src_node, src_idx);
-
- /* In case of:
- <src> <dst> ( <subexp> )
- ( <subexp> ) <src> <dst>
- ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */
- if (src_pos == dst_pos)
- continue; /* This is unrelated limitation. */
- else
- return 1;
- }
- return 0;
-}
-
-static int
-check_dst_limits_calc_pos (dfa, mctx, limit, eclosures, subexp_idx, node,
- str_idx)
- re_dfa_t *dfa;
- re_match_context_t *mctx;
- re_node_set *eclosures;
- int limit, subexp_idx, node, str_idx;
-{
- struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
- int pos = (str_idx < lim->subexp_from ? -1
- : (lim->subexp_to < str_idx ? 1 : 0));
- if (pos == 0
- && (str_idx == lim->subexp_from || str_idx == lim->subexp_to))
- {
- int node_idx;
- for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
- {
- int node = eclosures->elems[node_idx];
- re_token_type_t type= dfa->nodes[node].type;
- if (type == OP_BACK_REF)
- {
- int bi = search_cur_bkref_entry (mctx, str_idx);
- for (; bi < mctx->nbkref_ents; ++bi)
- {
- struct re_backref_cache_entry *ent = mctx->bkref_ents + bi;
- if (ent->str_idx > str_idx)
- break;
- if (ent->node == node && ent->subexp_from == ent->subexp_to)
- {
- int cpos, dst;
- dst = dfa->edests[node].elems[0];
- cpos = check_dst_limits_calc_pos (dfa, mctx, limit,
- dfa->eclosures + dst,
- subexp_idx, dst,
- str_idx);
- if ((str_idx == lim->subexp_from && cpos == -1)
- || (str_idx == lim->subexp_to && cpos == 0))
- return cpos;
- }
- }
- }
- if (type == OP_OPEN_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
- && str_idx == lim->subexp_from)
- {
- pos = -1;
- break;
- }
- if (type == OP_CLOSE_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
- && str_idx == lim->subexp_to)
- break;
- }
- if (node_idx == eclosures->nelem && str_idx == lim->subexp_to)
- pos = 1;
- }
- return pos;
-}
-
-/* Check the limitations of sub expressions LIMITS, and remove the nodes
- which are against limitations from DEST_NODES. */
-
-static reg_errcode_t
-check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx)
- re_dfa_t *dfa;
- re_node_set *dest_nodes;
- const re_node_set *candidates;
- re_node_set *limits;
- struct re_backref_cache_entry *bkref_ents;
- int str_idx;
-{
- reg_errcode_t err;
- int node_idx, lim_idx;
-
- for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
- {
- int subexp_idx;
- struct re_backref_cache_entry *ent;
- ent = bkref_ents + limits->elems[lim_idx];
-
- if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
- continue; /* This is unrelated limitation. */
-
- subexp_idx = dfa->nodes[ent->node].opr.idx - 1;
- if (ent->subexp_to == str_idx)
- {
- int ops_node = -1;
- int cls_node = -1;
- for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
- {
- int node = dest_nodes->elems[node_idx];
- re_token_type_t type= dfa->nodes[node].type;
- if (type == OP_OPEN_SUBEXP
- && subexp_idx == dfa->nodes[node].opr.idx)
- ops_node = node;
- else if (type == OP_CLOSE_SUBEXP
- && subexp_idx == dfa->nodes[node].opr.idx)
- cls_node = node;
- }
-
- /* Check the limitation of the open subexpression. */
- /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */
- if (ops_node >= 0)
- {
- err = sub_epsilon_src_nodes(dfa, ops_node, dest_nodes,
- candidates);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- /* Check the limitation of the close subexpression. */
- for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
- {
- int node = dest_nodes->elems[node_idx];
- if (!re_node_set_contains (dfa->inveclosures + node, cls_node)
- && !re_node_set_contains (dfa->eclosures + node, cls_node))
- {
- /* It is against this limitation.
- Remove it form the current sifted state. */
- err = sub_epsilon_src_nodes(dfa, node, dest_nodes,
- candidates);
- if (BE (err != REG_NOERROR, 0))
- return err;
- --node_idx;
- }
- }
- }
- else /* (ent->subexp_to != str_idx) */
- {
- for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
- {
- int node = dest_nodes->elems[node_idx];
- re_token_type_t type= dfa->nodes[node].type;
- if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
- {
- if (subexp_idx != dfa->nodes[node].opr.idx)
- continue;
- if ((type == OP_CLOSE_SUBEXP && ent->subexp_to != str_idx)
- || (type == OP_OPEN_SUBEXP))
- {
- /* It is against this limitation.
- Remove it form the current sifted state. */
- err = sub_epsilon_src_nodes(dfa, node, dest_nodes,
- candidates);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- }
- }
- }
- }
- return REG_NOERROR;
-}
-
-static reg_errcode_t
-sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes)
- const regex_t *preg;
- re_match_context_t *mctx;
- re_sift_context_t *sctx;
- int str_idx;
- re_node_set *dest_nodes;
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
- int node_idx, node;
- re_sift_context_t local_sctx;
- const re_node_set *candidates;
- candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
- : &mctx->state_log[str_idx]->nodes);
- local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */
-
- for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
- {
- int cur_bkref_idx = re_string_cur_idx (mctx->input);
- re_token_type_t type;
- node = candidates->elems[node_idx];
- type = dfa->nodes[node].type;
- if (node == sctx->cur_bkref && str_idx == cur_bkref_idx)
- continue;
- /* Avoid infinite loop for the REs like "()\1+". */
- if (node == sctx->last_node && str_idx == sctx->last_str_idx)
- continue;
- if (type == OP_BACK_REF)
- {
- int enabled_idx = search_cur_bkref_entry (mctx, str_idx);
- for (; enabled_idx < mctx->nbkref_ents; ++enabled_idx)
- {
- int disabled_idx, subexp_len, to_idx, dst_node;
- struct re_backref_cache_entry *entry;
- entry = mctx->bkref_ents + enabled_idx;
- if (entry->str_idx > str_idx)
- break;
- if (entry->node != node)
- continue;
- subexp_len = entry->subexp_to - entry->subexp_from;
- to_idx = str_idx + subexp_len;
- dst_node = (subexp_len ? dfa->nexts[node]
- : dfa->edests[node].elems[0]);
-
- if (to_idx > sctx->last_str_idx
- || sctx->sifted_states[to_idx] == NULL
- || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx],
- dst_node)
- || check_dst_limits (dfa, &sctx->limits, mctx, node,
- str_idx, dst_node, to_idx))
- continue;
- {
- re_dfastate_t *cur_state;
- entry->flag = 0;
- for (disabled_idx = enabled_idx + 1;
- disabled_idx < mctx->nbkref_ents; ++disabled_idx)
- {
- struct re_backref_cache_entry *entry2;
- entry2 = mctx->bkref_ents + disabled_idx;
- if (entry2->str_idx > str_idx)
- break;
- entry2->flag = (entry2->node == node) ? 1 : entry2->flag;
- }
-
- if (local_sctx.sifted_states == NULL)
- {
- local_sctx = *sctx;
- err = re_node_set_init_copy (&local_sctx.limits,
- &sctx->limits);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- }
- local_sctx.last_node = node;
- local_sctx.last_str_idx = str_idx;
- err = re_node_set_insert (&local_sctx.limits, enabled_idx);
- if (BE (err < 0, 0))
- {
- err = REG_ESPACE;
- goto free_return;
- }
- cur_state = local_sctx.sifted_states[str_idx];
- err = sift_states_backward (preg, mctx, &local_sctx);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- if (sctx->limited_states != NULL)
- {
- err = merge_state_array (dfa, sctx->limited_states,
- local_sctx.sifted_states,
- str_idx + 1);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- }
- local_sctx.sifted_states[str_idx] = cur_state;
- re_node_set_remove (&local_sctx.limits, enabled_idx);
- /* We must not use the variable entry here, since
- mctx->bkref_ents might be realloced. */
- mctx->bkref_ents[enabled_idx].flag = 1;
- }
- }
- enabled_idx = search_cur_bkref_entry (mctx, str_idx);
- for (; enabled_idx < mctx->nbkref_ents; ++enabled_idx)
- {
- struct re_backref_cache_entry *entry;
- entry = mctx->bkref_ents + enabled_idx;
- if (entry->str_idx > str_idx)
- break;
- if (entry->node == node)
- entry->flag = 0;
- }
- }
- }
- err = REG_NOERROR;
- free_return:
- if (local_sctx.sifted_states != NULL)
- {
- re_node_set_free (&local_sctx.limits);
- }
-
- return err;
-}
-
-
-#ifdef RE_ENABLE_I18N
-static int
-sift_states_iter_mb (preg, mctx, sctx, node_idx, str_idx, max_str_idx)
- const regex_t *preg;
- const re_match_context_t *mctx;
- re_sift_context_t *sctx;
- int node_idx, str_idx, max_str_idx;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- int naccepted;
- /* Check the node can accept `multi byte'. */
- naccepted = check_node_accept_bytes (preg, node_idx, mctx->input, str_idx);
- if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
- !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
- dfa->nexts[node_idx]))
- /* The node can't accept the `multi byte', or the
- destination was already throwed away, then the node
- could't accept the current input `multi byte'. */
- naccepted = 0;
- /* Otherwise, it is sure that the node could accept
- `naccepted' bytes input. */
- return naccepted;
-}
-#endif /* RE_ENABLE_I18N */
-
-\f
-/* Functions for state transition. */
-
-/* Return the next state to which the current state STATE will transit by
- accepting the current input byte, and update STATE_LOG if necessary.
- If STATE can accept a multibyte char/collating element/back reference
- update the destination of STATE_LOG. */
-
-static re_dfastate_t *
-transit_state (err, preg, mctx, state, fl_search)
- reg_errcode_t *err;
- const regex_t *preg;
- re_match_context_t *mctx;
- re_dfastate_t *state;
- int fl_search;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- re_dfastate_t **trtable, *next_state;
- unsigned char ch;
- int cur_idx;
-
- if (re_string_cur_idx (mctx->input) + 1 >= mctx->input->bufs_len
- || (re_string_cur_idx (mctx->input) + 1 >= mctx->input->valid_len
- && mctx->input->valid_len < mctx->input->len))
- {
- *err = extend_buffers (mctx);
- if (BE (*err != REG_NOERROR, 0))
- return NULL;
- }
-
- *err = REG_NOERROR;
- if (state == NULL)
- {
- next_state = state;
- re_string_skip_bytes (mctx->input, 1);
- }
- else
- {
-#ifdef RE_ENABLE_I18N
- /* If the current state can accept multibyte. */
- if (state->accept_mb)
- {
- *err = transit_state_mb (preg, state, mctx);
- if (BE (*err != REG_NOERROR, 0))
- return NULL;
- }
-#endif /* RE_ENABLE_I18N */
-
- /* Then decide the next state with the single byte. */
- if (1)
- {
- /* Use transition table */
- ch = re_string_fetch_byte (mctx->input);
- trtable = fl_search ? state->trtable_search : state->trtable;
- if (trtable == NULL)
- {
- trtable = build_trtable (preg, state, fl_search);
- if (fl_search)
- state->trtable_search = trtable;
- else
- state->trtable = trtable;
- }
- next_state = trtable[ch];
- }
- else
- {
- /* don't use transition table */
- next_state = transit_state_sb (err, preg, state, fl_search, mctx);
- if (BE (next_state == NULL && err != REG_NOERROR, 0))
- return NULL;
- }
- }
-
- cur_idx = re_string_cur_idx (mctx->input);
- /* Update the state_log if we need. */
- if (mctx->state_log != NULL)
- {
- if (cur_idx > mctx->state_log_top)
- {
- mctx->state_log[cur_idx] = next_state;
- mctx->state_log_top = cur_idx;
- }
- else if (mctx->state_log[cur_idx] == 0)
- {
- mctx->state_log[cur_idx] = next_state;
- }
- else
- {
- re_dfastate_t *pstate;
- unsigned int context;
- re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
- /* If (state_log[cur_idx] != 0), it implies that cur_idx is
- the destination of a multibyte char/collating element/
- back reference. Then the next state is the union set of
- these destinations and the results of the transition table. */
- pstate = mctx->state_log[cur_idx];
- log_nodes = pstate->entrance_nodes;
- if (next_state != NULL)
- {
- table_nodes = next_state->entrance_nodes;
- *err = re_node_set_init_union (&next_nodes, table_nodes,
- log_nodes);
- if (BE (*err != REG_NOERROR, 0))
- return NULL;
- }
- else
- next_nodes = *log_nodes;
- /* Note: We already add the nodes of the initial state,
- then we don't need to add them here. */
-
- context = re_string_context_at (mctx->input,
- re_string_cur_idx (mctx->input) - 1,
- mctx->eflags, preg->newline_anchor);
- next_state = mctx->state_log[cur_idx]
- = re_acquire_state_context (err, dfa, &next_nodes, context);
- /* We don't need to check errors here, since the return value of
- this function is next_state and ERR is already set. */
-
- if (table_nodes != NULL)
- re_node_set_free (&next_nodes);
- }
- }
-
- /* Check OP_OPEN_SUBEXP in the current state in case that we use them
- later. We must check them here, since the back references in the
- next state might use them. */
- if (dfa->nbackref && next_state/* && fl_process_bkref */)
- {
- *err = check_subexp_matching_top (dfa, mctx, &next_state->nodes,
- cur_idx);
- if (BE (*err != REG_NOERROR, 0))
- return NULL;
- }
-
- /* If the next state has back references. */
- if (next_state != NULL && next_state->has_backref)
- {
- *err = transit_state_bkref (preg, &next_state->nodes, mctx);
- if (BE (*err != REG_NOERROR, 0))
- return NULL;
- next_state = mctx->state_log[cur_idx];
- }
- return next_state;
-}
-
-/* Helper functions for transit_state. */
-
-/* From the node set CUR_NODES, pick up the nodes whose types are
- OP_OPEN_SUBEXP and which have corresponding back references in the regular
- expression. And register them to use them later for evaluating the
- correspoding back references. */
-
-static reg_errcode_t
-check_subexp_matching_top (dfa, mctx, cur_nodes, str_idx)
- re_dfa_t *dfa;
- re_match_context_t *mctx;
- re_node_set *cur_nodes;
- int str_idx;
-{
- int node_idx;
- reg_errcode_t err;
-
- /* TODO: This isn't efficient.
- Because there might be more than one nodes whose types are
- OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
- nodes.
- E.g. RE: (a){2} */
- for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
- {
- int node = cur_nodes->elems[node_idx];
- if (dfa->nodes[node].type == OP_OPEN_SUBEXP
- && dfa->used_bkref_map & (1 << dfa->nodes[node].opr.idx))
- {
- err = match_ctx_add_subtop (mctx, node, str_idx);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- }
- return REG_NOERROR;
-}
-
-/* Return the next state to which the current state STATE will transit by
- accepting the current input byte. */
-
-static re_dfastate_t *
-transit_state_sb (err, preg, state, fl_search, mctx)
- reg_errcode_t *err;
- const regex_t *preg;
- re_dfastate_t *state;
- int fl_search;
- re_match_context_t *mctx;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- re_node_set next_nodes;
- re_dfastate_t *next_state;
- int node_cnt, cur_str_idx = re_string_cur_idx (mctx->input);
- unsigned int context;
-
- *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
- if (BE (*err != REG_NOERROR, 0))
- return NULL;
- for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
- {
- int cur_node = state->nodes.elems[node_cnt];
- if (check_node_accept (preg, dfa->nodes + cur_node, mctx, cur_str_idx))
- {
- *err = re_node_set_merge (&next_nodes,
- dfa->eclosures + dfa->nexts[cur_node]);
- if (BE (*err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return NULL;
- }
- }
- }
- if (fl_search)
- {
-#ifdef RE_ENABLE_I18N
- int not_initial = 0;
- if (MB_CUR_MAX > 1)
- for (node_cnt = 0; node_cnt < next_nodes.nelem; ++node_cnt)
- if (dfa->nodes[next_nodes.elems[node_cnt]].type == CHARACTER)
- {
- not_initial = dfa->nodes[next_nodes.elems[node_cnt]].mb_partial;
- break;
- }
- if (!not_initial)
-#endif
- {
- *err = re_node_set_merge (&next_nodes,
- dfa->init_state->entrance_nodes);
- if (BE (*err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return NULL;
- }
- }
- }
- context = re_string_context_at (mctx->input, cur_str_idx, mctx->eflags,
- preg->newline_anchor);
- next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
- /* We don't need to check errors here, since the return value of
- this function is next_state and ERR is already set. */
-
- re_node_set_free (&next_nodes);
- re_string_skip_bytes (mctx->input, 1);
- return next_state;
-}
-
-#ifdef RE_ENABLE_I18N
-static reg_errcode_t
-transit_state_mb (preg, pstate, mctx)
- const regex_t *preg;
- re_dfastate_t *pstate;
- re_match_context_t *mctx;
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- int i;
-
- for (i = 0; i < pstate->nodes.nelem; ++i)
- {
- re_node_set dest_nodes, *new_nodes;
- int cur_node_idx = pstate->nodes.elems[i];
- int naccepted = 0, dest_idx;
- unsigned int context;
- re_dfastate_t *dest_state;
-
- if (dfa->nodes[cur_node_idx].constraint)
- {
- context = re_string_context_at (mctx->input,
- re_string_cur_idx (mctx->input),
- mctx->eflags, preg->newline_anchor);
- if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
- context))
- continue;
- }
-
- /* How many bytes the node can accepts? */
- if (ACCEPT_MB_NODE (dfa->nodes[cur_node_idx].type))
- naccepted = check_node_accept_bytes (preg, cur_node_idx, mctx->input,
- re_string_cur_idx (mctx->input));
- if (naccepted == 0)
- continue;
-
- /* The node can accepts `naccepted' bytes. */
- dest_idx = re_string_cur_idx (mctx->input) + naccepted;
- mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
- : mctx->max_mb_elem_len);
- err = clean_state_log_if_need (mctx, dest_idx);
- if (BE (err != REG_NOERROR, 0))
- return err;
-#ifdef DEBUG
- assert (dfa->nexts[cur_node_idx] != -1);
-#endif
- /* `cur_node_idx' may point the entity of the OP_CONTEXT_NODE,
- then we use pstate->nodes.elems[i] instead. */
- new_nodes = dfa->eclosures + dfa->nexts[pstate->nodes.elems[i]];
-
- dest_state = mctx->state_log[dest_idx];
- if (dest_state == NULL)
- dest_nodes = *new_nodes;
- else
- {
- err = re_node_set_init_union (&dest_nodes,
- dest_state->entrance_nodes, new_nodes);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- context = re_string_context_at (mctx->input, dest_idx - 1, mctx->eflags,
- preg->newline_anchor);
- mctx->state_log[dest_idx]
- = re_acquire_state_context (&err, dfa, &dest_nodes, context);
- if (dest_state != NULL)
- re_node_set_free (&dest_nodes);
- if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
- return err;
- }
- return REG_NOERROR;
-}
-#endif /* RE_ENABLE_I18N */
-
-static reg_errcode_t
-transit_state_bkref (preg, nodes, mctx)
- const regex_t *preg;
- re_node_set *nodes;
- re_match_context_t *mctx;
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- int i;
- int cur_str_idx = re_string_cur_idx (mctx->input);
-
- for (i = 0; i < nodes->nelem; ++i)
- {
- int dest_str_idx, prev_nelem, bkc_idx;
- int node_idx = nodes->elems[i];
- unsigned int context;
- re_token_t *node = dfa->nodes + node_idx;
- re_node_set *new_dest_nodes;
-
- /* Check whether `node' is a backreference or not. */
- if (node->type != OP_BACK_REF)
- continue;
-
- if (node->constraint)
- {
- context = re_string_context_at (mctx->input, cur_str_idx,
- mctx->eflags, preg->newline_anchor);
- if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
- continue;
- }
-
- /* `node' is a backreference.
- Check the substring which the substring matched. */
- bkc_idx = mctx->nbkref_ents;
- err = get_subexp (preg, mctx, node_idx, cur_str_idx);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
-
- /* And add the epsilon closures (which is `new_dest_nodes') of
- the backreference to appropriate state_log. */
-#ifdef DEBUG
- assert (dfa->nexts[node_idx] != -1);
-#endif
- for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
- {
- int subexp_len;
- re_dfastate_t *dest_state;
- struct re_backref_cache_entry *bkref_ent;
- bkref_ent = mctx->bkref_ents + bkc_idx;
- if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
- continue;
- subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
- new_dest_nodes = (subexp_len == 0
- ? dfa->eclosures + dfa->edests[node_idx].elems[0]
- : dfa->eclosures + dfa->nexts[node_idx]);
- dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
- - bkref_ent->subexp_from);
- context = re_string_context_at (mctx->input, dest_str_idx - 1,
- mctx->eflags, preg->newline_anchor);
- dest_state = mctx->state_log[dest_str_idx];
- prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
- : mctx->state_log[cur_str_idx]->nodes.nelem);
- /* Add `new_dest_node' to state_log. */
- if (dest_state == NULL)
- {
- mctx->state_log[dest_str_idx]
- = re_acquire_state_context (&err, dfa, new_dest_nodes,
- context);
- if (BE (mctx->state_log[dest_str_idx] == NULL
- && err != REG_NOERROR, 0))
- goto free_return;
- }
- else
- {
- re_node_set dest_nodes;
- err = re_node_set_init_union (&dest_nodes,
- dest_state->entrance_nodes,
- new_dest_nodes);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&dest_nodes);
- goto free_return;
- }
- mctx->state_log[dest_str_idx]
- = re_acquire_state_context (&err, dfa, &dest_nodes, context);
- re_node_set_free (&dest_nodes);
- if (BE (mctx->state_log[dest_str_idx] == NULL
- && err != REG_NOERROR, 0))
- goto free_return;
- }
- /* We need to check recursively if the backreference can epsilon
- transit. */
- if (subexp_len == 0
- && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
- {
- err = check_subexp_matching_top (dfa, mctx, new_dest_nodes,
- cur_str_idx);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- err = transit_state_bkref (preg, new_dest_nodes, mctx);
- if (BE (err != REG_NOERROR, 0))
- goto free_return;
- }
- }
- }
- err = REG_NOERROR;
- free_return:
- return err;
-}
-
-/* Enumerate all the candidates which the backreference BKREF_NODE can match
- at BKREF_STR_IDX, and register them by match_ctx_add_entry().
- Note that we might collect inappropriate candidates here.
- However, the cost of checking them strictly here is too high, then we
- delay these checking for prune_impossible_nodes(). */
-
-static reg_errcode_t
-get_subexp (preg, mctx, bkref_node, bkref_str_idx)
- const regex_t *preg;
- re_match_context_t *mctx;
- int bkref_node, bkref_str_idx;
-{
- int subexp_num, sub_top_idx;
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- char *buf = (char *) re_string_get_buffer (mctx->input);
- /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */
- int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
- for (; cache_idx < mctx->nbkref_ents; ++cache_idx)
- {
- struct re_backref_cache_entry *entry = mctx->bkref_ents + cache_idx;
- if (entry->str_idx > bkref_str_idx)
- break;
- if (entry->node == bkref_node)
- return REG_NOERROR; /* We already checked it. */
- }
- subexp_num = dfa->nodes[bkref_node].opr.idx - 1;
-
- /* For each sub expression */
- for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
- {
- reg_errcode_t err;
- re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
- re_sub_match_last_t *sub_last;
- int sub_last_idx, sl_str;
- char *bkref_str;
-
- if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
- continue; /* It isn't related. */
-
- sl_str = sub_top->str_idx;
- bkref_str = buf + bkref_str_idx;
- /* At first, check the last node of sub expressions we already
- evaluated. */
- for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
- {
- int sl_str_diff;
- sub_last = sub_top->lasts[sub_last_idx];
- sl_str_diff = sub_last->str_idx - sl_str;
- /* The matched string by the sub expression match with the substring
- at the back reference? */
- if (sl_str_diff > 0
- && memcmp (bkref_str, buf + sl_str, sl_str_diff) != 0)
- break; /* We don't need to search this sub expression any more. */
- bkref_str += sl_str_diff;
- sl_str += sl_str_diff;
- err = get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node,
- bkref_str_idx);
- if (err == REG_NOMATCH)
- continue;
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- if (sub_last_idx < sub_top->nlasts)
- continue;
- if (sub_last_idx > 0)
- ++sl_str;
- /* Then, search for the other last nodes of the sub expression. */
- for (; sl_str <= bkref_str_idx; ++sl_str)
- {
- int cls_node, sl_str_off;
- re_node_set *nodes;
- sl_str_off = sl_str - sub_top->str_idx;
- /* The matched string by the sub expression match with the substring
- at the back reference? */
- if (sl_str_off > 0
- && memcmp (bkref_str++, buf + sl_str - 1, 1) != 0)
- break; /* We don't need to search this sub expression any more. */
- if (mctx->state_log[sl_str] == NULL)
- continue;
- /* Does this state have a ')' of the sub expression? */
- nodes = &mctx->state_log[sl_str]->nodes;
- cls_node = find_subexp_node (dfa, nodes, subexp_num, 0);
- if (cls_node == -1)
- continue; /* No. */
- if (sub_top->path == NULL)
- {
- sub_top->path = calloc (sizeof (state_array_t),
- sl_str - sub_top->str_idx + 1);
- if (sub_top->path == NULL)
- return REG_ESPACE;
- }
- /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
- in the current context? */
- err = check_arrival (preg, mctx, sub_top->path, sub_top->node,
- sub_top->str_idx, cls_node, sl_str, 0);
- if (err == REG_NOMATCH)
- continue;
- if (BE (err != REG_NOERROR, 0))
- return err;
- sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
- if (BE (sub_last == NULL, 0))
- return REG_ESPACE;
- err = get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node,
- bkref_str_idx);
- if (err == REG_NOMATCH)
- continue;
- }
- }
- return REG_NOERROR;
-}
-
-/* Helper functions for get_subexp(). */
-
-/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
- If it can arrive, register the sub expression expressed with SUB_TOP
- and SUB_LAST. */
-
-static reg_errcode_t
-get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node, bkref_str)
- const regex_t *preg;
- re_match_context_t *mctx;
- re_sub_match_top_t *sub_top;
- re_sub_match_last_t *sub_last;
- int bkref_node, bkref_str;
-{
- reg_errcode_t err;
- int to_idx;
- /* Can the subexpression arrive the back reference? */
- err = check_arrival (preg, mctx, &sub_last->path, sub_last->node,
- sub_last->str_idx, bkref_node, bkref_str, 1);
- if (err != REG_NOERROR)
- return err;
- err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
- sub_last->str_idx);
- if (BE (err != REG_NOERROR, 0))
- return err;
- to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
- clean_state_log_if_need (mctx, to_idx);
- return REG_NOERROR;
-}
-
-/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
- Search '(' if FL_OPEN, or search ')' otherwise.
- TODO: This function isn't efficient...
- Because there might be more than one nodes whose types are
- OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
- nodes.
- E.g. RE: (a){2} */
-
-static int
-find_subexp_node (dfa, nodes, subexp_idx, fl_open)
- re_dfa_t *dfa;
- re_node_set *nodes;
- int subexp_idx, fl_open;
-{
- int cls_idx;
- for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
- {
- int cls_node = nodes->elems[cls_idx];
- re_token_t *node = dfa->nodes + cls_node;
- if (((fl_open && node->type == OP_OPEN_SUBEXP)
- || (!fl_open && node->type == OP_CLOSE_SUBEXP))
- && node->opr.idx == subexp_idx)
- return cls_node;
- }
- return -1;
-}
-
-/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
- LAST_NODE at LAST_STR. We record the path onto PATH since it will be
- heavily reused.
- Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */
-
-static reg_errcode_t
-check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
- fl_open)
- const regex_t *preg;
- re_match_context_t *mctx;
- state_array_t *path;
- int top_node, top_str, last_node, last_str, fl_open;
-{
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- reg_errcode_t err;
- int subexp_num, backup_cur_idx, str_idx, null_cnt;
- re_dfastate_t *cur_state = NULL;
- re_node_set *cur_nodes, next_nodes;
- re_dfastate_t **backup_state_log;
- unsigned int context;
-
- subexp_num = dfa->nodes[top_node].opr.idx;
- /* Extend the buffer if we need. */
- if (path->alloc < last_str + mctx->max_mb_elem_len + 1)
- {
- re_dfastate_t **new_array;
- int old_alloc = path->alloc;
- path->alloc += last_str + mctx->max_mb_elem_len + 1;
- new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
- if (new_array == NULL)
- return REG_ESPACE;
- path->array = new_array;
- memset (new_array + old_alloc, '\0',
- sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
- }
-
- str_idx = path->next_idx == 0 ? top_str : path->next_idx;
-
- /* Temporary modify MCTX. */
- backup_state_log = mctx->state_log;
- backup_cur_idx = mctx->input->cur_idx;
- mctx->state_log = path->array;
- mctx->input->cur_idx = str_idx;
-
- /* Setup initial node set. */
- context = re_string_context_at (mctx->input, str_idx - 1, mctx->eflags,
- preg->newline_anchor);
- if (str_idx == top_str)
- {
- err = re_node_set_init_1 (&next_nodes, top_node);
- if (BE (err != REG_NOERROR, 0))
- return err;
- err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, fl_open);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- }
- else
- {
- cur_state = mctx->state_log[str_idx];
- if (cur_state && cur_state->has_backref)
- {
- err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
- if (BE ( err != REG_NOERROR, 0))
- return err;
- }
- else
- re_node_set_init_empty (&next_nodes);
- }
- if (str_idx == top_str || (cur_state && cur_state->has_backref))
- {
- if (next_nodes.nelem)
- {
- err = expand_bkref_cache (preg, mctx, &next_nodes, str_idx, last_str,
- subexp_num, fl_open);
- if (BE ( err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- }
- cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
- if (BE (cur_state == NULL && err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- mctx->state_log[str_idx] = cur_state;
- }
-
- for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
- {
- re_node_set_empty (&next_nodes);
- if (mctx->state_log[str_idx + 1])
- {
- err = re_node_set_merge (&next_nodes,
- &mctx->state_log[str_idx + 1]->nodes);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- }
- if (cur_state)
- {
- err = check_arrival_add_next_nodes(preg, dfa, mctx, str_idx,
- &cur_state->nodes, &next_nodes);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- }
- ++str_idx;
- if (next_nodes.nelem)
- {
- err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num,
- fl_open);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- err = expand_bkref_cache (preg, mctx, &next_nodes, str_idx, last_str,
- subexp_num, fl_open);
- if (BE ( err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- }
- context = re_string_context_at (mctx->input, str_idx - 1, mctx->eflags,
- preg->newline_anchor);
- cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
- if (BE (cur_state == NULL && err != REG_NOERROR, 0))
- {
- re_node_set_free (&next_nodes);
- return err;
- }
- mctx->state_log[str_idx] = cur_state;
- null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
- }
- re_node_set_free (&next_nodes);
- cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
- : &mctx->state_log[last_str]->nodes);
- path->next_idx = str_idx;
-
- /* Fix MCTX. */
- mctx->state_log = backup_state_log;
- mctx->input->cur_idx = backup_cur_idx;
-
- if (cur_nodes == NULL)
- return REG_NOMATCH;
- /* Then check the current node set has the node LAST_NODE. */
- return (re_node_set_contains (cur_nodes, last_node)
- || re_node_set_contains (cur_nodes, last_node) ? REG_NOERROR
- : REG_NOMATCH);
-}
-
-/* Helper functions for check_arrival. */
-
-/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
- to NEXT_NODES.
- TODO: This function is similar to the functions transit_state*(),
- however this function has many additional works.
- Can't we unify them? */
-
-static reg_errcode_t
-check_arrival_add_next_nodes (preg, dfa, mctx, str_idx, cur_nodes, next_nodes)
- const regex_t *preg;
- re_dfa_t *dfa;
- re_match_context_t *mctx;
- int str_idx;
- re_node_set *cur_nodes, *next_nodes;
-{
- int cur_idx;
- reg_errcode_t err;
- re_node_set union_set;
- re_node_set_init_empty (&union_set);
- for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
- {
- int naccepted = 0;
- int cur_node = cur_nodes->elems[cur_idx];
- re_token_type_t type = dfa->nodes[cur_node].type;
- if (IS_EPSILON_NODE(type))
- continue;
-#ifdef RE_ENABLE_I18N
- /* If the node may accept `multi byte'. */
- if (ACCEPT_MB_NODE (type))
- {
- naccepted = check_node_accept_bytes (preg, cur_node, mctx->input,
- str_idx);
- if (naccepted > 1)
- {
- re_dfastate_t *dest_state;
- int next_node = dfa->nexts[cur_node];
- int next_idx = str_idx + naccepted;
- dest_state = mctx->state_log[next_idx];
- re_node_set_empty (&union_set);
- if (dest_state)
- {
- err = re_node_set_merge (&union_set, &dest_state->nodes);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&union_set);
- return err;
- }
- err = re_node_set_insert (&union_set, next_node);
- if (BE (err < 0, 0))
- {
- re_node_set_free (&union_set);
- return REG_ESPACE;
- }
- }
- else
- {
- err = re_node_set_insert (&union_set, next_node);
- if (BE (err < 0, 0))
- {
- re_node_set_free (&union_set);
- return REG_ESPACE;
- }
- }
- mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
- &union_set);
- if (BE (mctx->state_log[next_idx] == NULL
- && err != REG_NOERROR, 0))
- {
- re_node_set_free (&union_set);
- return err;
- }
- }
- }
-#endif /* RE_ENABLE_I18N */
- if (naccepted
- || check_node_accept (preg, dfa->nodes + cur_node, mctx,
- str_idx))
- {
- err = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
- if (BE (err < 0, 0))
- {
- re_node_set_free (&union_set);
- return REG_ESPACE;
- }
- }
- }
- re_node_set_free (&union_set);
- return REG_NOERROR;
-}
-
-/* For all the nodes in CUR_NODES, add the epsilon closures of them to
- CUR_NODES, however exclude the nodes which are:
- - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
- - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
-*/
-
-static reg_errcode_t
-check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, fl_open)
- re_dfa_t *dfa;
- re_node_set *cur_nodes;
- int ex_subexp, fl_open;
-{
- reg_errcode_t err;
- int idx, outside_node;
- re_node_set new_nodes;
-#ifdef DEBUG
- assert (cur_nodes->nelem);
-#endif
- err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
- if (BE (err != REG_NOERROR, 0))
- return err;
- /* Create a new node set NEW_NODES with the nodes which are epsilon
- closures of the node in CUR_NODES. */
-
- for (idx = 0; idx < cur_nodes->nelem; ++idx)
- {
- int cur_node = cur_nodes->elems[idx];
- re_node_set *eclosure = dfa->eclosures + cur_node;
- outside_node = find_subexp_node (dfa, eclosure, ex_subexp, fl_open);
- if (outside_node == -1)
- {
- /* There are no problematic nodes, just merge them. */
- err = re_node_set_merge (&new_nodes, eclosure);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&new_nodes);
- return err;
- }
- }
- else
- {
- /* There are problematic nodes, re-calculate incrementally. */
- err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
- ex_subexp, fl_open);
- if (BE (err != REG_NOERROR, 0))
- {
- re_node_set_free (&new_nodes);
- return err;
- }
- }
- }
- re_node_set_free (cur_nodes);
- *cur_nodes = new_nodes;
- return REG_NOERROR;
-}
-
-/* Helper function for check_arrival_expand_ecl.
- Check incrementally the epsilon closure of TARGET, and if it isn't
- problematic append it to DST_NODES. */
-
-static reg_errcode_t
-check_arrival_expand_ecl_sub (dfa, dst_nodes, target, ex_subexp, fl_open)
- re_dfa_t *dfa;
- int target, ex_subexp, fl_open;
- re_node_set *dst_nodes;
-{
- int cur_node, type;
- for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
- {
- int err;
- type = dfa->nodes[cur_node].type;
-
- if (((type == OP_OPEN_SUBEXP && fl_open)
- || (type == OP_CLOSE_SUBEXP && !fl_open))
- && dfa->nodes[cur_node].opr.idx == ex_subexp)
- {
- if (!fl_open)
- {
- err = re_node_set_insert (dst_nodes, cur_node);
- if (BE (err == -1, 0))
- return REG_ESPACE;
- }
- break;
- }
- err = re_node_set_insert (dst_nodes, cur_node);
- if (BE (err == -1, 0))
- return REG_ESPACE;
- if (dfa->edests[cur_node].nelem == 0)
- break;
- if (dfa->edests[cur_node].nelem == 2)
- {
- err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
- dfa->edests[cur_node].elems[1],
- ex_subexp, fl_open);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- cur_node = dfa->edests[cur_node].elems[0];
- }
- return REG_NOERROR;
-}
-
-
-/* For all the back references in the current state, calculate the
- destination of the back references by the appropriate entry
- in MCTX->BKREF_ENTS. */
-
-static reg_errcode_t
-expand_bkref_cache (preg, mctx, cur_nodes, cur_str, last_str, subexp_num,
- fl_open)
- const regex_t *preg;
- re_match_context_t *mctx;
- int cur_str, last_str, subexp_num, fl_open;
- re_node_set *cur_nodes;
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- int cache_idx, cache_idx_start;
- /* The current state. */
-
- cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
- for (cache_idx = cache_idx_start; cache_idx < mctx->nbkref_ents; ++cache_idx)
- {
- int to_idx, next_node;
- struct re_backref_cache_entry *ent = mctx->bkref_ents + cache_idx;
- if (ent->str_idx > cur_str)
- break;
- /* Is this entry ENT is appropriate? */
- if (!re_node_set_contains (cur_nodes, ent->node))
- continue; /* No. */
-
- to_idx = cur_str + ent->subexp_to - ent->subexp_from;
- /* Calculate the destination of the back reference, and append it
- to MCTX->STATE_LOG. */
- if (to_idx == cur_str)
- {
- /* The backreference did epsilon transit, we must re-check all the
- node in the current state. */
- re_node_set new_dests;
- reg_errcode_t err2, err3;
- next_node = dfa->edests[ent->node].elems[0];
- if (re_node_set_contains (cur_nodes, next_node))
- continue;
- err = re_node_set_init_1 (&new_dests, next_node);
- err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num,
- fl_open);
- err3 = re_node_set_merge (cur_nodes, &new_dests);
- re_node_set_free (&new_dests);
- if (BE (err != REG_NOERROR || err2 != REG_NOERROR
- || err3 != REG_NOERROR, 0))
- {
- err = (err != REG_NOERROR ? err
- : (err2 != REG_NOERROR ? err2 : err3));
- return err;
- }
- /* TODO: It is still inefficient... */
- cache_idx = cache_idx_start - 1;
- continue;
- }
- else
- {
- re_node_set union_set;
- next_node = dfa->nexts[ent->node];
- if (mctx->state_log[to_idx])
- {
- int ret;
- if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
- next_node))
- continue;
- err = re_node_set_init_copy (&union_set,
- &mctx->state_log[to_idx]->nodes);
- ret = re_node_set_insert (&union_set, next_node);
- if (BE (err != REG_NOERROR || ret < 0, 0))
- {
- re_node_set_free (&union_set);
- err = err != REG_NOERROR ? err : REG_ESPACE;
- return err;
- }
- }
- else
- {
- err = re_node_set_init_1 (&union_set, next_node);
- if (BE (err != REG_NOERROR, 0))
- return err;
- }
- mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
- re_node_set_free (&union_set);
- if (BE (mctx->state_log[to_idx] == NULL
- && err != REG_NOERROR, 0))
- return err;
- }
- }
- return REG_NOERROR;
-}
-
-/* Build transition table for the state.
- Return the new table if succeeded, otherwise return NULL. */
-
-static re_dfastate_t **
-build_trtable (preg, state, fl_search)
- const regex_t *preg;
- const re_dfastate_t *state;
- int fl_search;
-{
- reg_errcode_t err;
- re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- int i, j, k, ch;
- int dests_node_malloced = 0, dest_states_malloced = 0;
- int ndests; /* Number of the destination states from `state'. */
- re_dfastate_t **trtable;
- re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
- re_node_set follows, *dests_node;
- bitset *dests_ch;
- bitset acceptable;
-
- /* We build DFA states which corresponds to the destination nodes
- from `state'. `dests_node[i]' represents the nodes which i-th
- destination state contains, and `dests_ch[i]' represents the
- characters which i-th destination state accepts. */
-#ifdef _LIBC
- if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX))
- dests_node = (re_node_set *)
- alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX);
- else
-#endif
- {
- dests_node = (re_node_set *)
- malloc ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX);
- if (BE (dests_node == NULL, 0))
- return NULL;
- dests_node_malloced = 1;
- }
- dests_ch = (bitset *) (dests_node + SBC_MAX);
-
- /* Initialize transiton table. */
- trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
- if (BE (trtable == NULL, 0))
- {
- if (dests_node_malloced)
- free (dests_node);
- return NULL;
- }
-
- /* At first, group all nodes belonging to `state' into several
- destinations. */
- ndests = group_nodes_into_DFAstates (preg, state, dests_node, dests_ch);
- if (BE (ndests <= 0, 0))
- {
- if (dests_node_malloced)
- free (dests_node);
- /* Return NULL in case of an error, trtable otherwise. */
- if (ndests == 0)
- return trtable;
- free (trtable);
- return NULL;
- }
-
- err = re_node_set_alloc (&follows, ndests + 1);
- if (BE (err != REG_NOERROR, 0))
- goto out_free;
-
-#ifdef _LIBC
- if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX
- + ndests * 3 * sizeof (re_dfastate_t *)))
- dest_states = (re_dfastate_t **)
- alloca (ndests * 3 * sizeof (re_dfastate_t *));
- else
-#endif
- {
- dest_states = (re_dfastate_t **)
- malloc (ndests * 3 * sizeof (re_dfastate_t *));
- if (BE (dest_states == NULL, 0))
- {
-out_free:
- if (dest_states_malloced)
- free (dest_states);
- re_node_set_free (&follows);
- for (i = 0; i < ndests; ++i)
- re_node_set_free (dests_node + i);
- free (trtable);
- if (dests_node_malloced)
- free (dests_node);
- return NULL;
- }
- dest_states_malloced = 1;
- }
- dest_states_word = dest_states + ndests;
- dest_states_nl = dest_states_word + ndests;
- bitset_empty (acceptable);
-
- /* Then build the states for all destinations. */
- for (i = 0; i < ndests; ++i)
- {
- int next_node;
- re_node_set_empty (&follows);
- /* Merge the follows of this destination states. */
- for (j = 0; j < dests_node[i].nelem; ++j)
- {
- next_node = dfa->nexts[dests_node[i].elems[j]];
- if (next_node != -1)
- {
- err = re_node_set_merge (&follows, dfa->eclosures + next_node);
- if (BE (err != REG_NOERROR, 0))
- goto out_free;
- }
- }
- /* If search flag is set, merge the initial state. */
- if (fl_search)
- {
-#ifdef RE_ENABLE_I18N
- int not_initial = 0;
- for (j = 0; j < follows.nelem; ++j)
- if (dfa->nodes[follows.elems[j]].type == CHARACTER)
- {
- not_initial = dfa->nodes[follows.elems[j]].mb_partial;
- break;
- }
- if (!not_initial)
-#endif
- {
- err = re_node_set_merge (&follows,
- dfa->init_state->entrance_nodes);
- if (BE (err != REG_NOERROR, 0))
- goto out_free;
- }
- }
- dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
- if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
- goto out_free;
- /* If the new state has context constraint,
- build appropriate states for these contexts. */
- if (dest_states[i]->has_constraint)
- {
- dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
- CONTEXT_WORD);
- if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
- goto out_free;
- dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
- CONTEXT_NEWLINE);
- if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
- goto out_free;
- }
- else
- {
- dest_states_word[i] = dest_states[i];
- dest_states_nl[i] = dest_states[i];
- }
- bitset_merge (acceptable, dests_ch[i]);
- }
-
- /* Update the transition table. */
- /* For all characters ch...: */
- for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
- for (j = 0; j < UINT_BITS; ++j, ++ch)
- if ((acceptable[i] >> j) & 1)
- {
- /* The current state accepts the character ch. */
- if (IS_WORD_CHAR (ch))
- {
- for (k = 0; k < ndests; ++k)
- if ((dests_ch[k][i] >> j) & 1)
- {
- /* k-th destination accepts the word character ch. */
- trtable[ch] = dest_states_word[k];
- /* There must be only one destination which accepts
- character ch. See group_nodes_into_DFAstates. */
- break;
- }
- }
- else /* not WORD_CHAR */
- {
- for (k = 0; k < ndests; ++k)
- if ((dests_ch[k][i] >> j) & 1)
- {
- /* k-th destination accepts the non-word character ch. */
- trtable[ch] = dest_states[k];
- /* There must be only one destination which accepts
- character ch. See group_nodes_into_DFAstates. */
- break;
- }
- }
- }
- /* new line */
- if (bitset_contain (acceptable, NEWLINE_CHAR))
- {
- /* The current state accepts newline character. */
- for (k = 0; k < ndests; ++k)
- if (bitset_contain (dests_ch[k], NEWLINE_CHAR))
- {
- /* k-th destination accepts newline character. */
- trtable[NEWLINE_CHAR] = dest_states_nl[k];
- /* There must be only one destination which accepts
- newline. See group_nodes_into_DFAstates. */
- break;
- }
- }
-
- if (dest_states_malloced)
- free (dest_states);
-
- re_node_set_free (&follows);
- for (i = 0; i < ndests; ++i)
- re_node_set_free (dests_node + i);
-
- if (dests_node_malloced)
- free (dests_node);
-
- return trtable;
-}
-
-/* Group all nodes belonging to STATE into several destinations.
- Then for all destinations, set the nodes belonging to the destination
- to DESTS_NODE[i] and set the characters accepted by the destination
- to DEST_CH[i]. This function return the number of destinations. */
-
-static int
-group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
- const regex_t *preg;
- const re_dfastate_t *state;
- re_node_set *dests_node;
- bitset *dests_ch;
-{
- reg_errcode_t err;
- const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- int i, j, k;
- int ndests; /* Number of the destinations from `state'. */
- bitset accepts; /* Characters a node can accept. */
- const re_node_set *cur_nodes = &state->nodes;
- bitset_empty (accepts);
- ndests = 0;
-
- /* For all the nodes belonging to `state', */
- for (i = 0; i < cur_nodes->nelem; ++i)
- {
- re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
- re_token_type_t type = node->type;
- unsigned int constraint = node->constraint;
-
- /* Enumerate all single byte character this node can accept. */
- if (type == CHARACTER)
- bitset_set (accepts, node->opr.c);
- else if (type == SIMPLE_BRACKET)
- {
- bitset_merge (accepts, node->opr.sbcset);
- }
- else if (type == OP_PERIOD)
- {
- bitset_set_all (accepts);
- if (!(preg->syntax & RE_DOT_NEWLINE))
- bitset_clear (accepts, '\n');
- if (preg->syntax & RE_DOT_NOT_NULL)
- bitset_clear (accepts, '\0');
- }
- else
- continue;
-
- /* Check the `accepts' and sift the characters which are not
- match it the context. */
- if (constraint)
- {
- if (constraint & NEXT_WORD_CONSTRAINT)
- for (j = 0; j < BITSET_UINTS; ++j)
- accepts[j] &= dfa->word_char[j];
- if (constraint & NEXT_NOTWORD_CONSTRAINT)
- for (j = 0; j < BITSET_UINTS; ++j)
- accepts[j] &= ~dfa->word_char[j];
- if (constraint & NEXT_NEWLINE_CONSTRAINT)
- {
- int accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
- bitset_empty (accepts);
- if (accepts_newline)
- bitset_set (accepts, NEWLINE_CHAR);
- else
- continue;
- }
- }
-
- /* Then divide `accepts' into DFA states, or create a new
- state. */
- for (j = 0; j < ndests; ++j)
- {
- bitset intersec; /* Intersection sets, see below. */
- bitset remains;
- /* Flags, see below. */
- int has_intersec, not_subset, not_consumed;
-
- /* Optimization, skip if this state doesn't accept the character. */
- if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
- continue;
-
- /* Enumerate the intersection set of this state and `accepts'. */
- has_intersec = 0;
- for (k = 0; k < BITSET_UINTS; ++k)
- has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
- /* And skip if the intersection set is empty. */
- if (!has_intersec)
- continue;
-
- /* Then check if this state is a subset of `accepts'. */
- not_subset = not_consumed = 0;
- for (k = 0; k < BITSET_UINTS; ++k)
- {
- not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
- not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
- }
-
- /* If this state isn't a subset of `accepts', create a
- new group state, which has the `remains'. */
- if (not_subset)
- {
- bitset_copy (dests_ch[ndests], remains);
- bitset_copy (dests_ch[j], intersec);
- err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
- if (BE (err != REG_NOERROR, 0))
- goto error_return;
- ++ndests;
- }
-
- /* Put the position in the current group. */
- err = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
- if (BE (err < 0, 0))
- goto error_return;
-
- /* If all characters are consumed, go to next node. */
- if (!not_consumed)
- break;
- }
- /* Some characters remain, create a new group. */
- if (j == ndests)
- {
- bitset_copy (dests_ch[ndests], accepts);
- err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
- if (BE (err != REG_NOERROR, 0))
- goto error_return;
- ++ndests;
- bitset_empty (accepts);
- }
- }
- return ndests;
- error_return:
- for (j = 0; j < ndests; ++j)
- re_node_set_free (dests_node + j);
- return -1;
-}
-
-#ifdef RE_ENABLE_I18N
-/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
- Return the number of the bytes the node accepts.
- STR_IDX is the current index of the input string.
-
- This function handles the nodes which can accept one character, or
- one collating element like '.', '[a-z]', opposite to the other nodes
- can only accept one byte. */
-
-static int
-check_node_accept_bytes (preg, node_idx, input, str_idx)
- const regex_t *preg;
- int node_idx, str_idx;
- const re_string_t *input;
-{
- const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
- const re_token_t *node = dfa->nodes + node_idx;
- int elem_len = re_string_elem_size_at (input, str_idx);
- int char_len = re_string_char_size_at (input, str_idx);
- int i;
-# ifdef _LIBC
- int j;
- uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
-# endif /* _LIBC */
- if (elem_len <= 1 && char_len <= 1)
- return 0;
- if (node->type == OP_PERIOD)
- {
- /* '.' accepts any one character except the following two cases. */
- if ((!(preg->syntax & RE_DOT_NEWLINE) &&
- re_string_byte_at (input, str_idx) == '\n') ||
- ((preg->syntax & RE_DOT_NOT_NULL) &&
- re_string_byte_at (input, str_idx) == '\0'))
- return 0;
- return char_len;
- }
- else if (node->type == COMPLEX_BRACKET)
- {
- const re_charset_t *cset = node->opr.mbcset;
-# ifdef _LIBC
- const unsigned char *pin = ((char *) re_string_get_buffer (input)
- + str_idx);
-# endif /* _LIBC */
- int match_len = 0;
- wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
- ? re_string_wchar_at (input, str_idx) : 0);
-
- /* match with multibyte character? */
- for (i = 0; i < cset->nmbchars; ++i)
- if (wc == cset->mbchars[i])
- {
- match_len = char_len;
- goto check_node_accept_bytes_match;
- }
- /* match with character_class? */
- for (i = 0; i < cset->nchar_classes; ++i)
- {
- wctype_t wt = cset->char_classes[i];
- if (__iswctype (wc, wt))
- {
- match_len = char_len;
- goto check_node_accept_bytes_match;
- }
- }
-
-# ifdef _LIBC
- if (nrules != 0)
- {
- unsigned int in_collseq = 0;
- const int32_t *table, *indirect;
- const unsigned char *weights, *extra;
- const char *collseqwc;
- int32_t idx;
- /* This #include defines a local function! */
-# include <locale/weight.h>
-
- /* match with collating_symbol? */
- if (cset->ncoll_syms)
- extra = (const unsigned char *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
- for (i = 0; i < cset->ncoll_syms; ++i)
- {
- const unsigned char *coll_sym = extra + cset->coll_syms[i];
- /* Compare the length of input collating element and
- the length of current collating element. */
- if (*coll_sym != elem_len)
- continue;
- /* Compare each bytes. */
- for (j = 0; j < *coll_sym; j++)
- if (pin[j] != coll_sym[1 + j])
- break;
- if (j == *coll_sym)
- {
- /* Match if every bytes is equal. */
- match_len = j;
- goto check_node_accept_bytes_match;
- }
- }
-
- if (cset->nranges)
- {
- if (elem_len <= char_len)
- {
- collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
- in_collseq = collseq_table_lookup (collseqwc, wc);
- }
- else
- in_collseq = find_collation_sequence_value (pin, elem_len);
- }
- /* match with range expression? */
- for (i = 0; i < cset->nranges; ++i)
- if (cset->range_starts[i] <= in_collseq
- && in_collseq <= cset->range_ends[i])
- {
- match_len = elem_len;
- goto check_node_accept_bytes_match;
- }
-
- /* match with equivalence_class? */
- if (cset->nequiv_classes)
- {
- const unsigned char *cp = pin;
- table = (const int32_t *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
- weights = (const unsigned char *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
- extra = (const unsigned char *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
- indirect = (const int32_t *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
- idx = findidx (&cp);
- if (idx > 0)
- for (i = 0; i < cset->nequiv_classes; ++i)
- {
- int32_t equiv_class_idx = cset->equiv_classes[i];
- size_t weight_len = weights[idx];
- if (weight_len == weights[equiv_class_idx])
- {
- int cnt = 0;
- while (cnt <= weight_len
- && (weights[equiv_class_idx + 1 + cnt]
- == weights[idx + 1 + cnt]))
- ++cnt;
- if (cnt > weight_len)
- {
- match_len = elem_len;
- goto check_node_accept_bytes_match;
- }
- }
- }
- }
- }
- else
-# endif /* _LIBC */
- {
- /* match with range expression? */
-#if __GNUC__ >= 2
- wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
-#else
- wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
- cmp_buf[2] = wc;
-#endif
- for (i = 0; i < cset->nranges; ++i)
- {
- cmp_buf[0] = cset->range_starts[i];
- cmp_buf[4] = cset->range_ends[i];
- if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
- && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
- {
- match_len = char_len;
- goto check_node_accept_bytes_match;
- }
- }
- }
- check_node_accept_bytes_match:
- if (!cset->non_match)
- return match_len;
- else
- {
- if (match_len > 0)
- return 0;
- else
- return (elem_len > char_len) ? elem_len : char_len;
- }
- }
- return 0;
-}
-
-# ifdef _LIBC
-static unsigned int
-find_collation_sequence_value (mbs, mbs_len)
- const unsigned char *mbs;
- size_t mbs_len;
-{
- uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
- if (nrules == 0)
- {
- if (mbs_len == 1)
- {
- /* No valid character. Match it as a single byte character. */
- const unsigned char *collseq = (const unsigned char *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
- return collseq[mbs[0]];
- }
- return UINT_MAX;
- }
- else
- {
- int32_t idx;
- const unsigned char *extra = (const unsigned char *)
- _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
-
- for (idx = 0; ;)
- {
- int mbs_cnt, found = 0;
- int32_t elem_mbs_len;
- /* Skip the name of collating element name. */
- idx = idx + extra[idx] + 1;
- elem_mbs_len = extra[idx++];
- if (mbs_len == elem_mbs_len)
- {
- for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
- if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
- break;
- if (mbs_cnt == elem_mbs_len)
- /* Found the entry. */
- found = 1;
- }
- /* Skip the byte sequence of the collating element. */
- idx += elem_mbs_len;
- /* Adjust for the alignment. */
- idx = (idx + 3) & ~3;
- /* Skip the collation sequence value. */
- idx += sizeof (uint32_t);
- /* Skip the wide char sequence of the collating element. */
- idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
- /* If we found the entry, return the sequence value. */
- if (found)
- return *(uint32_t *) (extra + idx);
- /* Skip the collation sequence value. */
- idx += sizeof (uint32_t);
- }
- }
-}
-# endif /* _LIBC */
-#endif /* RE_ENABLE_I18N */
-
-/* Check whether the node accepts the byte which is IDX-th
- byte of the INPUT. */
-
-static int
-check_node_accept (preg, node, mctx, idx)
- const regex_t *preg;
- const re_token_t *node;
- const re_match_context_t *mctx;
- int idx;
-{
- unsigned char ch;
- if (node->constraint)
- {
- /* The node has constraints. Check whether the current context
- satisfies the constraints. */
- unsigned int context = re_string_context_at (mctx->input, idx,
- mctx->eflags,
- preg->newline_anchor);
- if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
- return 0;
- }
- ch = re_string_byte_at (mctx->input, idx);
- if (node->type == CHARACTER)
- return node->opr.c == ch;
- else if (node->type == SIMPLE_BRACKET)
- return bitset_contain (node->opr.sbcset, ch);
- else if (node->type == OP_PERIOD)
- return !((ch == '\n' && !(preg->syntax & RE_DOT_NEWLINE))
- || (ch == '\0' && (preg->syntax & RE_DOT_NOT_NULL)));
- else
- return 0;
-}
-
-/* Extend the buffers, if the buffers have run out. */
-
-static reg_errcode_t
-extend_buffers (mctx)
- re_match_context_t *mctx;
-{
- reg_errcode_t ret;
- re_string_t *pstr = mctx->input;
-
- /* Double the lengthes of the buffers. */
- ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
- if (BE (ret != REG_NOERROR, 0))
- return ret;
-
- if (mctx->state_log != NULL)
- {
- /* And double the length of state_log. */
- re_dfastate_t **new_array;
- new_array = re_realloc (mctx->state_log, re_dfastate_t *,
- pstr->bufs_len * 2);
- if (BE (new_array == NULL, 0))
- return REG_ESPACE;
- mctx->state_log = new_array;
- }
-
- /* Then reconstruct the buffers. */
- if (pstr->icase)
- {
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- build_wcs_upper_buffer (pstr);
- else
-#endif /* RE_ENABLE_I18N */
- build_upper_buffer (pstr);
- }
- else
- {
-#ifdef RE_ENABLE_I18N
- if (MB_CUR_MAX > 1)
- build_wcs_buffer (pstr);
- else
-#endif /* RE_ENABLE_I18N */
- {
- if (pstr->trans != NULL)
- re_string_translate_buffer (pstr);
- else
- pstr->valid_len = pstr->bufs_len;
- }
- }
- return REG_NOERROR;
-}
-
-\f
-/* Functions for matching context. */
-
-/* Initialize MCTX. */
-
-static reg_errcode_t
-match_ctx_init (mctx, eflags, input, n)
- re_match_context_t *mctx;
- int eflags, n;
- re_string_t *input;
-{
- mctx->eflags = eflags;
- mctx->input = input;
- mctx->match_last = -1;
- if (n > 0)
- {
- mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
- mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
- if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
- return REG_ESPACE;
- }
- else
- mctx->bkref_ents = NULL;
- mctx->nbkref_ents = 0;
- mctx->abkref_ents = n;
- mctx->max_mb_elem_len = 1;
- mctx->nsub_tops = 0;
- mctx->asub_tops = n;
- return REG_NOERROR;
-}
-
-/* Clean the entries which depend on the current input in MCTX.
- This function must be invoked when the matcher changes the start index
- of the input, or changes the input string. */
-
-static void
-match_ctx_clean (mctx)
- re_match_context_t *mctx;
-{
- match_ctx_free_subtops (mctx);
- mctx->nsub_tops = 0;
- mctx->nbkref_ents = 0;
-}
-
-/* Free all the memory associated with MCTX. */
-
-static void
-match_ctx_free (mctx)
- re_match_context_t *mctx;
-{
- match_ctx_free_subtops (mctx);
- re_free (mctx->sub_tops);
- re_free (mctx->bkref_ents);
-}
-
-/* Free all the memory associated with MCTX->SUB_TOPS. */
-
-static void
-match_ctx_free_subtops (mctx)
- re_match_context_t *mctx;
-{
- int st_idx;
- for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
- {
- int sl_idx;
- re_sub_match_top_t *top = mctx->sub_tops[st_idx];
- for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
- {
- re_sub_match_last_t *last = top->lasts[sl_idx];
- re_free (last->path.array);
- re_free (last);
- }
- re_free (top->lasts);
- if (top->path)
- {
- re_free (top->path->array);
- re_free (top->path);
- }
- free (top);
- }
-}
-
-/* Add a new backreference entry to MCTX.
- Note that we assume that caller never call this function with duplicate
- entry, and call with STR_IDX which isn't smaller than any existing entry.
-*/
-
-static reg_errcode_t
-match_ctx_add_entry (mctx, node, str_idx, from, to)
- re_match_context_t *mctx;
- int node, str_idx, from, to;
-{
- if (mctx->nbkref_ents >= mctx->abkref_ents)
- {
- struct re_backref_cache_entry* new_entry;
- new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
- mctx->abkref_ents * 2);
- if (BE (new_entry == NULL, 0))
- {
- re_free (mctx->bkref_ents);
- return REG_ESPACE;
- }
- mctx->bkref_ents = new_entry;
- memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
- sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
- mctx->abkref_ents *= 2;
- }
- mctx->bkref_ents[mctx->nbkref_ents].node = node;
- mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
- mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
- mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
- mctx->bkref_ents[mctx->nbkref_ents++].flag = 0;
- if (mctx->max_mb_elem_len < to - from)
- mctx->max_mb_elem_len = to - from;
- return REG_NOERROR;
-}
-
-/* Search for the first entry which has the same str_idx.
- Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */
-
-static int
-search_cur_bkref_entry (mctx, str_idx)
- re_match_context_t *mctx;
- int str_idx;
-{
- int left, right, mid;
- right = mctx->nbkref_ents;
- for (left = 0; left < right;)
- {
- mid = (left + right) / 2;
- if (mctx->bkref_ents[mid].str_idx < str_idx)
- left = mid + 1;
- else
- right = mid;
- }
- return left;
-}
-
-static void
-match_ctx_clear_flag (mctx)
- re_match_context_t *mctx;
-{
- int i;
- for (i = 0; i < mctx->nbkref_ents; ++i)
- {
- mctx->bkref_ents[i].flag = 0;
- }
-}
-
-/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
- at STR_IDX. */
-
-static reg_errcode_t
-match_ctx_add_subtop (mctx, node, str_idx)
- re_match_context_t *mctx;
- int node, str_idx;
-{
-#ifdef DEBUG
- assert (mctx->sub_tops != NULL);
- assert (mctx->asub_tops > 0);
-#endif
- if (mctx->nsub_tops == mctx->asub_tops)
- {
- re_sub_match_top_t **new_array;
- mctx->asub_tops *= 2;
- new_array = re_realloc (mctx->sub_tops, re_sub_match_top_t *,
- mctx->asub_tops);
- if (BE (new_array == NULL, 0))
- return REG_ESPACE;
- mctx->sub_tops = new_array;
- }
- mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
- if (mctx->sub_tops[mctx->nsub_tops] == NULL)
- return REG_ESPACE;
- mctx->sub_tops[mctx->nsub_tops]->node = node;
- mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
- return REG_NOERROR;
-}
-
-/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
- at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */
-
-static re_sub_match_last_t *
-match_ctx_add_sublast (subtop, node, str_idx)
- re_sub_match_top_t *subtop;
- int node, str_idx;
-{
- re_sub_match_last_t *new_entry;
- if (subtop->nlasts == subtop->alasts)
- {
- re_sub_match_last_t **new_array;
- subtop->alasts = 2 * subtop->alasts + 1;
- new_array = re_realloc (subtop->lasts, re_sub_match_last_t *,
- subtop->alasts);
- if (BE (new_array == NULL, 0))
- return NULL;
- subtop->lasts = new_array;
- }
- new_entry = calloc (1, sizeof (re_sub_match_last_t));
- if (BE (new_entry == NULL, 0))
- return NULL;
- subtop->lasts[subtop->nlasts] = new_entry;
- new_entry->node = node;
- new_entry->str_idx = str_idx;
- ++subtop->nlasts;
- return new_entry;
-}
-
-static void
-sift_ctx_init (sctx, sifted_sts, limited_sts, last_node, last_str_idx,
- check_subexp)
- re_sift_context_t *sctx;
- re_dfastate_t **sifted_sts, **limited_sts;
- int last_node, last_str_idx, check_subexp;
-{
- sctx->sifted_states = sifted_sts;
- sctx->limited_states = limited_sts;
- sctx->last_node = last_node;
- sctx->last_str_idx = last_str_idx;
- sctx->check_subexp = check_subexp;
- sctx->cur_bkref = -1;
- sctx->cls_subexp_idx = -1;
- re_node_set_init_empty (&sctx->limits);
-}
/* Elements */
+struct xml_dtd_elems_table;
+
+static void
+xml_dtd_elems_init_data(struct xml_dtd_elems_table *tab UNUSED, struct xml_dtd_elem *e)
+{
+ slist_init(&e->attrs);
+}
+
#define HASH_PREFIX(x) xml_dtd_elems_##x
#define HASH_NODE struct xml_dtd_elem
#define HASH_KEY_STRING name
#define HASH_WANT_FIND
#define HASH_WANT_LOOKUP
#define HASH_GIVE_ALLOC
+#define HASH_GIVE_INIT_DATA
#define HASH_TABLE_ALLOC
XML_HASH_GIVE_ALLOC
#include "lib/hashtable.h"
{
attr->elem = elem;
attr->name = name;
+ slist_add_tail(&elem->attrs, &attr->n);
}
#define HASH_PREFIX(x) xml_dtd_attrs_##x
uns type;
char *name;
struct xml_dtd_elem_node *node;
+ slist attrs;
void *user; /* User-defined */
};
};
struct xml_dtd_attr {
+ snode n;
char *name; /* Attribute name */
struct xml_dtd_elem *elem; /* Owner element */
uns type; /* See enum xml_dtd_attr_type */
uns len = xml_end_chars(ctx, &text), rlen;
if (len)
{
+ if (ctx->flags & XML_NO_CHARS)
+ {
+ if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_ignorable)
+ ctx->h_ignorable(ctx, text, len);
+ mp_restore(ctx->pool, &ctx->chars_state);
+ return 0;
+ }
if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_block && (rlen = xml_report_chars(ctx, &rtext)))
ctx->h_block(ctx, rtext, rlen);
- if (!(ctx->flags & XML_ALLOC_CHARS) && (!(ctx->flags & XML_REPORT_CHARS) || !ctx->h_chars))
+ if (!(ctx->flags & XML_ALLOC_CHARS) && !(ctx->flags & XML_REPORT_CHARS))
{
mp_restore(ctx->pool, &ctx->chars_state);
return 0;
{
TRACE(ctx, "append_chars");
struct fastbuf *out = &ctx->chars;
- while (xml_get_char(ctx) != '<')
- if (xml_last_char(ctx) == '&')
- {
- xml_inc(ctx);
- xml_parse_ref(ctx);
- }
- else
- bput_utf8_32(out, xml_last_char(ctx));
+ if (ctx->flags & XML_NO_CHARS)
+ while (xml_get_char(ctx) != '<')
+ if (xml_last_cat(ctx) & XML_CHAR_WHITE)
+ bput_utf8_32(out, xml_last_char(ctx));
+ else
+ {
+ xml_error(ctx, "This element must not contain character data");
+ while (xml_get_char(ctx) != '<');
+ break;
+ }
+ else
+ while (xml_get_char(ctx) != '<')
+ if (xml_last_char(ctx) == '&')
+ {
+ xml_inc(ctx);
+ xml_parse_ref(ctx);
+ }
+ else
+ bput_utf8_32(out, xml_last_char(ctx));
xml_unget_char(ctx);
}
/*** CDATA sections ***/
+static void
+xml_skip_cdata(struct xml_context *ctx)
+{
+ TRACE(ctx, "skip_cdata");
+ xml_parse_seq(ctx, "CDATA[");
+ while (xml_get_char(ctx) != ']' || xml_get_char(ctx) != ']' || xml_get_char(ctx) != '>');
+ xml_dec(ctx);
+}
+
static void
xml_append_cdata(struct xml_context *ctx)
{
/* CDSect :== '<![CDATA[' (Char* - (Char* ']]>' Char*)) ']]>'
* Already parsed: '<![' */
TRACE(ctx, "append_cdata");
+ if (ctx->flags & XML_NO_CHARS)
+ {
+ xml_error(ctx, "This element must not contain CDATA");
+ xml_skip_cdata(ctx);
+ return;
+ }
xml_parse_seq(ctx, "CDATA[");
struct fastbuf *out = &ctx->chars;
uns rlen;
xml_dec(ctx);
}
-static void UNUSED
-xml_skip_cdata(struct xml_context *ctx)
-{
- TRACE(ctx, "skip_cdata");
- xml_parse_seq(ctx, "CDATA[");
- while (xml_get_char(ctx) != ']' || xml_get_char(ctx) != ']' || xml_get_char(ctx) != '>');
- xml_dec(ctx);
-}
-
/*** Attribute values ***/
char *
return xml_attrs_find(ctx->tab_attrs, node, name);
}
+char *
+xml_attr_value(struct xml_context *ctx, struct xml_node *node, char *name)
+{
+ struct xml_attr *attr = xml_attrs_find(ctx->tab_attrs, node, name);
+ if (attr)
+ return attr->val;
+ if (!node->dtd)
+ return NULL;
+ struct xml_dtd_attr *dtd = xml_dtd_find_attr(ctx, node->dtd, name);
+ return dtd ? dtd->default_value : NULL;
+}
+
void
xml_attrs_table_init(struct xml_context *ctx)
{
/*** Elements ***/
+static uns
+xml_validate_element(struct xml_dtd_elem_node *root, struct xml_dtd_elem *elem)
+{
+ if (root->elem)
+ return elem == root->elem;
+ else
+ SLIST_FOR_EACH(struct xml_dtd_elem_node *, son, root->sons)
+ if (xml_validate_element(son, elem))
+ return 1;
+ return 0;
+}
+
static void
xml_push_element(struct xml_context *ctx)
{
xml_error(ctx, "Undefined element <%s>", e->name);
else
{
- // FIXME: validate regular expressions
+ struct xml_dtd_elem *dtd = e->dtd, *parent_dtd = e->parent ? e->parent->dtd : NULL;
+ if (dtd->type == XML_DTD_ELEM_MIXED)
+ ctx->flags &= ~XML_NO_CHARS;
+ else
+ ctx->flags |= XML_NO_CHARS;
+ if (parent_dtd)
+ if (parent_dtd->type == XML_DTD_ELEM_EMPTY)
+ xml_error(ctx, "Empty element must not contain children");
+ else if (parent_dtd->type != XML_DTD_ELEM_ANY)
+ {
+ // FIXME: validate regular expressions
+ if (!xml_validate_element(parent_dtd->node, dtd))
+ xml_error(ctx, "Unexpected element <%s>", e->name);
+ }
}
while (1)
{
xml_unget_char(ctx);
xml_parse_attr(ctx);
}
+ if (e->dtd)
+ SLIST_FOR_EACH(struct xml_dtd_attr *, a, e->dtd->attrs)
+ if (a->default_mode == XML_ATTR_REQUIRED)
+ {
+ if (!xml_attrs_find(ctx->tab_attrs, e, a->name))
+ xml_error(ctx, "Missing required attribute %s in element <%s>", a->name, e->name);
+ }
+ else if (a->default_mode != XML_ATTR_IMPLIED && ctx->flags & XML_ALLOC_DEFAULT_ATTRS)
+ {
+ struct xml_attr *attr = xml_attrs_lookup(ctx->tab_attrs, e, a->name);
+ if (!attr->val)
+ attr->val = a->default_value;
+ }
if ((ctx->flags & XML_REPORT_TAGS) && ctx->h_stag)
ctx->h_stag(ctx);
}
ctx->flags |= XML_HAS_EXTERNAL_SUBSET;
}
if (xml_peek_char(ctx) == '[')
- ctx->flags |= XML_HAS_INTERNAL_SUBSET;
+ {
+ ctx->flags |= XML_HAS_INTERNAL_SUBSET;
+ xml_skip_char(ctx);
+ xml_inc(ctx);
+ }
if (ctx->h_doctype_decl)
ctx->h_doctype_decl(ctx);
}
/* DTD: Internal subset */
static void
-xml_parse_internal_subset(struct xml_context *ctx)
+xml_parse_subset(struct xml_context *ctx, uns external)
{
- // FIXME: comments/pi have no parent
+ // FIXME:
+ // -- comments/pi have no parent
+ // -- conditional sections in external subset
+ // -- check corectness of parameter entities
+
/* '[' intSubset ']'
* intSubset :== (markupdecl | DeclSep)
- * Already parsed: ']' */
+ * Already parsed: '['
+ *
+ * extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep)*
+ */
while (1)
{
xml_parse_white(ctx, 0);
goto invalid_markup;
else if (c == '%')
xml_parse_pe_ref(ctx);
- else if (c == ']')
- break;
+ else if (c == ']' && !external)
+ {
+ break;
+ }
+ else if (c == '>' && external)
+ {
+ break;
+ }
else
goto invalid_markup;
}
xml_dec(ctx);
- xml_dec(ctx);
return;
-invalid_markup:
- xml_fatal(ctx, "Invalid markup in the internal subset");
+invalid_markup: ;
+ xml_fatal(ctx, "Invalid markup in the %s subset", external ? "external" : "internal");
}
/*** The State Machine ***/
xml_unget_char(ctx);
xml_parse_doctype_decl(ctx);
PULL(DOCTYPE_DECL);
- if (xml_peek_char(ctx) == '[')
- {
- xml_skip_char(ctx);
- xml_inc(ctx);
- if (ctx->flags & XML_PARSE_DTD)
- {
- xml_dtd_init(ctx);
- if (ctx->h_dtd_start)
- ctx->h_dtd_start(ctx);
- // FIXME: pull iface?
- xml_parse_internal_subset(ctx);
- // FIXME: external subset
- if (ctx->h_dtd_end)
- ctx->h_dtd_end(ctx);
- }
- else
- xml_skip_internal_subset(ctx);
- }
+ if (ctx->flags & XML_HAS_DTD)
+ if (ctx->flags & XML_PARSE_DTD)
+ {
+ xml_dtd_init(ctx);
+ if (ctx->h_dtd_start)
+ ctx->h_dtd_start(ctx);
+ if (ctx->flags & XML_HAS_INTERNAL_SUBSET)
+ {
+ xml_parse_subset(ctx, 0);
+ xml_dec(ctx);
+ }
+ if (ctx->flags & XML_HAS_EXTERNAL_SUBSET)
+ {
+ struct xml_dtd_entity ent = {
+ .system_id = ctx->system_id,
+ .public_id = ctx->public_id,
+ };
+ xml_parse_white(ctx, 0);
+ xml_parse_char(ctx, '>');
+ xml_unget_char(ctx);
+ ASSERT(ctx->h_resolve_entity);
+ ctx->h_resolve_entity(ctx, &ent);
+ ctx->flags |= XML_SRC_EXPECTED_DECL;
+ xml_parse_subset(ctx, 1);
+ xml_unget_char(ctx);;
+ }
+ if (ctx->h_dtd_end)
+ ctx->h_dtd_end(ctx);
+ }
+ else if (ctx->flags & XML_HAS_INTERNAL_SUBSET)
+ xml_skip_internal_subset(ctx);
xml_parse_white(ctx, 0);
xml_parse_char(ctx, '>');
xml_dec(ctx);
ASSERT(0);
}
+uns
+xml_next_state(struct xml_context *ctx, uns pull)
+{
+ uns saved = ctx->pull;
+ ctx->pull = pull;
+ uns res = xml_next(ctx);
+ ctx->pull = saved;
+ return res;
+}
+
+uns
+xml_skip_element(struct xml_context *ctx)
+{
+ ASSERT(ctx->state == XML_STATE_STAG);
+ struct xml_node *node = ctx->node;
+ uns saved = ctx->pull, res;
+ ctx->pull = XML_PULL_ETAG;
+ while ((res = xml_next(ctx)) && ctx->node != node);
+ ctx->pull = saved;
+ return res;
+}
+
uns
xml_parse(struct xml_context *ctx)
{
while (xml_next(ctx));
return ctx->err_code;
}
+
+char *
+xml_merge_chars(struct xml_context *ctx UNUSED, struct xml_node *node, struct mempool *pool)
+{
+ ASSERT(node->type == XML_NODE_ELEM);
+ char *p = mp_start_noalign(pool, 1);
+ XML_NODE_FOR_EACH(son, node)
+ if (son->type == XML_NODE_CHARS)
+ {
+ p = mp_spread(pool, p, son->len + 1);
+ memcpy(p, son->text, son->len);
+ p += son->len;
+ }
+ *p++ = 0;
+ return mp_end(pool, p);
+}
+
+static char *
+xml_append_dom_chars(char *p, struct mempool *pool, struct xml_node *node)
+{
+ XML_NODE_FOR_EACH(son, node)
+ if (son->type == XML_NODE_CHARS)
+ {
+ p = mp_spread(pool, p, son->len + 1);
+ memcpy(p, son->text, son->len);
+ p += son->len;
+ }
+ else if (son->type == XML_NODE_ELEM)
+ p = xml_append_dom_chars(p, pool, son);
+ return p;
+}
+
+char *
+xml_merge_dom_chars(struct xml_context *ctx UNUSED, struct xml_node *node, struct mempool *pool)
+{
+ ASSERT(node->type == XML_NODE_ELEM);
+ char *p = mp_start_noalign(pool, 1);
+ p = xml_append_dom_chars(p, pool, node);
+ *p++ = 0;
+ return mp_end(pool, p);
+}
src->next = ctx->src;
src->saved_depth = ctx->depth;
ctx->src = src;
- ctx->flags &= ~(XML_SRC_EOF | XML_SRC_EXPECTED_DECL | XML_SRC_NEW_LINE | XML_SRC_DOCUMENT);
+ ctx->flags &= ~(XML_SRC_EOF | XML_SRC_EXPECTED_DECL | XML_SRC_DOCUMENT);
ctx->bstop = ctx->bptr = src->buf;
ctx->depth = 0;
return src;
struct fastbuf *fb = src->fb; \
if (ctx->bptr == ctx->bstop) \
ctx->bptr = ctx->bstop = src->buf; \
- uns f = ctx->flags, c, t1 = src->refill_cat1, t2 = src->refill_cat2, row = src->row; \
+ uns c, t1 = src->refill_cat1, t2 = src->refill_cat2, row = src->row; \
u32 *bend = src->buf + ARRAY_SIZE(src->buf), *bstop = ctx->bstop, \
- *last_0xd = (f & XML_SRC_NEW_LINE) ? bstop : bend; \
+ *last_0xd = src->pending_0xd ? bstop : NULL; \
do \
{ \
c = func(fb, ##params); \
last_0xd = bstop + 2; \
else if (c != 0x2028 && last_0xd == bstop) \
{ \
- last_0xd = bend; \
+ last_0xd = NULL; \
continue; \
} \
xml_add_char(&bstop, 0xa), row++; \
else \
{ \
/* EOF */ \
- f |= XML_SRC_EOF; \
+ ctx->flags |= XML_SRC_EOF; \
break; \
} \
} \
while (bstop < bend); \
- ctx->flags = (last_0xd == bstop) ? f | XML_SRC_NEW_LINE : f & ~XML_SRC_NEW_LINE; \
+ src->pending_0xd = (last_0xd == bstop); \
ctx->bstop = bstop; \
src->row = row;
WANT_IGNORE_COMMENTS,
WANT_IGNORE_PIS,
WANT_REPORT_BLOCKS,
+ WANT_REPORT_IGNORABLE,
WANT_FILE_ENTITIES,
};
{ "hide-errors", 0, 0, WANT_HIDE_ERRORS },
{ "ignore-comments", 0, 0, WANT_IGNORE_COMMENTS },
{ "ignore-pis", 0, 0, WANT_IGNORE_PIS },
- { "reports-blocks", 0, 0, WANT_REPORT_BLOCKS },
+ { "report-blocks", 0, 0, WANT_REPORT_BLOCKS },
+ { "report-ignorable", 0, 0, WANT_REPORT_IGNORABLE },
{ "file-entities", 0, 0, WANT_FILE_ENTITIES },
{ NULL, 0, 0, 0 }
};
--ignore-comments Ignore comments\n\
--ignore-pis Ignore processing instructions\n\
--report-blocks Report blocks or characters and CDATA sections\n\
+ --report-ignorable Report ignorable whitespace\n\
--file-entities Resolve file external entities (not fully normative)\n\
\n", stderr);
exit(1);
static uns want_ignore_comments;
static uns want_ignore_pis;
static uns want_report_blocks;
+static uns want_report_ignorable;
static uns want_file_entities;
static struct fastbuf *out;
bprintf(out, "SAX: cdata text='%s'\n", text);
}
+static void
+h_ignorable(struct xml_context *ctx UNUSED, char *text, uns len UNUSED)
+{
+ bprintf(out, "SAX: ignorable text='%s'\n", text);
+}
+
static void
h_dtd_start(struct xml_context *ctx UNUSED)
{
case WANT_REPORT_BLOCKS:
want_report_blocks++;
break;
+ case WANT_REPORT_IGNORABLE:
+ want_report_ignorable++;
+ break;
case WANT_FILE_ENTITIES:
want_file_entities++;
break;
ctx.h_block = h_block;
ctx.h_cdata = h_cdata;
}
+ if (want_report_ignorable)
+ ctx.h_ignorable = h_ignorable;
ctx.h_dtd_start = h_dtd_start;
ctx.h_dtd_end = h_dtd_end;
}
Run: ../obj/sherlock/xml/xml-test -sptd
In: <?xml version="1.0"?>
<!DOCTYPE root [
- <!ELEMENT root ANY>
+ <!ELEMENT root (#PCDATA|a)*>
<!ENTITY % pe1 "<!ENTITY e1 'text'>">
%pe1;
<!ENTITY e2 '<&e1;>'>
- <!ELEMENT a ANY>
+ <!ELEMENT a (#PCDATA)*>
]>
<root>&e1;<a>&e2;</a></root>
Out: PULL: start
/* Other parameters */
XML_VALIDATING = 0x00000100, /* Validate everything (not fully implemented!) */
XML_PARSE_DTD = 0x00000200, /* Enable parsing of DTD */
+ XML_NO_CHARS = 0x00000400, /* The current element must not contain character data (filled automaticaly if using DTD) */
+ XML_ALLOC_DEFAULT_ATTRS = 0x00000800, /* Allocate default attribute values so they can be found by XML_ATTR_FOR_EACH */
/* Internals, do not change! */
XML_EMPTY_ELEM_TAG = 0x00010000, /* The current element match EmptyElemTag */
XML_VERSION_1_1 = 0x00020000, /* XML version is 1.1, otherwise 1.0 */
XML_HAS_EXTERNAL_SUBSET = 0x00040000, /* The document contains a reference to external DTD subset */
XML_HAS_INTERNAL_SUBSET = 0x00080000, /* The document contains an internal subset */
+ XML_HAS_DTD = XML_HAS_EXTERNAL_SUBSET | XML_HAS_INTERNAL_SUBSET,
XML_SRC_EOF = 0x00100000, /* EOF reached */
XML_SRC_EXPECTED_DECL = 0x00200000, /* Just before optional or required XMLDecl/TextDecl */
- XML_SRC_NEW_LINE = 0x00400000, /* The last read character is 0xD */
- XML_SRC_DOCUMENT = 0x00800000, /* The document entity */
- XML_SRC_EXTERNAL = 0x01000000, /* An external entity */
+ XML_SRC_DOCUMENT = 0x00400000, /* The document entity */
+ XML_SRC_EXTERNAL = 0x00800000, /* An external entity */
};
enum xml_node_type {
void (*refill)(struct xml_context *ctx); /* Callback to decode source characters to the buffer */
unsigned short *refill_in_to_x; /* Libcharset input table */
uns saved_depth; /* Saved ctx->depth */
+ uns pending_0xd; /* The last read character is 0xD */
};
struct xml_context {
void (*h_chars)(struct xml_context *ctx); /* Called after some characters (only with XML_REPORT_CHARS) */
void (*h_block)(struct xml_context *ctx, char *text, uns len); /* Called for each continuous block of characters not reported by h_cdata() (only with XML_REPORT_CHARS) */
void (*h_cdata)(struct xml_context *ctx, char *text, uns len); /* Called for each CDATA section (only with XML_REPORT_CHARS) */
+ void (*h_ignorable)(struct xml_context *ctx, char *text, uns len); /* Called for ignorable whitespace (content in tags without #PCDATA) */
void (*h_dtd_start)(struct xml_context *ctx); /* Called just after the DTD structure is initialized */
void (*h_dtd_end)(struct xml_context *ctx); /* Called after DTD subsets subsets */
struct xml_dtd_entity *(*h_find_entity)(struct xml_context *ctx, char *name); /* Called when needed to resolve a general entity */
/* Parse with the PUSH interface, return XML_STATE_x (zero on EOF or fatal error) */
uns xml_next(struct xml_context *ctx);
+/* Equivalent to xml_next, but with temporarily changed ctx->pull value */
+uns xml_next_state(struct xml_context *ctx, uns pull);
+
+/* May be called on XML_STATE_STAG to skip it's content; can return XML_STATE_ETAG or XML_STATE_EOF on fatal error */
+uns xml_skip_element(struct xml_context *ctx);
+
/* Returns the current row number in the document entity */
uns xml_row(struct xml_context *ctx);
/* Finds a given attribute value in a XML_NODE_ELEM node */
struct xml_attr *xml_attr_find(struct xml_context *ctx, struct xml_node *node, char *name);
+/* Similar to xml_attr_find, but it deals also with default values */
+char *xml_attr_value(struct xml_context *ctx, struct xml_node *node, char *name);
+
/* The default value of h_find_entity(), knows <, >, &, ' and " */
struct xml_dtd_entity *xml_def_find_entity(struct xml_context *ctx, char *name);
/* Remove leading/trailing spaces and replaces sequences of spaces to a single space character (non-CDATA attribute normalization) */
uns xml_normalize_white(struct xml_context *ctx, char *value);
+/* Merge character contents of a given element to a single string (not recursive) */
+char *xml_merge_chars(struct xml_context *ctx, struct xml_node *node, struct mempool *pool);
+
+/* Merge character contents of a given subtree to a single string */
+char *xml_merge_dom_chars(struct xml_context *ctx, struct xml_node *node, struct mempool *pool);
+
/* Public part of error handling */
void xml_warn(struct xml_context *ctx, const char *format, ...);
void xml_error(struct xml_context *ctx, const char *format, ...);