diff options
author | Christian Persch <chpe@src.gnome.org> | 2019-05-08 18:00:01 +0200 |
---|---|---|
committer | Christian Persch <chpe@src.gnome.org> | 2019-05-08 18:00:01 +0200 |
commit | 7457b47d64ecd079d59cb69bf5d1039eea0328d6 (patch) | |
tree | 35cabdb2c61815d877b33b92e8fc335da2a3ec94 | |
parent | a874048ced3a0f414c3e858c4295ab0c6decf5df (diff) | |
download | gnome-terminal-wip/regex-builtins.tar.gz |
regex: Use vte builtin regexes instead of our ownwip/regex-builtins
https://gitlab.gnome.org/GNOME/vte/issues/114
-rw-r--r-- | src/Makefile.am | 20 | ||||
-rw-r--r-- | src/terminal-regex.c | 374 | ||||
-rw-r--r-- | src/terminal-regex.h | 154 | ||||
-rw-r--r-- | src/terminal-screen.c | 23 | ||||
-rw-r--r-- | src/terminal-screen.h | 3 | ||||
-rw-r--r-- | src/terminal-util.c | 26 | ||||
-rw-r--r-- | src/terminal-util.h | 3 | ||||
-rw-r--r-- | src/terminal-window.c | 25 |
8 files changed, 20 insertions, 608 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index b36cc8e3..f7a6ff9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,8 +6,6 @@ bin_PROGRAMS = gnome-terminal libexec_PROGRAMS = gnome-terminal-server noinst_PROGRAMS = -check_PROGRAMS = terminal-regex - if WITH_NAUTILUS_EXTENSION nautilusextension_LTLIBRARIES = libterminal-nautilus.la endif # WITH_NAUTILUS_EXTENSION @@ -74,7 +72,6 @@ gnome_terminal_server_SOURCES = \ terminal-prefs.h \ terminal-profiles-list.c \ terminal-profiles-list.h \ - terminal-regex.h \ terminal-schemas.h \ terminal-settings-list.c \ terminal-settings-list.h \ @@ -183,26 +180,11 @@ terminal-resources.h terminal-resources.c: terminal.gresource.xml Makefile $(she # Checks TESTS = \ - terminal-regex \ $(NULL) # Check programmes -terminal_regex_CPPFLAGS = \ - $(AM_CPPFLAGS) -terminal_regex_SOURCES = \ - terminal-regex.c \ - terminal-regex.h \ - $(NULL) -terminal_regex_CFLAGS = \ - -DTERMINAL_REGEX_MAIN \ - $(TERM_CFLAGS) \ - $(WARN_CFLAGS) \ - $(AM_CFLAGS) -terminal_regex_LDFLAGS = \ - $(AM_LDFLAGS) -terminal_regex_LDADD = \ - $(TERM_LIBS) +# none so far # Legacy terminal client diff --git a/src/terminal-regex.c b/src/terminal-regex.c deleted file mode 100644 index cf514531..00000000 --- a/src/terminal-regex.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright © 2015 Egmont Koblinger - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include <glib.h> -#include <stdio.h> - -#include "terminal-regex.h" - -#ifdef TERMINAL_REGEX_MAIN - -/* Shorthand for expecting the pattern to match the entire input string */ -#define ENTIRE ((char *) 1) - -static char* -get_match (const char *pattern, const char *string, GRegexMatchFlags match_flags) -{ - GRegex *regex; - GMatchInfo *match_info; - gchar *match; - - regex = g_regex_new (pattern, 0, 0, NULL); - g_regex_match (regex, string, match_flags, &match_info); - match = g_match_info_fetch (match_info, 0); - - g_free (regex); - g_free (match_info); - return match; -} - -/* Macros rather than functions to report useful line numbers on failure. */ -#define assert_match(__pattern, __string, __expected) do { \ - gchar *__actual_match = get_match(__pattern, __string, 0); \ - const gchar *__expected_match = __expected; \ - if (__expected_match == ENTIRE) __expected_match = __string; \ - g_assert_cmpstr(__actual_match, ==, __expected_match); \ - g_free (__actual_match); \ -} while (0) - -#define assert_match_anchored(__pattern, __string, __expected) do { \ - gchar *__actual_match = get_match(__pattern, __string, G_REGEX_MATCH_ANCHORED); \ - const gchar *__expected_match = __expected; \ - if (__expected_match == ENTIRE) __expected_match = __string; \ - g_assert_cmpstr(__actual_match, ==, __expected_match); \ - g_free (__actual_match); \ -} while (0) - -int -main (int argc, char **argv) -{ - /* SCHEME is case insensitive */ - assert_match_anchored (SCHEME, "http", ENTIRE); - assert_match_anchored (SCHEME, "HTTPS", ENTIRE); - - /* USER is nonempty, alphanumeric, dot, plus and dash */ - assert_match_anchored (USER, "", NULL); - assert_match_anchored (USER, "dr.john-smith", ENTIRE); - assert_match_anchored (USER, "abc+def@ghi", "abc+def"); - - /* PASS is optional colon-prefixed value, allowing quite some characters, but definitely not @ */ - assert_match_anchored (PASS, "", ENTIRE); - assert_match_anchored (PASS, "nocolon", ""); - assert_match_anchored (PASS, ":s3cr3T", ENTIRE); - assert_match_anchored (PASS, ":$?#@host", ":$?#"); - - /* Hostname of at least 1 component, containing at least one non-digit in at least one of the segments */ - assert_match_anchored (HOSTNAME1, "example.com", ENTIRE); - assert_match_anchored (HOSTNAME1, "a-b.c-d", ENTIRE); - assert_match_anchored (HOSTNAME1, "a_b", "a"); /* TODO: can/should we totally abort here? */ - assert_match_anchored (HOSTNAME1, "déjà-vu.com", ENTIRE); - assert_match_anchored (HOSTNAME1, "➡.ws", ENTIRE); - assert_match_anchored (HOSTNAME1, "cömbining-áccents", ENTIRE); - assert_match_anchored (HOSTNAME1, "12", NULL); - assert_match_anchored (HOSTNAME1, "12.34", NULL); - assert_match_anchored (HOSTNAME1, "12.ab", ENTIRE); -// assert_match_anchored (HOSTNAME1, "ab.12", NULL); /* errr... could we fail here?? */ - - /* Hostname of at least 2 components, containing at least one non-digit in at least one of the segments */ - assert_match_anchored (HOSTNAME2, "example.com", ENTIRE); - assert_match_anchored (HOSTNAME2, "example", NULL); - assert_match_anchored (HOSTNAME2, "12", NULL); - assert_match_anchored (HOSTNAME2, "12.34", NULL); - assert_match_anchored (HOSTNAME2, "12.ab", ENTIRE); - assert_match_anchored (HOSTNAME2, "ab.12", NULL); -// assert_match_anchored (HOSTNAME2, "ab.cd.12", NULL); /* errr... could we fail here?? */ - - /* IPv4 segment (number between 0 and 255) */ - assert_match_anchored (DEFS "(?&S4)", "0", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "1", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "9", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "10", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "99", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "100", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "200", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "250", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "255", ENTIRE); - assert_match_anchored (DEFS "(?&S4)", "256", NULL); - assert_match_anchored (DEFS "(?&S4)", "260", NULL); - assert_match_anchored (DEFS "(?&S4)", "300", NULL); - assert_match_anchored (DEFS "(?&S4)", "1000", NULL); - assert_match_anchored (DEFS "(?&S4)", "", NULL); - assert_match_anchored (DEFS "(?&S4)", "a1b", NULL); - - /* IPv4 addresses */ - assert_match_anchored (DEFS "(?&IPV4)", "11.22.33.44", ENTIRE); - assert_match_anchored (DEFS "(?&IPV4)", "0.1.254.255", ENTIRE); - assert_match_anchored (DEFS "(?&IPV4)", "75.150.225.300", NULL); - assert_match_anchored (DEFS "(?&IPV4)", "1.2.3.4.5", "1.2.3.4"); /* we could also bail out and not match at all */ - - /* IPv6 addresses */ - assert_match_anchored (DEFS "(?&IPV6)", "11:::22", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22::33:44::55:66", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "dead::beef", ENTIRE); - assert_match_anchored (DEFS "(?&IPV6)", "faded::bee", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "live::pork", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "::1", ENTIRE); - assert_match_anchored (DEFS "(?&IPV6)", "11::22:33::44", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:::33", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "dead:beef::192.168.1.1", ENTIRE); - assert_match_anchored (DEFS "(?&IPV6)", "192.168.1.1", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:77:87654", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22::33:45678", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:192.168.1.12345", NULL); - - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:77", NULL); /* no :: */ - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:77:88", ENTIRE); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:77:88:99", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "::11:22:33:44:55:66:77", ENTIRE); /* :: at the start */ - assert_match_anchored (DEFS "(?&IPV6)", "::11:22:33:44:55:66:77:88", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33::44:55:66:77", ENTIRE); /* :: in the middle */ - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33::44:55:66:77:88", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:77::", ENTIRE); /* :: at the end */ - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:77:88::", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "::", ENTIRE); /* :: only */ - - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:192.168.1.1", NULL); /* no :: */ - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:192.168.1.1", ENTIRE); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66:77:192.168.1.1", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "::11:22:33:44:55:192.168.1.1", ENTIRE); /* :: at the start */ - assert_match_anchored (DEFS "(?&IPV6)", "::11:22:33:44:55:66:192.168.1.1", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33::44:55:192.168.1.1", ENTIRE); /* :: in the imddle */ - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33::44:55:66:192.168.1.1", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55::192.168.1.1", ENTIRE); /* :: at the end(ish) */ - assert_match_anchored (DEFS "(?&IPV6)", "11:22:33:44:55:66::192.168.1.1", NULL); - assert_match_anchored (DEFS "(?&IPV6)", "::192.168.1.1", ENTIRE); /* :: only(ish) */ - - /* URL_HOST is either a hostname, or an IPv4 address, or a bracket-enclosed IPv6 address */ - assert_match_anchored (DEFS URL_HOST, "example", ENTIRE); - assert_match_anchored (DEFS URL_HOST, "example.com", ENTIRE); - assert_match_anchored (DEFS URL_HOST, "11.22.33.44", ENTIRE); - assert_match_anchored (DEFS URL_HOST, "[11.22.33.44]", NULL); - assert_match_anchored (DEFS URL_HOST, "dead::be:ef", "dead"); /* TODO: can/should we totally abort here? */ - assert_match_anchored (DEFS URL_HOST, "[dead::be:ef]", ENTIRE); - - /* EMAIL_HOST is either an at least two-component hostname, or a bracket-enclosed IPv[46] address */ - assert_match_anchored (DEFS EMAIL_HOST, "example", NULL); - assert_match_anchored (DEFS EMAIL_HOST, "example.com", ENTIRE); - assert_match_anchored (DEFS EMAIL_HOST, "11.22.33.44", NULL); - assert_match_anchored (DEFS EMAIL_HOST, "[11.22.33.44]", ENTIRE); - assert_match_anchored (DEFS EMAIL_HOST, "[11.22.33.456]", NULL); - assert_match_anchored (DEFS EMAIL_HOST, "dead::be:ef", NULL); - assert_match_anchored (DEFS EMAIL_HOST, "[dead::be:ef]", ENTIRE); - - /* Number between 1 and 65535 (helper for port) */ - assert_match_anchored (N_1_65535, "0", NULL); - assert_match_anchored (N_1_65535, "1", ENTIRE); - assert_match_anchored (N_1_65535, "10", ENTIRE); - assert_match_anchored (N_1_65535, "100", ENTIRE); - assert_match_anchored (N_1_65535, "1000", ENTIRE); - assert_match_anchored (N_1_65535, "10000", ENTIRE); - assert_match_anchored (N_1_65535, "60000", ENTIRE); - assert_match_anchored (N_1_65535, "65000", ENTIRE); - assert_match_anchored (N_1_65535, "65500", ENTIRE); - assert_match_anchored (N_1_65535, "65530", ENTIRE); - assert_match_anchored (N_1_65535, "65535", ENTIRE); - assert_match_anchored (N_1_65535, "65536", NULL); - assert_match_anchored (N_1_65535, "65540", NULL); - assert_match_anchored (N_1_65535, "65600", NULL); - assert_match_anchored (N_1_65535, "66000", NULL); - assert_match_anchored (N_1_65535, "70000", NULL); - assert_match_anchored (N_1_65535, "100000", NULL); - assert_match_anchored (N_1_65535, "", NULL); - assert_match_anchored (N_1_65535, "a1b", NULL); - - /* PORT is an optional colon-prefixed value */ - assert_match_anchored (PORT, "", ENTIRE); - assert_match_anchored (PORT, ":1", ENTIRE); - assert_match_anchored (PORT, ":65535", ENTIRE); - assert_match_anchored (PORT, ":65536", ""); /* TODO: can/should we totally abort here? */ - - /* Parentheses are only allowed in matching pairs, see bug 763980. */ - /* TODO: add tests for PATHCHARS and PATHNONTERM; and/or URLPATH */ - assert_match_anchored (DEFS URLPATH, "/ab/cd", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/ab/cd.html.", "/ab/cd.html"); - assert_match_anchored (DEFS URLPATH, "/The_Offspring_(album)", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/The_Offspring)", "/The_Offspring"); - assert_match_anchored (DEFS URLPATH, "/a((b(c)d)e(f))", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/a((b(c)d)e(f)))", "/a((b(c)d)e(f))"); - assert_match_anchored (DEFS URLPATH, "/a(b).(c).", "/a(b).(c)"); - assert_match_anchored (DEFS URLPATH, "/a.(b.(c.).).(d.(e.).).)", "/a.(b.(c.).).(d.(e.).)"); - assert_match_anchored (DEFS URLPATH, "/a)b(c", "/a"); - assert_match_anchored (DEFS URLPATH, "/.", "/"); - assert_match_anchored (DEFS URLPATH, "/(.", "/"); - assert_match_anchored (DEFS URLPATH, "/).", "/"); - assert_match_anchored (DEFS URLPATH, "/().", "/()"); - assert_match_anchored (DEFS URLPATH, "/", ENTIRE); - assert_match_anchored (DEFS URLPATH, "", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/php?param[]=value1¶m[]=value2", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/foo?param1[index1]=value1¶m2[index2]=value2", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/[[[]][]]", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/[([])]([()])", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/([()])[([])]", ENTIRE); - assert_match_anchored (DEFS URLPATH, "/[(])", "/"); - assert_match_anchored (DEFS URLPATH, "/([)]", "/"); - - - /* Put the components together and test the big picture */ - - assert_match (REGEX_URL_AS_IS, "There's no URL here http:/foo", NULL); - assert_match (REGEX_URL_AS_IS, "Visit http://example.com for details", "http://example.com"); - assert_match (REGEX_URL_AS_IS, "Trailing dot http://foo/bar.html.", "http://foo/bar.html"); - assert_match (REGEX_URL_AS_IS, "Trailing ellipsis http://foo/bar.html...", "http://foo/bar.html"); - assert_match (REGEX_URL_AS_IS, "Trailing comma http://foo/bar,baz,", "http://foo/bar,baz"); - assert_match (REGEX_URL_AS_IS, "Trailing semicolon http://foo/bar;baz;", "http://foo/bar;baz"); - assert_match (REGEX_URL_AS_IS, "See <http://foo/bar>", "http://foo/bar"); - assert_match (REGEX_URL_AS_IS, "<http://foo.bar/asdf.qwer.html>", "http://foo.bar/asdf.qwer.html"); - assert_match (REGEX_URL_AS_IS, "Go to http://192.168.1.1.", "http://192.168.1.1"); - assert_match (REGEX_URL_AS_IS, "If not, see <http://www.gnu.org/licenses/>.", "http://www.gnu.org/licenses/"); - assert_match (REGEX_URL_AS_IS, "<a href=\"http://foo/bar\">foo</a>", "http://foo/bar"); - assert_match (REGEX_URL_AS_IS, "<a href='http://foo/bar'>foo</a>", "http://foo/bar"); - assert_match (REGEX_URL_AS_IS, "<url>http://foo/bar</url>", "http://foo/bar"); - - assert_match (REGEX_URL_AS_IS, "http://", NULL); - assert_match (REGEX_URL_AS_IS, "http://a", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://aa.", "http://aa"); - assert_match (REGEX_URL_AS_IS, "http://aa.b", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://aa.bb", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://aa.bb/c", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://aa.bb/cc", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://aa.bb/cc/", ENTIRE); - - assert_match (REGEX_URL_AS_IS, "HtTp://déjà-vu.com:10000/déjà/vu", ENTIRE); - assert_match (REGEX_URL_AS_IS, "HTTP://joe:sEcReT@➡.ws:1080", ENTIRE); - assert_match (REGEX_URL_AS_IS, "https://cömbining-áccents", ENTIRE); - - assert_match (REGEX_URL_AS_IS, "http://111.222.33.44", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://111.222.33.44/", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://111.222.33.44/foo", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://1.2.3.4:5555/xyz", ENTIRE); - assert_match (REGEX_URL_AS_IS, "https://[dead::beef]:12345/ipv6", ENTIRE); - assert_match (REGEX_URL_AS_IS, "https://[dead::beef:11.22.33.44]", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://1.2.3.4:", "http://1.2.3.4"); /* TODO: can/should we totally abort here? */ - assert_match (REGEX_URL_AS_IS, "https://dead::beef/no-brackets-ipv6", "https://dead"); /* ditto */ - assert_match (REGEX_URL_AS_IS, "http://111.222.333.444/", NULL); - assert_match (REGEX_URL_AS_IS, "http://1.2.3.4:70000", "http://1.2.3.4"); /* TODO: can/should we totally abort here? */ - assert_match (REGEX_URL_AS_IS, "http://[dead::beef:111.222.333.444]", NULL); - - /* Username, password */ - assert_match (REGEX_URL_AS_IS, "http://joe@example.com", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://user.name:sec.ret@host.name", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://joe:secret@[::1]", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://dudewithnopassword:@example.com", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://safeguy:!#$%^&*@host", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http://invalidusername!@host", "http://invalidusername"); - - assert_match (REGEX_URL_AS_IS, "http://ab.cd/ef?g=h&i=j|k=l#m=n:o=p", ENTIRE); - assert_match (REGEX_URL_AS_IS, "http:///foo", NULL); - - /* Parentheses are only allowed in matching pairs, see bug 763980. */ - assert_match (REGEX_URL_AS_IS, "https://en.wikipedia.org/wiki/The_Offspring_(album)", ENTIRE); - assert_match (REGEX_URL_AS_IS, "[markdown](https://en.wikipedia.org/wiki/The_Offspring)", "https://en.wikipedia.org/wiki/The_Offspring"); - assert_match (REGEX_URL_AS_IS, "[markdown](https://en.wikipedia.org/wiki/The_Offspring_(album))", "https://en.wikipedia.org/wiki/The_Offspring_(album)"); - assert_match (REGEX_URL_AS_IS, "[markdown](http://foo.bar/(a(b)c)d)e)f", "http://foo.bar/(a(b)c)d"); - assert_match (REGEX_URL_AS_IS, "[markdown](http://foo.bar/a)b(c", "http://foo.bar/a"); - - /* Apostrophes are allowed, except at trailing position if the URL is preceded by an apostrophe, see bug 448044. */ - assert_match (REGEX_URL_AS_IS, "https://en.wikipedia.org/wiki/Moore's_law", ENTIRE); - assert_match (REGEX_URL_AS_IS, "<a href=\"https://en.wikipedia.org/wiki/Moore's_law\">", "https://en.wikipedia.org/wiki/Moore's_law"); - assert_match (REGEX_URL_AS_IS, "https://en.wikipedia.org/wiki/Cryin'", ENTIRE); - assert_match (REGEX_URL_AS_IS, "<a href=\"https://en.wikipedia.org/wiki/Cryin'\">", "https://en.wikipedia.org/wiki/Cryin'"); - assert_match (REGEX_URL_AS_IS, "<a href='https://en.wikipedia.org/wiki/Aerosmith'>", "https://en.wikipedia.org/wiki/Aerosmith"); - - /* No scheme */ - assert_match (REGEX_URL_HTTP, "www.foo.bar/baz", ENTIRE); - assert_match (REGEX_URL_HTTP, "WWW3.foo.bar/baz", ENTIRE); - assert_match (REGEX_URL_HTTP, "FTP.FOO.BAR/BAZ", ENTIRE); /* FIXME if no scheme is given and url starts with ftp, can we make the protocol ftp instead of http? */ - assert_match (REGEX_URL_HTTP, "ftpxy.foo.bar/baz", ENTIRE); -// assert_match (REGEX_URL_HTTP, "ftp.123/baz", NULL); /* errr... could we fail here?? */ - assert_match (REGEX_URL_HTTP, "foo.bar/baz", NULL); - assert_match (REGEX_URL_HTTP, "abc.www.foo.bar/baz", NULL); - assert_match (REGEX_URL_HTTP, "uvwww.foo.bar/baz", NULL); - assert_match (REGEX_URL_HTTP, "xftp.foo.bar/baz", NULL); - - /* file:/ or file://(hostname)?/ */ - assert_match (REGEX_URL_FILE, "file:", NULL); - assert_match (REGEX_URL_FILE, "file:/", ENTIRE); - assert_match (REGEX_URL_FILE, "file://", NULL); - assert_match (REGEX_URL_FILE, "file:///", ENTIRE); - assert_match (REGEX_URL_FILE, "file:////", NULL); - assert_match (REGEX_URL_FILE, "file:etc/passwd", NULL); - assert_match (REGEX_URL_FILE, "File:/etc/passwd", ENTIRE); - assert_match (REGEX_URL_FILE, "FILE:///etc/passwd", ENTIRE); - assert_match (REGEX_URL_FILE, "file:////etc/passwd", NULL); - assert_match (REGEX_URL_FILE, "file://host.name", NULL); - assert_match (REGEX_URL_FILE, "file://host.name/", ENTIRE); - assert_match (REGEX_URL_FILE, "file://host.name/etc", ENTIRE); - - assert_match (REGEX_URL_FILE, "See file:/.", "file:/"); - assert_match (REGEX_URL_FILE, "See file:///.", "file:///"); - assert_match (REGEX_URL_FILE, "See file:/lost+found.", "file:/lost+found"); - assert_match (REGEX_URL_FILE, "See file:///lost+found.", "file:///lost+found"); - - /* Email */ - assert_match (REGEX_EMAIL, "Write to foo@bar.com.", "foo@bar.com"); - assert_match (REGEX_EMAIL, "Write to <foo@bar.com>", "foo@bar.com"); - assert_match (REGEX_EMAIL, "Write to mailto:foo@bar.com.", "mailto:foo@bar.com"); - assert_match (REGEX_EMAIL, "Write to MAILTO:FOO@BAR.COM.", "MAILTO:FOO@BAR.COM"); - assert_match (REGEX_EMAIL, "Write to foo@[1.2.3.4]", "foo@[1.2.3.4]"); - assert_match (REGEX_EMAIL, "Write to foo@[1.2.3.456]", NULL); - assert_match (REGEX_EMAIL, "Write to foo@[1::2345]", "foo@[1::2345]"); - assert_match (REGEX_EMAIL, "Write to foo@[dead::beef]", "foo@[dead::beef]"); - assert_match (REGEX_EMAIL, "Write to foo@1.2.3.4", NULL); - assert_match (REGEX_EMAIL, "Write to foo@1.2.3.456", NULL); - assert_match (REGEX_EMAIL, "Write to foo@1::2345", NULL); - assert_match (REGEX_EMAIL, "Write to foo@dead::beef", NULL); - assert_match (REGEX_EMAIL, "<baz email=\"foo@bar.com\"/>", "foo@bar.com"); - assert_match (REGEX_EMAIL, "<baz email='foo@bar.com'/>", "foo@bar.com"); - assert_match (REGEX_EMAIL, "<email>foo@bar.com</email>", "foo@bar.com"); - - /* Sip, examples from rfc 3261 */ - assert_match (REGEX_URL_VOIP, "sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15", ENTIRE); - assert_match (REGEX_URL_VOIP, "sip:alice@atlanta.com", ENTIRE); - assert_match (REGEX_URL_VOIP, "sip:alice:secretword@atlanta.com;transport=tcp", ENTIRE); - assert_match (REGEX_URL_VOIP, "sips:alice@atlanta.com?subject=project%20x&priority=urgent", ENTIRE); - assert_match (REGEX_URL_VOIP, "sip:+1-212-555-1212:1234@gateway.com;user=phone", ENTIRE); - assert_match (REGEX_URL_VOIP, "sips:1212@gateway.com", ENTIRE); - assert_match (REGEX_URL_VOIP, "sip:alice@192.0.2.4", ENTIRE); - assert_match (REGEX_URL_VOIP, "sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com", ENTIRE); - assert_match (REGEX_URL_VOIP, "SIP:alice;day=tuesday@atlanta.com", ENTIRE); - assert_match (REGEX_URL_VOIP, "Dial sip:alice@192.0.2.4.", "sip:alice@192.0.2.4"); - - /* Extremely long match, bug 770147 */ - assert_match (REGEX_URL_AS_IS, "http://www.example.com/ThisPathConsistsOfMoreThan1024Characters" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", ENTIRE); - - printf("terminal-regex tests passed :)\n"); - return 0; -} - -#endif /* TERMINAL_REGEX_MAIN */ diff --git a/src/terminal-regex.h b/src/terminal-regex.h deleted file mode 100644 index 3a3e89a6..00000000 --- a/src/terminal-regex.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright © 2015 Egmont Koblinger - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Mini style-guide: - * - * #define'd fragments should preferably have an outermost group, for the - * exact same reason as why usually in C/C++ #define's the values are enclosed - * in parentheses: that is, so that you don't get surprised when you use the - * macro and append a quantifier. - * - * For repeated fragments prefer regex-style (?(DEFINE)(?<NAME>(...))) and use - * as (?&NAME), so that the regex string and the compiled regex object is - * smaller. - * - * Build small blocks, comment and unittest them heavily. - * - * Use free-spacing mode for improved readability. The hardest to read is - * which additional characters belong to a "(?" prefix. To improve - * readability, place a space after this, and for symmetry, before the closing - * parenthesis. Also place a space around "|" characters. No space before - * quantifiers. Try to be consistent with the existing style (yes I know the - * existing style is not consistent either, but please do your best). - * - * See http://www.rexegg.com/regex-disambiguation.html for all the "(?" - * syntaxes. - */ - -#ifndef TERMINAL_REGEX_H -#define TERMINAL_REGEX_H - -/* Lookbehind to see if there's a preceding apostrophe */ -#define APOS_START_DEF "(?<APOS_START>(?<='))?" - -#define SCHEME "(?ix: news | telnet | nntp | https? | ftps? | sftp | webcal )" - -#define USERCHARS "-+.[:alnum:]" -/* Nonempty username, e.g. "john.smith" */ -#define USER "[" USERCHARS "]+" - -#define PASSCHARS_CLASS "[-[:alnum:]\\Q,?;.:/!%$^*&~\"#'\\E]" -/* Optional colon-prefixed password. I guess empty password should be allowed, right? E.g. ":secret", ":", "" */ -#define PASS "(?x: :" PASSCHARS_CLASS "* )?" - -/* Optional at-terminated username (with perhaps a password too), e.g. "joe@", "pete:secret@", "" */ -#define USERPASS "(?:" USER PASS "@)?" - -/* S4: IPv4 segment (number between 0 and 255) with lookahead at the end so that we don't match "25" in the string "256". - The lookahead could go to the last segment of IPv4 only but this construct allows nicer unittesting. */ -#define S4_DEF "(?(DEFINE)(?<S4>(?x: (?: [0-9] | [1-9][0-9] | 1[0-9]{2} | 2[0-4][0-9] | 25[0-5] ) (?! [0-9] ) )))" - -/* IPV4: Decimal IPv4, e.g. "1.2.3.4", with lookahead (implemented in S4) at the end so that we don't match "192.168.1.123" in the string "192.168.1.1234". */ -#define IPV4_DEF S4_DEF "(?(DEFINE)(?<IPV4>(?x: (?: (?&S4) \\. ){3} (?&S4) )))" - -/* IPv6, including embedded IPv4, e.g. "::1", "dead:beef::1.2.3.4". - * Lookahead for the next char not being a dot or digit, so it doesn't get stuck matching "dead:beef::1" in "dead:beef::1.2.3.4". - * This is not required since the surrounding brackets would trigger backtracking, but it allows nicer unittesting. - * TODO: more strict check (right number of colons, etc.) - * TODO: add zone_id: RFC 4007 section 11, RFC 6874 */ - -/* S6: IPv6 segment, S6C: IPv6 segment followed by a comma, CS6: comma followed by an IPv6 segment */ -#define S6_DEF "(?(DEFINE)(?<S6>[[:xdigit:]]{1,4})(?<CS6>:(?&S6))(?<S6C>(?&S6):))" - -/* No :: shorthand */ -#define IPV6_FULL "(?x: (?&S6C){7} (?&S6) )" -/* Begins with :: */ -#define IPV6_LEFT "(?x: : (?&CS6){1,7} )" -/* :: somewhere in the middle - use negative lookahead to make sure there aren't too many colons in total */ -#define IPV6_MID "(?x: (?! (?: [[:xdigit:]]*: ){8} ) (?&S6C){1,6} (?&CS6){1,6} )" -/* Ends with :: */ -#define IPV6_RIGHT "(?x: (?&S6C){1,7} : )" -/* Is "::" and nothing more */ -#define IPV6_NULL "(?x: :: )" - -/* The same ones for IPv4-embedded notation, without the actual IPv4 part */ -#define IPV6V4_FULL "(?x: (?&S6C){6} )" -#define IPV6V4_LEFT "(?x: :: (?&S6C){0,5} )" /* includes "::<ipv4>" */ -#define IPV6V4_MID "(?x: (?! (?: [[:xdigit:]]*: ){7} ) (?&S6C){1,4} (?&CS6){1,4} ) :" -#define IPV6V4_RIGHT "(?x: (?&S6C){1,5} : )" - -/* IPV6: An IPv6 address (possibly with an embedded IPv4). - * This macro defines both IPV4 and IPV6, since the latter one requires the former. */ -#define IP_DEF IPV4_DEF S6_DEF "(?(DEFINE)(?<IPV6>(?x: (?: " IPV6_NULL " | " IPV6_LEFT " | " IPV6_MID " | " IPV6_RIGHT " | " IPV6_FULL " | (?: " IPV6V4_FULL " | " IPV6V4_LEFT " | " IPV6V4_MID " | " IPV6V4_RIGHT " ) (?&IPV4) ) (?! [.:[:xdigit:]] ) )))" - -/* Either an alphanumeric character or dash; or if [negative lookahead] not ASCII - * then any graphical Unicode character. - * A segment can consist entirely of numbers. - * (Note: PCRE doesn't support character class subtraction/intersection.) */ -#define HOSTNAMESEGMENTCHARS_CLASS "(?x: [-[:alnum:]] | (?! [[:ascii:]] ) [[:graph:]] )" - -/* A hostname of at least 1 component. The last component cannot be entirely numbers. - * E.g. "foo", "example.com", "1234.com", but not "foo.123" */ -#define HOSTNAME1 "(?x: (?: " HOSTNAMESEGMENTCHARS_CLASS "+ \\. )* " HOSTNAMESEGMENTCHARS_CLASS "* (?! [0-9] ) " HOSTNAMESEGMENTCHARS_CLASS "+ )" - -/* A hostname of at least 2 components. The last component cannot be entirely numbers. - * E.g. "example.com", "1234.com", but not "1234.56" */ -#define HOSTNAME2 "(?x: (?: " HOSTNAMESEGMENTCHARS_CLASS "+ \\.)+ " HOSTNAME1 " )" - -/* For URL: Hostname, IPv4, or bracket-enclosed IPv6, e.g. "example.com", "1.2.3.4", "[::1]" */ -#define URL_HOST "(?x: " HOSTNAME1 " | (?&IPV4) | \\[ (?&IPV6) \\] )" - -/* For e-mail: Hostname of at least two segments, or bracket-enclosed IPv4 or IPv6, e.g. "example.com", "[1.2.3.4]", "[::1]". - * Technically an e-mail with a single-component hostname might be valid on a local network, but let's avoid tons of false positives (e.g. in a typical shell prompt). */ -#define EMAIL_HOST "(?x: " HOSTNAME2 " | \\[ (?: (?&IPV4) | (?&IPV6) ) \\] )" - -/* Number between 1 and 65535, with lookahead at the end so that we don't match "6789" in the string "67890", - and in turn we don't eventually match "http://host:6789" in "http://host:67890". */ -#define N_1_65535 "(?x: (?: [1-9][0-9]{0,3} | [1-5][0-9]{4} | 6[0-4][0-9]{3} | 65[0-4][0-9]{2} | 655[0-2][0-9] | 6553[0-5] ) (?! [0-9] ) )" - -/* Optional colon-prefixed port, e.g. ":1080", "" */ -#define PORT "(?x: \\:" N_1_65535 " )?" - -/* Omit the parentheses, see below */ -#define PATHCHARS_CLASS "[-[:alnum:]\\Q_$.+!*,:;@&=?/~#|%'\\E]" -/* Chars to end a URL. Apostrophe only allowed if there wasn't one in front of the URL, see bug 448044 */ -#define PATHTERM_CLASS "[-[:alnum:]\\Q_$+*:@&=/~#|%'\\E]" -#define PATHTERM_NOAPOS_CLASS "[-[:alnum:]\\Q_$+*:@&=/~#|%\\E]" - -/* Recursive definition of PATH that allows parentheses and square brackets only if balanced, see bug 763980. */ -#define PATH_INNER_DEF "(?(DEFINE)(?<PATH_INNER>(?x: (?: " PATHCHARS_CLASS "* (?: \\( (?&PATH_INNER) \\) | \\[ (?&PATH_INNER) \\] ) )* " PATHCHARS_CLASS "* )))" -/* Same as above, but the last character (if exists and is not a parenthesis) must be from PATHTERM_CLASS. */ -#define PATH_DEF "(?(DEFINE)(?<PATH>(?x: (?: " PATHCHARS_CLASS "* (?: \\( (?&PATH_INNER) \\) | \\[ (?&PATH_INNER) \\] ) )* (?: " PATHCHARS_CLASS "* (?(<APOS_START>)" PATHTERM_NOAPOS_CLASS "|" PATHTERM_CLASS ") )? )))" - -#define URLPATH "(?x: /(?&PATH) )?" -#define VOIP_PATH "(?x: [;?](?&PATH) )?" - -/* Now let's put these fragments together */ - -#define DEFS APOS_START_DEF IP_DEF PATH_INNER_DEF PATH_DEF - -#define REGEX_URL_AS_IS DEFS SCHEME "://" USERPASS URL_HOST PORT URLPATH -/* TODO: also support file:/etc/passwd */ -#define REGEX_URL_FILE DEFS "(?ix: file:/ (?: / (?: " HOSTNAME1 " )? / )? (?! / ) )(?&PATH)" -/* Lookbehind so that we don't catch "abc.www.foo.bar", bug 739757. Lookahead for www/ftp for convenience (so that we can reuse HOSTNAME1). */ -#define REGEX_URL_HTTP DEFS "(?<!(?:" HOSTNAMESEGMENTCHARS_CLASS "|[.]))(?=(?i:www|ftp))" HOSTNAME1 PORT URLPATH -#define REGEX_URL_VOIP DEFS "(?i:h323:|sips?:)" USERPASS URL_HOST PORT VOIP_PATH -#define REGEX_EMAIL DEFS "(?i:mailto:)?" USER "@" EMAIL_HOST -#define REGEX_NEWS_MAN "(?i:news:|man:|info:)[-[:alnum:]\\Q^_{|}~!\"#$%&'()*+,./;:=?`\\E]+" - -#endif /* !TERMINAL_REGEX_H */ diff --git a/src/terminal-screen.c b/src/terminal-screen.c index 23134e80..3087d7e1 100644 --- a/src/terminal-screen.c +++ b/src/terminal-screen.c @@ -19,7 +19,6 @@ #include "config.h" #include "terminal-pcre2.h" -#include "terminal-regex.h" #include "terminal-screen.h" #include <errno.h> @@ -170,24 +169,12 @@ typedef struct { TerminalURLFlavor flavor; } TerminalRegexPattern; -static const TerminalRegexPattern url_regex_patterns[] = { - { REGEX_URL_AS_IS, FLAVOR_AS_IS }, - { REGEX_URL_HTTP, FLAVOR_DEFAULT_TO_HTTP }, - { REGEX_URL_FILE, FLAVOR_AS_IS }, - { REGEX_URL_VOIP, FLAVOR_VOIP_CALL }, - { REGEX_EMAIL, FLAVOR_EMAIL }, - { REGEX_NEWS_MAN, FLAVOR_AS_IS }, -}; - static const TerminalRegexPattern extra_regex_patterns[] = { { "(0[Xx][[:xdigit:]]+|[[:digit:]]+)", FLAVOR_NUMBER }, }; -static VteRegex **url_regexes; static VteRegex **extra_regexes; -static TerminalURLFlavor *url_regex_flavors; static TerminalURLFlavor *extra_regex_flavors; -static guint n_url_regexes; static guint n_extra_regexes; /* See bug #697024 */ @@ -344,7 +331,6 @@ terminal_screen_init (TerminalScreen *screen) GtkTargetList *target_list; GtkTargetEntry *targets; int n_targets; - guint i; uuid_t u; char uuidstr[37]; @@ -360,14 +346,13 @@ terminal_screen_init (TerminalScreen *screen) vte_terminal_set_allow_hyperlink (terminal, TRUE); - for (i = 0; i < n_url_regexes; ++i) + vte_terminal_match_add_builtins (terminal); { TagData *tag_data; tag_data = g_slice_new (TagData); - tag_data->flavor = url_regex_flavors[i]; - tag_data->tag = vte_terminal_match_add_regex (terminal, url_regexes[i], 0); - vte_terminal_match_set_cursor_type (terminal, tag_data->tag, URL_MATCH_CURSOR); + tag_data->flavor = FLAVOR_AS_IS; + tag_data->tag = VTE_BUILTIN_MATCH_TAG_URI; priv->match_tags = g_slist_prepend (priv->match_tags, tag_data); } @@ -542,8 +527,6 @@ terminal_screen_class_init (TerminalScreenClass *klass) g_type_class_add_private (object_class, sizeof (TerminalScreenPrivate)); - n_url_regexes = G_N_ELEMENTS (url_regex_patterns); - precompile_regexes (url_regex_patterns, n_url_regexes, &url_regexes, &url_regex_flavors); n_extra_regexes = G_N_ELEMENTS (extra_regex_patterns); precompile_regexes (extra_regex_patterns, n_extra_regexes, &extra_regexes, &extra_regex_flavors); diff --git a/src/terminal-screen.h b/src/terminal-screen.h index ff77fcf7..44a4e755 100644 --- a/src/terminal-screen.h +++ b/src/terminal-screen.h @@ -28,9 +28,6 @@ G_BEGIN_DECLS typedef enum { FLAVOR_AS_IS, - FLAVOR_DEFAULT_TO_HTTP, - FLAVOR_VOIP_CALL, - FLAVOR_EMAIL, FLAVOR_NUMBER, } TerminalURLFlavor; diff --git a/src/terminal-util.c b/src/terminal-util.c index 72a0d401..317f2d02 100644 --- a/src/terminal-util.c +++ b/src/terminal-util.c @@ -286,34 +286,12 @@ terminal_util_set_atk_name_description (GtkWidget *widget, void terminal_util_open_url (GtkWidget *parent, - const char *orig_url, - TerminalURLFlavor flavor, + const char *uri, guint32 user_time) { gs_free_error GError *error = NULL; - gs_free char *uri = NULL; - g_return_if_fail (orig_url != NULL); - - switch (flavor) - { - case FLAVOR_DEFAULT_TO_HTTP: - uri = g_strdup_printf ("http://%s", orig_url); - break; - case FLAVOR_EMAIL: - if (g_ascii_strncasecmp ("mailto:", orig_url, 7) != 0) - uri = g_strdup_printf ("mailto:%s", orig_url); - else - uri = g_strdup (orig_url); - break; - case FLAVOR_VOIP_CALL: - case FLAVOR_AS_IS: - uri = g_strdup (orig_url); - break; - default: - uri = NULL; - g_assert_not_reached (); - } + g_return_if_fail (uri != NULL); if (!open_url (GTK_WINDOW (parent), uri, user_time, &error)) { diff --git a/src/terminal-util.h b/src/terminal-util.h index abd34fd7..591e43eb 100644 --- a/src/terminal-util.h +++ b/src/terminal-util.h @@ -40,8 +40,7 @@ void terminal_util_set_atk_name_description (GtkWidget *widget, const char *desc); void terminal_util_open_url (GtkWidget *parent, - const char *orig_url, - TerminalURLFlavor flavor, + const char *uri, guint32 user_time); void terminal_util_transform_uris_to_quoted_fuse_paths (char **uris); diff --git a/src/terminal-window.c b/src/terminal-window.c index 19198f49..5ed97859 100644 --- a/src/terminal-window.c +++ b/src/terminal-window.c @@ -999,7 +999,7 @@ action_open_match_cb (GSimpleAction *action, if (info->url == NULL) return; - terminal_util_open_url (GTK_WIDGET (window), info->url, info->url_flavor, + terminal_util_open_url (GTK_WIDGET (window), info->url, gtk_get_current_event_time ()); } @@ -1034,7 +1034,7 @@ action_open_hyperlink_cb (GSimpleAction *action, if (info->hyperlink == NULL) return; - terminal_util_open_url (GTK_WIDGET (window), info->hyperlink, FLAVOR_AS_IS, + terminal_util_open_url (GTK_WIDGET (window), info->hyperlink, gtk_get_current_event_time ()); } @@ -1728,23 +1728,21 @@ screen_show_popup_menu_cb (TerminalScreen *screen, /* Matched link section */ else if (info->url != NULL) { gs_unref_object GMenu *section2 = g_menu_new (); + gs_free char* scheme = g_uri_parse_scheme (info->url); const char *open_label = NULL, *copy_label = NULL; - switch (info->url_flavor) { - case FLAVOR_EMAIL: + if (scheme == NULL) { + /* Not a valid URI; do nothing. */ + } else if (g_ascii_strcasecmp (scheme, "mailto") == 0) { open_label = _("Send Mail _To…"); copy_label = _("Copy Mail _Address"); - break; - case FLAVOR_VOIP_CALL: + } else if (g_ascii_strcasecmp (scheme, "sip") == 0 || + g_ascii_strcasecmp (scheme, "sips") == 0) { open_label = _("Call _To…"); copy_label = _("Copy Call _Address "); - break; - case FLAVOR_AS_IS: - case FLAVOR_DEFAULT_TO_HTTP: - default: + } else { open_label = _("_Open Link"); copy_label = _("Copy _Link"); - break; } g_menu_append (section2, open_label, "win.open-match"); @@ -1848,8 +1846,11 @@ screen_match_clicked_cb (TerminalScreen *screen, if (screen != priv->active_screen) return FALSE; + if (url_flavor != FLAVOR_AS_IS) + return FALSE; + gtk_widget_grab_focus (GTK_WIDGET (screen)); - terminal_util_open_url (GTK_WIDGET (window), url, url_flavor, + terminal_util_open_url (GTK_WIDGET (window), url, gtk_get_current_event_time ()); return TRUE; |