]> Pileus Git - ~andy/lamechat/commitdiff
Add regex helpers.
authorAndy Spencer <andy.spencer@spacex.com>
Sun, 17 Feb 2019 00:36:56 +0000 (16:36 -0800)
committerAndy Spencer <andy753421@gmail.com>
Thu, 4 Apr 2019 19:42:18 +0000 (19:42 +0000)
util.c
util.h
view.c

diff --git a/util.c b/util.c
index afc1cc850347395f2a0b9fdc07a56c0000ec69b9..83812380473278e771a16b18831b3c9500ca0dfc 100644 (file)
--- a/util.c
+++ b/util.c
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <time.h>
 #include <unistd.h>
+#include <regex.h>
 
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
@@ -474,6 +475,78 @@ void idle_set(idle_t *idle, int first, int next)
                error("setting idle time");
 }
 
+/* Regular expressions */
+void reg_set(reg_t *reg, const char *pattern, int size)
+{
+       if (!pattern)
+               return;
+       if (!reg->regex)
+               reg->regex = new0(regex_t);
+       if (size != reg->size) {
+               reg->size = size;
+               reg->start = realloc(reg->start, sizeof(int)*size);
+               reg->stop = realloc(reg->stop, sizeof(int)*size);
+       }
+       strset(&reg->pattern, pattern);
+       regcomp(reg->regex, pattern, REG_EXTENDED | (size?0:REG_NOSUB));
+}
+
+void reg_clr(reg_t *reg)
+{
+       if (!reg->pattern)
+               return;
+       regfree(reg->regex);
+       free(reg->regex);
+       free(reg->pattern);
+}
+
+int reg_match(reg_t *reg, const char *text)
+{
+       if (!reg->regex)
+               return 0;
+       int si = 0, mi = 0;
+       regmatch_t match = {};
+       while (mi < reg->size && text[si] &&
+              !regexec(reg->regex, &text[si], 1, &match, 0)) {
+               reg->start[mi] = si + match.rm_so;
+               reg->stop[mi] = si + match.rm_eo - 1;
+               if (match.rm_eo == 0) {
+                       si += 1;
+               } else {
+                       si += match.rm_eo;
+                       mi += 1;
+               }
+       }
+       reg->count = mi;
+       reg->index = 0;
+       return mi;
+}
+
+int reg_check(reg_t *reg, int pos)
+{
+       if (!reg->count)
+               return 0;
+
+       int i = reg->index;
+       int first = 0;
+       int last = reg->count-1;
+       while (i < last && pos > reg->stop[i])
+               i++;
+       while (i > first && pos < reg->start[i])
+               i--;
+       reg->index = i;
+
+       if (pos < reg->start[i] || pos > reg->stop[i])
+               return MATCH_NONE;
+       if (pos == reg->start[i] && pos == reg->stop[i])
+               return MATCH_BOTH;
+       if (pos == reg->start[i])
+               return MATCH_START;
+       if (pos == reg->stop[i])
+               return MATCH_STOP;
+       return MATCH_HERE;
+}
+
 /* Debugging functions */
 void debug(char *fmt, ...)
 {
@@ -524,6 +597,25 @@ static void test_suffix(const char *str,  const char *suf,
                arg0, arg1);
 }
 
+static void test_regex(const char *pattern, const char *text,
+                       const int count, const char *expect)
+{
+       char actual[80] = {};
+       reg_t reg = {};
+       reg_set(&reg, pattern, 16);
+       reg_match(&reg, text);
+       for (int i = 0; text[i]; i++)
+               actual[i] = MATCH_STR[reg_check(&reg, i)];
+       printf("%s -- /%s/%*s [%s]%*s -> %d=%d [%s]=[%s]\n",
+               (match(actual, expect) && count == reg.count)
+                       ? "pass" : "fail",
+               pattern, (int)(8-strlen(pattern)), "",
+               text,    (int)(5-strlen(text)),    "",
+               count,  reg.count,
+               actual, expect);
+       reg_clr(&reg);
+}
+
 void util_test(void)
 {
        test_prefix("/foo",     "/foo",  1, NULL);
@@ -543,4 +635,12 @@ void util_test(void)
 
        test_suffix("@ ",       "@",     0, NULL);
        test_suffix("@ foo",    "@",     0, NULL);
+
+       test_regex("abc",       "abc",   1, "<->");
+       test_regex("a.c",       "abc",   1, "<->");
+       test_regex("a.*c",      "abc",   1, "<->");
+       test_regex("[ac]",      "abc",   2, "^ ^");
+       test_regex("a*",        "aa aa", 2, "<> <>");
+       test_regex("aa*",       "aa aa", 2, "<> <>");
+       test_regex("a",         "aa aa", 4, "^^ ^^");
 }
