aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2026-06-29 19:14:03 +0200
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2026-06-29 19:14:03 +0200
commitd71d7527a8be8654560867d761036598408bfe14 (patch)
tree57a3f056e8b1039b172a618aec264e8c172d4925
parent7c103ba4914cb47cb1e6c51cc9b8121aa5e6ef62 (diff)
downloadterex-d71d7527a8be8654560867d761036598408bfe14.tar.gz
implemented tere_set_is_interrupted_cb()
Allows you to set up a callback that's invoked repeatedly during matching. If it returns non-0 matching will abort and REG_EINTR is returned. Allows you to interrupt long-running regexp executions that would otherwise block the UI or hang the entire application.
-rw-r--r--README.md8
-rw-r--r--regcomp.c11
-rw-r--r--rege_dfa.c12
-rw-r--r--regerrs.h1
-rw-r--r--regex.h2
-rw-r--r--regexec.c5
-rw-r--r--regguts.h5
7 files changed, 39 insertions, 5 deletions
diff --git a/README.md b/README.md
index c35480e..6dbfbca 100644
--- a/README.md
+++ b/README.md
@@ -23,14 +23,12 @@ Compared to hsrex, this library has the following changes:
`tere_comp()` compilation flag.
* Support the `REG_ANCHORED` flag for `tere_exec()`.
* `tere_free()` ignores nullified `regex_t` objects.
+* `tere_set_is_interrupted_cb()` allows configuring a repeatedly
+ invoked callback. If it returns true (non-null) matching aborts
+ immediately and `tere_exec()` will return `REG_EINTR`.
## TODO
-* Hook into the matching algorithm.
- Even though runtime cannot be as catastrophically bad as in pure
- backtracking engines like PCRE, `tere_exec()` calls can still be slow
- on extremely large texts.
- The hook allows interruptions.
* Expose enough API to swap out the regular expression lexer.
Useful for custom DSLs like glob patterns or TECO patterns.
* Support splitting the subject string into two halves, so we can
diff --git a/regcomp.c b/regcomp.c
index bd332dc..e6c9f94 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -273,6 +273,17 @@ static struct fns functions = {
};
/*
+ - tere_set_is_interrupted_cb - set is_interrupted callback
+ ^ void tere_set_is_interrupted_cb(regex_t *, int (*cb)(void));
+ */
+void
+tere_set_is_interrupted_cb(
+ regex_t *re, int (*cb)(void))
+{
+ ((struct fns *)re->re_fns)->is_interrupted = cb;
+}
+
+/*
- compile - compile regular expression
^ int compile(regex_t *, const chr *, size_t, int);
*/
diff --git a/rege_dfa.c b/rege_dfa.c
index a055921..8c6f2fc 100644
--- a/rege_dfa.c
+++ b/rege_dfa.c
@@ -101,6 +101,9 @@ longest(
if (ss->flags & NOPROGRESS && v->eflags & REG_ANCHORED) {
return NULL;
}
+ if (IS_INTERRUPTED(v->re)) {
+ return NULL;
+ }
cp = nextchr(v, cp);
ss->lastseen = cp;
css = ss;
@@ -119,6 +122,9 @@ longest(
if (ss->flags & NOPROGRESS && v->eflags & REG_ANCHORED) {
return NULL;
}
+ if (IS_INTERRUPTED(v->re)) {
+ return NULL;
+ }
cp = nextchr(v, cp);
ss->lastseen = cp;
css = ss;
@@ -240,6 +246,9 @@ shortest(
if (ss->flags & NOPROGRESS && v->eflags & REG_ANCHORED) {
return NULL;
}
+ if (IS_INTERRUPTED(v->re)) {
+ return NULL;
+ }
cp = nextchr(v, cp);
ss->lastseen = cp;
css = ss;
@@ -261,6 +270,9 @@ shortest(
if (ss->flags & NOPROGRESS && v->eflags & REG_ANCHORED) {
return NULL;
}
+ if (IS_INTERRUPTED(v->re)) {
+ return NULL;
+ }
cp = nextchr(v, cp);
ss->lastseen = cp;
css = ss;
diff --git a/regerrs.h b/regerrs.h
index 259c0cb..6d23e42 100644
--- a/regerrs.h
+++ b/regerrs.h
@@ -17,3 +17,4 @@
{ REG_MIXED, "REG_MIXED", "character widths of regex and string differ" },
{ REG_BADOPT, "REG_BADOPT", "invalid embedded option" },
{ REG_ETOOBIG, "REG_ETOOBIG", "nfa has too many states" },
+{ REG_EINTR, "REG_EINTR", "interrupted" },
diff --git a/regex.h b/regex.h
index 5568ed7..d48f20e 100644
--- a/regex.h
+++ b/regex.h
@@ -290,6 +290,7 @@ typedef struct {
#define REG_MIXED 17 /* character widths of regex and string differ */
#define REG_BADOPT 18 /* invalid embedded option */
#define REG_ETOOBIG 19 /* nfa has too many states */
+#define REG_EINTR 20 /* interrupted by is_interrupted() callback */
/* two specials for debugging and testing */
#define REG_ATOI 101 /* convert error-code name to number */
#define REG_ITOA 102 /* convert error-code number to name */
@@ -320,6 +321,7 @@ MODULE_SCOPE int __REG_WIDE_EXEC(regex_t *, __REG_CONST __REG_WIDE_T *, size_t,
#endif
MODULE_SCOPE re_void regfree(regex_t *);
MODULE_SCOPE size_t regerror(int, __REG_CONST regex_t *, char *, size_t);
+MODULE_SCOPE void tere_set_is_interrupted_cb(regex_t *, int (*cb)(void));
/* automatically gathered by fwd; do not hand-edit */
/* =====^!^===== end forwards =====^!^===== */
diff --git a/regexec.c b/regexec.c
index aadb382..ae6ca77 100644
--- a/regexec.c
+++ b/regexec.c
@@ -328,6 +328,11 @@ exec(
memcpy(VS(pmatch), VS(v->pmatch), n*sizeof(regmatch_t));
}
+ if (IS_INTERRUPTED(re)) {
+ /* normalize return code in case of interruptions */
+ st = REG_EINTR;
+ }
+
/*
* Clean up.
*/
diff --git a/regguts.h b/regguts.h
index 0fd46c4..e941c28 100644
--- a/regguts.h
+++ b/regguts.h
@@ -385,8 +385,13 @@ struct subre {
struct fns {
VOID FUNCPTR(free, (regex_t *));
+ int FUNCPTR(is_interrupted, (void));
};
+#define IS_INTERRUPTED(re) \
+ (((struct fns *)(re)->re_fns)->is_interrupted && \
+ (*((struct fns *)(re)->re_fns)->is_interrupted)())
+
/*
* the insides of a regex_t, hidden behind a void *
*/