diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index e729e2d..15a4797 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -178,14 +178,18 @@ typedef struct {
   const char *pattern;
   TerminalURLFlavour flavor;
   gboolean caseless;
+  gboolean highlight;
 } TerminalRegexPattern;
 
 static const TerminalRegexPattern url_regex_patterns[] = {
-  { SCHEME "//(?:" USERPASS "\\@)?" HOST PORT URLPATH, FLAVOR_AS_IS, TRUE },
-  { "(?:www|ftp)" HOSTCHARS_CLASS "*\\." HOST PORT URLPATH , FLAVOR_DEFAULT_TO_HTTP, TRUE  },
-  { "(?:callto:|h323:|sip:)" USERCHARS_CLASS "[" USERCHARS ".]*(?:" PORT "/[a-z0-9]+)?\\@" HOST, FLAVOR_VOIP_CALL, TRUE },
-  { "(?:mailto:)?" USERCHARS_CLASS "[" USERCHARS ".]*\\@" HOSTCHARS_CLASS "+\\." HOST, FLAVOR_EMAIL, TRUE },
-  { "(?:news:|man:|info:)[-[:alnum:]\\Q^_{|}~!\"#$%&'()*+,./;:=?`\\E]+", FLAVOR_AS_IS, TRUE },
+  { SCHEME "//(?:" USERPASS "\\@)?" HOST PORT URLPATH, FLAVOR_AS_IS, TRUE, TRUE },
+  { "(?:www|ftp)" HOSTCHARS_CLASS "*\\." HOST PORT URLPATH , FLAVOR_DEFAULT_TO_HTTP, TRUE, TRUE },
+  { "(?:callto:|h323:|sip:)" USERCHARS_CLASS "[" USERCHARS ".]*(?:" PORT "/[a-z0-9]+)?\\@" HOST, FLAVOR_VOIP_CALL, TRUE, TRUE },
+  { "(?:mailto:)?" USERCHARS_CLASS "[" USERCHARS ".]*\\@" HOSTCHARS_CLASS "+\\." HOST, FLAVOR_EMAIL, TRUE, TRUE },
+  { "(?:news:|man:|info:)[-[:alnum:]\\Q^_{|}~!\"#$%&'()*+,./;:=?`\\E]+", FLAVOR_AS_IS, TRUE, TRUE },
+  /* The hex pattern should precede the decimal, so that we won't match a decimal number in the middle of a hexadecimal. */
+  { "0[Xx][\\dA-Fa-f]+", FLAVOR_NUMBER, FALSE, FALSE },
+  { "\\d+", FLAVOR_NUMBER, FALSE, FALSE },
 };
 
 #ifdef WITH_PCRE2
@@ -194,6 +198,7 @@ static VteRegex **url_regexes;
 static GRegex **url_regexes;
 #endif
 static TerminalURLFlavour *url_regex_flavors;
+static gboolean *url_regex_highlights;
 static guint n_url_regexes;
 
 /* See bug #697024 */
@@ -345,7 +350,9 @@ terminal_screen_init (TerminalScreen *screen)
 #else
       tag_data->tag = vte_terminal_match_add_gregex (terminal, url_regexes[i], 0);
 #endif
-      vte_terminal_match_set_cursor_type (terminal, tag_data->tag, URL_MATCH_CURSOR);
+      if (url_regex_highlights[i])
+        vte_terminal_match_set_cursor_type (terminal, tag_data->tag, URL_MATCH_CURSOR);
+      vte_terminal_match_set_underline (terminal, tag_data->tag, url_regex_highlights[i]);
 
       priv->match_tags = g_slist_prepend (priv->match_tags, tag_data);
     }
@@ -554,6 +561,7 @@ terminal_screen_class_init (TerminalScreenClass *klass)
   url_regexes = g_new0 (GRegex*, n_url_regexes);
 #endif
   url_regex_flavors = g_new0 (TerminalURLFlavour, n_url_regexes);
+  url_regex_highlights = g_new0 (gboolean, n_url_regexes);
 
   for (i = 0; i < n_url_regexes; ++i)
     {
@@ -580,6 +588,7 @@ terminal_screen_class_init (TerminalScreenClass *klass)
 #endif
 
       url_regex_flavors[i] = url_regex_patterns[i].flavor;
+      url_regex_highlights[i] = url_regex_patterns[i].highlight;
     }
 
   /* This fixes bug #329827 */
diff --git a/src/terminal-screen.h b/src/terminal-screen.h
index a168e57..c2a4bb5 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -30,7 +30,8 @@ typedef enum {
   FLAVOR_AS_IS,
   FLAVOR_DEFAULT_TO_HTTP,
   FLAVOR_VOIP_CALL,
-  FLAVOR_EMAIL
+  FLAVOR_EMAIL,
+  FLAVOR_NUMBER,
 } TerminalURLFlavour;
 
 /* Forward decls */
diff --git a/src/terminal-util.c b/src/terminal-util.c
index 2c4e0ff..057a2d4 100644
--- a/src/terminal-util.c
+++ b/src/terminal-util.c
@@ -26,6 +26,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <langinfo.h>
 #include <errno.h>
 
 #include <glib.h>
