diff -uNr putty-0.68/Recipe putty-url-0.68/Recipe
--- putty-0.68/Recipe	2017-02-18 18:10:15.000000000 +0100
+++ putty-url-0.68/Recipe	2017-02-21 21:10:27.000000000 +0100
@@ -28,6 +28,7 @@
 !srcdir charset/
 !srcdir windows/
 !srcdir unix/
+!srcdir windows/re_lib/
 
 # Help text added to the top of each Makefile, with /D converted
 # into -D as appropriate for the particular Makefile.
@@ -289,8 +290,8 @@
 # keywords [G] for Windows GUI app, [C] for Console app, [X] for
 # X/GTK Unix app, [U] for command-line Unix app.
 
-putty    : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS
-puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS
+putty    : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res urlhack regexp LIBS
+puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss urlhack regexp LIBS
 plink    : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC
          + winx11 plink.res winnojmp noterm LIBS
 pscp     : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
diff -uNr putty-0.68/putty.h putty-url-0.68/putty.h
--- putty-0.68/putty.h	2017-02-18 18:10:15.000000000 +0100
+++ putty-url-0.68/putty.h	2017-02-21 21:08:26.000000000 +0100
@@ -139,6 +139,22 @@
 #define ATTR_DEFBG   (258 << ATTR_BGSHIFT)
 #define ATTR_DEFAULT (ATTR_DEFFG | ATTR_DEFBG)
 
+/*
+ * HACK: PuttyTray / Nutty
+ * Hyperlink stuff: define
+ */
+#define CHAR_MASK    0x000000FFUL
+
+/*
+ * HACK: PuttyTray / Nutty
+ * Hyperlink stuff: Underline settings
+ */
+enum {
+	URLHACK_UNDERLINE_ALWAYS,
+	URLHACK_UNDERLINE_HOVER,
+	URLHACK_UNDERLINE_NEVER
+};
+
 struct sesslist {
     int nsessions;
     const char **sessions;
@@ -911,6 +927,15 @@
     X(INT, NONE, shadowboldoffset) \
     X(INT, NONE, crhaslf) \
     X(STR, NONE, winclass) \
+    /*                                                                \
+     * HACK: PuTTY-url settings                                       \
+     */ \
+    X(INT, NONE, url_ctrl_click) \
+    X(INT, NONE, url_underline) \
+    X(INT, NONE, url_defbrowser) \
+    X(INT, NONE, url_defregex) \
+    X(FILENAME, NONE, url_browser) \
+    X(STR, NONE, url_regex)
 
 /* Now define the actual enum of option keywords using that macro. */
 #define CONF_ENUM_DEF(valtype, keytype, keyword) CONF_ ## keyword,
@@ -1540,7 +1565,6 @@
                                    IS_LOW_SURROGATE(ls))
 #endif
 
-
 #define IS_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) &&   \
                            ((wch) <= LOW_SURROGATE_END))
 #define HIGH_SURROGATE_OF(codept) \
diff -uNr putty-0.68/settings.c putty-url-0.68/settings.c
--- putty-0.68/settings.c	2017-02-18 18:10:15.000000000 +0100
+++ putty-url-0.68/settings.c	2017-02-21 21:08:26.000000000 +0100
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include "putty.h"
 #include "storage.h"
+#include "urlhack.h"
 
 /* The cipher order given here is the default order. */
 static const struct keyvalwhere ciphernames[] = {
@@ -686,6 +687,14 @@
     write_setting_i(sesskey, "ConnectionSharingUpstream", conf_get_int(conf, CONF_ssh_connection_sharing_upstream));
     write_setting_i(sesskey, "ConnectionSharingDownstream", conf_get_int(conf, CONF_ssh_connection_sharing_downstream));
     wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, FALSE);
+
+    /* PuTTY-url */
+    write_setting_i(sesskey, "HyperlinkUnderline", conf_get_int(conf, CONF_url_underline));
+    write_setting_i(sesskey, "HyperlinkUseCtrlClick", conf_get_int(conf, CONF_url_ctrl_click));
+    write_setting_i(sesskey, "HyperlinkBrowserUseDefault", conf_get_int(conf, CONF_url_defbrowser));
+    write_setting_filename(sesskey, "HyperlinkBrowser", conf_get_filename(conf, CONF_url_browser));
+    write_setting_i(sesskey, "HyperlinkRegularExpressionUseDefault", conf_get_int(conf, CONF_url_defregex));
+    write_setting_s(sesskey, "HyperlinkRegularExpression", conf_get_str(conf, CONF_url_regex));
 }
 
 void load_settings(const char *section, Conf *conf)
@@ -705,6 +714,14 @@
     int i;
     char *prot;
 
+    /*
+     * HACK: PuTTY-url
+     * Set font quality to cleartype on Windows Vista and above
+     */
+    OSVERSIONINFO versioninfo;
+    versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    GetVersionEx(&versioninfo);
+
     conf_set_int(conf, CONF_ssh_subsys, 0);   /* FIXME: load this properly */
     conf_set_str(conf, CONF_remote_cmd, "");
     conf_set_str(conf, CONF_remote_cmd2, "");
@@ -940,7 +957,7 @@
 		 / 1000
 #endif
 		 );
-    gppi(sesskey, "ScrollbackLines", 2000, conf, CONF_savelines);
+    gppi(sesskey, "ScrollbackLines", 10000, conf, CONF_savelines); // HACK: PuTTY-url
     gppi(sesskey, "DECOriginMode", 0, conf, CONF_dec_om);
     gppi(sesskey, "AutoWrapMode", 1, conf, CONF_wrap_mode);
     gppi(sesskey, "LFImpliesCR", 0, conf, CONF_lfhascr);
@@ -952,7 +969,17 @@
     gppi(sesskey, "TermWidth", 80, conf, CONF_width);
     gppi(sesskey, "TermHeight", 24, conf, CONF_height);
     gppfont(sesskey, "Font", conf, CONF_font);
-    gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality);
+
+    /*
+     * HACK: PuTTY-url
+     * Set font quality to cleartype on Windows Vista and higher
+     */
+    if (versioninfo.dwMajorVersion >= 6) {
+        gppi(sesskey, "FontQuality", FQ_CLEARTYPE, conf, CONF_font_quality);
+    } else {
+        gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality);
+    }
+
     gppi(sesskey, "FontVTMode", VT_UNICODE, conf, CONF_vtmode);
     gppi(sesskey, "UseSystemColours", 0, conf, CONF_system_colour);
     gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette);
@@ -1076,6 +1103,14 @@
     gppi(sesskey, "ConnectionSharingUpstream", 1, conf, CONF_ssh_connection_sharing_upstream);
     gppi(sesskey, "ConnectionSharingDownstream", 1, conf, CONF_ssh_connection_sharing_downstream);
     gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys);
+
+    /* PuTTY-url */
+    gppi(sesskey, "HyperlinkUnderline", 1, conf, CONF_url_underline);
+    gppi(sesskey, "HyperlinkUseCtrlClick", 0, conf, CONF_url_ctrl_click);
+    gppi(sesskey, "HyperlinkBrowserUseDefault", 1, conf, CONF_url_defbrowser);
+    gppfile(sesskey, "HyperlinkBrowser", conf, CONF_url_browser);
+    gppi(sesskey, "HyperlinkRegularExpressionUseDefault", 1, conf, CONF_url_defregex);
+    gpps(sesskey, "HyperlinkRegularExpression", urlhack_default_regex, conf, CONF_url_regex);
 }
 
 void do_defaults(const char *session, Conf *conf)
diff -uNr putty-0.68/terminal.c putty-url-0.68/terminal.c
--- putty-0.68/terminal.c	2017-02-18 18:10:15.000000000 +0100
+++ putty-url-0.68/terminal.c	2017-02-21 21:17:51.000000000 +0100
@@ -12,6 +12,11 @@
 #include "putty.h"
 #include "terminal.h"
 
+/*
+ * HACK: PuttyTray / Nutty
+ */ 
+#include "urlhack.h"
+
 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
@@ -1642,6 +1647,11 @@
     term->bidi_cache_size = 0;
     term->pre_bidi_cache = term->post_bidi_cache = NULL;
 
+	/*
+	 * HACK: PuttyTray / Nutty
+	 */
+	term->url_update = TRUE;
+
     /* FULL-TERMCHAR */
     term->basic_erase_char.chr = CSET_ASCII | ' ';
     term->basic_erase_char.attr = ATTR_DEFAULT;
@@ -4933,6 +4943,50 @@
 #endif /* OPTIMISE_SCROLL */
     termchar *newline;
 
