commit 51d6c520b6f00e2f1015ed6b70278a80bc389cbd
parent 4b0f888f0c4b802051ceaff3cb1b6606972617be
Author: hhvn <dev@hhvn.uk>
Date: Wed, 8 Dec 2021 18:03:25 +0000
commands.c config.c struct.h: /grep
Diffstat:
M | commands.c | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | config.c | | | 10 | ++++++++++ |
M | struct.h | | | 11 | ++++++----- |
3 files changed, 86 insertions(+), 5 deletions(-)
diff --git a/commands.c b/commands.c
@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <regex.h>
#include <pwd.h>
#include <sys/types.h>
#include "hirc.h"
@@ -95,6 +96,14 @@ struct Command commands[] = {
{"echo", command_echo, {
"usage: /echo ...",
"Print temporarily to selected buffer.", NULL}},
+ {"grep", command_grep, {
+ "usage: /grep [-iE] [regex]",
+ "Search selected buffer",
+ " -i: case insensitive",
+ " -E: posix extended regex",
+ "If no argument supplied, clears previous search",
+ "Searches are also cleared after selecting another buffer",
+ "See also config variables: regex.extended and regex.icase", NULL}},
{NULL, NULL},
};
@@ -631,6 +640,67 @@ command_echo(struct Server *server, char *str) {
hist_format(selected.history, Activity_none, HIST_SHOW|HIST_TMP, "SELF_UI :%s", str);
}
+static
+void
+command_grep(struct Server *server, char *str) {
+ struct History *p;
+ regex_t re;
+ int regopt = 0, ret;
+ char errbuf[1024];
+ enum { opt_extended, opt_icase };
+ static struct CommandOpts opts[] = {
+ {"E", CMD_NARG, opt_extended},
+ {"i", CMD_NARG, opt_icase},
+ {NULL, 0, 0},
+ };
+
+ hist_purgeopt(selected.history, HIST_GREP);
+ windows[Win_main].refresh = 1;
+ if (!str) {
+ return;
+ }
+
+ while ((ret = command_getopt(&str, opts)) != opt_done) {
+ switch (ret) {
+ case opt_error:
+ return;
+ case opt_extended:
+ regopt |= REG_EXTENDED;
+ break;
+ case opt_icase:
+ regopt |= REG_ICASE;
+ break;
+ }
+ }
+
+ if (config_getl("regex.extended"))
+ regopt |= REG_EXTENDED;
+ if (config_getl("regex.icase"))
+ regopt |= REG_ICASE;
+
+ if ((ret = regcomp(&re, str, regopt)) != 0) {
+ regerror(ret, &re, errbuf, sizeof(errbuf));
+ regfree(&re);
+ ui_error("unable to compile regex '%s': %s", str, errbuf);
+ return;
+ }
+
+ hist_format(selected.history, Activity_none, HIST_SHOW|HIST_TMP|HIST_GREP, "SELF_GREP_START :%s", str);
+
+ /* Get oldest, but don't set p to NULL */
+ for (p = selected.history->history; p && p->next; p = p->next);
+
+ /* Traverse until we hit a message generated by us */
+ for (; p && !(p->options & HIST_GREP); p = p->prev) {
+ /* TODO: matching ui_format result by default,
+ * option for matching raw */
+ if (regexec(&re, p->raw, 0, NULL, 0) == 0)
+ hist_add(selected.history, p->from, p->raw, p->params, p->activity, p->timestamp, p->options | HIST_GREP);
+ }
+
+ hist_format(selected.history, Activity_none, HIST_SHOW|HIST_TMP|HIST_GREP, "SELF_GREP_END :end of /grep command");
+}
+
int
command_getopt(char **str, struct CommandOpts *opts) {
char *opt;
diff --git a/config.c b/config.c
@@ -85,6 +85,16 @@ struct Config config[] = {
.description = {
"Maximum reconnect interval in seconds.",
"See reconnect.interval", NULL}},
+ {"regex.extended", 1, Val_bool,
+ .num = 0,
+ .numhandle = NULL,
+ .description = {
+ "Use POSIX extended regex at all times.", NULL}},
+ {"regex.icase", 1, Val_bool,
+ .num = 0,
+ .numhandle = NULL,
+ .description = {
+ "Use case insensitive regex at all times.", NULL}},
{"nickcolour.self", 1, Val_colour,
.num = 90,
.numhandle = config_nickcolour_self,
diff --git a/struct.h b/struct.h
@@ -32,11 +32,12 @@ enum Activity {
};
enum HistOpt {
- HIST_SHOW = 1, /* show in buffer */
- HIST_LOG = 2, /* log to server->logfd */
- HIST_MAIN = 4, /* copy to &main_buf */
- HIST_SELF = 8, /* from = self */
- HIST_TMP = 16, /* purge later */
+ HIST_SHOW = 1, /* show in buffer */
+ HIST_LOG = 2, /* log to server->logfd */
+ HIST_MAIN = 4, /* copy to &main_buf */
+ HIST_SELF = 8, /* from = self */
+ HIST_TMP = 16, /* purge later */
+ HIST_GREP = 32, /* generated by /grep */
HIST_DFL = HIST_SHOW|HIST_LOG
};