diff --git a/util.h b/util.h
index 4434d9e26ab0b683c3bb810e839db97779067290..bcae22b1f636d2d6578fb4b9f9acbbdcf087b83c 100644 (file)
--- a/util.h
+++ b/util.h
 
 #define new0(type) alloc0(sizeof(type))
 
+#define MATCH_NONE  0b000
+#define MATCH_START 0b001
+#define MATCH_STOP  0b010
+#define MATCH_BOTH  0b011
+#define MATCH_HERE  0b100
+#define MATCH_STR   " <>^-"
+
 /* Types */
 typedef void (*cb_t)(void *data);
 
@@ -46,10 +53,20 @@ typedef struct {
        void     *data;
 } idle_t;
 
+typedef struct {
+       char     *pattern; // regex string
+       void     *regex;   // compiled regex
+       int       count;   // number of matches
+       int       index;   // cached match position
+       int       size;    // max number of matches
+       int      *start;   // start pos of each match
+       int      *stop;    // end pos of each match
+} reg_t;
+
 /* Debug functions */
 void util_init(void);
 
-/* Stirng functions */
+/* String functions */
 void strsub(char *str, char find, char repl);
 char *strcopy(const char *str);
 void strset(char **old, const char *str);
@@ -84,6 +101,12 @@ void poll_quit(void);
 void idle_add(idle_t *idle);
 void idle_set(idle_t *idle, int first, int next);
 
+/* Regex functions */
+void reg_set(reg_t *reg, const char *pattern, int flags);
+void reg_clr(reg_t *reg);
+int reg_match(reg_t *reg, const char *text);
+int reg_check(reg_t *reg, int pos);
+
 /* Debug functions */
 void debug(char *fmt, ...);
 void error(char *fmt, ...);
diff --git a/view.c b/view.c
index a0d4ea240ee5674d6147a32d81be849daa3aef38..d9d557f91727c1fc88455cb4cd7ec5806d023c82 100644 (file)
--- a/view.c
+++ b/view.c
@@ -26,7 +26,6 @@
 #include <signal.h>
 #include <sys/signalfd.h>
 #include <unistd.h>
-#include <regex.h>
 
 #include <ncursesw/ncurses.h>
 
@@ -37,8 +36,6 @@
 
 /* View constants */
 #define MATCHES    16
-#define RE_MFLAGS  (REG_EXTENDED)
-#define RE_FLAGS   (REG_EXTENDED|REG_NOSUB)
 
 /* Extra colors */
 #define COLOR_BROWN 130
@@ -71,10 +68,9 @@ typedef struct window_t {
        int        pinned;
        int        custom;
 
-       char      *filter;
+       reg_t      filter;
        server_t  *server;
        channel_t *channel;
-       regex_t    regex;
 
        channel_t *dest;
        channel_t *saved;
@@ -98,9 +94,6 @@ typedef struct {
        int pick;
        int prow;
        int pcol;
-       int mcnt;
-       int mstart[MATCHES];
-       int mstop[MATCHES];
 } printer_t;
 
 typedef enum {
@@ -135,7 +128,7 @@ static int    full;
 static int    date;
 static int    draw;
 static int    view;
-static char  *watch;
+static reg_t  watch;
 
 static char   cmd_buf[4096];
 static int    cmd_pos;
@@ -155,8 +148,6 @@ static int    hdr_lines;
 static int    sts_lines;
 static int    cmd_lines;
 
-static regex_t    regex;
-
 static server_t   sys_srv;
 static channel_t  sys_chan;
 static window_t   sys_win;
@@ -306,19 +297,6 @@ static int str_color(const char *str)
        return color(x, -1);
 }
 
-static int match_all(const char *str, regex_t *preg, int *mstart, int *mstop)
-{
-       int si = 0, mi = 0;
-       regmatch_t pmatch = {};
-       while (mi < MATCHES && str[si] && !regexec(preg, &str[si], 1, &pmatch, 0)) {
-               mstart[mi] = si + pmatch.rm_so;
-               mstop[mi] = si + pmatch.rm_eo - 1;
-               si += pmatch.rm_eo;
-               mi += 1;
-       }
-       return mi;
-}
-
 /* Window functions */
 static int in_window(message_t *msg, window_t *win)
 {
@@ -326,7 +304,7 @@ static int in_window(message_t *msg, window_t *win)
                return 0;
        if (win->channel && win->channel != msg->channel)
                return 0;
-       if (win->filter && regexec(&win->regex, msg->channel->name, 0, 0, 0))
+       if (reg_match(&win->filter, msg->channel->name))
                return 0;
        return 1;
 }
@@ -605,7 +583,6 @@ static int irc_color(const char *txt, int *i)
 static void print_string(printer_t *pr, const char *txt)
 {
        int i, n;
-       int mi = 0;
        int li = 0;
        wchar_t wc;
        const char *l = 0, *r = 0;
@@ -619,7 +596,7 @@ static void print_string(printer_t *pr, const char *txt)
                        pr->prow = pr->row;
                        pr->pcol = pr->col;
                }
-               if (mi < pr->mcnt && i >= pr->mstart[mi])
+               if (reg_check(&watch, i) & MATCH_START)
                        attron(color_watch | A_BOLD);
                if (irc_color(txt, &i))
                        continue;
@@ -652,10 +629,8 @@ static void print_string(printer_t *pr, const char *txt)
                                pr->col += 1;
                                break;
                }