@@ -44,6 +45,8 @@
 #include "terminal-window.h"
 #include "terminal-libgsystem.h"
 
+static const char* iec_prefixes[] = { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
+
 /**
  * terminal_util_show_error_dialog:
  * @transient_parent: parent of the future dialog window;
@@ -275,7 +278,7 @@ terminal_util_set_atk_name_description (GtkWidget  *widget,
     atk_object_set_name (obj, name);
 }
 
-void
+gboolean
 terminal_util_open_url (GtkWidget *parent,
                         const char *orig_url,
                         TerminalURLFlavour flavor,
@@ -284,7 +287,7 @@ terminal_util_open_url (GtkWidget *parent,
   gs_free_error GError *error = NULL;
   gs_free char *uri = NULL;
 
-  g_return_if_fail (orig_url != NULL);
+  g_return_val_if_fail (orig_url != NULL, FALSE);
 
   switch (flavor)
     {
@@ -301,6 +304,8 @@ terminal_util_open_url (GtkWidget *parent,
     case FLAVOR_AS_IS:
       uri = g_strdup (orig_url);
       break;
+    case FLAVOR_NUMBER:
+      return FALSE;
     default:
       uri = NULL;
       g_assert_not_reached ();
@@ -312,6 +317,7 @@ terminal_util_open_url (GtkWidget *parent,
                                        _("Could not open the address “%s”"),
                                        uri);
     }
+  return TRUE;
 }
 
 /**
@@ -951,3 +957,111 @@ terminal_util_bind_mnemonic_label_sensitivity (GtkWidget *widget)
                            (GtkCallback) terminal_util_bind_mnemonic_label_sensitivity,
                            NULL);
 }
+
+/*
+ * "1234567", "'", 3 -> "1'234'567"
+ */
+static char *
+add_separators (const char *in, const char *sep, int groupby)
+{
+  int inlen, outlen, seplen, firstgrouplen;
+  char *out, *ret;
+
+  if (in[0] == '\0')
+    return g_strdup("");
+
+  inlen = strlen(in);
+  seplen = strlen(sep);
+  outlen = inlen + (inlen - 1) / groupby * seplen;
+  ret = out = g_malloc(outlen + 1);
+
+  firstgrouplen = (inlen - 1) % groupby + 1;
+  strncpy(out, in, firstgrouplen);
+  in += firstgrouplen;
+  out += firstgrouplen;
+
+  while (*in != '\0') {
+    strncpy(out, sep, seplen);
+    out += seplen;
+    strncpy(out, in, groupby);
+    in += groupby;
+    out += groupby;
+  }
+
+  g_assert(out - ret == outlen);
+  *out = '\0';
+  return ret;
+}
+
+/**
+ * terminal_util_number_info:
+ * @str: a dec or hex number as string
+ *
+ * Returns: Useful info about @str, or %NULL if it's too large
+ */
+char *
+terminal_util_number_info (const char *str)
+{
+  gs_free char *decstr = NULL;
+  gs_free char *hextmp = NULL;
+  gs_free char *hexstr = NULL;
+  gs_free char *magnitudestr = NULL;
+  unsigned long long num;
+  gboolean exact = TRUE;
+  gboolean hex = FALSE;
+  const char *thousep;
+
+  errno = 0;
+  if (str[1] == 'x' || str[1] == 'X') {
+    num = strtoull(str + 2, NULL, 16);
+    hex = TRUE;
+  } else {
+    num = strtoull(str, NULL, 10);
+  }
+  if (errno) {
+    return NULL;
+  }
+
+  /* No use in dec-hex conversion for so small numbers */
+  if (num < 10) {
+    return NULL;
+  }
+
+  /* Group the decimal digits */
+  thousep = nl_langinfo(THOUSEP);
+  if (thousep[0] != '\0') {
+    /* If thousep is nonempty, use printf's magic which can handle
+       more complex separating logics, e.g. 2+2+2+3 for some locales */
+    decstr = g_strdup_printf("%'llu", num);
+  } else {
+    /* If, however, thousep is empty, override it with a space so that we
+       do always group the digits (that's the whole point of this feature)
+       (and btw no chance of conflicting with the decimal separator) */
+    gs_free char *tmp = g_strdup_printf("%llu", num);
+    thousep = " ";
+    decstr = add_separators(tmp, thousep, 3);
+  }
+
+  /* Group the hex digits by 4 using the same nonempty separator */
+  hextmp = g_strdup_printf("%llx", num);
+  hexstr = add_separators(hextmp, thousep, 4);
+
+  /* Find out the human-readable magnitude, e.g. 15.99 Mi */
+  if (num >= 1024) {
+    int pow = 0;
+    while (num >= 1024 * 1024) {
+      pow++;
+      if (num % 1024 != 0) exact = FALSE;
+      num /= 1024;
+    }
+    /* Show 2 fraction digits, always rounding downwards. Printf rounds floats to the nearest representable value,
+       so do the calculation with integers until we get 100-fold the desired value, and then switch to float. */
+    if (100 * num % 1024 != 0) exact = FALSE;
+    num = 100 * num / 1024;
+    magnitudestr = g_strdup_printf(" %s %.2f %s", exact ? "=" : "≈", (double) num / 100, iec_prefixes[pow]);
+  } else {
+    magnitudestr = g_strdup("");
+  }
+
+  return g_strdup_printf(hex ? "0x%2$s = %1$s%3$s" : "%s = 0x%s%s", decstr, hexstr, magnitudestr);
+}
diff --git a/src/terminal-util.h b/src/terminal-util.h
index d4b5ae3..7a59bce 100644
--- a/src/terminal-util.h
+++ b/src/terminal-util.h
@@ -41,10 +41,10 @@ void terminal_util_set_atk_name_description (GtkWidget  *widget,
                                              const char *name,
                                              const char *desc);
 
-void terminal_util_open_url (GtkWidget *parent,
-                             const char *orig_url,
-                             TerminalURLFlavour flavor,
-                             guint32 user_time);
+gboolean terminal_util_open_url (GtkWidget *parent,
+                                 const char *orig_url,
+                                 TerminalURLFlavour flavor,
+                                 guint32 user_time);
 
 void terminal_util_transform_uris_to_quoted_fuse_paths (char **uris);
 
@@ -89,6 +89,8 @@ void terminal_g_settings_set_rgba_palette (GSettings      *settings,
 
 void terminal_util_bind_mnemonic_label_sensitivity (GtkWidget *widget);
 
+char *terminal_util_number_info (const char *str);
+
 G_END_DECLS
 
 #endif /* TERMINAL_UTIL_H */
diff --git a/src/terminal-window.c b/src/terminal-window.c
index d32bb3b..4a3b291 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -2090,7 +2090,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   TerminalScreen *screen = info->screen;
   GtkWidget *popup_menu;
   GtkAction *action;
-  gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link;
+  gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link, show_number;
 
   window = terminal_screen_popup_info_ref_window (info);
   if (window == NULL ||
@@ -2111,6 +2111,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   show_link = info->string != NULL && (info->flavour == FLAVOR_AS_IS || info->flavour == FLAVOR_DEFAULT_TO_HTTP);
   show_email_link = info->string != NULL && info->flavour == FLAVOR_EMAIL;
   show_call_link = info->string != NULL && info->flavour == FLAVOR_VOIP_CALL;
+  show_number = info->string != NULL && info->flavour == FLAVOR_NUMBER;
 
   action = gtk_action_group_get_action (priv->action_group, "PopupSendEmail");
   gtk_action_set_visible (action, show_email_link);
@@ -2124,6 +2125,17 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   gtk_action_set_visible (action, show_link);
   action = gtk_action_group_get_action (priv->action_group, "PopupCopyLinkAddress");
   gtk_action_set_visible (action, show_link);
+  action = gtk_action_group_get_action (priv->action_group, "PopupNumber");
+  if (show_number) {
+    gs_free char *numfmt = terminal_util_number_info (info->string);
+    if (numfmt == NULL) {
+      show_number = FALSE;
+    } else {
+      gtk_action_set_label (action, numfmt);
+    }
+  }
+  gtk_action_set_sensitive (action, FALSE);
+  gtk_action_set_visible (action, show_number);
 
   action = gtk_action_group_get_action (priv->action_group, "PopupCopy");
   gtk_action_set_sensitive (action, vte_terminal_get_has_selection (VTE_TERMINAL (screen)));
@@ -2181,10 +2193,8 @@ screen_match_clicked_cb (TerminalScreen *screen,
     return FALSE;
 
   gtk_widget_grab_focus (GTK_WIDGET (screen));
-  terminal_util_open_url (GTK_WIDGET (window), match, flavour,
-                          gtk_get_current_event_time ());
-
-  return TRUE;
+  return terminal_util_open_url (GTK_WIDGET (window), match, flavour,
+                                 gtk_get_current_event_time ());
 }
 
 static void
@@ -2657,6 +2667,9 @@ terminal_window_init (TerminalWindow *window)
       { "PopupCopyLinkAddress", NULL, N_("_Copy Link Address"), NULL,
         NULL,
         G_CALLBACK (popup_copy_url_callback) },
+      { "PopupNumber", NULL, "", NULL,
+        NULL,
+        NULL },
       { "PopupTerminalProfiles", NULL, N_("P_rofiles") },
       { "PopupCopy", "edit-copy", N_("Copy"), "",
         NULL,
diff --git a/src/terminal.xml b/src/terminal.xml
index d2065b3..3c30755 100644
--- a/src/terminal.xml
+++ b/src/terminal.xml
@@ -82,6 +82,7 @@
     <menuitem action="PopupCopyCallAddress" />
     <menuitem action="PopupOpenLink" />
     <menuitem action="PopupCopyLinkAddress" />
+    <menuitem action="PopupNumber" />
     <separator />
     <menuitem action="PopupNewTerminal" />
     <separator />
