Files
1ac3bc07d6 Implement the new Redis Array type (#15162)
# Redis Array

For years, Redis has been missing a real indexed data structure for the
use cases where the index and the spatial relationship of elements are
semantic. Hashes give you random lookups, but you have to store an index
as a key, and have no range visibility. Lists give you appending and
trimming, but what is in the middle remains hard to access. Streams give
you append-only events, which is another (useful, indeed) beast. None of
these is what you want when the *position itself* has business meaning —
slot 37, step 4, row 18552, day from 2934 to 2949, file line 11, 12, 15
and so forth. And, all those types, for different reasons, are all
suboptimal when you want a **ring buffer** able to store the latest N
observed samples of something.

Up to now, users found ways (they always do \o/) using the fact that the
data structures that are obvious in this universe are also extremely
powerful, if well implemented. But this forces compromises. Arrays
handle these index-first requirements natively, and usually with much
better memory and CPU usage than the workarounds. If the use case is the
right one, Arrays often provide much better space, time and usability at
the same time.

## Internal encoding

1. When dense, an Array is essentially a more fancy C array. You don't
pay anything for storing the index.
2. Yet, instead of going really flat, arrays are sliced into
4096-element slices, and each slice, when it contains just a few
elements, uses a special sparse encoding. When a slice is empty it's
just a `NULL` stored in the directory.
3. Small ints, floats, and short strings are pointer-tagged, so they
cost zero additional memory beyond the pointer slot itself.
4. When very sparse, a super-directory of windowed directories is used.
This allows the data type to be safe, instead of exhibiting pathological
space or time behavior. This representation is only triggered when there
are more than 8 million elements or very high indexes set.

## Use cases

Arrays are mostly stateless if not for the fact that each array
remembers the index of the latest added item, allowing `ARINSERT` and
`ARRING` to work properly. Otherwise it is a set/get at this index game,
with solid support for both setting / getting ranges, server-side
scanning, returning only populated elements in a time which is
proportional not to the range size, but to the population size.

A few concrete examples, that may work as mental models for the set of
problems that are similar to them (from the POV of the data modeling).

**Thermometer.** A sensor reporting once per minute, with gaps:

```
ARSET       temp:room12:day7 123 22.3
ARGETRANGE  temp:room12:day7 600 660     # the 10:00–11:00 window, with NULLs
ARSCAN      temp:room12:day7 600 660     # only populated elements
AROP        temp:room12:day7 0 1439 MAX  # peak of the day, server-side
```

Missing minutes cost little to nothing. Numeric aggregation runs inside
Redis. Telemetry, IoT, meter readings, KPI rollups.

**Calendar.** A clinic with 96 fifteen-minute slots per day:

```
ARSET       sched:room12:day 32 booking:991
ARSCAN      sched:room12:day 0 95      # only occupied slots
ARGETRANGE  sched:room12:day 48 63     # the afternoon full view to render
```

The slot number is the business key in this case. Room booking, parking
spaces, warehouse bins, lockers, ...

**Ring buffer.** ARRING replaces the classic LPUSH+LTRIM pattern.
Imagine remote `dmesg`.

```
ARRING       machine:123 200 "[141087.430123]: arm_cpu_init(): cpu 14 online" # Capped to 200 entries
ARLASTITEMS  machine:123 50 REV       # 50 newest first
```

Faster than LPUSH+LTRIM, keep indexed access to past elements. Last-N
alarms, recent fraud scores, access history, remote logs, device events.
Ok here the use cases are mainly the ones of the old pattern: it is just
a better fit and allows to access random items in the middle, aggregate
server-side, and so forth.

**Workflow.** Step number is the index, value is the status. Gaps are
meaningful:

```
ARSET       claim:99172 0 received
ARSET       claim:99172 3 waiting:reviewer42
ARSET       claim:99172 5 approved
ARGETRANGE  claim:99172 0 5     # full workflow view, with NULLs for missing steps
ARSCAN      claim:99172 0 5     # only steps that have a state
ARCOUNT     claim:99172         # number of recorded steps
ARLEN       claim:99172         # highest reached step + 1
```

**Skills knowledge base for agents.** Arrays are good at representing /
grepping into Markdown files:

```
ARSET   skill:metal_gpu 0 "...."
ARSET   skill:metal_gpu 1 "...."
ARSET   skill:metal_gpu 2 "...."
ARGREP  skill:metal_gpu - + RE "M3|M4" WITHVALUES
```

ARGREP has EXACT, MATCH, GLOB, RE, you can have multiple predicates, can
select AND or OR behavior.

**Bulk import results.** Sparse row annotations over millions of rows /
CSV / ...:

```
ARSET  import:job551 18552 ERR:bad_email
ARSCAN import:job551 0 1000000        # Provides only rows that have something
```

## TLDR

If the position is part of the meaning, use an Array. If you want to
aggregate or grep remotely, use an Array. Feedback welcome :)

---------

Co-authored-by: debing.sun <[email protected]>
Co-authored-by: Shubham S Taple <[email protected]>
Co-authored-by: Yuan Wang <[email protected]>
Co-authored-by: Marc Gravell <[email protected]>
2026-05-14 00:56:44 +08:00

1872 lines
63 KiB
C