-               if (mi < pr->mcnt && i >= pr->mstop[mi]) {
+               if (reg_check(&watch, i) & MATCH_STOP)
                        attroff(color_watch | A_BOLD);
-                       mi++;
-               }
        }
        if (i <= pr->pick) {
                pr->prow = pr->row;
@@ -716,8 +691,7 @@ static void print_message(printer_t *pr, message_t *msg)
        }
 
        /* Set matches */
-       if (watch)
-               pr->mcnt = match_all(msg->text, &regex, pr->mstart, pr->mstop);
+       reg_match(&watch, msg->text);
 
        /* Print message */
        pr->indent = pr->col;
@@ -725,8 +699,7 @@ static void print_message(printer_t *pr, message_t *msg)
        pr->row++;
 
        /* Clear matches */
-       if (watch)
-               pr->mcnt = 0;
+       watch.count = 0;
 }
 
 static void print_status(printer_t *pr)
@@ -896,9 +869,7 @@ static int send_command(const char *text)
                                abbrev = i;
        }
        else if (prefix(text, "/watch", &arg)) {
-               strset(&watch, arg);
-               if (watch)
-                       regcomp(&regex, watch, RE_MFLAGS);
+               reg_set(&watch, arg, MATCHES);
        }
        else if (prefix(text, "/open", &arg)) {
                win = find_window(arg ?: focus->dest->name);
@@ -924,9 +895,7 @@ static int send_command(const char *text)
                win->dest    = win->channel ?: win->dest;
        }
        else if (prefix(text, "/filter", &arg)) {
-               strset(&focus->filter, arg);
-               if (focus->filter)
-                       regcomp(&focus->regex, focus->filter, RE_FLAGS);
+               reg_set(&focus->filter, arg, 0);
        }
        else if (focus->dest != &sys_chan) {
                chat_send(focus->dest, text);
@@ -1362,10 +1331,6 @@ static void input_menu(wint_t chr)
 /* View init */
 void view_init(void)
 {
-       /* General setup */
-       if (watch)
-               regcomp(&regex, watch, RE_MFLAGS);
-
        /* System windows */
        sys_srv.name = strcopy("system");
        sys_srv.protocol = -1;
@@ -1381,11 +1346,6 @@ void view_init(void)
 
        windows = focus = &sys_win;
 
-       /* Setup windows */
-       for (window_t *win = windows; win; win = win->next)
-               if (win->filter)
-                       regcomp(&win->regex, win->filter, RE_FLAGS);
-
        /* Print welcome message */
        chat_recv(&sys_chan, NULL, "Welcome to lamechat!");
 
@@ -1448,7 +1408,7 @@ void view_config(const char *group, const char *name, const char *key, const cha
                else if (match(key, "defocus"))
                        defocus = get_number(value);
                else if (match(key, "watch"))
-                       watch = get_string(value);
+                       reg_set(&watch, get_string(value), MATCHES);
        }
 
        if (match(group, "window")) {
@@ -1466,7 +1426,7 @@ void view_config(const char *group, const char *name, const char *key, const cha
                        win->dest    = win->channel ?: win->dest;
                }
                else if (match(key, "filter")) {
-                       win->filter = get_string(value);
+                       reg_set(&win->filter, get_string(value), 0);
                }
        }
 }