#include <errno.h>
#include <time.h>
#include <unistd.h>
+#include <regex.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
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(®->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, ...)
{
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(®, pattern, 16);
+ reg_match(®, text);
+ for (int i = 0; text[i]; i++)
+ actual[i] = MATCH_STR[reg_check(®, 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(®);
+}
+
void util_test(void)
{
test_prefix("/foo", "/foo", 1, NULL);
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, "^^ ^^");
}
#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);
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);
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, ...);
#include <signal.h>
#include <sys/signalfd.h>
#include <unistd.h>
-#include <regex.h>
#include <ncursesw/ncurses.h>
/* View constants */
#define MATCHES 16
-#define RE_MFLAGS (REG_EXTENDED)
-#define RE_FLAGS (REG_EXTENDED|REG_NOSUB)
/* Extra colors */
#define COLOR_BROWN 130
int pinned;
int custom;
- char *filter;
+ reg_t filter;
server_t *server;
channel_t *channel;
- regex_t regex;
channel_t *dest;
channel_t *saved;
int pick;
int prow;
int pcol;
- int mcnt;
- int mstart[MATCHES];
- int mstop[MATCHES];
} printer_t;
typedef enum {
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;
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;
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)
{
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;
}
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;
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;
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;
}
/* Set matches */
- if (watch)
- pr->mcnt = match_all(msg->text, ®ex, pr->mstart, pr->mstop);
+ reg_match(&watch, msg->text);
/* Print message */
pr->indent = pr->col;
pr->row++;
/* Clear matches */
- if (watch)
- pr->mcnt = 0;
+ watch.count = 0;
}
static void print_status(printer_t *pr)
abbrev = i;
}
else if (prefix(text, "/watch", &arg)) {
- strset(&watch, arg);
- if (watch)
- regcomp(®ex, watch, RE_MFLAGS);
+ reg_set(&watch, arg, MATCHES);
}
else if (prefix(text, "/open", &arg)) {
win = find_window(arg ?: focus->dest->name);
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);
/* View init */
void view_init(void)
{
- /* General setup */
- if (watch)
- regcomp(®ex, watch, RE_MFLAGS);
-
/* System windows */
sys_srv.name = strcopy("system");
sys_srv.protocol = -1;
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!");
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")) {
win->dest = win->channel ?: win->dest;
}
else if (match(key, "filter")) {
- win->filter = get_string(value);
+ reg_set(&win->filter, get_string(value), 0);
}
}
}