+    /*
+     * HACK: PuttyTray / Nutty
+     * Hyperlink stuff: Find visible hyperlinks
+     *
+     * TODO: We should find out somehow that the stuff on screen has changed since last
+     *       paint. How to do it?
+     */
+    int urlhack_underline_always = conf_get_int(term->conf, CONF_url_underline) == URLHACK_UNDERLINE_ALWAYS;
+
+    int urlhack_underline =
+        conf_get_int(term->conf, CONF_url_underline) == URLHACK_UNDERLINE_ALWAYS ||
+        (conf_get_int(term->conf, CONF_url_underline) == URLHACK_UNDERLINE_HOVER && (!conf_get_int(term->conf, CONF_url_ctrl_click) || urlhack_is_ctrl_pressed())) ? 1 : 0;
+
+    int urlhack_is_link = 0, urlhack_hover_current = 0;
+    int urlhack_toggle_x = term->cols, urlhack_toggle_y = term->rows;
+    int urlhack_region_index = 0;
+    text_region urlhack_region;
+
+    if (term->url_update) {
+        urlhack_reset();
+
+        for (i = 0; i < term->rows; i++) {
+            termline *lp = lineptr(term->disptop + i);
+
+            for (j = 0; j < term->cols; j++) {
+                unsigned long termchr = lp->chars[j].chr;
+                urlhack_putchar(termchr & CHAR_MASK ? (char)(termchr & CHAR_MASK) : ' ');
+            }
+
+            unlineptr(lp);
+        }
+
+        urlhack_go_find_me_some_hyperlinks(term->cols);
+    }
+    urlhack_region = urlhack_get_link_region(urlhack_region_index);
+    urlhack_toggle_x = urlhack_region.x0;
+    urlhack_toggle_y = urlhack_region.y0;
+
+    if (urlhack_underline_always)
+        urlhack_hover_current = 1;
+    else
+        urlhack_hover_current = urlhack_is_in_this_link_region(urlhack_region, urlhack_mouse_old_x, urlhack_mouse_old_y);
+    /* HACK: PuttyTray / Nutty : END */
+
     chlen = 1024;
     ch = snewn(chlen, wchar_t);
 
@@ -5086,6 +5140,47 @@
 	    if (j < term->cols-1 && d[1].chr == UCSWIDE)
 		tattr |= ATTR_WIDE;
 
+		/*
+		 * HACK: PuttyTray / Nutty
+		 * Hyperlink stuff: Underline link regions if user has configured us so
+		 */
+		if (urlhack_underline) {
+			if (j == urlhack_toggle_x && i == urlhack_toggle_y) {
+				urlhack_is_link = urlhack_is_link == 1 ? 0 : 1;
+
+				// Find next bound for the toggle
+				
+				if (urlhack_is_link == 1) {
+					urlhack_toggle_x = urlhack_region.x1;
+					urlhack_toggle_y = urlhack_region.y1;
+
+					if (urlhack_toggle_x == term->cols - 1) {
+						// Handle special case where link ends at the last char of the row
+						urlhack_toggle_y++;
+						urlhack_toggle_x = 0;
+					}
+				}
+				else {
+					urlhack_region = urlhack_get_link_region(++urlhack_region_index);
+
+					if (urlhack_underline_always)
+						urlhack_hover_current = 1;
+					else
+						urlhack_hover_current = urlhack_is_in_this_link_region(urlhack_region, urlhack_mouse_old_x, urlhack_mouse_old_y);
+
+					urlhack_toggle_x = urlhack_region.x0;
+					urlhack_toggle_y = urlhack_region.y0;
+				}
+			}
+
+			if (urlhack_is_link == 1 && urlhack_hover_current == 1) {	
+				tattr |= ATTR_UNDER;
+			}
+
+			term->url_update = 0;
+		}
+		/* HACK: PuttyTray / Nutty : END */
+
 	    /* Video reversing things */
 	    if (term->selstate == DRAGGING || term->selstate == SELECTED) {
 		if (term->seltype == LEXICOGRAPHIC)
@@ -5393,6 +5488,11 @@
     int shift;
 #endif /* OPTIMISE_SCROLL */
 
+	/*
+	 * HACK: PuttyTray / Nutty
+	 */
+	term->url_update = TRUE;
+
     term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where;
     if (term->disptop < sbtop)
 	term->disptop = sbtop;
@@ -5998,7 +6098,7 @@
     }
 
     selpoint.x = x;
-    unlineptr(ldata);
+    //unlineptr(ldata); // HACK: REMOVED FOR HYPERLINK STUFF
 
     /*
      * If we're in the middle of a selection operation, we ignore raw
@@ -6049,8 +6149,10 @@
                     return;
             } else switch (a) {
 	      case MA_DRAG:
-		if (term->xterm_mouse == 1)
-		    return;
+			if (term->xterm_mouse == 1) {// HACK: ADDED FOR hyperlink stuff
+				unlineptr(ldata); 
+				return;
+			}
 		encstate += 0x20;
 		break;
 	      case MA_RELEASE:
@@ -6060,8 +6162,10 @@
 		term->mouse_is_down = 0;
 		break;
 	      case MA_CLICK:
-		if (term->mouse_is_down == braw)
-		    return;
+			  if (term->mouse_is_down == braw) {// HACK: ADDED FOR hyperlink stuff
+				  unlineptr(ldata); 
+				  return;
+			  }
 		term->mouse_is_down = braw;
 		break;
               default:
@@ -6085,6 +6189,7 @@
             if (len > 0)
                 ldisc_send(term->ldisc, abuf, len, 0);
 	}
+	unlineptr(ldata); // HACK: ADDED FOR hyperlink stuff
 	return;
     }
 
@@ -6107,6 +6212,60 @@
 	term->seltype = default_seltype;
 	term->selanchor = selpoint;
 	term->selmode = SM_CHAR;
+
+	/*
+	 * HACK: PuttyTray / Nutty
+	 * Hyperlink stuff: Check whether the click coordinates are inside link
+	 * region, if so -> copy url to temporary buffer and launch it. Delete
+	 * the temporary buffer.
+	 */
+	} else if (bcooked == MBT_SELECT && a == MA_RELEASE && term->selstate == ABOUT_TO) {
+	deselect(term);
+	term->selstate = NO_SELECTION;
+
+	if ((!conf_get_int(term->conf, CONF_url_ctrl_click) || (conf_get_int(term->conf, CONF_url_ctrl_click) && urlhack_is_ctrl_pressed())) && urlhack_is_in_link_region(x, y)) {
+		int i;
+		char *linkbuf = NULL;
+		text_region region = urlhack_get_link_bounds(x, y);
+
+		if (region.y0 == region.y1) {
+			linkbuf = snewn(region.x1 - region.x0 + 2, char);
+			
+			for (i = region.x0; i < region.x1; i++) {
+				linkbuf[i - region.x0] = (char)(ldata->chars[i].chr);
+			}
+
+			linkbuf[i - region.x0] = '\0';
+		}
+		else {
+			termline *urldata = lineptr(region.y0 + term->disptop);
+			int linklen, pos = region.x0, row = region.y0 + term->disptop;
+
+			linklen = (term->cols - region.x0) +
+				((region.y1 - region.y0 - 1) * term->cols) + region.x1 + 1;
+
+			linkbuf = snewn(linklen, char);
+
+			for (i = region.x0; i < linklen + region.x0; i++) {
+				linkbuf[i - region.x0] = (char)(urldata->chars[i % term->cols].chr);
+				
+				// Jump to next line?
+				if (((i + 1) % term->cols) == 0) {
+					row++;
+					urldata = lineptr(row);
+				}
+			}
+
+			linkbuf[linklen - 1] = '\0';
+			unlineptr(urldata);
+		}
+		
+		urlhack_launch_url(!conf_get_int(term->conf, CONF_url_defbrowser) ? conf_get_filename(term->conf, CONF_url_browser)->path : NULL, linkbuf);
+		
+		sfree(linkbuf);
+	}
+	/* HACK: PuttyTray / Nutty : END */
+
     } else if (bcooked == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
 	deselect(term);
 	term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
@@ -6130,8 +6289,10 @@
              */
             return;
         }