/*
retest.c - TRE regression test program
This software is released under a BSD-style license.
See the file LICENSE for details and copyright.
*/
/*
This is just a simple test application containing various hands-written
tests for regression testing TRE. I've tried to surround TRE specific
tests inside ifdefs, so this can be used to test any POSIX compatible
regexp implementation.
*/
/*
2023/06 - Compilers now sometimes require the input string constants to be
properly encoded, but how they decide on which encoding (if any)
is poorly documented and different for different platforms.
The non-ASCII encoded strings are now guarded by #ifdefs with one
of the following values. Define/undef whichever one(s) you need.
#define SRC_IN_ISO_8859_1
#define SRC_IN_UTF_8
#define SRC_IN_EUC_JP
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <locale.h>
/* look for getopt in order to use a -o option for output. */
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#elif defined(HAVE_GETOPT_H)
#include <getopt.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif /* HAVE_MALLOC_H */
#ifdef TRE_VERSION
#define HAVE_REGNEXEC 1
#define HAVE_REGNCOMP 1
#include "xmalloc.h"
#else /* !TRE_VERSION */
#define xmalloc malloc
#define xfree free
#endif /* !TRE_VERSION */
#include "tre-internal.h"
#ifdef WRETEST
#include <wchar.h>
#define CHAR_T wchar_t
#define L(x) (L ## x)
#define MAXSTRSIZE 8192
static wchar_t wstr[MAXSTRSIZE];
static wchar_t wregex[MAXSTRSIZE];
static int woffs[MAXSTRSIZE];
#ifdef TRE_USE_SYSTEM_REGEX_H
/* Avoid some redefinition warnings from including tre.h. */
#ifdef tre_regexec
#undef tre_regexec
/* No need for the *n* fn, it isn't in the system abi. */
#endif
#endif
#define tre_regexec tre_regwexec
#define tre_regnexec tre_regwnexec
#ifdef TRE_USE_SYSTEM_REGEX_H
/* Avoid some redefinition warnings from including tre.h. */
#ifdef tre_regcomp
#undef tre_regcomp
#endif
/* No need for the *n* fn, it isn't in the system abi. */
#endif
#define tre_regcomp tre_regwcomp
#define tre_regncomp tre_regwncomp
/* Iterate mbrtowc over the multi-byte sequence STR of length LEN,
store the result in BUF and memoize the successive byte offsets
in OFF. */
static int
mbntowc (wchar_t *buf, const char *str, size_t len, int *off)
{
int n, wlen;
#ifdef HAVE_MBSTATE_T
mbstate_t cst;
memset(&cst, 0, sizeof(cst));
#endif
if (len >= MAXSTRSIZE)
{
fprintf(stderr, "Increase MAXSTRSIZE to %ld or more and recompile!\n",
(long)len + 1);
exit(EXIT_FAILURE);
}
if (off)
{
memset(off + 1, -1, len * sizeof(int));
*off = 0;
}
wlen = 0;
while (len > 0)
{
n = tre_mbrtowc(buf ? buf++ : NULL, str, len, &cst);
if (n < 0)
return n;
if (n == 0)
n = 1;
str += n;
len -= n;
wlen += 1;
if (off)
*(off += n) = wlen;
}
return(wlen);
}
#else /* !WRETEST */
#define CHAR_T char
#define L(x) (x)
#endif /* !WRETEST */
static FILE *outf = NULL;
static int valid_reobj = 0;
static regex_t reobj;
static regmatch_t pmatch_global[32];
static const CHAR_T *regex_pattern;
static int cflags_global;
static int use_regnexec = 0;
static int use_regncomp = 0;
static int avoid_eflags = 0;
static int comp_tests = 0;
static int exec_tests = 0;
static int comp_errors = 0;
static int exec_errors = 0;
#ifndef REG_OK
#define REG_OK 0
#endif /* REG_OK */
#define END -2
static void
test_status(char c)
{
static int k = 0;
fprintf(outf, "%c", c);
if (++k % 79 == 0)
fprintf(outf, "\n");
fflush(outf);
}
static int
wrap_regexec(const CHAR_T *data, size_t len,
size_t pmatch_len, regmatch_t *pmatch, int eflags)
{
CHAR_T *buf = NULL;
int result;
if (len == 0 && use_regnexec)
{
/* Zero length string and using tre_regnexec(), the pointer we give
should not be dereferenced at all. */
buf = NULL;
}
else
{
/* Copy the data to a separate buffer to make a better test for
tre_regexec() and tre_regnexec(). */
buf = xmalloc((len + !use_regnexec) * sizeof(CHAR_T));
if (!buf)
return REG_ESPACE;
memcpy(buf, data, len * sizeof(CHAR_T));
test_status('#');
}
#ifdef HAVE_REGNEXEC
if (use_regnexec)
{
if (len == 0)
result = tre_regnexec(&reobj, NULL, len, pmatch_len, pmatch, eflags);
else
result = tre_regnexec(&reobj, buf, len, pmatch_len, pmatch, eflags);
}
else
#endif /* HAVE_REGNEXEC */
{
buf[len] = L('\0');
result = tre_regexec(&reobj, buf, pmatch_len, pmatch, eflags);
}
xfree(buf);
return result;
}
static int
wrap_regcomp(regex_t *preg, const CHAR_T *data, size_t len, int cflags)
{
#ifdef HAVE_REGNCOMP
if (use_regncomp)
return tre_regncomp(preg, data, len, cflags);
else
return tre_regcomp(preg, data, cflags);
#else /* !HAVE_REGNCOMP */
fprintf(stderr, "%s\n", data);
return tre_regcomp(preg, data, cflags);
#endif /* !HAVE_REGNCOMP */
}
static int
execute(const CHAR_T *data, int len, size_t pmatch_len, regmatch_t *pmatch,
int eflags)
{
#ifdef MALLOC_DEBUGGING
int i = 0;
int ret;
while (1)
{
xmalloc_configure(i);
comp_tests++;
ret = wrap_regexec(data, len, pmatch_len, pmatch, eflags);
if (ret != REG_ESPACE)
{
break;
}
#ifdef REGEX_DEBUG
xmalloc_dump_leaks();
#endif /* REGEX_DEBUG */
i++;
}
return ret;
#else /* !MALLOC_DEBUGGING */
return wrap_regexec(data, len, pmatch_len, pmatch, eflags);
#endif /* !MALLOC_DEBUGGING */
}
static int
check(va_list ap, int ret, const CHAR_T *str,
size_t pmatch_len, regmatch_t *pmatch, int eflags)
{
int fail = 0;
if (ret != va_arg(ap, int))
{
#ifndef WRETEST
fprintf(outf, "Exec error, regex: \"%s\", cflags %d, "
"string: \"%s\", eflags %d\n", regex_pattern, cflags_global,
str, eflags);
#else /* WRETEST */
fprintf(outf, "Exec error, regex: \"%ls\", cflags %d, "
"string: \"%ls\", eflags %d\n", regex_pattern, cflags_global,
str, eflags);
#endif /* WRETEST */
fprintf(outf, " got %smatch (tre_regexec returned %d)\n", ret ? "no " : "", ret);
return 1;
}
if (ret == 0)
{
unsigned int i;
for (i = 0; i < pmatch_len; i++)
{
int rm_so, rm_eo;
rm_so = va_arg(ap, int);
if (rm_so == END)
break;
rm_eo = va_arg(ap, int);
#ifdef WRETEST
if (rm_so >= 0)
{
int n = rm_so;
if ((rm_so = woffs[rm_so]) < 0 ||
(n = rm_eo, rm_eo = woffs[rm_eo]) < 0)
{
fprintf(outf, "Invalid or incomplete multi-byte sequence "
"in string %ls before byte offset %d\n", str, n);
return 1;
}
}
#endif /* WRETEST */
if (pmatch[i].rm_so != rm_so
|| pmatch[i].rm_eo != rm_eo)
{
#ifndef WRETEST
fprintf(outf, "Exec error, regex: \"%s\", string: \"%s\"\n",
regex_pattern, str);
fprintf(outf, " group %d: expected (%d, %d) \"%.*s\", "
"got (%d, %d) \"%.*s\"\n",
#else /* WRETEST */
fprintf(outf, "Exec error, regex: \"%ls\", string: \"%ls\"\n",
regex_pattern, str);
fprintf(outf, " group %d: expected (%d, %d) \"%.*ls\", "
"got (%d, %d) \"%.*ls\"\n",
#endif /* WRETEST */
i, rm_so, rm_eo, rm_eo - rm_so, str + rm_so,
(int)pmatch[i].rm_so, (int)pmatch[i].rm_eo,
(int)(pmatch[i].rm_eo - pmatch[i].rm_so),
str + pmatch[i].rm_so);
fail = 1;
}
}
if (!(cflags_global & REG_NOSUB) && reobj.re_nsub != i - 1
&& reobj.re_nsub <= pmatch_len && pmatch)
{
#ifndef WRETEST
fprintf(outf, "Comp error, regex: \"%s\"\n", regex_pattern);
#else /* WRETEST */
fprintf(outf, "Comp error, regex: \"%ls\"\n", regex_pattern);
#endif /* WRETEST */
fprintf(outf, " re_nsub is %d, should be %d\n", (int)reobj.re_nsub, i - 1);
fail = 1;
}
for (; i < pmatch_len; i++)
if (pmatch[i].rm_so != -1 || pmatch[i].rm_eo != -1)
{
if (!fail)
#ifndef WRETEST
fprintf(outf, "Exec error, regex: \"%s\", string: \"%s\"\n",
regex_pattern, str);
#else /* WRETEST */
fprintf(outf, "Exec error, regex: \"%ls\", string: \"%ls\"\n",
regex_pattern, str);
#endif /* WRETEST */
fprintf(outf, " group %d: expected (-1, -1), got (%d, %d)\n",
i, (int)pmatch[i].rm_so, (int)pmatch[i].rm_eo);
fail = 1;
}
}
return fail;
}
static void
test_nexec(const char *data, size_t len, int eflags, ...)
{
int m;
int fail = 0;
int extra_flags[] = {0, REG_BACKTRACKING_MATCHER, REG_APPROX_MATCHER};
size_t i;
va_list ap;
if (!valid_reobj)
{
exec_errors++;
return;
}
#ifdef WRETEST
{
int wlen = mbntowc(wstr, data, len, woffs);
if (wlen < 0)
{
exec_errors++;
fprintf(outf, "Invalid or incomplete multi-byte sequence in %s\n", data);
return;
}
wstr[wlen] = L'\0';
len = wlen;
}
#define data wstr
#endif /* WRETEST */
use_regnexec = 1;
for (i = 0; i < elementsof(extra_flags); i++)
{
int final_flags = eflags | extra_flags[i];
if ((final_flags & REG_BACKTRACKING_MATCHER
&& tre_have_approx(&reobj))
|| (final_flags & REG_APPROX_MATCHER
&& tre_have_backrefs(&reobj))
|| (final_flags & avoid_eflags))
continue;
/* Test with a pmatch array. */
exec_tests++;
m = execute(data, len, elementsof(pmatch_global), pmatch_global,
final_flags);
va_start(ap, eflags);
fail |= check(ap, m, data, elementsof(pmatch_global), pmatch_global,
final_flags);
va_end(ap);
/* Same test with a NULL pmatch. */
exec_tests++;
m = execute(data, len, 0, NULL, final_flags);
va_start(ap, eflags);
fail |= check(ap, m, data, 0, NULL, final_flags);
va_end(ap);
}
#ifdef WRETEST
#undef data
#endif /* WRETEST */
if (fail)
exec_errors++;
}
static void
test_exec(const char *str, int eflags, ...)
{
int m;
int fail = 0;
size_t len = strlen(str);
int extra_flags[] = {0,
REG_BACKTRACKING_MATCHER,
REG_APPROX_MATCHER,
REG_BACKTRACKING_MATCHER | REG_APPROX_MATCHER};
size_t i;
va_list ap;
if (!valid_reobj)
{
exec_errors++;
return;
}
#ifdef WRETEST
{
int wlen = mbntowc(wstr, str, len, woffs);
if (wlen < 0)
{
exec_errors++;
fprintf(outf, "Invalid or incomplete multi-byte sequence in %s\n", str);
return;
}
wstr[wlen] = L'\0';
len = wlen;
}
#define str wstr
#endif /* WRETEST */
for (use_regnexec = 0; use_regnexec < 2; use_regnexec++)
{
for (i = 0; i < elementsof(extra_flags); i++)
{
int final_flags = eflags | extra_flags[i];
if ((final_flags & REG_BACKTRACKING_MATCHER
&& tre_have_approx(&reobj))
|| (final_flags & REG_APPROX_MATCHER
&& tre_have_backrefs(&reobj))
|| (final_flags & avoid_eflags))
continue;
/* Test with a pmatch array. */
exec_tests++;
m = execute(str, len, elementsof(pmatch_global), pmatch_global,
final_flags);
va_start(ap, eflags);
fail |= check(ap, m, str, elementsof(pmatch_global), pmatch_global,
final_flags);
va_end(ap);
/* Same test with a NULL pmatch. */
exec_tests++;
m = execute(str, len, 0, NULL, final_flags);
va_start(ap, eflags);
fail |= check(ap, m, str, 0, NULL, final_flags);
va_end(ap);
}
}
#ifdef WRETEST
#undef str
#endif /* WRETEST */
if (fail)
exec_errors++;
}
static void
test_comp(const char *re, int flags, int ret)
{
int errcode = 0;
int len = re ? strlen(re) : 0;
if (valid_reobj)
{
tre_regfree(&reobj);
valid_reobj = 0;
}
comp_tests++;
#ifdef WRETEST
{
int wlen = mbntowc(wregex, re, len, NULL);
if (wlen < 0)
{
comp_errors++;
fprintf(outf, "Invalid or incomplete multi-byte sequence in %s\n", re);
return;
}
wregex[wlen] = L'\0';
len = wlen;
}
#define re wregex
#endif /* WRETEST */
regex_pattern = re;
cflags_global = flags;
#ifdef MALLOC_DEBUGGING
xmalloc_configure(-1);
if (ret != REG_ESPACE) {
static int j = 0;
int i = 0;
while (1)
{
xmalloc_configure(i);
comp_tests++;
if (j++ % 20 == 0)
test_status('.');
errcode = wrap_regcomp(&reobj, re, len, flags);
if (errcode != REG_ESPACE)
{
test_status('*');
break;
}
#ifdef REGEX_DEBUG
xmalloc_dump_leaks();
#endif /* REGEX_DEBUG */
i++;
}
} else
#endif /* !MALLOC_DEBUGGING */
errcode = wrap_regcomp(&reobj, re, len, flags);
#ifdef WRETEST
#undef re
#endif /* WRETEST */
if (errcode != ret)
{
#ifndef WRETEST
fprintf(outf, "Comp error, regex: \"%s\"\n", regex_pattern);
#else /* WRETEST */
fprintf(outf, "Comp error, regex: \"%ls\"\n", regex_pattern);
#endif /* WRETEST */
fprintf(outf, " expected return code %d, got %d.\n",
ret, errcode);
comp_errors++;
}
if (errcode == 0)
valid_reobj = 1;
}
/* To enable tests for known bugs, set this to 1. */
#define KNOWN_BUG 0
int
main(int argc, char **argv)
{
outf = stdout;
#if defined(HAVE_UNISTD_H) || defined(HAVE_GETOPT_H)
int opt;
while ((opt = getopt(argc, argv, "o:")) != EOF)
{
switch (opt)
{
case 'o':
if ((outf = fopen(optarg, "w")) == NULL)
{
perror(optarg);
exit(1);
}
break;
default:
/* getopt() will have printed an error message already */
exit(1);
}
}
#endif /* HAVE_UNISTD_H */
#ifdef WRETEST
/* Need an 8-bit locale. Or move the two tests with non-ascii
characters to the localized internationalization tests. */
if (setlocale(LC_CTYPE, "en_US.ISO-8859-1") == NULL &&
setlocale(LC_CTYPE, "en_US.ISO8859-1") == NULL)
fprintf(stderr, "Could not set locale en_US.ISO-8859-1. Expect some\n"
"`Invalid or incomplete multi-byte sequence' errors.\n");
#endif /* WRETEST */
/* Large number of macros in one regexp. */
test_comp("[A-Z]\\d\\s?\\d[A-Z]{2}|[A-Z]\\d{2}\\s?\\d[A-Z]{2}|[A-Z]{2}\\d"
"\\s?\\d[A-Z]{2}|[A-Z]{2}\\d{2}\\s?\\d[A-Z]{2}|[A-Z]\\d[A-Z]\\s?"
"\\d[A-Z]{2}|[A-Z]{2}\\d[A-Z]\\s?\\d[A-Z]{2}|[A-Z]{3}\\s?\\d[A-Z]"
"{2}", REG_EXTENDED, 0);
test_comp("a{11}(b{2}c){2}", REG_EXTENDED, 0);
test_comp("a{2}{2}xb+xc*xd?x", REG_EXTENDED, 0);
test_comp("^!packet [0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3} [0-9]+",
REG_EXTENDED, 0);
test_comp("^!pfast [0-9]{1,15} ([0-9]{1,3}\\.){3}[0-9]{1,3}[0-9]{1,5}$",
REG_EXTENDED, 0);
#if KNOWN_BUG
/* Should these match or not? */
test_comp("(a)*-\\1b", REG_EXTENDED, 0);
test_exec("aaa-b", 0, REG_NOMATCH);
test_comp("((.*)\\1)+", REG_EXTENDED, 0);
test_exec("xxxxxx", 0, REG_NOMATCH);
#endif
#ifdef TRE_APPROX
/*
* Approximate matching tests.
*
* The approximate matcher always searches for the best match, and returns
* the leftmost and longest one if there are several best matches.
*/
test_comp("(fou){# ~1}", REG_EXTENDED, 0);
test_comp("(fuu){#}", REG_EXTENDED, 0);
test_comp("(fuu){# ~}", REG_EXTENDED, 0);
test_comp("(anaconda){ 1i + 1d < 1, #1}", REG_EXTENDED, 0);
test_comp("(anaconda){ 1i + 1d < 1 #1 ~10 }", REG_EXTENDED, 0);
test_comp("(anaconda){ #1, ~1, 1i + 1d < 1 }", REG_EXTENDED, 0);
test_comp("(znacnda){ #1 ~3 1i + 1d < 1 }", REG_EXTENDED, 0);
test_exec("molasses anaconda foo bar baz smith anderson ",
0, REG_NOMATCH);
test_comp("(znacnda){ #1 ~3 1i + 1d < 2 }", REG_EXTENDED, 0);
test_exec("molasses anaconda foo bar baz smith anderson ",
0, REG_OK, 9, 17, 9, 17, END);
test_comp("(ananda){ 1i + 1d < 2 }", REG_EXTENDED, 0);
test_exec("molasses anaconda foo bar baz smith anderson ",
0, REG_NOMATCH);
test_comp("(fuu){ +3 -3 ~5}", REG_EXTENDED, 0);
test_exec("anaconda foo bar baz smith anderson",
0, REG_OK, 9, 10, 9, 10, END);
test_comp("(fuu){ +2 -2 ~5}", REG_EXTENDED, 0);
test_exec("anaconda foo bar baz smith anderson",
0, REG_OK, 9, 10, 9, 10, END);
test_comp("(fuu){ +3 -3 ~}", REG_EXTENDED, 0);
test_exec("anaconda foo bar baz smith anderson",
0, REG_OK, 9, 10, 9, 10, END);
test_comp("(laurikari){ #3, 1i + 1d < 3 }", REG_EXTENDED, 0);
/* No cost limit. */
test_comp("(foobar){~}", REG_EXTENDED, 0);
test_exec("xirefoabralfobarxie", 0, REG_OK, 11, 16, 11, 16, END);
/* At most two errors. */
test_comp("(foobar){~2}", REG_EXTENDED, 0);
test_exec("xirefoabrzlfd", 0, REG_OK, 4, 9, 4, 9, END);
test_exec("xirefoabzlfd", 0, REG_NOMATCH);
/* At most two inserts or substitutions and max two errors total. */
test_comp("(foobar){+2#2~2}", REG_EXTENDED, 0);
test_exec("oobargoobaploowap", 0, REG_OK, 5, 11, 5, 11, END);
/* Find best whole word match for "foobar". */
test_comp("\\<(foobar){~}\\>", REG_EXTENDED, 0);
test_exec("zfoobarz", 0, REG_OK, 0, 8, 0, 8, END);
test_exec("boing zfoobarz goobar woop", 0, REG_OK, 15, 21, 15, 21, END);
/* Match whole string, allow only 1 error. */
test_comp("^(foobar){~1}$", REG_EXTENDED, 0);
test_exec("foobar", 0, REG_OK, 0, 6, 0, 6, END);
test_exec("xfoobar", 0, REG_OK, 0, 7, 0, 7, END);
/*
This currently fails.
test_exec("foobarx", 0, REG_OK, 0, 7, 0, 7, END);
*/
test_exec("fooxbar", 0, REG_OK, 0, 7, 0, 7, END);
test_exec("foxbar", 0, REG_OK, 0, 6, 0, 6, END);
test_exec("xoobar", 0, REG_OK, 0, 6, 0, 6, END);
test_exec("foobax", 0, REG_OK, 0, 6, 0, 6, END);
test_exec("oobar", 0, REG_OK, 0, 5, 0, 5, END);
test_exec("fobar", 0, REG_OK, 0, 5, 0, 5, END);
test_exec("fooba", 0, REG_OK, 0, 5, 0, 5, END);
test_exec("xfoobarx", 0, REG_NOMATCH);
test_exec("foobarxx", 0, REG_NOMATCH);
test_exec("xxfoobar", 0, REG_NOMATCH);
test_exec("xfoxbar", 0, REG_NOMATCH);
test_exec("foxbarx", 0, REG_NOMATCH);
/* At most one insert, two deletes, and three substitutions.
Additionally, deletes cost two and substitutes one, and total
cost must be less than 4. */
test_comp("(foobar){+1 -2 #3, 2d + 1s < 4}", REG_EXTENDED, 0);
test_exec("3oifaowefbaoraofuiebofasebfaobfaorfeoaro",
0, REG_OK, 26, 33, 26, 33, END);
/* Partially approximate matches. */
test_comp("foo(bar){~1}zap", REG_EXTENDED, 0);
test_exec("foobarzap", 0, REG_OK, 0, 9, 3, 6, END);
test_exec("fobarzap", 0, REG_NOMATCH);
test_exec("foobrzap", 0, REG_OK, 0, 8, 3, 5, END);
test_comp("^.*(dot.org){~}.*$", REG_EXTENDED, 0);
test_exec("www.cnn.com 64.236.16.20\n"
"www.slashdot.org 66.35.250.150\n"
"For useful information, use www.slashdot.org\n"
"this is demo data!\n",
0, REG_OK, 0, 120, 93, 100, END);
/* Approximate matching and back referencing cannot be used together. */
test_comp("(foo{~})\\1", REG_EXTENDED, REG_BADPAT);
#endif /* TRE_APPROX */
/*
* Basic tests with pure regular expressions
*/
/* Basic string matching. */
test_comp("foobar", REG_EXTENDED, 0);
test_exec("foobar", 0, REG_OK, 0, 6, END);
test_exec("xxxfoobarzapzot", 0, REG_OK, 3, 9, END);
test_comp("foobar", REG_EXTENDED | REG_NOSUB, 0);
test_exec("foobar", 0, REG_OK, END);
test_comp("aaaa", REG_EXTENDED, 0);
test_exec("xxaaaaaaaaaaaaaaaaa", 0, REG_OK, 2, 6, END);
/* Test zero length matches. */
test_comp("(a*)", REG_EXTENDED, 0);
test_exec("", 0, REG_OK, 0, 0, 0, 0, END);
test_comp("(a*)*", REG_EXTENDED, 0);
test_exec("", 0, REG_OK, 0, 0, 0, 0, END);
test_comp("((a*)*)*", REG_EXTENDED, 0);
test_exec("", 0, REG_OK, 0, 0, 0, 0, 0, 0, END);
test_comp("(a*bcd)*", REG_EXTENDED, 0);
test_exec("aaaaaaaaaaaabcxbcxbcxaabcxaabcx", 0, REG_OK, 0, 0, -1, -1, END);
test_exec("aaaaaaaaaaaabcxbcxbcxaabcxaabc", 0, REG_OK, 0, 0, -1, -1, END);
test_exec("aaaaaaaaaaaabcxbcdbcxaabcxaabc", 0, REG_OK, 0, 0, -1, -1, END);
test_exec("aaaaaaaaaaaabcdbcdbcxaabcxaabc", 0, REG_OK, 0, 18, 15, 18, END);
test_comp("(a*)+", REG_EXTENDED, 0);
test_exec("-", 0, REG_OK, 0, 0, 0, 0, END);
/* This test blows up the backtracking matcher. */
avoid_eflags = REG_BACKTRACKING_MATCHER;
test_comp("((a*)*b)*b", REG_EXTENDED, 0);
test_exec("aaaaaaaaaaaaaaaaaaaaaaaaab", 0, REG_OK,
25, 26, -1, -1, -1, -1, END);
avoid_eflags = 0;
test_comp("", 0, 0);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("foo", 0, REG_OK, 0, 0, END);
/* Test for submatch addressing which requires arbitrary lookahead. */
test_comp("(a*)aaaaaa", REG_EXTENDED, 0);
test_exec("aaaaaaaaaaaaaaax", 0, REG_OK, 0, 15, 0, 9, END);
/* Test leftmost and longest matching and some tricky submatches. */
test_comp("(a*)(a*)", REG_EXTENDED, 0);
test_exec("aaaa", 0, REG_OK, 0, 4, 0, 4, 4, 4, END);
test_comp("(abcd|abc)(d?)", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 4, 0, 4, 4, 4, END);
test_comp("(abc|abcd)(d?)", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 4, 0, 4, 4, 4, END);
test_comp("(abc|abcd)(d?)e", REG_EXTENDED, 0);
test_exec("abcde", 0, REG_OK, 0, 5, 0, 4, 4, 4, END);
test_comp("(abcd|abc)(d?)e", REG_EXTENDED, 0);
test_exec("abcde", 0, REG_OK, 0, 5, 0, 4, 4, 4, END);
test_comp("a(bc|bcd)(d?)", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 4, 1, 4, 4, 4, END);
test_comp("a(bcd|bc)(d?)", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 4, 1, 4, 4, 4, END);
test_comp("a*(a?bc|bcd)(d?)", REG_EXTENDED, 0);
test_exec("aaabcd", 0, REG_OK, 0, 6, 3, 6, 6, 6, END);
test_comp("a*(bcd|a?bc)(d?)", REG_EXTENDED, 0);
test_exec("aaabcd", 0, REG_OK, 0, 6, 3, 6, 6, 6, END);
test_comp("(a|(a*b*))*", REG_EXTENDED, 0);
test_exec("", 0, REG_OK, 0, 0, 0, 0, 0, 0, END);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, -1, -1, END);
test_exec("aa", 0, REG_OK, 0, 2, 0, 2, 0, 2, END);
test_exec("aaa", 0, REG_OK, 0, 3, 0, 3, 0, 3, END);
test_exec("bbb", 0, REG_OK, 0, 3, 0, 3, 0, 3, END);
test_exec("aaabbb", 0, REG_OK, 0, 6, 0, 6, 0, 6, END);
test_exec("bbbaaa", 0, REG_OK, 0, 6, 3, 6, 3, 6, END);
test_comp("((a*b*)|a)*", REG_EXTENDED, 0);
test_exec("", 0, REG_OK, 0, 0, 0, 0, 0, 0, END);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, 0, 1, END);
test_exec("aa", 0, REG_OK, 0, 2, 0, 2, 0, 2, END);
test_exec("aaa", 0, REG_OK, 0, 3, 0, 3, 0, 3, END);
test_exec("bbb", 0, REG_OK, 0, 3, 0, 3, 0, 3, END);
test_exec("aaabbb", 0, REG_OK, 0, 6, 0, 6, 0, 6, END);
test_exec("bbbaaa", 0, REG_OK, 0, 6, 3, 6, 3, 6, END);
test_comp("a.*(.*b.*(.*c.*).*d.*).*e.*(.*f.*).*g", REG_EXTENDED, 0);
test_exec("aabbccddeeffgg", 0, REG_OK, 0, 14, 3, 9, 5, 7, 11, 13, END);
test_comp("(wee|week)(night|knights)s*", REG_EXTENDED, 0);
test_exec("weeknights", 0, REG_OK, 0, 10, 0, 3, 3, 10, END);
test_exec("weeknightss", 0, REG_OK, 0, 11, 0, 3, 3, 10, END);
test_comp("a*", REG_EXTENDED, 0);
test_exec("aaaaaaaaaa", 0, REG_OK, 0, 10, END);
test_comp("aa*", REG_EXTENDED, 0);
test_exec("aaaaaaaaaa", 0, REG_OK, 0, 10, END);
test_comp("aaa*", REG_EXTENDED, 0);
test_exec("aaaaaaaaaa", 0, REG_OK, 0, 10, END);
test_comp("aaaa*", REG_EXTENDED, 0);
test_exec("aaaaaaaaaa", 0, REG_OK, 0, 10, END);
/* Test clearing old submatch data with nesting parentheses
and iteration. */
test_comp("((a)|(b))*c", REG_EXTENDED, 0);
test_exec("aaabc", 0, REG_OK, 0, 5, 3, 4, -1, -1, 3, 4, END);
test_exec("aaaac", 0, REG_OK, 0, 5, 3, 4, 3, 4, -1, -1, END);
test_comp("foo((bar)*)*zot", REG_EXTENDED, 0);
test_exec("foozot", 0, REG_OK, 0, 6, 3, 3, -1, -1, END);
test_exec("foobarzot", 0, REG_OK, 0, 9, 3, 6, 3, 6, END);
test_exec("foobarbarzot", 0, REG_OK, 0, 12, 3, 9, 6, 9, END);
test_comp("foo((zup)*|(bar)*|(zap)*)*zot", REG_EXTENDED, 0);
test_exec("foobarzapzot", 0, REG_OK,
0, 12, 6, 9, -1, -1, -1, -1, 6, 9, END);
test_exec("foobarbarzapzot", 0, REG_OK,
0, 15, 9, 12, -1, -1, -1, -1, 9, 12, END);
test_exec("foozupzot", 0, REG_OK,
0, 9, 3, 6, 3, 6, -1, -1, -1, -1, END);
test_exec("foobarzot", 0, REG_OK,
0, 9, 3, 6, -1, -1, 3, 6, -1, -1, END);
test_exec("foozapzot", 0, REG_OK,
0, 9, 3, 6, -1, -1, -1, -1, 3, 6, END);
test_exec("foozot", 0, REG_OK,
0, 6, 3, 3, -1, -1, -1, -1, -1, -1, END);
/* Test case where, e.g., Perl and Python regexp functions, and many
other backtracking matchers, fail to produce the longest match.
It is not exactly a bug since Perl does not claim to find the
longest match, but a confusing feature and, in my opinion, a bad
design choice because the union operator is traditionally defined
to be commutative (with respect to the language denoted by the RE). */
test_comp("(a|ab)(blip)?", REG_EXTENDED, 0);
test_exec("ablip", 0, REG_OK, 0, 5, 0, 1, 1, 5, END);
test_exec("ab", 0, REG_OK, 0, 2, 0, 2, -1, -1, END);
test_comp("(ab|a)(blip)?", REG_EXTENDED, 0);
test_exec("ablip", 0, REG_OK, 0, 5, 0, 1, 1, 5, END);
test_exec("ab", 0, REG_OK, 0, 2, 0, 2, -1, -1, END);
/* Test more submatch addressing. */
test_comp("((a|b)*)a(a|b)*", REG_EXTENDED, 0);
test_exec("aaaaabaaaba", 0, REG_OK, 0, 11, 0, 10, 9, 10, -1, -1, END);
test_exec("aaaaabaaab", 0, REG_OK, 0, 10, 0, 8, 7, 8, 9, 10, END);
test_exec("caa", 0, REG_OK, 1, 3, 1, 2, 1, 2, -1, -1, END);
test_comp("((a|aba)*)(ababbaba)((a|b)*)", REG_EXTENDED, 0);
test_exec("aabaababbabaaababbab", 0, REG_OK,
0, 20, 0, 4, 1, 4, 4, 12, 12, 20, 19, 20, END);
test_exec("aaaaababbaba", 0, REG_OK,
0, 12, 0, 4, 3, 4, 4, 12, 12, 12, -1, -1, END);
test_comp("((a|aba|abb|bba|bab)*)(ababbababbabbbabbbbbbabbaba)((a|b)*)",
REG_EXTENDED, 0);
test_exec("aabaabbbbabababaababbababbabbbabbbbbbabbabababbababababbabababa",
0, REG_OK, 0, 63, 0, 16, 13, 16, 16, 43, 43, 63, 62, 63, END);
/* Test for empty subexpressions. */
test_comp("", 0, 0);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("foo", 0, REG_OK, 0, 0, END);
test_comp("(a|)", REG_EXTENDED, 0);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, END);
test_exec("b", 0, REG_OK, 0, 0, 0, 0, END);
test_exec("", 0, REG_OK, 0, 0, 0, 0, END);
test_comp("a|", REG_EXTENDED, 0);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("b", 0, REG_OK, 0, 0, END);
test_exec("", 0, REG_OK, 0, 0, END);
test_comp("|a", REG_EXTENDED, 0);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("b", 0, REG_OK, 0, 0, END);
test_exec("", 0, REG_OK, 0, 0, END);
/* Miscellaneous tests. */
test_comp("(a*)b(c*)", REG_EXTENDED, 0);
test_exec("abc", 0, REG_OK, 0, 3, 0, 1, 2, 3, END);
test_exec("***abc***", 0, REG_OK, 3, 6, 3, 4, 5, 6, END);
test_comp("(a)", REG_EXTENDED, 0);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, END);
test_comp("((a))", REG_EXTENDED, 0);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, 0, 1, END);
test_comp("(((a)))", REG_EXTENDED, 0);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, 0, 1, 0, 1, END);
test_comp("((((((((((((((((((((a))))))))))))))))))))", REG_EXTENDED, 0);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, END);
test_comp("ksntoeaiksntoeaikstneoaiksnteoaiksntoeaiskntoeaiskntoekainstoei"
"askntoeakisntoeksaitnokesantiksoentaikosentaiksoentaiksnoeaiskn"
"teoaksintoekasitnoeksaitkosetniaksoetnaisknoetakistoeksintokesa"
"nitksoentaisknoetaisknoetiaksotneaikstoekasitoeskatioksentaikso"
"enatiksoetnaiksonateiksoteaeskanotisknetaiskntoeasknitoskenatis"
"konetaisknoteai", 0, 0);
test_comp("((aab)|(aac)|(aa*))c", REG_EXTENDED, 0);
test_exec("aabc", 0, REG_OK, 0, 4, 0, 3, 0, 3, -1, -1, -1, -1, END);
test_exec("aacc", 0, REG_OK, 0, 4, 0, 3, -1, -1, 0, 3, -1, -1, END);
test_exec("aaac", 0, REG_OK, 0, 4, 0, 3, -1, -1, -1, -1, 0, 3, END);
test_comp("^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$",
REG_EXTENDED, 0);
test_exec("foo!bar!bas", 0, REG_OK,
0, 11, 0, 11, -1, -1, -1, -1, 4, 8, 8, 11, END);
test_comp("^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$",
REG_EXTENDED, 0);
test_exec("foo!bar!bas", 0, REG_OK,
0, 11, -1, -1, -1, -1, 4, 8, 8, 11, END);
test_comp("^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$",
REG_EXTENDED, 0);
test_exec("foo!bar!bas", 0, REG_OK,
0, 11, 0, 11, -1, -1, -1, -1, 4, 8, 8, 11, END);
test_comp("M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]",
REG_EXTENDED, 0);
test_exec("Muammar Quathafi", 0, REG_OK, 0, 16, -1, -1, 11, 13, END);
test_comp("(Ab|cD)*", REG_EXTENDED | REG_ICASE, 0);
test_exec("aBcD", 0, REG_OK, 0, 4, 2, 4, END);
test_comp("a**", REG_EXTENDED, REG_BADRPT);
test_comp("a*+", REG_EXTENDED, REG_BADRPT);
test_comp("a+*", REG_EXTENDED, REG_BADRPT);
test_comp("a++", REG_EXTENDED, REG_BADRPT);
test_comp("a?+", REG_EXTENDED, REG_BADRPT);
test_comp("a?*", REG_EXTENDED, REG_BADRPT);
test_comp("a{1,2}*", REG_EXTENDED, REG_BADRPT);
test_comp("a{1,2}+", REG_EXTENDED, REG_BADRPT);
/*
* Many of the following tests were mostly inspired by (or copied from) the
* libhackerlab posix test suite by Tom Lord.
*/
test_comp("a", 0, 0);
test_exec("a", 0, REG_OK, 0, 1, END);
test_comp("\\.", 0, 0);
test_exec(".", 0, REG_OK, 0, 1, END);
test_comp("\\[", 0, 0);
test_exec("[", 0, REG_OK, 0, 1, END);
test_comp("\\\\", 0, 0);
test_exec("\\", 0, REG_OK, 0, 1, END);
test_comp("\\*", 0, 0);
test_exec("*", 0, REG_OK, 0, 1, END);
test_comp("\\^", 0, 0);
test_exec("^", 0, REG_OK, 0, 1, END);
test_comp("\\$", 0, 0);
test_exec("$", 0, REG_OK, 0, 1, END);
test_comp("\\", 0, REG_EESCAPE);
test_comp("x\\.", 0, 0);
test_exec("x.", 0, REG_OK, 0, 2, END);
test_comp("x\\[", 0, 0);
test_exec("x[", 0, REG_OK, 0, 2, END);
test_comp("x\\\\", 0, 0);
test_exec("x\\", 0, REG_OK, 0, 2, END);
test_comp("x\\*", 0, 0);
test_exec("x*", 0, REG_OK, 0, 2, END);
test_comp("x\\^", 0, 0);
test_exec("x^", 0, REG_OK, 0, 2, END);
test_comp("x\\$", 0, 0);
test_exec("x$", 0, REG_OK, 0, 2, END);
test_comp("x\\", 0, REG_EESCAPE);
test_comp(".", 0, 0);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("\n", 0, REG_OK, 0, 1, END);
test_comp("(+|?)", 0, 0);
test_exec("(+|?)", 0, REG_OK, 0, 5, END);
test_exec("+|?", 0, REG_NOMATCH);
test_exec("(+)", 0, REG_NOMATCH);
test_exec("+", 0, REG_NOMATCH);
/*
* Test bracket expressions.
*/
test_comp("[", 0, REG_EBRACK);
test_comp("[]", 0, REG_EBRACK);
test_comp("[^]", 0, REG_EBRACK);
test_comp("[]x]", 0, 0);
test_exec("]", 0, REG_OK, 0, 1, END);
test_exec("x", 0, REG_OK, 0, 1, END);
test_comp("[.]", 0, 0);
test_exec(".", 0, REG_OK, 0, 1, END);
test_exec("a", 0, REG_NOMATCH);
test_comp("[*]", 0, 0);
test_exec("*", 0, REG_OK, 0, 1, END);
test_comp("[[]", 0, 0);
test_exec("[", 0, REG_OK, 0, 1, END);
test_comp("[\\]", 0, 0);
test_exec("\\", 0, REG_OK, 0, 1, END);
test_comp("[-x]", 0, 0);
test_exec("-", 0, REG_OK, 0, 1, END);
test_exec("x", 0, REG_OK, 0, 1, END);
test_comp("[x-]", 0, 0);
test_exec("-", 0, REG_OK, 0, 1, END);
test_exec("x", 0, REG_OK, 0, 1, END);
test_comp("[-]", 0, 0);
test_exec("-", 0, REG_OK, 0, 1, END);
test_comp("[abc]", 0, 0);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("b", 0, REG_OK, 0, 1, END);
test_exec("c", 0, REG_OK, 0, 1, END);
test_exec("d", 0, REG_NOMATCH);
test_exec("xa", 0, REG_OK, 1, 2, END);
test_exec("xb", 0, REG_OK, 1, 2, END);
test_exec("xc", 0, REG_OK, 1, 2, END);
test_exec("xd", 0, REG_NOMATCH);
test_comp("x[abc]", 0, 0);
test_exec("xa", 0, REG_OK, 0, 2, END);
test_exec("xb", 0, REG_OK, 0, 2, END);
test_exec("xc", 0, REG_OK, 0, 2, END);
test_exec("xd", 0, REG_NOMATCH);
test_comp("[^abc]", 0, 0);
test_exec("a", 0, REG_NOMATCH);
test_exec("b", 0, REG_NOMATCH);
test_exec("c", 0, REG_NOMATCH);
test_exec("d", 0, REG_OK, 0, 1, END);
test_exec("xa", 0, REG_OK, 0, 1, END);
test_exec("xb", 0, REG_OK, 0, 1, END);
test_exec("xc", 0, REG_OK, 0, 1, END);
test_exec("xd", 0, REG_OK, 0, 1, END);
test_comp("x[^abc]", 0, 0);
test_exec("xa", 0, REG_NOMATCH);
test_exec("xb", 0, REG_NOMATCH);
test_exec("xc", 0, REG_NOMATCH);
test_exec("xd", 0, REG_OK, 0, 2, END);
test_comp("[()+?*\\]+", REG_EXTENDED, 0);
test_exec("x\\*?+()x", 0, REG_OK, 1, 7, END);
/* Standard character classes. */
test_comp("[[:alnum:]]+", REG_EXTENDED, 0);
test_exec("%abc123890XYZ=", 0, REG_OK, 1, 13, END);
test_comp("[[:cntrl:]]+", REG_EXTENDED, 0);
test_exec("%\n\t\015\f ", 0, REG_OK, 1, 5, END);
test_comp("[[:lower:]]+", REG_EXTENDED, 0);
test_exec("AbcdE", 0, REG_OK, 1, 4, END);
test_comp("[[:lower:]]+", REG_EXTENDED | REG_ICASE, 0);
test_exec("AbcdE", 0, REG_OK, 0, 5, END);
test_comp("[[:space:]]+", REG_EXTENDED, 0);
test_exec("x \t\f\nx", 0, REG_OK, 1, 5, END);
test_comp("[[:alpha:]]+", REG_EXTENDED, 0);
test_exec("%abC123890xyz=", 0, REG_OK, 1, 4, END);
test_comp("[[:digit:]]+", REG_EXTENDED, 0);
test_exec("%abC123890xyz=", 0, REG_OK, 4, 10, END);
test_comp("[^[:digit:]]+", REG_EXTENDED, 0);
test_exec("%abC123890xyz=", 0, REG_OK, 0, 4, END);
test_comp("[[:print:]]+", REG_EXTENDED, 0);
test_exec("\n\t %abC12\f", 0, REG_OK, 2, 9, END);
test_comp("[[:upper:]]+", REG_EXTENDED, 0);
test_exec("\n aBCDEFGHIJKLMNOPQRSTUVWXYz", 0, REG_OK, 3, 27, END);
test_comp("[[:upper:]]+", REG_EXTENDED | REG_ICASE, 0);
test_exec("\n aBCDEFGHIJKLMNOPQRSTUVWXYz", 0, REG_OK, 2, 28, END);
#ifdef HAVE_ISWBLANK
#ifdef HAVE_ISBLANK
test_comp("[[:blank:]]+", REG_EXTENDED, 0);
test_exec("\na \t b", 0, REG_OK, 2, 5, END);
#endif /* HAVE_ISBLANK */
#endif /* HAVE_ISWBLANK */
test_comp("[[:graph:]]+", REG_EXTENDED, 0);
test_exec("\n %abC12\f", 0, REG_OK, 2, 8, END);
test_comp("[[:punct:]]+", REG_EXTENDED, 0);
test_exec("a~!@#$%^&*()_+=-`[]{};':\"|\\,./?>< ",
0, REG_OK, 1, 33, END);
test_comp("[[:xdigit:]]+", REG_EXTENDED, 0);
test_exec("-0123456789ABCDEFabcdef", 0, REG_OK, 1, 23, END);
test_comp("[[:bogus-character-class-name:]", REG_EXTENDED, REG_ECTYPE);
test_comp("[[:\xff:", REG_EXTENDED, REG_ECTYPE);
/* Range expressions (assuming that the C locale is being used). */
test_comp("[a-z]+", REG_EXTENDED, 0);
test_exec("ABCabcxyzABC", 0, REG_OK, 3, 9, END);
test_comp("[z-a]+", REG_EXTENDED, REG_ERANGE);
test_comp("[a-b-c]", 0, REG_ERANGE);
test_comp("[a-a]+", REG_EXTENDED, 0);
test_exec("zaaaaab", 0, REG_OK, 1, 6, END);
test_comp("[--Z]+", REG_EXTENDED, 0);
test_exec("!ABC-./XYZ~", 0, REG_OK, 1, 10, END);
test_comp("[*--]", 0, 0);
test_exec("-", 0, REG_OK, 0, 1, END);
test_exec("*", 0, REG_OK, 0, 1, END);
test_comp("[*--Z]+", REG_EXTENDED, 0);
test_exec("!+*,---ABC", 0, REG_OK, 1, 7, END);
test_comp("[a-]+", REG_EXTENDED, 0);
test_exec("xa-a--a-ay", 0, REG_OK, 1, 9, END);
/* REG_ICASE and character sets. */
test_comp("[a-c]*", REG_ICASE | REG_EXTENDED, 0);
test_exec("cABbage", 0, REG_OK, 0, 5, END);
test_comp("[^a-c]*", REG_ICASE | REG_EXTENDED, 0);
test_exec("tObAcCo", 0, REG_OK, 0, 2, END);
test_comp("[A-C]*", REG_ICASE | REG_EXTENDED, 0);
test_exec("cABbage", 0, REG_OK, 0, 5, END);
test_comp("[^A-C]*", REG_ICASE | REG_EXTENDED, 0);
test_exec("tObAcCo", 0, REG_OK, 0, 2, END);
/* Complex character sets. */
test_comp("[[:digit:]a-z#$%]+", REG_EXTENDED, 0);
test_exec("__abc#lmn012$x%yz789*", 0, REG_OK, 2, 20, END);
test_comp("[[:digit:]a-z#$%]+", REG_ICASE | REG_EXTENDED, 0);
test_exec("__abcLMN012x%#$yz789*", 0, REG_OK, 2, 20, END);
test_comp("[^[:digit:]a-z#$%]+", REG_EXTENDED, 0);
test_exec("abc#lmn012$x%yz789--@*,abc", 0, REG_OK, 18, 23, END);
test_comp("[^[:digit:]a-z#$%]+", REG_ICASE | REG_EXTENDED, 0);
test_exec("abc#lmn012$x%yz789--@*,abc", 0, REG_OK, 18, 23, END);
test_comp("[^[:digit:]#$%[:xdigit:]]+", REG_ICASE | REG_EXTENDED, 0);
test_exec("abc#lmn012$x%yz789--@*,abc", 0, REG_OK, 4, 7, END);
test_comp("[^-]+", REG_EXTENDED, 0);
test_exec("---afd*(&,ml---", 0, REG_OK, 3, 12, END);
test_comp("[^--Z]+", REG_EXTENDED, 0);
test_exec("---AFD*(&,ml---", 0, REG_OK, 6, 12, END);
test_comp("[^--Z]+", REG_ICASE | REG_EXTENDED, 0);
test_exec("---AFD*(&,ml---", 0, REG_OK, 6, 10, END);
/* Unsupported things (equivalence classes and multicharacter collating
elements) */
test_comp("[[.foo.]]", 0, REG_ECOLLATE);
test_comp("[[=foo=]]", 0, REG_ECOLLATE);
test_comp("[[..]]", 0, REG_ECOLLATE);
test_comp("[[==]]", 0, REG_ECOLLATE);
test_comp("[[.]]", 0, REG_ECOLLATE);
test_comp("[[=]]", 0, REG_ECOLLATE);
test_comp("[[.]", 0, REG_ECOLLATE);
test_comp("[[=]", 0, REG_ECOLLATE);
test_comp("[[.", 0, REG_ECOLLATE);
test_comp("[[=", 0, REG_ECOLLATE);
/* Miscellaneous tests. */
test_comp("abc\\(\\(de\\)\\(fg\\)\\)hi", 0, 0);
test_exec("xabcdefghiy", 0, REG_OK, 1, 10, 4, 8, 4, 6, 6, 8, END);
test_comp("abc*def", 0, 0);
test_exec("xabdefy", 0, REG_OK, 1, 6, END);
test_exec("xabcdefy", 0, REG_OK, 1, 7, END);
test_exec("xabcccccccdefy", 0, REG_OK, 1, 13, END);
test_comp("abc\\(def\\)*ghi", 0, 0);
test_exec("xabcghiy", 0, REG_OK, 1, 7, -1, -1, END);
test_exec("xabcdefghi", 0, REG_OK, 1, 10, 4, 7, END);
test_exec("xabcdefdefdefghi", 0, REG_OK, 1, 16, 10, 13, END);
test_comp("a?", REG_EXTENDED, REG_OK);
test_exec("aaaaa", 0, REG_OK, 0, 1, END);
test_exec("xaaaaa", 0, REG_OK, 0, 0, END);
test_comp("a+", REG_EXTENDED, REG_OK);
test_exec("aaaaa", 0, REG_OK, 0, 5, END);
test_exec("xaaaaa", 0, REG_OK, 1, 6, END);
/*
* Test anchors and their behaviour with the REG_NEWLINE compilation
* flag and the REG_NOTBOL, REG_NOTEOL execution flags.
*/
/* Normally, `^' matches the empty string at beginning of input.
If REG_NOTBOL is used, `^' won't match the zero length string. */
test_comp("^abc", 0, 0);
test_exec("abcdef", 0, REG_OK, 0, 3, END);
test_exec("abcdef", REG_NOTBOL, REG_NOMATCH);
test_exec("xyzabcdef", 0, REG_NOMATCH);
test_exec("xyzabcdef", REG_NOTBOL, REG_NOMATCH);
test_exec("\nabcdef", 0, REG_NOMATCH);
test_exec("\nabcdef", REG_NOTBOL, REG_NOMATCH);
/* Normally, `$' matches the empty string at end of input.
If REG_NOTEOL is used, `$' won't match the zero length string. */
test_comp("abc$", 0, 0);
test_exec("defabc", 0, REG_OK, 3, 6, END);
test_exec("defabc", REG_NOTEOL, REG_NOMATCH);
test_exec("defabcxyz", 0, REG_NOMATCH);
test_exec("defabcxyz", REG_NOTEOL, REG_NOMATCH);
test_exec("defabc\n", 0, REG_NOMATCH);
test_exec("defabc\n", REG_NOTEOL, REG_NOMATCH);
test_comp("^abc$", 0, 0);
test_exec("abc", 0, REG_OK, 0, 3, END);
test_exec("abc", REG_NOTBOL, REG_NOMATCH);
test_exec("abc", REG_NOTEOL, REG_NOMATCH);
test_exec("abc", REG_NOTBOL | REG_NOTEOL, REG_NOMATCH);
test_exec("\nabc\n", 0, REG_NOMATCH);
test_exec("defabc\n", 0, REG_NOMATCH);
test_exec("\nabcdef", 0, REG_NOMATCH);
test_exec("abcdef", 0, REG_NOMATCH);
test_exec("defabc", 0, REG_NOMATCH);
test_exec("abc\ndef", 0, REG_NOMATCH);
test_exec("def\nabc", 0, REG_NOMATCH);
/* If REG_NEWLINE is used, `^' matches the empty string immediately after
a newline, regardless of whether execution flags contain REG_NOTBOL.
Similarly, if REG_NEWLINE is used, `$' matches the empty string
immediately before a newline, regardless of execution flags. */
test_comp("^abc", REG_NEWLINE, 0);
test_exec("abcdef", 0, REG_OK, 0, 3, END);
test_exec("abcdef", REG_NOTBOL, REG_NOMATCH);
test_exec("xyzabcdef", 0, REG_NOMATCH);
test_exec("xyzabcdef", REG_NOTBOL, REG_NOMATCH);
test_exec("\nabcdef", 0, REG_OK, 1, 4, END);
test_exec("\nabcdef", REG_NOTBOL, 0, 1, 4, END);
test_comp("abc$", REG_NEWLINE, 0);
test_exec("defabc", 0, REG_OK, 3, 6, END);
test_exec("defabc", REG_NOTEOL, REG_NOMATCH);
test_exec("defabcxyz", 0, REG_NOMATCH);
test_exec("defabcxyz", REG_NOTEOL, REG_NOMATCH);
test_exec("defabc\n", 0, REG_OK, 3, 6, END);
test_exec("defabc\n", REG_NOTEOL, 0, 3, 6, END);
test_comp("^abc$", REG_NEWLINE, 0);
test_exec("abc", 0, REG_OK, 0, 3, END);
test_exec("abc", REG_NOTBOL, REG_NOMATCH);
test_exec("abc", REG_NOTEOL, REG_NOMATCH);
test_exec("abc", REG_NOTBOL | REG_NOTEOL, REG_NOMATCH);
test_exec("\nabc\n", 0, REG_OK, 1, 4, END);
test_exec("defabc\n", 0, REG_NOMATCH);
test_exec("\nabcdef", 0, REG_NOMATCH);
test_exec("abcdef", 0, REG_NOMATCH);
test_exec("abcdef", REG_NOTBOL, REG_NOMATCH);
test_exec("defabc", 0, REG_NOMATCH);
test_exec("defabc", REG_NOTEOL, REG_NOMATCH);
test_exec("abc\ndef", 0, REG_OK, 0, 3, END);
test_exec("abc\ndef", REG_NOTBOL, REG_NOMATCH);
test_exec("abc\ndef", REG_NOTEOL, 0, 0, 3, END);
test_exec("abc\ndef", REG_NOTBOL | REG_NOTEOL, REG_NOMATCH);
test_exec("def\nabc", 0, REG_OK, 4, 7, END);
test_exec("def\nabc", REG_NOTBOL, 0, 4, 7, END);
test_exec("def\nabc", REG_NOTEOL, REG_NOMATCH);
test_exec("def\nabc", REG_NOTBOL | REG_NOTEOL, REG_NOMATCH);
/* With BRE syntax, `^' has a special meaning only at the beginning of the
RE or the beginning of a parenthesized subexpression. */
test_comp("a\\{0,1\\}^bc", 0, 0);
test_exec("bc", 0, REG_NOMATCH);
test_exec("^bc", 0, REG_OK, 0, 3, END);
test_exec("abc", 0, REG_NOMATCH);
test_exec("a^bc", 0, REG_OK, 0, 4, END);
test_comp("a\\{0,1\\}\\(^bc\\)", 0, 0);
test_exec("bc", 0, REG_OK, 0, 2, 0, 2, END);
test_exec("^bc", 0, REG_NOMATCH);
test_exec("abc", 0, REG_NOMATCH);
test_exec("a^bc", 0, REG_NOMATCH);
test_comp("(^a", 0, 0);
test_exec("(^a", 0, REG_OK, 0, 3, END);
/* With BRE syntax, `$' has a special meaning only at the end of the
RE or the end of a parenthesized subexpression. */
test_comp("ab$c\\{0,1\\}", 0, 0);
test_exec("ab", 0, REG_NOMATCH);
test_exec("ab$", 0, REG_OK, 0, 3, END);
test_exec("abc", 0, REG_NOMATCH);
test_exec("ab$c", 0, REG_OK, 0, 4, END);
test_comp("\\(ab$\\)c\\{0,1\\}", 0, 0);
test_exec("ab", 0, REG_OK, 0, 2, 0, 2, END);
test_exec("ab$", 0, REG_NOMATCH);
test_exec("abc", 0, REG_NOMATCH);
test_exec("ab$c", 0, REG_NOMATCH);
test_comp("a$)", 0, 0);
test_exec("a$)", 0, REG_OK, 0, 3, END);
/* Miscellaneous tests for `^' and `$'. */
test_comp("foo^$", REG_EXTENDED, 0);
test_exec("foo", 0, REG_NOMATCH);
test_comp("x$\n^y", REG_EXTENDED | REG_NEWLINE, 0);
test_exec("foo\nybarx\nyes\n", 0, REG_OK, 8, 11, END);
test_comp("^$", 0, 0);
test_exec("x", 0, REG_NOMATCH);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("\n", 0, REG_NOMATCH);
test_comp("^$", REG_NEWLINE, 0);
test_exec("x", 0, REG_NOMATCH);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("\n", 0, REG_OK, 0, 0, END);
/* REG_NEWLINE causes `.' not to match newlines. */
test_comp(".*", 0, 0);
test_exec("ab\ncd", 0, REG_OK, 0, 5, END);
test_comp(".*", REG_NEWLINE, 0);
test_exec("ab\ncd", 0, REG_OK, 0, 2, END);
/*
* Tests for nonstandard syntax extensions.
*/
/* Zero width assertions. */
test_comp("\\<x", REG_EXTENDED, 0);
test_exec("aax xaa", 0, REG_OK, 4, 5, END);
test_exec("xaa", 0, REG_OK, 0, 1, END);
test_comp("x\\>", REG_EXTENDED, 0);
test_exec("axx xaa", 0, REG_OK, 2, 3, END);
test_exec("aax", 0, REG_OK, 2, 3, END);
test_comp("\\bx", REG_EXTENDED, 0);
test_exec("axx xaa", 0, REG_OK, 4, 5, END);
test_exec("aax", 0, REG_NOMATCH);
test_exec("xax", 0, REG_OK, 0, 1, END);
test_comp("x\\b", REG_EXTENDED, 0);
test_exec("axx xaa", 0, REG_OK, 2, 3, END);
test_exec("aax", 0, REG_OK, 2, 3, END);
test_exec("xaa", 0, REG_NOMATCH);
test_comp("\\Bx", REG_EXTENDED, 0);
test_exec("aax xxa", 0, REG_OK, 2, 3, END);
test_comp("\\Bx\\b", REG_EXTENDED, 0);
test_exec("aax xxx", 0, REG_OK, 2, 3, END);
test_comp("\\<.", REG_EXTENDED, 0);
test_exec(";xaa", 0, REG_OK, 1, 2, END);
/* Shorthands for character classes. */
test_comp("\\w+", REG_EXTENDED, 0);
#ifdef SRC_IN_ISO_8859_1
test_exec(",.(a23_Nt-�o)", 0, REG_OK, 3, 9, END);
#else
#ifdef SRC_IN_UTF_8
/* iconv -f ISO-8859-1 -t UTF-8 file_with_lines_above > www_utf_8 */
test_exec(",.(a23_Nt-öo)", 0, REG_OK, 3, 9, END);
#else
unsigned char str_000[] = {
',','.','(','a','2','3','_','N','t','-',0xF6,'o',0x00
};
test_exec((char const *)str_000, 0, REG_OK, 3, 9, END);
#endif
#endif
test_comp("\\d+", REG_EXTENDED, 0);
test_exec("uR120_4=v4", 0, REG_OK, 2, 5, END);
test_comp("\\D+", REG_EXTENDED, 0);
test_exec("120d_=vA4s", 0, REG_OK, 3, 8, END);
/* Quoted special characters. */
test_comp("\\t", REG_EXTENDED, 0);
test_comp("\\e", REG_EXTENDED, 0);
/* Test the \x1B and \x{263a} extensions for specifying 8 bit and wide
characters in hexadecimal. */
test_comp("\\x41", REG_EXTENDED, 0);
test_exec("ABC", 0, REG_OK, 0, 1, END);
test_comp("\\x5", REG_EXTENDED, 0);
test_exec("\005", 0, REG_OK, 0, 1, END);
test_comp("\\x5r", REG_EXTENDED, 0);
test_exec("\005r", 0, REG_OK, 0, 2, END);
test_comp("\\x", REG_EXTENDED, 0);
test_nexec("\000", 1, 0, REG_OK, 0, 1, END);
test_comp("\\xr", REG_EXTENDED, 0);
test_nexec("\000r", 2, 0, REG_OK, 0, 2, END);
test_comp("\\x{41}", REG_EXTENDED, 0);
test_exec("ABC", 0, REG_OK, 0, 1, END);
test_comp("\\x{5}", REG_EXTENDED, 0);
test_exec("\005", 0, REG_OK, 0, 1, END);
test_comp("\\x{5}r", REG_EXTENDED, 0);
test_exec("\005r", 0, REG_OK, 0, 2, END);
test_comp("\\x{}", REG_EXTENDED, 0);
test_nexec("\000", 1, 0, REG_OK, 0, 1, END);
test_comp("\\x{}r", REG_EXTENDED, 0);
test_nexec("\000r", 2, 0, REG_OK, 0, 2, END);
test_comp("\\x{00000000}", REG_EXTENDED, 0);
test_comp("\\x{000000000}", REG_EXTENDED, REG_EBRACE);
/* Tests for (?inrU-inrU) and (?inrU-inrU:) */
test_comp("foo(?i)bar", REG_EXTENDED, 0);
test_exec("fooBaR", 0, REG_OK, 0, 6, END);
test_comp("foo(?i)bar|zap", REG_EXTENDED, 0);
test_exec("fooBaR", 0, REG_OK, 0, 6, END);
test_exec("foozap", 0, REG_OK, 0, 6, END);
test_exec("foozAp", 0, REG_OK, 0, 6, END);
test_exec("zap", 0, REG_NOMATCH);
test_comp("foo(?-i:zap)zot", REG_EXTENDED | REG_ICASE, 0);
test_exec("FoOzapZOt", 0, REG_OK, 0, 9, END);
test_exec("FoOzApZOt", 0, REG_NOMATCH);
test_comp("foo(?i:bar|zap)", REG_EXTENDED, 0);
test_exec("foozap", 0, REG_OK, 0, 6, END);
test_exec("foobar", 0, REG_OK, 0, 6, END);
test_exec("foobAr", 0, REG_OK, 0, 6, END);
test_exec("fooZaP", 0, REG_OK, 0, 6, END);
test_comp("foo(?U:o*)(o*)", REG_EXTENDED, 0);
test_exec("foooo", 0, REG_OK, 0, 5, 3, 5, END);
/* Test comment syntax. */
test_comp("foo(?# This here is a comment. )bar", REG_EXTENDED, 0);
test_exec("foobar", 0, REG_OK, 0, 6, END);
/* Tests for \Q and \E. */
test_comp("\\((\\Q)?:\\<[^$\\E)", REG_EXTENDED, 0);
test_exec("()?:\\<[^$", 0, REG_OK, 0, 9, 1, 9, END);
test_comp("\\Qabc\\E.*", REG_EXTENDED, 0);
test_exec("abcdef", 0, REG_OK, 0, 6, END);
test_comp("\\Qabc\\E.*|foo", REG_EXTENDED, 0);
test_exec("parabc123wxyz", 0, REG_OK, 3, 13, END);
test_exec("fooabc123wxyz", 0, REG_OK, 0, 3, END);
/*
* Test integer parser used for bounded repititions.
*/
test_comp("a{9223372036854775808,}", REG_EXTENDED, REG_BADMAX);
test_comp("a{9223372036854775808}", REG_EXTENDED, REG_BADMAX);
test_comp("a{9223372036854775807,}", REG_EXTENDED, REG_BADMAX);
test_comp("a{9223372036854775807}", REG_EXTENDED, REG_BADMAX);
test_comp("a{2147483648,}", REG_EXTENDED, REG_BADMAX);
test_comp("a{2147483648}", REG_EXTENDED, REG_BADMAX);
test_comp("a{2147483647,}", REG_EXTENDED, REG_BADMAX);
test_comp("a{2147483647}", REG_EXTENDED, REG_BADMAX);
test_comp("a{32768,}", REG_EXTENDED, REG_BADMAX);
test_comp("a{32768}", REG_EXTENDED, REG_BADMAX);
test_comp("a{32767,}", REG_EXTENDED, REG_BADMAX);
test_comp("a{32767}", REG_EXTENDED, REG_BADMAX);
test_comp("a{256,}", REG_EXTENDED, REG_BADMAX);
test_comp("a{256}", REG_EXTENDED, REG_BADMAX);
test_comp("a{255,}", REG_EXTENDED, REG_OK);
test_comp("a{255}", REG_EXTENDED, REG_OK);
/*
* Test bounded repetitions.
*/
test_comp("a{0,0}", REG_EXTENDED, REG_OK);
test_exec("aaa", 0, REG_OK, 0, 0, END);
test_comp("a{0,1}", REG_EXTENDED, REG_OK);
test_exec("aaa", 0, REG_OK, 0, 1, END);
test_comp("a{1,1}", REG_EXTENDED, REG_OK);
test_exec("aaa", 0, REG_OK, 0, 1, END);
test_comp("a{1,3}", REG_EXTENDED, REG_OK);
test_exec("xaaaaa", 0, REG_OK, 1, 4, END);
test_comp("a{0,3}", REG_EXTENDED, REG_OK);
test_exec("aaaaa", 0, REG_OK, 0, 3, END);
test_comp("a{0,}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("aa", 0, REG_OK, 0, 2, END);
test_exec("aaa", 0, REG_OK, 0, 3, END);
test_comp("a{1,}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_NOMATCH);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("aa", 0, REG_OK, 0, 2, END);
test_exec("aaa", 0, REG_OK, 0, 3, END);
test_comp("a{2,}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_NOMATCH);
test_exec("a", 0, REG_NOMATCH);
test_exec("aa", 0, REG_OK, 0, 2, END);
test_exec("aaa", 0, REG_OK, 0, 3, END);
test_comp("a{3,}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_NOMATCH);
test_exec("a", 0, REG_NOMATCH);
test_exec("aa", 0, REG_NOMATCH);
test_exec("aaa", 0, REG_OK, 0, 3, END);
test_exec("aaaa", 0, REG_OK, 0, 4, END);
test_exec("aaaaa", 0, REG_OK, 0, 5, END);
test_exec("aaaaaa", 0, REG_OK, 0, 6, END);
test_exec("aaaaaaa", 0, REG_OK, 0, 7, END);
test_comp("a{,}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("aaa", 0, REG_OK, 0, 3, END);
test_comp("a{,0}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("aaa", 0, REG_OK, 0, 0, END);
test_comp("a{,1}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("aa", 0, REG_OK, 0, 1, END);
test_comp("a{,2}", REG_EXTENDED, REG_OK);
test_exec("", 0, REG_OK, 0, 0, END);
test_exec("a", 0, REG_OK, 0, 1, END);
test_exec("aa", 0, REG_OK, 0, 2, END);
test_exec("aaa", 0, REG_OK, 0, 2, END);
test_comp("a{5,10}", REG_EXTENDED, REG_OK);
test_comp("a{6,6}", REG_EXTENDED, REG_OK);
test_exec("aaaaaaaaaaaa", 0, REG_OK, 0, 6, END);
test_exec("xxaaaaaaaaaaaa", 0, REG_OK, 2, 8, END);
test_exec("xxaaaaa", 0, REG_NOMATCH);
test_comp("a{5,6}", REG_EXTENDED, REG_OK);
test_exec("aaaaaaaaaaaa", 0, REG_OK, 0, 6, END);
test_exec("xxaaaaaaaaaaaa", 0, REG_OK, 2, 8, END);
test_exec("xxaaaaa", 0, REG_OK, 2, 7, END);
test_exec("xxaaaa", 0, REG_NOMATCH);
/* Trickier ones... */
test_comp("([ab]{5,10})*b", REG_EXTENDED, REG_OK);
test_exec("bbbbbabaaaaab", 0, REG_OK, 0, 13, 5, 12, END);
test_exec("bbbbbbaaaaab", 0, REG_OK, 0, 12, 5, 11, END);
test_exec("bbbbbbaaaab", 0, REG_OK, 0, 11, 0, 10, END);
test_exec("bbbbbbaaab", 0, REG_OK, 0, 10, 0, 9, END);
test_exec("bbbbbbaab", 0, REG_OK, 0, 9, 0, 8, END);
test_exec("bbbbbbab", 0, REG_OK, 0, 8, 0, 7, END);
test_comp("([ab]*)(ab[ab]{5,10})ba", REG_EXTENDED, REG_OK);
test_exec("abbabbbabaabbbbbbbbbbbbbabaaaabab", 0, REG_OK,
0, 10, 0, 0, 0, 8, END);
test_exec("abbabbbabaabbbbbbbbbbbbabaaaaabab", 0, REG_OK,
0, 32, 0, 23, 23, 30, END);
test_exec("abbabbbabaabbbbbbbbbbbbabaaaabab", 0, REG_OK,
0, 24, 0, 10, 10, 22, END);
test_exec("abbabbbabaabbbbbbbbbbbba", 0, REG_OK,
0, 24, 0, 10, 10, 22, END);
test_comp("^((a{1,2})?x)*y", REG_EXTENDED | REG_NOSUB, REG_OK);
test_exec("y", 0, REG_OK, END);
test_exec("xy", 0, REG_OK, END);
test_exec("axy", 0, REG_OK, END);
test_exec("aaxy", 0, REG_OK, END);
test_exec("aaaxy", 0, REG_NOMATCH, END);
/* Test repeating something that has submatches inside. */
test_comp("(a){0,5}", REG_EXTENDED, 0);
test_exec("", 0, REG_OK, 0, 0, -1, -1, END);
test_exec("a", 0, REG_OK, 0, 1, 0, 1, END);
test_exec("aa", 0, REG_OK, 0, 2, 1, 2, END);
test_exec("aaa", 0, REG_OK, 0, 3, 2, 3, END);
test_exec("aaaa", 0, REG_OK, 0, 4, 3, 4, END);
test_exec("aaaaa", 0, REG_OK, 0, 5, 4, 5, END);
test_exec("aaaaaa", 0, REG_OK, 0, 5, 4, 5, END);
test_comp("(a){2,3}", REG_EXTENDED, 0);
test_exec("", 0, REG_NOMATCH);
test_exec("a", 0, REG_NOMATCH);
test_exec("aa", 0, REG_OK, 0, 2, 1, 2, END);
test_exec("aaa", 0, REG_OK, 0, 3, 2, 3, END);
test_exec("aaaa", 0, REG_OK, 0, 3, 2, 3, END);
test_comp("\\(a\\)\\{4\\}", 0, 0);
test_exec("aaaa", 0, REG_OK, 0, 4, 3, 4, END);
test_comp("\\(a*\\)\\{2\\}", 0, 0);
test_exec("a", 0, REG_OK, 0, 1, 1, 1, END);
test_comp("((..)|(.)){2}", REG_EXTENDED, 0);
test_exec("aa", 0, REG_OK, 0, 2, 1, 2, -1, -1, 1, 2, END);
/* Nested repeats. */
test_comp("(.){2}{3}", REG_EXTENDED, 0);
test_exec("xxxxx", 0, REG_NOMATCH);
test_exec("xxxxxx", 0, REG_OK, 0, 6, 5, 6, END);
test_comp("(..){2}{3}", REG_EXTENDED, 0);
test_exec("xxxxxxxxxxx", 0, REG_NOMATCH);
test_exec("xxxxxxxxxxxx", 0, REG_OK, 0, 12, 10, 12, END);
test_comp("((..){2}.){3}", REG_EXTENDED, 0);
test_exec("xxxxxxxxxxxxxx", 0, REG_NOMATCH);
test_exec("xxxxxxxxxxxxxxx", 0, REG_OK, 0, 15, 10, 15, 12, 14, END);
test_comp("((..){1,2}.){3}", REG_EXTENDED, 0);
test_exec("xxxxxxxx", 0, REG_NOMATCH);
test_exec("xxxxxxxxx", 0, REG_OK, 0, 9, 6, 9, 6, 8, END);
test_exec("xxxxxxxxxx", 0, REG_OK, 0, 9, 6, 9, 6, 8, END);
test_exec("xxxxxxxxxxx", 0, REG_OK, 0, 11, 8, 11, 8, 10, END);
test_comp("a{2}{2}x", REG_EXTENDED, 0);
test_exec("", 0, REG_NOMATCH);
test_exec("x", 0, REG_NOMATCH);
test_exec("ax", 0, REG_NOMATCH);
test_exec("aax", 0, REG_NOMATCH);
test_exec("aaax", 0, REG_NOMATCH);
test_exec("aaaax", 0, REG_OK, 0, 5, END);
test_exec("aaaaax", 0, REG_OK, 1, 6, END);
test_exec("aaaaaax", 0, REG_OK, 2, 7, END);
test_exec("aaaaaaax", 0, REG_OK, 3, 8, END);
test_exec("aaaaaaaax", 0, REG_OK, 4, 9, END);
/* Repeats with iterations inside. */
test_comp("([a-z]+){2,5}", REG_EXTENDED, 0);
test_exec("a\n", 0, REG_NOMATCH);
test_exec("aa\n", 0, REG_OK, 0, 2, 1, 2, END);
/* Multiple repeats in one regexp. */
test_comp("a{3}b{3}", REG_EXTENDED, 0);
test_exec("aaabbb", 0, REG_OK, 0, 6, END);
test_exec("aaabbbb", 0, REG_OK, 0, 6, END);
test_exec("aaaabbb", 0, REG_OK, 1, 7, END);
test_exec("aabbb", 0, REG_NOMATCH);
test_exec("aaabb", 0, REG_NOMATCH);
/* Test that different types of repetitions work correctly when used
in the same regexp. */
test_comp("a{2}{2}xb+xc*xd?x", REG_EXTENDED, 0);
test_exec("aaaaxbxcxdx", 0, REG_OK, 0, 11, END);
test_exec("aaaxbxcxdx", 0, REG_NOMATCH);
test_exec("aabxcxdx", 0, REG_NOMATCH);
test_exec("aaaacxdx", 0, REG_NOMATCH);
test_exec("aaaaxbdx", 0, REG_NOMATCH);
test_comp("^!packet [0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3} [0-9]+",
REG_EXTENDED, 0);
test_exec("!packet 10.0.2.4 12765 ei voittoa", 0, REG_OK, 0, 22, END);
/*
* Back referencing tests.
*/
test_comp("([a-z]*) \\1", REG_EXTENDED, 0);
test_exec("foobar foobar", 0, REG_OK, 0, 13, 0, 6, END);
/* Searching for a leftmost longest square (repeated string) */
test_comp("(.*)\\1", REG_EXTENDED, 0);
test_exec("foobarfoobar", 0, REG_OK, 0, 12, 0, 6, END);
test_comp("a(b)*c\\1", REG_EXTENDED, 0);
test_exec("acb", 0, REG_OK, 0, 2, -1, -1, END);
test_exec("abbcbbb", 0, REG_OK, 0, 5, 2, 3, END);
test_exec("abbdbd", 0, REG_NOMATCH);
test_comp("([a-c]*)\\1", REG_EXTENDED, 0);
test_exec("abcacdef", 0, REG_OK, 0, 0, 0, 0, END);
test_exec("abcabcabcd", 0, REG_OK, 0, 6, 0, 3, END);
test_comp("(.{1,3})\\1", REG_EXTENDED, 0);
test_exec("foo", 0, REG_OK, 1, 3, 1, 2, END);
test_comp("\\(a*\\)*\\(x\\)\\(\\1\\)", 0, 0);
test_exec("x", 0, REG_OK, 0, 1, 0, 0, 0, 1, 1, 1, END);
#if KNOWN_BUG
test_exec("ax", 0, REG_OK, 0, 2, 1, 1, 1, 2, 2, 2, END);
#endif
test_comp("(a)\\1{1,2}", REG_EXTENDED, 0);
test_exec("aabc", 0, REG_OK, 0, 2, 0, 1, END);
test_comp("((.*)\\1)+", REG_EXTENDED, 0);
test_exec("aa", 0, REG_OK, 0, 2, 0, 2, 0, 1, END);
#if KNOWN_BUG
test_comp("()(\\1\\1)*", REG_EXTENDED, 0);
test_exec("", 0, REG_OK, 0, 0, 0, 0, 0, 0, END);
#endif
/* Check that back references work with REG_NOSUB. */
test_comp("(o)\\1", REG_EXTENDED | REG_NOSUB, 0);
test_exec("foobar", 0, REG_OK, END);
test_comp("(o)\\1", REG_EXTENDED, 0);
test_exec("foobar", 0, REG_OK, 1, 3, 1, 2, END);
test_comp("(o)\\1", REG_EXTENDED, 0);
test_exec("fobar", 0, REG_NOMATCH);
test_comp("\\1foo", REG_EXTENDED, REG_ESUBREG);
test_comp("\\1foo(bar)", REG_EXTENDED, 0);
/* Back reference with zero-width assertion. */
test_comp("(.)\\1$", REG_EXTENDED, 0);
test_exec("foox", 0, REG_NOMATCH);
test_exec("foo", 0, REG_OK, 1, 3, 1, 2, END);
/* Back references together with {}. */
test_comp("([0-9]{5})\\1", REG_EXTENDED, 0);
test_exec("12345", 0, REG_NOMATCH);
test_exec("1234512345", 0, REG_OK, 0, 10, 0, 5, END);
test_comp("([0-9]{4})\\1", REG_EXTENDED, 0);
test_exec("1234", 0, REG_NOMATCH);
test_exec("12341234", 0, REG_OK, 0, 8, 0, 4, END);
/*
* Test minimal repetitions (non-greedy repetitions)
*/
avoid_eflags = REG_BACKTRACKING_MATCHER | REG_APPROX_MATCHER;
/* Basic .*/
test_comp(".*?", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 0, END);
test_comp(".+?", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 1, END);
test_comp(".??", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 0, END);
test_comp(".{2,5}?", REG_EXTENDED, 0);
test_exec("abcd", 0, REG_OK, 0, 2, END);
/* More complicated. */
test_comp("<b>(.*?)</b>", REG_EXTENDED, 0);
test_exec("<b>text1</b><b>text2</b>", 0, REG_OK, 0, 12, 3, 8, END);
test_comp("a(.*?)(foo|bar|zap)", REG_EXTENDED, 0);
test_exec("hubba wooga-booga zabar gafoo wazap", 0, REG_OK,
4, 23, 5, 20, 20, 23, END);
/* Test REG_UNGREEDY. */
test_comp(".*", REG_EXTENDED | REG_UNGREEDY, 0);
test_exec("abcd", 0, REG_OK, 0, 0, END);
test_comp(".*?", REG_EXTENDED | REG_UNGREEDY, 0);
test_exec("abcd", 0, REG_OK, 0, 4, END);
avoid_eflags = 0;
/*
* Error reporting tests.
*/
test_comp("\\", REG_EXTENDED, REG_EESCAPE);
test_comp("\\\\", REG_EXTENDED, REG_OK);
test_exec("\\", 0, REG_OK, 0, 1, END);
test_comp("(", REG_EXTENDED, REG_EPAREN);
test_comp("(aaa", REG_EXTENDED, REG_EPAREN);
test_comp(")", REG_EXTENDED, REG_OK);
test_exec(")", 0, REG_OK, 0, 1, END);
test_comp("a{1", REG_EXTENDED, REG_EBRACE);
test_comp("a{1,x}", REG_EXTENDED, REG_BADBR);
test_comp("a{1x}", REG_EXTENDED, REG_BADBR);
test_comp("a{1,0}", REG_EXTENDED, REG_BADBR);
test_comp("a{x}", REG_EXTENDED, REG_BADBR);
test_comp("a{}", REG_EXTENDED, REG_BADBR);
test_comp("\\", 0, REG_EESCAPE);
test_comp("\\(", 0, REG_EPAREN);
test_comp("\\)", 0, REG_EPAREN);
test_comp("a\\{1", 0, REG_EBRACE);
test_comp("a\\{1,x\\}", 0, REG_BADBR);
test_comp("a\\{1x\\}", 0, REG_BADBR);
test_comp("a\\{1,0\\}", 0, REG_BADBR);
test_comp("a\\{x\\}", 0, REG_BADBR);
test_comp("a\\{\\}", 0, REG_BADBR);
test_comp("a\\{1,256\\}", 0, REG_BADMAX);
test_comp(NULL, REG_BASIC, REG_OK);
test_comp(NULL, REG_EXTENDED, REG_OK);
/*
* Internationalization tests.
*/
/* This same test with the correct locale is below.
TBR: This is a guess for the source encoding, see comments below after the locale is set to a Japanese locale. */
#ifdef SRC_IN_EUC_JP
test_comp("��+", REG_EXTENDED, 0);
test_exec("���ξޤϡ�����������������",
0, REG_OK, 10, 13, END);
#else
#ifdef SRC_IN_UTF_8
/* iconv -f EUC_JP -t UTF-8 file_with_lines_above > zzz_utf_8
This may be incorrect because the match results might be incorrect for UTF-8, I (TBR) just don't know enough to be certain.
It compiles and runs successfully on my desktop with the C.UTF-8 locale. */
test_comp("機+", REG_EXTENDED, 0);
test_exec("この賞は、機・利便性・セキ",
0, REG_OK, 15, 18, END);
#else
/* Represent the test strings as a sequence of bytes so we don't run afoul of the compiler's expected source-charset. */
unsigned char str_001[] = {
0xB5,0xA1,'+',0x00
};
unsigned char str_002[] = {
0xA4,0xB3,0xA4,0xCE,0xBE,0xDE,0xA4,0xCF,0xA1,0xA2,0xB5,0xA1,0xA1,0xA6,0xCD,0xF8,0xCA,0xD8,0xC0,0xAD,0xA1,0xA6,0xA5,0xBB,0xA5,0xAD,0x00
};
test_comp((char const *)str_001, REG_EXTENDED, 0);
test_exec((char const *)str_002, 0, REG_OK, 10, 13, END);
#endif
#endif
#if !defined(WIN32) && !defined(__OpenBSD__)
if (setlocale(LC_CTYPE, "en_US.ISO-8859-1") != NULL ||
setlocale(LC_CTYPE, "en_US.ISO8859-1") != NULL)
{
fprintf(outf, "\nTesting LC_CTYPE en_US.ISO-8859-1\n");
#ifdef SRC_IN_ISO_8859_1
test_comp("aBCdeFghiJKlmnoPQRstuvWXyZ���", REG_ICASE, 0);
test_exec("abCDefGhiJKlmNoPqRStuVwXyz���", 0, REG_OK, 0, 29, END);
#else
#ifdef SRC_IN_UTF_8
/* iconv -f ISO-8859-1 -t UTF-8 file_with_lines_above > yyy_utf_8 */
/* This fails with no match on freebsd, but succeeds in linux. */
test_comp("aBCdeFghiJKlmnoPQRstuvWXyZåäö", REG_ICASE, 0);
test_exec("abCDefGhiJKlmNoPqRStuVwXyzÅÄÖ", 0, REG_OK, 0, 29, END);
#else
/* Represent the test strings as a sequence of bytes so we don't run afoul of the compiler's expected source-charset. */
unsigned char str_003[] = {
'a','B','C','d','e','F','g','h','i','J','K','l','m','n','o','P','Q','R','s','t','u','v','W','X','y','Z',0xE5,0xE4,0xF6,0x00
};
unsigned char str_004[] = {
'a','b','C','D','e','f','G','h','i','J','K','l','m','N','o','P','q','R','S','t','u','V','w','X','y','z',0xC5,0xC4,0xD6,0x00
};
test_comp((char const *)str_003, REG_ICASE, 0);
test_exec((char const *)str_004, 0, REG_OK, 0, 29, END);
#endif
#endif
}
#ifdef TRE_MULTIBYTE
if (setlocale(LC_CTYPE, "ja_JP.eucjp") != NULL ||
setlocale(LC_CTYPE, "ja_JP.eucJP") != NULL)
{
fprintf(outf, "\nTesting LC_CTYPE ja_JP.eucjp\n");
/* I tried to make a test where implementations not aware of multibyte
character sets will fail. I have no idea what the japanese text here
means, I took it from http://www.ipsec.co.jp/. */
/* TBR 2023/03/22: iconv has (at least) the following encoding names for Japanese:
EUC-JIS-2004 EUC-JISX0213
EUC-JP-MS EUCJP-MS EUCJP-OPEN EUCJP-WIN EUCJPMS
EUC-JP CSEUCPKDFMTJAPANESE EUCJP IBM-EUCJP
ISO-2022-JP-1 ISO2022-JP1
ISO-2022-JP-2 CSISO2022JP2 ISO2022-JP2 ISO-2022-JP-2004 ISO-2022-JP-3 ISO2022-JP2004 ISO2022-JP3
ISO-2022-JP CSISO2022JP ISO2022-JP
Both iconv arguments of EUC-JP and EUC-JP-MS produced the converted strings below,
all the others I tried resulted in invalid characters. So guess at EUC-JP.
If anyone knows what the encoding actually was, feel free to let me know at tbr at acm dot org :). */
#ifdef SRC_IN_EUC_JP
test_comp("��+", REG_EXTENDED, 0);
test_exec("���ξޤϡ�����������������", 0, REG_OK, 10, 12, END);
#else
#ifdef SRC_IN_UTF_8
/* iconv -f EUC_JP -t UTF-8 file_with_lines_above > zzz_utf_8
This may fail because the match results might be incorrect for UTF-8, I (TBR) just don't know enough to be certain.
It compiles and runs successfully on my desktop with the C.UTF-8 locale. */
test_comp("機+", REG_EXTENDED, 0);
test_exec("この賞は、機・利便性・セキ", 0, REG_OK, 10, 12, END);
#else
/* Represent the test strings as a sequence of bytes so we don't run afoul of the compiler's expected source-charset. */
/* This test uses the same strings (str_001 and str_002) as above, now with a Japanese locale.
NOTE THE DIFFERENCE IN MATCH RESULTS - (10,13) earlier with the default locale, and (10,12) here with the Japanese locale. */
test_comp((char const *)str_001, REG_EXTENDED, 0);
test_exec((char const *)str_002, 0, REG_OK, 10, 12, END);
#endif
#endif
test_comp("a", REG_EXTENDED, 0);
test_nexec("foo\000bar", 7, 0, REG_OK, 5, 6, END);
test_comp("c$", REG_EXTENDED, 0);
test_exec("abc", 0, REG_OK, 2, 3, END);
}
else
{
fprintf(outf, "\nTRE_MULTIBYTE enabled, but skipping LC_CTYPE ja_JP.eucJP (locale unavailable)\n");
}
#endif /* TRE_MULTIBYTE */
#endif
tre_regfree(&reobj);
fprintf(outf, "\n");
if (comp_errors || exec_errors)
fprintf(outf, "%d (%d + %d) out of %d tests FAILED!\n",
comp_errors + exec_errors, comp_errors, exec_errors,
comp_tests + exec_tests);
else
fprintf(outf, "All %d tests passed.\n", comp_tests + exec_tests);
#ifdef MALLOC_DEBUGGING
if (xmalloc_dump_leaks())
return 1;
#endif /* MALLOC_DEBUGGING */
return comp_errors || exec_errors;
}
/* EOF */