summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornicm <nicm@openbsd.org>2019-06-13 19:46:00 +0000
committernicm <nicm@openbsd.org>2019-06-13 19:46:00 +0000
commitda6434566ed3fc91cdd8deb51016b6d92cb61e34 (patch)
treeba3d238c0fb9c6387ac48a369db9b8a009cecf3a
parentsu(I) goes back all the way to v1: (diff)
downloadwireguard-openbsd-da6434566ed3fc91cdd8deb51016b6d92cb61e34.tar.xz
wireguard-openbsd-da6434566ed3fc91cdd8deb51016b6d92cb61e34.zip
Add regular expression support for the format search, match and
substitute modifiers.
-rw-r--r--usr.bin/tmux/Makefile3
-rw-r--r--usr.bin/tmux/format.c112
-rw-r--r--usr.bin/tmux/regsub.c98
-rw-r--r--usr.bin/tmux/tmux.150
-rw-r--r--usr.bin/tmux/tmux.h8
-rw-r--r--usr.bin/tmux/window.c36
6 files changed, 240 insertions, 67 deletions
diff --git a/usr.bin/tmux/Makefile b/usr.bin/tmux/Makefile
index 1c241af6948..d6466e0b736 100644
--- a/usr.bin/tmux/Makefile
+++ b/usr.bin/tmux/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.99 2019/05/25 07:29:04 nicm Exp $
+# $OpenBSD: Makefile,v 1.100 2019/06/13 19:46:00 nicm Exp $
PROG= tmux
SRCS= alerts.c \
@@ -95,6 +95,7 @@ SRCS= alerts.c \
paste.c \
proc.c \
procname.c \
+ regsub.c \
resize.c \
screen-redraw.c \
screen-write.c \
diff --git a/usr.bin/tmux/format.c b/usr.bin/tmux/format.c
index bdc7e65a708..9b3b4bb7a12 100644
--- a/usr.bin/tmux/format.c
+++ b/usr.bin/tmux/format.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.201 2019/05/31 21:41:17 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.202 2019/06/13 19:46:00 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -23,6 +23,7 @@
#include <errno.h>
#include <fnmatch.h>
#include <libgen.h>
+#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -1247,7 +1248,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
cp++;
/* Check single character modifiers with no arguments. */
- if (strchr("lmCbdtqETSWP<>", cp[0]) != NULL &&
+ if (strchr("lbdtqETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@@ -1268,7 +1269,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
}
/* Now try single character with arguments. */
- if (strchr("s=", cp[0]) == NULL)
+ if (strchr("mCs=", cp[0]) == NULL)
break;
c = cp[0];
@@ -1329,39 +1330,67 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
return list;
}
-/* Perform substitution in string. */
+/* Match against an fnmatch(3) pattern or regular expression. */
static char *
-format_substitute(const char *source, const char *from, const char *to)
+format_match(struct format_modifier *fm, const char *pattern, const char *text)
{
- char *copy, *new;
- const char *cp;
- size_t fromlen, tolen, newlen, used;
-
- fromlen = strlen(from);
- tolen = strlen(to);
-
- newlen = strlen(source) + 1;
- copy = new = xmalloc(newlen);
-
- for (cp = source; *cp != '\0'; /* nothing */) {
- if (strncmp(cp, from, fromlen) != 0) {
- *new++ = *cp++;
- continue;
+ const char *s = "";
+ regex_t r;
+ int flags = 0;
+
+ if (fm->argc >= 1)
+ s = fm->argv[0];
+ if (strchr(s, 'r') == NULL) {
+ if (strchr(s, 'i') != NULL)
+ flags |= FNM_CASEFOLD;
+ if (fnmatch(pattern, text, flags) != 0)
+ return (xstrdup("0"));
+ } else {
+ flags = REG_EXTENDED|REG_NOSUB;
+ if (strchr(s, 'i') != NULL)
+ flags |= REG_ICASE;
+ if (regcomp(&r, pattern, flags) != 0)
+ return (xstrdup("0"));
+ if (regexec(&r, text, 0, NULL, 0) != 0) {
+ regfree(&r);
+ return (xstrdup("0"));
}
- used = new - copy;
-
- newlen += tolen;
- copy = xrealloc(copy, newlen);
+ regfree(&r);
+ }
+ return (xstrdup("1"));
+}
- new = copy + used;
- memcpy(new, to, tolen);
+/* Perform substitution in string. */
+static char *
+format_sub(struct format_modifier *fm, const char *text, const char *pattern,
+ const char *with)
+{
+ char *value;
+ int flags = REG_EXTENDED;
+
+ if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
+ flags |= REG_ICASE;
+ value = regsub(pattern, with, text, flags);
+ if (value == NULL)
+ return (xstrdup(text));
+ return (value);
+}
- new += tolen;
- cp += fromlen;
+/* Search inside pane. */
+static char *
+format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
+{
+ int ignore = 0, regex = 0;
+ char *value;
+
+ if (fm->argc >= 1) {
+ if (strchr(fm->argv[0], 'i') != NULL)
+ ignore = 1;
+ if (strchr(fm->argv[0], 'r') != NULL)
+ regex = 1;
}
-
- *new = '\0';
- return (copy);
+ xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
+ return (value);
}
/* Loop over sessions. */
@@ -1506,11 +1535,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
char *copy0, *condition, *found, *new;
char *value, *left, *right;
size_t valuelen;
- int modifiers = 0, limit = 0;
+ int modifiers = 0, limit = 0, j;
struct format_modifier *list, *fm, *cmp = NULL, *search = NULL;
struct format_modifier *sub = NULL;
u_int i, count;
- int j;
/* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen);
@@ -1537,18 +1565,18 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
search = fm;
break;
case 's':
- if (fm->argc != 2)
+ if (fm->argc < 2)
break;
sub = fm;
break;
case '=':
- if (fm->argc != 1 && fm->argc != 2)
+ if (fm->argc < 1)
break;
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr);
if (errptr != NULL)
limit = 0;
- if (fm->argc == 2 && fm->argv[1] != NULL)
+ if (fm->argc >= 2 && fm->argv[1] != NULL)
marker = fm->argv[1];
break;
case 'l':
@@ -1619,7 +1647,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
value = xstrdup("0");
} else {
format_log(ft, "search '%s' pane %%%u", copy, wp->id);
- xasprintf(&value, "%u", window_pane_search(wp, copy));
+ value = format_search(fm, wp, copy);
}
} else if (cmp != NULL) {
/* Comparison of left and right. */
@@ -1671,12 +1699,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
value = xstrdup("1");
else
value = xstrdup("0");
- } else if (strcmp(cmp->modifier, "m") == 0) {
- if (fnmatch(left, right, 0) == 0)
- value = xstrdup("1");
- else
- value = xstrdup("0");
- }
+ } else if (strcmp(cmp->modifier, "m") == 0)
+ value = format_match(fm, left, right);
free(right);
free(left);
@@ -1754,8 +1778,8 @@ done:
/* Perform substitution if any. */
if (sub != NULL) {
- new = format_substitute(value, sub->argv[0], sub->argv[1]);
- format_log(ft, "substituted '%s' to '%s: %s", sub->argv[0],
+ new = format_sub(sub, value, sub->argv[0], sub->argv[1]);
+ format_log(ft, "substituted '%s' to '%s': %s", sub->argv[0],
sub->argv[1], new);
free(value);
value = new;
diff --git a/usr.bin/tmux/regsub.c b/usr.bin/tmux/regsub.c
new file mode 100644
index 00000000000..a7e14505346
--- /dev/null
+++ b/usr.bin/tmux/regsub.c
@@ -0,0 +1,98 @@
+/* $OpenBSD: regsub.c,v 1.1 2019/06/13 19:46:00 nicm Exp $ */
+
+/*
+ * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <regex.h>
+#include <string.h>
+
+#include "tmux.h"
+
+static void
+regsub_copy(char **buf, size_t *len, const char *text, size_t start,
+ size_t end)
+{
+ size_t add = end - start;
+
+ *buf = xrealloc(*buf, (*len) + add + 1);
+ memcpy((*buf) + *len, text + start, add);
+ (*len) += add;
+}
+
+static void
+regsub_expand(char **buf, size_t *len, const char *with, const char *text,
+ regmatch_t *m, u_int n)
+{
+ const char *cp;
+ u_int i;
+
+ for (cp = with; *cp != '\0'; cp++) {
+ if (*cp == '\\') {
+ cp++;
+ if (*cp >= '0' && *cp <= '9') {
+ i = *cp - '0';
+ if (i < n && m[i].rm_so != m[i].rm_eo) {
+ regsub_copy(buf, len, text, m[i].rm_so,
+ m[i].rm_eo);
+ continue;
+ }
+ }
+ }
+ *buf = xrealloc(*buf, (*len) + 2);
+ (*buf)[(*len)++] = *cp;
+ }
+}
+
+char *
+regsub(const char *pattern, const char *with, const char *text, int flags)
+{
+ regex_t r;
+ regmatch_t m[10];
+ size_t start, end, len = 0;
+ char *buf = NULL;
+
+ if (*text == '\0')
+ return (xstrdup(""));
+ if (regcomp(&r, pattern, flags) != 0)
+ return (NULL);
+
+ start = 0;
+ end = strlen(text);
+
+ while (start != end) {
+ m[0].rm_so = start;
+ m[0].rm_eo = end;
+
+ if (regexec(&r, text, nitems(m), m, REG_STARTEND) != 0) {
+ regsub_copy(&buf, &len, text, start, end);
+ break;
+ }
+ if (m[0].rm_so == m[0].rm_eo) {
+ regsub_copy(&buf, &len, text, start, end);
+ break;
+ }
+
+ regsub_copy(&buf, &len, text, start, m[0].rm_so);
+ regsub_expand(&buf, &len, with, text, m, nitems(m));
+ start = m[0].rm_eo;
+ }
+ buf[len] = '\0';
+
+ regfree(&r);
+ return (buf);
+}
diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1
index 1d37be69cc4..309e6d8daba 100644
--- a/usr.bin/tmux/tmux.1
+++ b/usr.bin/tmux/tmux.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.663 2019/06/05 20:00:53 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.664 2019/06/13 19:46:00 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: June 5 2019 $
+.Dd $Mdocdate: June 13 2019 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -4012,25 +4012,45 @@ if running on
.Ql myhost ,
otherwise by
.Ql 0 .
-An
-.Ql m
-specifies an
-.Xr fnmatch 3
-comparison where the first argument is the pattern and the second the string to
-compare, for example
-.Ql #{m:*foo*,#{host}} .
.Ql ||
and
.Ql &&
evaluate to true if either or both of two comma-separated alternatives are
true, for example
.Ql #{||:#{pane_in_mode},#{alternate_on}} .
+.Pp
+An
+.Ql m
+specifies an
+.Xr fnmatch 3
+or regular expression comparison.
+The first argument is the pattern and the second the string to compare.
+An optional third argument specifies flags:
+.Ql r
+means the pattern is a regular expression instead of the default
+.Xr fnmatch 3
+pattern, and
+.Ql i
+means to ignore case.
+For example:
+.Ql #{m:*foo*,#{host}}
+or
+.Ql #{m/ri:^A,MYVAR} .
A
.Ql C
performs a search for an
.Xr fnmatch 3
-pattern in the pane content and evaluates to zero if not found, or a line
-number if found.
+pattern or regular expression in the pane content and evaluates to zero if not
+found, or a line number if found.
+Like
+.Ql m ,
+a
+.Ql r
+flag means search for a regular expression and
+.Ql i
+ignores case.
+For example:
+.Ql #{C/r:^Start}
.Pp
A limit may be placed on the length of the resultant string by prefixing it
by an
@@ -4103,6 +4123,14 @@ will substitute
with
.Ql bar
throughout.
+The first argument may be an extended regular expression and a final argument may be
+.Ql i
+to ignore case, for example
+.Ql s/a(.)/\1x/i:
+would change
+.Ql abABab
+into
+.Ql bxBxbx .
.Pp
In addition, the first line of a shell command's output may be inserted using
.Ql #() .
diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h
index b62bf8334a1..51c421f033e 100644
--- a/usr.bin/tmux/tmux.h
+++ b/usr.bin/tmux/tmux.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.910 2019/06/07 20:09:17 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.911 2019/06/13 19:46:00 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2396,7 +2396,8 @@ void window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
int window_pane_visible(struct window_pane *);
-u_int window_pane_search(struct window_pane *, const char *);
+u_int window_pane_search(struct window_pane *, const char *, int,
+ int);
const char *window_printable_flags(struct winlink *);
struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
@@ -2631,4 +2632,7 @@ int style_is_default(struct style *);
struct winlink *spawn_window(struct spawn_context *, char **);
struct window_pane *spawn_pane(struct spawn_context *, char **);
+/* regsub.c */
+char *regsub(const char *, const char *, const char *, int);
+
#endif /* TMUX_H */
diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c
index 75401cc183b..103c6e30c13 100644
--- a/usr.bin/tmux/window.c
+++ b/usr.bin/tmux/window.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window.c,v 1.231 2019/06/09 06:50:24 nicm Exp $ */
+/* $OpenBSD: window.c,v 1.232 2019/06/13 19:46:00 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -22,6 +22,7 @@
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
+#include <regex.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
@@ -1206,24 +1207,41 @@ window_pane_visible(struct window_pane *wp)
}
u_int
-window_pane_search(struct window_pane *wp, const char *searchstr)
+window_pane_search(struct window_pane *wp, const char *term, int regex,
+ int ignore)
{
struct screen *s = &wp->base;
- char *newsearchstr, *line;
+ regex_t r;
+ char *new = NULL, *line;
u_int i;
+ int flags = 0, found;
- xasprintf(&newsearchstr, "*%s*", searchstr);
+ if (!regex) {
+ if (ignore)
+ flags |= FNM_CASEFOLD;
+ xasprintf(&new, "*%s*", term);
+ } else {
+ if (ignore)
+ flags |= REG_ICASE;
+ if (regcomp(&r, term, flags|REG_EXTENDED) != 0)
+ return (0);
+ }
for (i = 0; i < screen_size_y(s); i++) {
line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
- if (fnmatch(newsearchstr, line, 0) == 0) {
- free(line);
- break;
- }
+ if (!regex)
+ found = (fnmatch(new, line, 0) == 0);
+ else
+ found = (regexec(&r, line, 0, NULL, 0) == 0);
free(line);
+ if (found)
+ break;
}
+ if (!regex)
+ free(new);
+ else
+ regfree(&r);
- free(newsearchstr);
if (i == screen_size_y(s))
return (0);
return (i + 1);