-	if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))
-	    return;
+	if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint)) {
+		unlineptr(ldata);
+		return;
+	}
 	if (bcooked == MBT_EXTEND && a != MA_DRAG &&
 	    term->selstate == SELECTED) {
 	    if (term->seltype == LEXICOGRAPHIC) {
@@ -6314,6 +6475,11 @@
 	if (term->selstate != DRAGGING)
 	    term_out(term);
 	term->in_term_out = FALSE;
+
+	/*
+	 * HACK: PuttyTray / Nutty
+	 */
+	term->url_update = TRUE;
     }
 
     /*
diff -uNr putty-0.68/terminal.h putty-url-0.68/terminal.h
--- putty-0.68/terminal.h	2017-02-18 18:10:15.000000000 +0100
+++ putty-url-0.68/terminal.h	2017-02-21 21:08:26.000000000 +0100
@@ -323,6 +323,11 @@
     int scroll_on_disp;
     int scroll_on_key;
     int xterm_256_colour;
+
+    /*
+     * HACK: PuTTY-url
+     */
+    int url_update;
 };
 
 #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)
diff -uNr putty-0.68/version.h putty-url-0.68/version.h
--- putty-0.68/version.h	2017-02-18 18:10:16.000000000 +0100
+++ putty-url-0.68/version.h	2017-02-21 21:19:17.000000000 +0100
@@ -1,6 +1,6 @@
 /* Generated by automated build script */
 #define RELEASE 0.68
-#define TEXTVER "Release 0.68"
-#define SSHVER "PuTTY-Release-0.68"
+#define TEXTVER "Release 0.68 (url)"
+#define SSHVER "PuTTY-Release-url-0.68"
 #define BINARY_VERSION 0,68,0,0
 #define SOURCE_COMMIT "23fbc4f56b04ca5d387c16720caa05ddf2d63e2f"
diff -uNr putty-0.68/windows/re_lib/regexp.c putty-url-0.68/windows/re_lib/regexp.c
--- putty-0.68/windows/re_lib/regexp.c	1970-01-01 01:00:00.000000000 +0100
+++ putty-url-0.68/windows/re_lib/regexp.c	2017-02-21 21:08:39.000000000 +0100
@@ -0,0 +1,1212 @@
+/*
+ * regcomp and regexec -- regsub and regerror are elsewhere
+ * @(#)regexp.c	1.3 of 18 April 87
+ *
+ *	Copyright (c) 1986 by University of Toronto.
+ *	Written by Henry Spencer.  Not derived from licensed software.
+ *
+ *	Permission is granted to anyone to use this software for any
+ *	purpose on any computer system, and to redistribute it freely,
+ *	subject to the following restrictions:
+ *
+ *	1. The author is not responsible for the consequences of use of
+ *		this software, no matter how awful, even if they arise
+ *		from defects in it.
+ *
+ *	2. The origin of this software must not be misrepresented, either
+ *		by explicit claim or by omission.
+ *
+ *	3. Altered versions must be plainly marked as such, and must not
+ *		be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions.  Serious changes in
+ * regular-expression syntax might require a total rethink.
+ */
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "regexp.h"
+#include "regmagic.h"
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases.  They are:
+ *
+ * regstart	char that must begin a match; '\0' if none obvious
+ * reganch	is the match anchored (at beginning-of-line only)?
+ * regmust	string (pointer into program) that match must include, or NULL
+ * regmlen	length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot.  Regmust permits fast rejection
+ * of lines that cannot possibly match.  The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup).  Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is computing
+ * it anyway.
+ */
+
+/*
+ * Structure for regexp "program".  This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology).  Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives.  (Here we
+ * have one of the subtle syntax dependencies:  an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.)  The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM.  In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure:  the tail of the branch connects
+ * to the thing following the set of BRANCHes.)  The opcodes are:
+ */
+
+/* definition	number	opnd?	meaning */
+#define	END	0	/* no	End of program. */
+#define	BOL	1	/* no	Match "" at beginning of line. */
+#define	EOL	2	/* no	Match "" at end of line. */
+#define	ANY	3	/* no	Match any one character. */
+#define	ANYOF	4	/* str	Match any character in this string. */
+#define	ANYBUT	5	/* str	Match any character not in this string. */
+#define	BRANCH	6	/* node	Match this alternative, or the next... */
+#define	BACK	7	/* no	Match "", "next" ptr points backward. */
+#define	EXACTLY	8	/* str	Match this string. */
+#define	NOTHING	9	/* no	Match empty string. */
+#define	STAR	10	/* node	Match this (simple) thing 0 or more times. */
+#define	PLUS	11	/* node	Match this (simple) thing 1 or more times. */
+#define	OPEN	20	/* no	Mark this point in input as start of #n. */
+			/*	OPEN+1 is number 1, etc. */
+#define	CLOSE	OPEN + NSUBEXP	/* no	Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH	The set of branches constituting a single choice are hooked
+ *		together with their "next" pointers, since precedence prevents
+ *		anything being concatenated to any individual branch.  The
+ *		"next" pointer of the last BRANCH in a choice points to the
+ *		thing following the whole choice.  This is also where the
+ *		final "next" pointer of each individual branch points; each
+ *		branch starts with the operand node of a BRANCH node.
+ *
+ * BACK		Normal "next" pointers all implicitly point forward; BACK
+ *		exists to make loop structures possible.
+ *
+ * STAR,PLUS	'?', and complex '*' and '+', are implemented as circular
+ *		BRANCH structures using BACK.  Simple cases (one character
+ *		per match) are implemented with STAR and PLUS for speed
+ *		and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE	...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first.  The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node.  (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define	OP(p)	(*(p))
+#define	NEXT(p)	(((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define	OPERAND(p)	((p) + 3)
+
+/*
+ * See regmagic.h for one further detail of program structure.
+ */
+
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define	UCHARAT(p)	((int)*(unsigned char *)(p))
+#else
+#define	UCHARAT(p)	((int)*(p)&CHARBITS)
+#endif
+
+#define	FAIL(m)	{ regerror(m); return(NULL); }
+#define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?')
+#define	META	"^$.[()|?+*\\"
+
+/*
+ * Flags to be passed up and down.
+ */
+#define	HASWIDTH	01	/* Known never to match null string. */
+#define	SIMPLE		02	/* Simple enough to be STAR/PLUS operand. */
+#define	SPSTART		04	/* Starts with * or +. */
+#define	WORST		0	/* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse;		/* Input-scan pointer. */
+static int regnpar;		/* () count. */
+static char regdummy;
+static char *regcode;		/* Code-emit pointer; &regdummy = don't. */
+static long regsize;		/* Code size. */
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define	STATIC	static
+#endif
+STATIC char* reg( int paren, int* flagp);
+STATIC char* regbranch( int* flagp);
+STATIC char* regpiece( int* flagp);
+STATIC char* regatom( int* flagp);
+STATIC char* regnode( char op);
+STATIC char* regnext( char* p);
+STATIC void regc( char c);
+STATIC void reginsert( char op, char* opnd);
+STATIC void regtail( char* p, char* val);
+STATIC void regoptail( char* p,  char* val);
+#ifdef STRCSPN
+STATIC int strcspn();
+#endif
+
+static void (*regerror_func)( char* s) = 0;
+
+void regerror( char* s)
+{
+	if( regerror_func)
+		(*regerror_func)( s);
+}
+
+void set_regerror_func( void (*func)( char*))
+{
+	regerror_func = func;
+}
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code.  So we cheat:  we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it.  (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *
+regcomp( char* exp)
+{
+	register regexp *r;
+	register char *scan;
+	register char *longest;
+	register int len;
+	int flags;
+
+	if (exp == NULL)
+		FAIL("NULL argument");
+
+	/* First pass: determine size, legality. */
+	regparse = exp;
+	regnpar = 1;
+	regsize = 0L;
+	regcode = &regdummy;
+	regc(MAGIC);
+	if (reg(0, &flags) == NULL)
+		return(NULL);
+
+	/* Small enough for pointer-storage convention? */
+	if (regsize >= 32767L)		/* Probably could be 65535L. */
+		FAIL("regexp too big");
+
+	/* Allocate space. */
+	r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
+	if (r == NULL)
+		FAIL("out of space");
+
+	/* Second pass: emit code. */
+	regparse = exp;
+	regnpar = 1;
+	regcode = r->program;
+	regc(MAGIC);
+	if (reg(0, &flags) == NULL)
+		return(NULL);
+
+	/* Dig out information for optimizations. */
+	r->regstart = '\0';	/* Worst-case defaults. */
+	r->reganch = 0;
+	r->regmust = NULL;
+	r->regmlen = 0;
+	scan = r->program+1;			/* First BRANCH. */
+	if (OP(regnext(scan)) == END) {		/* Only one top-level choice. */
+		scan = OPERAND(scan);
+
+		/* Starting-point info. */
+		if (OP(scan) == EXACTLY)
+			r->regstart = *OPERAND(scan);
+		else if (OP(scan) == BOL)
+			r->reganch++;
+
+		/*
+		 * If there's something expensive in the r.e., find the
+		 * longest literal string that must appear and make it the
+		 * regmust.  Resolve ties in favor of later strings, since
+		 * the regstart check works with the beginning of the r.e.
+		 * and avoiding duplication strengthens checking.  Not a
+		 * strong reason, but sufficient in the absence of others.
+		 */
+		if (flags&SPSTART) {
+			longest = NULL;
+			len = 0;
+			for (; scan != NULL; scan = regnext(scan))
+				if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+					longest = OPERAND(scan);
+					len = strlen(OPERAND(scan));
+				}
+			r->regmust = longest;
+			r->regmlen = len;
+		}
+	}
+
+	return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg( int paren, int* flagp)	/* paren - Parenthesized? */
+{
+	register char *ret;
+	register char *br;
+	register char *ender;
+	register int parno;
+	int flags;
+
+	*flagp = HASWIDTH;	/* Tentatively. */
+
+	/* Make an OPEN node, if parenthesized. */
+	if (paren) {
+		if (regnpar >= NSUBEXP)
+			FAIL("too many ()");
+		parno = regnpar;
+		regnpar++;
+		ret = regnode(OPEN+parno);
+	} else
+		ret = NULL;
+
+	/* Pick up the branches, linking them together. */
+	br = regbranch(&flags);
+	if (br == NULL)
+		return(NULL);
+	if (ret != NULL)
+		regtail(ret, br);	/* OPEN -> first. */
+	else
+		ret = br;
+	if (!(flags&HASWIDTH))
+		*flagp &= ~HASWIDTH;
+	*flagp |= flags&SPSTART;
+	while (*regparse == '|') {
+		regparse++;
+		br = regbranch(&flags);
+		if (br == NULL)
+			return(NULL);
+		regtail(ret, br);	/* BRANCH -> BRANCH. */
+		if (!(flags&HASWIDTH))
+			*flagp &= ~HASWIDTH;
+		*flagp |= flags&SPSTART;
+	}
+
+	/* Make a closing node, and hook it on the end. */
+	ender = regnode((paren) ? CLOSE+parno : END);	
+	regtail(ret, ender);
+
+	/* Hook the tails of the branches to the closing node. */
+	for (br = ret; br != NULL; br = regnext(br))
+		regoptail(br, ender);
+
+	/* Check for proper termination. */
+	if (paren && *regparse++ != ')') {
+		FAIL("unmatched ()");
+	} else if (!paren && *regparse != '\0') {
+		if (*regparse == ')') {
+			FAIL("unmatched ()");
+		} else
+			FAIL("junk on end");	/* "Can't happen". */
+		/* NOTREACHED */
+	}
+
+	return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch( int* flagp)
+{
+	register char *ret;
+	register char *chain;
+	register char *latest;
+	int flags;
+
+	*flagp = WORST;		/* Tentatively. */
+
+	ret = regnode(BRANCH);
+	chain = NULL;
+	while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+		latest = regpiece(&flags);
+		if (latest == NULL)
+			return(NULL);
+		*flagp |= flags&HASWIDTH;
+		if (chain == NULL)	/* First piece. */
+			*flagp |= flags&SPSTART;
+		else
+			regtail(chain, latest);
+		chain = latest;
+	}
+	if (chain == NULL)	/* Loop ran zero times. */
+		(void) regnode(NOTHING);
+
+	return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized:  they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece( int* flagp)
+{
+	register char *ret;
+	register char op;
+	register char *next;
+	int flags;
+
+	ret = regatom(&flags);
+	if (ret == NULL)
+		return(NULL);
+
+	op = *regparse;
+	if (!ISMULT(op)) {
+		*flagp = flags;
+		return(ret);
+	}
+
+	if (!(flags&HASWIDTH) && op != '?')
+		FAIL("*+ operand could be empty");
+	*flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+	if (op == '*' && (flags&SIMPLE))
+		reginsert(STAR, ret);
+	else if (op == '*') {
+		/* Emit x* as (x&|), where & means "self". */
+		reginsert(BRANCH, ret);			/* Either x */
+		regoptail(ret, regnode(BACK));		/* and loop */
+		regoptail(ret, ret);			/* back */
+		regtail(ret, regnode(BRANCH));		/* or */
+		regtail(ret, regnode(NOTHING));		/* null. */
+	} else if (op == '+' && (flags&SIMPLE))
+		reginsert(PLUS, ret);
+	else if (op == '+') {
+		/* Emit x+ as x(&|), where & means "self". */
+		next = regnode(BRANCH);			/* Either */
+		regtail(ret, next);
+		regtail(regnode(BACK), ret);		/* loop back */
+		regtail(next, regnode(BRANCH));		/* or */
+		regtail(ret, regnode(NOTHING));		/* null. */
+	} else if (op == '?') {
+		/* Emit x? as (x|) */
+		reginsert(BRANCH, ret);			/* Either x */
+		regtail(ret, regnode(BRANCH));		/* or */
+		next = regnode(NOTHING);		/* null. */
+		regtail(ret, next);
+		regoptail(ret, next);
+	}
+	regparse++;
+	if (ISMULT(*regparse))
+		FAIL("nested *?+");
+
+	return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization:  gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run.  Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *
+regatom( int* flagp)
+{
+	register char *ret;
+	int flags;
+
+	*flagp = WORST;		/* Tentatively. */
+
+	switch (*regparse++) {
+	case '^':
+		ret = regnode(BOL);
+		break;
+	case '$':
+		ret = regnode(EOL);
+		break;
+	case '.':
+		ret = regnode(ANY);
+		*flagp |= HASWIDTH|SIMPLE;
+		break;
+	case '[': {
+			int rclass;
+			int classend;
+
+			if (*regparse == '^') {	/* Complement of range. */
+				ret = regnode(ANYBUT);
+				regparse++;
+			} else
+				ret = regnode(ANYOF);
+			if (*regparse == ']' || *regparse == '-')
+				regc(*regparse++);
+			while (*regparse != '\0' && *regparse != ']') {
+				if (*regparse == '-') {
+					regparse++;
+					if (*regparse == ']' || *regparse == '\0')
+						regc('-');
+					else {
+						rclass = UCHARAT(regparse-2)+1;
+						classend = UCHARAT(regparse);
+						if (rclass > classend+1)
+							FAIL("invalid [] range");
+						for (; rclass <= classend; rclass++)
+							regc(rclass);
+						regparse++;
+					}
+				} else
+					regc(*regparse++);
+			}
+			regc('\0');
+			if (*regparse != ']')
+				FAIL("unmatched []");
+			regparse++;
+			*flagp |= HASWIDTH|SIMPLE;
+		}
+		break;
+	case '(':
+		ret = reg(1, &flags);
+		if (ret == NULL)
+			return(NULL);
+		*flagp |= flags&(HASWIDTH|SPSTART);
+		break;
+	case '\0':
+	case '|':
+	case ')':
+		FAIL("internal urp");	/* Supposed to be caught earlier. */
+		break;
+	case '?':
+	case '+':
+	case '*':
+		FAIL("?+* follows nothing");
+		break;
+	case '\\':
+		if (*regparse == '\0')
+			FAIL("trailing \\");
+		ret = regnode(EXACTLY);
+		regc(*regparse++);
+		regc('\0');
+		*flagp |= HASWIDTH|SIMPLE;
+		break;
+	default: {
+			register int len;
+			register char ender;
+
+			regparse--;
+			len = strcspn(regparse, META);
+			if (len <= 0)
+				FAIL("internal disaster");
+			ender = *(regparse+len);
+			if (len > 1 && ISMULT(ender))
+				len--;		/* Back off clear of ?+* operand. */
+			*flagp |= HASWIDTH;
+			if (len == 1)
+				*flagp |= SIMPLE;
+			ret = regnode(EXACTLY);
+			while (len > 0) {
+				regc(*regparse++);
+				len--;
+			}
+			regc('\0');
+		}
+		break;
+	}
+
+	return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char *			/* Location. */
+regnode( char op)
+{
+	register char *ret;
+	register char *ptr;
+
+	ret = regcode;
+	if (ret == &regdummy) {
+		regsize += 3;
+		return(ret);
+	}
+
+	ptr = ret;
+	*ptr++ = op;
+	*ptr++ = '\0';		/* Null "next" pointer. */
+	*ptr++ = '\0';
+	regcode = ptr;
+
+	return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc( char b)
+{
+	if (regcode != &regdummy)
+		*regcode++ = b;
+	else
+		regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert( char op, char* opnd)
+{
+	register char *src;
+	register char *dst;
+	register char *place;
+
+	if (regcode == &regdummy) {
+		regsize += 3;
+		return;
+	}
+
+	src = regcode;
+	regcode += 3;
+	dst = regcode;
+	while (src > opnd)
+		*--dst = *--src;
+
+	place = opnd;		/* Op node, where operand used to be. */
+	*place++ = op;
+	*place++ = '\0';
+	*place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail( char* p, char* val)
+{
+	register char *scan;
+	register char *temp;
+	register int offset;
+
+	if (p == &regdummy)
+		return;
+
+	/* Find last node. */
+	scan = p;
+	for (;;) {
+		temp = regnext(scan);
+		if (temp == NULL)
+			break;
+		scan = temp;
+	}
+
+	if (OP(scan) == BACK)
+		offset = scan - val;
+	else
+		offset = val - scan;
+	*(scan+1) = (offset>>8)&0377;
+	*(scan+2) = offset&0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail( char* p,  char* val)
+{
+	/* "Operandless" and "op != BRANCH" are synonymous in practice. */
+	if (p == NULL || p == &regdummy || OP(p) != BRANCH)
+		return;
+	regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput;		/* String-input pointer. */
+static char *regbol;		/* Beginning of input, for ^ check. */
+static char **regstartp;	/* Pointer to startp array. */
+static char **regendp;		/* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+STATIC int regtry( regexp* prog, char* string);
+STATIC int regmatch( char* prog);
+STATIC int regrepeat( char* p);
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump( regexp* r);
+STATIC char* regprop( char* op);
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec( regexp* prog, char* string)
+{
+	register char *s;
+
+	/* Be paranoid... */
+	if (prog == NULL || string == NULL) {
+		regerror("NULL parameter");
+		return(0);
+	}
+
+	/* Check validity of program. */
+	if (UCHARAT(prog->program) != MAGIC) {
+		regerror("corrupted program");
+		return(0);
+	}
+
+	/* If there is a "must appear" string, look for it. */
+	if (prog->regmust != NULL) {
+		s = string;
+		while ((s = strchr(s, prog->regmust[0])) != NULL) {
+			if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+				break;	/* Found it. */
+			s++;
+		}
+		if (s == NULL)	/* Not present. */
+			return(0);
+	}
+
+	/* Mark beginning of line for ^ . */
+	regbol = string;
+
+	/* Simplest case:  anchored match need be tried only once. */
+	if (prog->reganch)
+		return(regtry(prog, string));
+
+	/* Messy cases:  unanchored match. */
+	s = string;
+	if (prog->regstart != '\0')
+		/* We know what char it must start with. */
+		while ((s = strchr(s, prog->regstart)) != NULL) {
+			if (regtry(prog, s))
+				return(1);
+			s++;
+		}
+	else
+		/* We don't -- general case. */
+		do {
+			if (regtry(prog, s))
+				return(1);
+		} while (*s++ != '\0');
+
+	/* Failure. */
+	return(0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int			/* 0 failure, 1 success */
+regtry( regexp* prog, char* string)
+{
+	register int i;
+	register char **sp;
+	register char **ep;
+
+	reginput = string;
+	regstartp = prog->startp;
+	regendp = prog->endp;
+
+	sp = prog->startp;
+	ep = prog->endp;
+	for (i = NSUBEXP; i > 0; i--) {
+		*sp++ = NULL;
+		*ep++ = NULL;
+	}
+	if (regmatch(prog->program + 1)) {
+		prog->startp[0] = string;
+		prog->endp[0] = reginput;
+		return(1);
+	} else
+		return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple:  check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly.  In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int			/* 0 failure, 1 success */
+regmatch( char* prog)
+{
+	register char *scan;	/* Current node. */
+	char *next;		/* Next node. */
+
+	scan = prog;
+#ifdef DEBUG
+	if (scan != NULL && regnarrate)
+		fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+	while (scan != NULL) {
+#ifdef DEBUG
+		if (regnarrate)
+			fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+		next = regnext(scan);
+
+		switch (OP(scan)) {
+		case BOL:
+			if (reginput != regbol)
+				return(0);
+			break;
+		case EOL:
+			if (*reginput != '\0')
+				return(0);
+			break;
+		case ANY:
+			if (*reginput == '\0')
+				return(0);
+			reginput++;
+			break;
+		case EXACTLY: {
+				register int len;
+				register char *opnd;
+
+				opnd = OPERAND(scan);
+				/* Inline the first character, for speed. */
+				if (*opnd != *reginput)
+					return(0);
+				len = strlen(opnd);
+				if (len > 1 && strncmp(opnd, reginput, len) != 0)
+					return(0);
+				reginput += len;
+			}
+			break;
+		case ANYOF:
+			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
+				return(0);
+			reginput++;
+			break;
+		case ANYBUT:
+			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
+				return(0);
+			reginput++;
+			break;
+		case NOTHING:
+			break;
+		case BACK:
+			break;
+		case OPEN+1:
+		case OPEN+2:
+		case OPEN+3:
+		case OPEN+4:
+		case OPEN+5:
+		case OPEN+6:
+		case OPEN+7:
+		case OPEN+8:
+		case OPEN+9:
+		case OPEN+10:
+		case OPEN+11:
+		case OPEN+12:
+		case OPEN+13:
+		case OPEN+14:
+		case OPEN+15: {
+				register int no;
+				register char *save;
+
+				no = OP(scan) - OPEN;
+				save = reginput;
+
+				if (regmatch(next)) {
+					/*
+					 * Don't set startp if some later
+					 * invocation of the same parentheses
+					 * already has.
+					 */
+					if (regstartp[no] == NULL)
+						regstartp[no] = save;
+					return(1);
+				} else
+					return(0);
+			}
+			break;
+		case CLOSE+1:
+		case CLOSE+2:
+		case CLOSE+3:
+		case CLOSE+4:
+		case CLOSE+5:
+		case CLOSE+6:
+		case CLOSE+7:
+		case CLOSE+8:
+		case CLOSE+9:
+		case CLOSE+10:
+		case CLOSE+11:
+		case CLOSE+12:
+		case CLOSE+13:
+		case CLOSE+14:
+		case CLOSE+15: {
+				register int no;
+				register char *save;
+
+				no = OP(scan) - CLOSE;
+				save = reginput;
+
+				if (regmatch(next)) {
+					/*
+					 * Don't set endp if some later
+					 * invocation of the same parentheses
+					 * already has.
+					 */
+					if (regendp[no] == NULL)
+						regendp[no] = save;
+					return(1);
+				} else
+					return(0);
+			}
+			break;
+		case BRANCH: {
+				register char *save;
+
+				if (OP(next) != BRANCH)		/* No choice. */
+					next = OPERAND(scan);	/* Avoid recursion. */
+				else {
+					do {
+						save = reginput;
+						if (regmatch(OPERAND(scan)))
+							return(1);
+						reginput = save;
+						scan = regnext(scan);
+					} while (scan != NULL && OP(scan) == BRANCH);
+					return(0);
+					/* NOTREACHED */
+				}
+			}
+			break;
+		case STAR:
+		case PLUS: {
+				register char nextch;
+				register int no;
+				register char *save;
+				register int min;
+
+				/*
+				 * Lookahead to avoid useless match attempts
+				 * when we know what character comes next.
+				 */
+				nextch = '\0';
+				if (OP(next) == EXACTLY)
+					nextch = *OPERAND(next);
+				min = (OP(scan) == STAR) ? 0 : 1;
+				save = reginput;
+				no = regrepeat(OPERAND(scan));
+				while (no >= min) {
+					/* If it could work, try it. */
+					if (nextch == '\0' || *reginput == nextch)
+						if (regmatch(next))
+							return(1);
+					/* Couldn't or didn't -- back up. */
+					no--;
+					reginput = save + no;
+				}
+				return(0);
+			}
+			break;
+		case END:
+			return(1);	/* Success! */
+			break;
+		default:
+			regerror("memory corruption");
+			return(0);
+			break;
+		}
+
+		scan = next;
+	}
+
+	/*
+	 * We get here only if there's trouble -- normally "case END" is
+	 * the terminating point.
+	 */
+	regerror("corrupted pointers");
+	return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat( char* p)
+{
+	register int count = 0;
+	register char *scan;
+	register char *opnd;
+
+	scan = reginput;
+	opnd = OPERAND(p);
+	switch (OP(p)) {
+	case ANY:
+		count = strlen(scan);
+		scan += count;
+		break;
+	case EXACTLY:
+		while (*opnd == *scan) {
+			count++;
+			scan++;
+		}
+		break;
+	case ANYOF:
+		while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
+			count++;
+			scan++;
+		}
+		break;
+	case ANYBUT:
+		while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
+			count++;
+			scan++;
+		}
+		break;
+	default:		/* Oh dear.  Called inappropriately. */
+		regerror("internal foulup");
+		count = 0;	/* Best compromise. */
+		break;
+	}
+	reginput = scan;
+
+	return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *
+regnext( char* p)
+{
+	register int offset;
+
+	if (p == &regdummy)
+		return(NULL);
+
+	offset = NEXT(p);
+	if (offset == 0)
+		return(NULL);
+
+	if (OP(p) == BACK)
+		return(p-offset);
+	else
+		return(p+offset);
+}
+
+#ifdef DEBUG
+
+STATIC char *regprop();
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump( regexp* r)
+{
+	register char *s;
+	register char op = EXACTLY;	/* Arbitrary non-END op. */
+	register char *next;
+
+	s = r->program + 1;
+	while (op != END) {	/* While that wasn't END last time... */
+		op = OP(s);
+		printf("%2d%s", s-r->program, regprop(s));	/* Where, what. */
+		next = regnext(s);
+		if (next == NULL)		/* Next ptr. */
+			printf("(0)");
+		else 
+			printf("(%d)", (s-r->program)+(next-s));
+		s += 3;
+		if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+			/* Literal string, where present. */
+			while (*s != '\0') {
+				putchar(*s);
+				s++;
+			}
+			s++;
+		}
+		putchar('\n');
+	}
+
+	/* Header fields of interest. */
+	if (r->regstart != '\0')
+		printf("start `%c' ", r->regstart);
+	if (r->reganch)
+		printf("anchored ");
+	if (r->regmust != NULL)
+		printf("must have \"%s\"", r->regmust);
+	printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *
+regprop( char* op)
+{
+	register char *p;
+	static char buf[50];
+
+	(void) strcpy(buf, ":");
+
+	switch (OP(op)) {
+	case BOL:
+		p = "BOL";
+		break;
+	case EOL:
+		p = "EOL";
+		break;
+	case ANY:
+		p = "ANY";
+		break;
+	case ANYOF:
+		p = "ANYOF";
+		break;
+	case ANYBUT:
+		p = "ANYBUT";
+		break;
+	case BRANCH:
+		p = "BRANCH";
+		break;
+	case EXACTLY:
+		p = "EXACTLY";
+		break;
+	case NOTHING:
+		p = "NOTHING";
+		break;
+	case BACK:
+		p = "BACK";
+		break;
+	case END:
+		p = "END";
+		break;
+	case OPEN+1:
+	case OPEN+2:
+	case OPEN+3:
+	case OPEN+4:
+	case OPEN+5:
+	case OPEN+6:
+	case OPEN+7:
+	case OPEN+8:
+	case OPEN+9:
+		sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+		p = NULL;
+		break;
+	case CLOSE+1:
+	case CLOSE+2:
+	case CLOSE+3:
+	case CLOSE+4:
+	case CLOSE+5:
+	case CLOSE+6:
+	case CLOSE+7:
+	case CLOSE+8:
+	case CLOSE+9:
+		sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+		p = NULL;
+		break;
+	case STAR:
+		p = "STAR";
+		break;
+	case PLUS:
+		p = "PLUS";
+		break;
+	default:
+		regerror("corrupted opcode");
+		break;
+	}
+	if (p != NULL)
+		(void) strcat(buf, p);
+	return(buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries.  They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+static int
+strcspn( char* s1, char* s2)
+{
+	register char *scan1;
+	register char *scan2;
+	register int count;
+
+	count = 0;
+	for (scan1 = s1; *scan1 != '\0'; scan1++) {
+		for (scan2 = s2; *scan2 != '\0';)	/* ++ moved down. */
+			if (*scan1 == *scan2++)
+				return(count);
+		count++;
+	}
+	return(count);
+}
+#endif
diff -uNr putty-0.68/windows/re_lib/regexp.h putty-url-0.68/windows/re_lib/regexp.h
--- putty-0.68/windows/re_lib/regexp.h	1970-01-01 01:00:00.000000000 +0100
+++ putty-url-0.68/windows/re_lib/regexp.h	2017-02-21 21:08:39.000000000 +0100
@@ -0,0 +1,22 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP  16
+typedef struct regexp {
+	char *startp[NSUBEXP];
+	char *endp[NSUBEXP];
+	char regstart;		/* Internal use only. */
+	char reganch;		/* Internal use only. */
+	char *regmust;		/* Internal use only. */
+	int regmlen;		/* Internal use only. */
+	char program[1];	/* Unwarranted chumminess with compiler. */
+} regexp;
+
+regexp *regcomp( char* re);
+int regexec( regexp* r, char* str);
+void regsub( regexp* r, char* str, char* substr);
+void regerror( char* s);	/* for internal use only */
+void set_regerror_func( void (*func)( char*));
diff -uNr putty-0.68/windows/re_lib/regmagic.h putty-url-0.68/windows/re_lib/regmagic.h
--- putty-0.68/windows/re_lib/regmagic.h	1970-01-01 01:00:00.000000000 +0100
+++ putty-url-0.68/windows/re_lib/regmagic.h	2017-02-21 21:08:39.000000000 +0100
@@ -0,0 +1,5 @@
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define	MAGIC	0234
diff -uNr putty-0.68/windows/urlhack.c putty-url-0.68/windows/urlhack.c
--- putty-0.68/windows/urlhack.c	1970-01-01 01:00:00.000000000 +0100
+++ putty-url-0.68/windows/urlhack.c	2017-02-21 21:08:39.000000000 +0100
@@ -0,0 +1,236 @@
+/*
+ * HACK: PuttyTray / Nutty
+ * Hyperlink stuff: CORE FILE! Don't forget to COPY IT TO THE NEXT VERSION
+ */
+#include <windows.h>
+#include <string.h>
+#include "urlhack.h"
+#include "misc.h"
+#include "puttymem.h"
+
+
+extern int urlhack_mouse_old_x = -1, urlhack_mouse_old_y = -1, urlhack_current_region = -1;
+
+static text_region **link_regions;
+static unsigned int link_regions_len;
+static unsigned int link_regions_current_pos;
+
+/*
+ * ((https?|ftp)://|www\.).(([^ ]*\([^ ]*\))([^ ()]*[^ ,;.:"')>])?|([^ ()]*[^ ,;.:"')>]))
+ */
+extern const char* urlhack_default_regex = "((https?|ftp)://|www\\.).(([^ ]*\\([^ ]*\\))([^ ()]*[^ ,;.:\"')>])?|([^ ()]*[^ ,;.:\"')>]))";
+
+int urlhack_is_in_link_region(int x, int y)
+{
+    unsigned int i = 0;
+
+    while (i != link_regions_current_pos) {
+        text_region r = *link_regions[i];
+
+        if ((r.y0 == r.y1 && y == r.y0 && y == r.y1 && x >= r.x0 && x < r.x1) ||
+            (r.y0 != r.y1 && ((y == r.y0 && x >= r.x0) || (y == r.y1 && x < r.x1) || (y > r.y0 && y < r.y1))))
+            return i+1;
+        i++;
+    }
+    
+    return 0;
+}
+
+int urlhack_is_in_this_link_region(text_region r, int x, int y)
+{
+    if ((r.y0 == r.y1 && y == r.y0 && y == r.y1 && x >= r.x0 && x < r.x1) || 
+        (r.y0 != r.y1 && ((y == r.y0 && x >= r.x0) || (y == r.y1 && x < r.x1) || (y > r.y0 && y < r.y1)))) {
+        return 1;
+    }
+    
+    return 0;
+}
+
+text_region urlhack_get_link_bounds(int x, int y)
+{
+    unsigned int i = 0;
+    text_region region;
+
+    while (i != link_regions_current_pos) {
+        text_region r = *link_regions[i];
+
+        if ((r.y0 == r.y1 && y == r.y0 && y == r.y1 && x >= r.x0 && x < r.x1) ||
+            (r.y0 != r.y1 && ((y == r.y0 && x >= r.x0) || (y == r.y1 && x < r.x1) || (y > r.y0 && y < r.y1)))) {
+            return *link_regions[i];
+        }
+
+        i++;
+    }
+
+    region.x0 = region.y0 = region.x1 = region.y1 = -1;
+    return region;
+}
+
+text_region urlhack_get_link_region(int index)
+{
+    text_region region;
+
+    if (index < 0 || index >= link_regions_current_pos) {
+        region.x0 = region.y0 = region.x1 = region.y1 = -1;
+        return region;
+    }
+    else {
+        return *link_regions[index];
+    }
+}
+
+void urlhack_add_link_region(int x0, int y0, int x1, int y1)
+{
+    if (link_regions_current_pos >= link_regions_len) {
+        unsigned int i;
+        link_regions_len *= 2;
+        link_regions = sresize(link_regions, link_regions_len, text_region*);
+        for (i = link_regions_current_pos; i < link_regions_len; ++i) {
+            link_regions[i] = NULL;
+        }
+    }
+
+    link_regions[link_regions_current_pos] = snew(text_region);
+    link_regions[link_regions_current_pos]->x0 = x0;
+    link_regions[link_regions_current_pos]->y0 = y0;
+    link_regions[link_regions_current_pos]->x1 = x1;
+    link_regions[link_regions_current_pos]->y1 = y1;
+
+    link_regions_current_pos++;
+}
+
+void urlhack_launch_url(const char* app, const char *url)
+{
+    if (app) {
+        ShellExecute(NULL, NULL, app, url, NULL, SW_SHOWNORMAL);
+    } else {
+        ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
+    }
+}
+
+int urlhack_is_ctrl_pressed()
+{
+    return HIWORD(GetAsyncKeyState(VK_CONTROL));
+}
+
+void urlhack_link_regions_clear()
+{
+    unsigned int i;
+    for (i = 0; i < link_regions_len; ++i) {
+        if (link_regions[i] != NULL) {
+            sfree(link_regions[i]);
+            link_regions[i] = NULL;
+        }
+    }
+    link_regions_current_pos = 0;
+}
+
+// Regular expression stuff
+
+static int urlhack_disabled = 0;
+static int is_regexp_compiled = 0;
+static regexp* urlhack_rx;
+
+static char *window_text;
+static int window_text_len;
+static int window_text_current_pos;
+
+void urlhack_init()
+{
+    unsigned int i;
+
+    /* 32 links seems like a sane base value */
+    link_regions_current_pos = 0;
+    link_regions_len = 32;
+    link_regions = snewn(link_regions_len, text_region*);
+
+    for (i = 0; i < link_regions_len; ++i) {
+        link_regions[i] = NULL;
+    }
+
+    /* Start with default terminal size */
+    window_text_len = 80*24+1;
+    window_text = snewn(window_text_len, char);
+    urlhack_reset();
+}
+
+void urlhack_cleanup()
+{
+    urlhack_link_regions_clear();
+    sfree(link_regions);
+    sfree(window_text);
+}
+
+void urlhack_putchar(char ch)
+{
+    if (window_text_current_pos >= window_text_len) {
+        window_text = sresize(window_text, 2 * window_text_len, char);
+        memset(window_text + window_text_current_pos, '\0', window_text_len - window_text_current_pos);
+        window_text_len *= 2;
+    }
+    window_text[window_text_current_pos++] = ch;
+}
+
+void urlhack_reset()
+{
+    memset(window_text, '\0', window_text_len);
+    window_text_current_pos = 0;
+}
+
+static void rtfm(char *error)
+{
+    char std_msg[] = "The following error occured when compiling the regular expression\n" \
+        "for the hyperlink support. Hyperlink detection is disabled during\n" \
+        "this session (restart PuTTY Tray to try again).\n\n";
+
+    char *full_msg = dupprintf("%s%s", std_msg, error);
+
+    MessageBox(0, full_msg, "PuTTY Tray Error", MB_OK);
+    free(full_msg);
+}
+
+void urlhack_set_regular_expression(const char* expression)
+{
+    is_regexp_compiled = 0;
+    urlhack_disabled = 0;
+
+    set_regerror_func(rtfm);
+    urlhack_rx = regcomp((char*)(expression));
+
+    if (urlhack_rx == 0) {
+        urlhack_disabled = 1;
+    }
+
+    is_regexp_compiled = 1;
+}
+
+void urlhack_go_find_me_some_hyperlinks(int screen_width)
+{
+    char* text_pos;
+
+    if (urlhack_disabled != 0) return;
+
+    if (is_regexp_compiled == 0) {
+        urlhack_set_regular_expression(urlhack_default_regex);
+    }
+
+    urlhack_link_regions_clear();
+
+    text_pos = window_text;
+
+    while (regexec(urlhack_rx, text_pos) == 1) {
+        char* start_pos = *urlhack_rx->startp[0] == ' ' ? urlhack_rx->startp[0] + 1: urlhack_rx->startp[0];
+
+        int x0 = (start_pos - window_text) % screen_width;
+        int y0 = (start_pos - window_text) / screen_width;
+        int x1 = (urlhack_rx->endp[0] - window_text) % screen_width;
+        int y1 = (urlhack_rx->endp[0] - window_text) / screen_width;
+
+        if (x0 >= screen_width) x0 = screen_width - 1;
+        if (x1 >= screen_width) x1 = screen_width - 1;
+
+        urlhack_add_link_region(x0, y0, x1, y1);
+
+        text_pos = urlhack_rx->endp[0] + 1;
+    }
+}
diff -uNr putty-0.68/windows/urlhack.h putty-url-0.68/windows/urlhack.h
--- putty-0.68/windows/urlhack.h	1970-01-01 01:00:00.000000000 +0100
+++ putty-url-0.68/windows/urlhack.h	2017-02-21 21:08:39.000000000 +0100
@@ -0,0 +1,31 @@
+/*
+ * HACK: PuttyTray / Nutty
+ * Hyperlink stuff: CORE FILE! Don't forget to COPY IT TO THE NEXT VERSION
+ */
+#ifndef _URLHACK_H
+#define _URLHACK_H
+
+#include "re_lib/regexp.h"
+
+typedef struct { int x0, y0, x1, y1; } text_region;
+
+const char* urlhack_default_regex;
+int urlhack_mouse_old_x, urlhack_mouse_old_y, urlhack_current_region;
+
+void urlhack_reset();
+void urlhack_go_find_me_some_hyperlinks(int screen_width);
+void urlhack_putchar(char ch);
+text_region urlhack_get_link_region(int index);
+
+int urlhack_is_in_link_region(int x, int y);
+int urlhack_is_in_this_link_region(text_region r, int x, int y);
+text_region urlhack_get_link_bounds(int x, int y);
+void urlhack_add_link_region(int x0, int y0, int x1, int y1);
+void urlhack_launch_url(const char* app, const char *url);
+int urlhack_is_ctrl_pressed();
+void urlhack_set_regular_expression(const char* expression);
+
+void urlhack_init();
+void urlhack_cleanup();
+
+#endif // _URLHACK_H
diff -uNr putty-0.68/windows/wincfg.c putty-url-0.68/windows/wincfg.c
--- putty-0.68/windows/wincfg.c	2017-02-18 18:10:16.000000000 +0100
+++ putty-url-0.68/windows/wincfg.c	2017-02-21 21:08:39.000000000 +0100
@@ -346,6 +346,51 @@
 		  conf_checkbox_handler,
 		  I(CONF_fullscreenonaltenter));
 
+	/*
+	 * HACK: PuttyTray / Nutty
+	 * Hyperlink stuff: The Window/Hyperlinks panel.
+	 */
+	ctrl_settitle(b, "Window/Hyperlinks", "Options controlling behaviour of hyperlinks");
+	s = ctrl_getset(b, "Window/Hyperlinks", "general", "General options for hyperlinks");
+
+	ctrl_radiobuttons(s, "Underline hyperlinks:", 'u', 1,
+			  HELPCTX(no_help),
+			  conf_radiobutton_handler,
+			  I(CONF_url_underline),
+			  "Always", I(URLHACK_UNDERLINE_ALWAYS),
+			  "When hovered upon", I(URLHACK_UNDERLINE_HOVER),
+			  "Never", I(URLHACK_UNDERLINE_NEVER),
+			  NULL);
+
+	ctrl_checkbox(s, "Use ctrl+click to launch hyperlinks", 'l',
+		  HELPCTX(no_help),
+		  conf_checkbox_handler, I(CONF_url_ctrl_click));
+
+	s = ctrl_getset(b, "Window/Hyperlinks", "browser", "Browser application");
+
+	ctrl_checkbox(s, "Use the default browser", 'b',
+		  HELPCTX(no_help),
+		  conf_checkbox_handler, I(CONF_url_defbrowser));
+
+	ctrl_filesel(s, "or specify an application to open hyperlinks with:", 's',
+		"Application (*.exe)\0*.exe\0All files (*.*)\0*.*\0\0", TRUE,
+		"Select executable to open hyperlinks with", HELPCTX(no_help),
+		 conf_filesel_handler, I(CONF_url_browser));
+
+	s = ctrl_getset(b, "Window/Hyperlinks", "regexp", "Regular expression");
+
+	ctrl_checkbox(s, "Use the default regular expression", 'r',
+		  HELPCTX(no_help),
+		  conf_checkbox_handler, I(CONF_url_defregex));
+
+	ctrl_editbox(s, "or specify your own:", NO_SHORTCUT, 100,
+		 HELPCTX(no_help),
+		 conf_editbox_handler, I(CONF_url_regex),
+		 I(1));
+
+	ctrl_text(s, "The single white space will be cropped in front of the link, if exists.",
+		  HELPCTX(no_help));
+
     /*
      * Windows supports a local-command proxy. This also means we
      * must adjust the text on the `Telnet command' control.
diff -uNr putty-0.68/windows/windefs.c putty-url-0.68/windows/windefs.c
--- putty-0.68/windows/windefs.c	2017-02-18 18:10:16.000000000 +0100
+++ putty-url-0.68/windows/windefs.c	2017-02-21 21:08:39.000000000 +0100
@@ -8,10 +8,21 @@
 
 FontSpec *platform_default_fontspec(const char *name)
 {
-    if (!strcmp(name, "Font"))
+    /*
+     * HACK: PuTTY-url
+     * Set font to Consolas on Windows Vista and above
+     */
+    OSVERSIONINFO versioninfo;
+    versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    GetVersionEx(&versioninfo);
+
+    if (!strcmp(name, "Font")) {
+        if (versioninfo.dwMajorVersion >= 6) {
+            return fontspec_new("Consolas", 0, 10, ANSI_CHARSET);
+        }
         return fontspec_new("Courier New", 0, 10, ANSI_CHARSET);
-    else
-        return fontspec_new("", 0, 0, 0);
+    }
+    return fontspec_new("", 0, 0, 0);
 }
 
 Filename *platform_default_filename(const char *name)
diff -uNr putty-0.68/windows/window.c putty-url-0.68/windows/window.c
--- putty-0.68/windows/window.c	2017-02-18 18:10:16.000000000 +0100
+++ putty-url-0.68/windows/window.c	2017-02-21 21:08:39.000000000 +0100
@@ -14,6 +14,11 @@
 #define NO_MULTIMON                    /* winelib doesn't have this */
 #endif
 
+/*
+ * HACK: PuttyTray / Nutty
+ */ 
+#include "urlhack.h"
+
 #ifndef NO_MULTIMON
 #define COMPILE_MULTIMON_STUBS
 #endif
@@ -228,6 +233,11 @@
 const int share_can_be_downstream = TRUE;
 const int share_can_be_upstream = TRUE;
 
+/*
+ * HACK: PuttyTray / Nutty
+ */ 
+static int urlhack_cursor_is_hand = 0;
+
 /* Dummy routine, only required in plink. */
 void frontend_echoedit_update(void *frontend, int echo, int edit)
 {
@@ -395,6 +405,8 @@
 
     conf = conf_new();
 
+    urlhack_init();
+
     /*
      * Initialize COM.
      */
@@ -850,6 +862,14 @@
 
     start_backend();
 
+	/*
+	 * HACK: PuttyTray / Nutty
+	 * Hyperlink stuff: Set the regular expression
+	 */
+	if (conf_get_int(term->conf, CONF_url_defregex) == 0) {
+		urlhack_set_regular_expression(conf_get_str(term->conf, CONF_url_regex));
+	}
+
     /*
      * Set up the initial input locale.
      */
@@ -959,6 +979,7 @@
     /*
      * Clean up.
      */
+    urlhack_cleanup();
     deinit_fonts();
     sfree(logpal);
     if (pal)
@@ -2100,6 +2121,11 @@
     static UINT last_mousemove = 0;
     int resize_action;
 
+	/*
+	 * HACK: PuttyTray / Nutty
+	 */ 
+	POINT cursor_pt;
+
     switch (message) {
       case WM_TIMER:
 	if ((UINT_PTR)wParam == TIMING_TIMER_ID) {
@@ -2306,6 +2332,16 @@
 		if (back)
 		    back->reconfig(backhandle, conf);
 
+		/*
+		 * HACK: PuttyTray / Nutty
+		 * Reconfigure
+		 */
+		if (conf_get_int(conf, CONF_url_defregex) == 0) {
+			urlhack_set_regular_expression(conf_get_str(conf, CONF_url_regex));
+		}
+		term->url_update = TRUE;
+		term_update(term);
+
 		/* Screen size changed ? */
 		if (conf_get_int(conf, CONF_height) !=
 		    conf_get_int(prev_conf, CONF_height) ||
@@ -2624,6 +2660,37 @@
 	 */
 	noise_ultralight(lParam);
 
+	/*
+	 * HACK: PuttyTray / Nutty
+	 * Hyperlink stuff: Change cursor type if hovering over link
+	 */ 
+	if (urlhack_mouse_old_x != TO_CHR_X(X_POS(lParam)) || urlhack_mouse_old_y != TO_CHR_Y(Y_POS(lParam))) {
+		urlhack_mouse_old_x = TO_CHR_X(X_POS(lParam));
+		urlhack_mouse_old_y = TO_CHR_Y(Y_POS(lParam));
+
+		if ((!conf_get_int(term->conf, CONF_url_ctrl_click) || urlhack_is_ctrl_pressed()) &&
+			urlhack_is_in_link_region(urlhack_mouse_old_x, urlhack_mouse_old_y)) {
+				if (urlhack_cursor_is_hand == 0) {
+					SetClassLongPtr(hwnd, GCLP_HCURSOR, LoadCursor(NULL, IDC_HAND));
+					urlhack_cursor_is_hand = 1;
+					term_update(term); // Force the terminal to update, otherwise the underline will not show (bug somewhere, this is an ugly fix)
+				}
+		}
+		else if (urlhack_cursor_is_hand == 1) {
+			SetClassLongPtr(hwnd, GCLP_HCURSOR, LoadCursor(NULL, IDC_IBEAM));
+			urlhack_cursor_is_hand = 0;
+			term_update(term); // Force the terminal to update, see above
+		}
+
+		// If mouse jumps from one link directly into another, we need a forced terminal update too
+		if (urlhack_is_in_link_region(urlhack_mouse_old_x, urlhack_mouse_old_y) != urlhack_current_region) {
+			urlhack_current_region = urlhack_is_in_link_region(urlhack_mouse_old_x, urlhack_mouse_old_y);
+			term_update(term);
+		}
+
+	}
+	/* HACK: PuttyTray / Nutty : END */
+
 	if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
 	    GetCapture() == hwnd) {
 	    Mouse_Button b;
@@ -3094,10 +3161,39 @@
 	    }
 	}
 	return FALSE;
-      case WM_KEYDOWN:
-      case WM_SYSKEYDOWN:
-      case WM_KEYUP:
-      case WM_SYSKEYUP:
+
+	/*
+	 * HACK: PuttyTray / Nutty
+	 * Hyperlink stuff: Change cursor if we are in ctrl+click link mode
+	 *
+	 * WARNING: Spans over multiple CASEs
+	 */
+	case WM_KEYDOWN:
+		if (wParam == VK_CONTROL && conf_get_int(term->conf, CONF_url_ctrl_click)) {
+			GetCursorPos(&cursor_pt);
+			ScreenToClient(hwnd, &cursor_pt);
+
+			if (urlhack_is_in_link_region(TO_CHR_X(cursor_pt.x), TO_CHR_Y(cursor_pt.y))) {
+				SetCursor(LoadCursor(NULL, IDC_HAND));
+				term_update(term);
+			}
+		
+			goto KEY_END;
+		}	
+
+	case WM_KEYUP:
+		if (wParam == VK_CONTROL && conf_get_int(term->conf, CONF_url_ctrl_click)) {
+			SetCursor(LoadCursor(NULL, IDC_IBEAM));
+			term_update(term);
+		
+			goto KEY_END;
+		}
+	KEY_END:
+
+	case WM_SYSKEYDOWN:
+	case WM_SYSKEYUP:
+	/* HACK: PuttyTray / Nutty : END */
+
 	/*
 	 * Add the scan code and keypress timing to the random
 	 * number noise.
diff -uNr putty-0.68/windows/winshare.c putty-url-0.68/windows/winshare.c
--- putty-0.68/windows/winshare.c	2017-02-18 18:10:16.000000000 +0100
+++ putty-url-0.68/windows/winshare.c	2017-02-21 21:08:39.000000000 +0100
@@ -33,6 +33,17 @@
 #endif
 #endif
 
+/*
+ * PuTTY-url: These are not (yet) available in wincrypt.h from mingw as of now
+ * (2015-06-23, w32api 3.17.2).
+ */
+#ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE
+#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16
+#endif
+#ifndef CRYPTPROTECTMEMORY_CROSS_PROCESS
+#define CRYPTPROTECTMEMORY_CROSS_PROCESS 1
+#endif
+
 #define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare"
 #define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex"
 
