add support for OSC 8 sequence for inline links/references - webdump - HTML to plain-text converter for webpages
HTML git clone git://git.codemadness.org/webdump
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
DIR commit 682550f60fad5c354456257dc63a213d64862d67
DIR parent fe6d27fcb8b5f7b1b3d3c3343d8d22349d4260b3
HTML Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Wed, 15 Jul 2026 00:33:08 +0200
add support for OSC 8 sequence for inline links/references
Initial patch by Devank Karkihalli <devank@envs.net>:
See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
for specification. See https://github.com/Alhadis/OSC8-Adoption/ for
list of supported terminal emulators.
With some changes and man page documentation by me.
This feature can be insecure in some terminals, it is disabled by default.
Example and reasoning:
putty: https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/osc8.html
The "passive form" described on the page could be by using the webdump -x
feature.
Diffstat:
M webdump.1 | 8 ++++++--
M webdump.c | 49 +++++++++++++++++++++++++++++--
2 files changed, 52 insertions(+), 5 deletions(-)
---
DIR diff --git a/webdump.1 b/webdump.1
@@ -1,4 +1,4 @@
-.Dd July 2, 2026
+.Dd July 15, 2026
.Dt WEBDUMP 1
.Os
.Sh NAME
@@ -6,7 +6,7 @@
.Nd convert HTML to plain-text
.Sh SYNOPSIS
.Nm
-.Op Fl 8adiIlrx
+.Op Fl 8adiIlorx
.Op Fl b Ar baseurl
.Op Fl s Ar selector
.Op Fl u Ar selector
@@ -45,6 +45,10 @@ not enabled.
.It Fl l
Toggle if link references are displayed at the bottom or not, by default it is
not enabled.
+.It Fl o
+Toggle OSC 8 escape sequence to mark inline hyperlinks, by default it is not
+enabled.
+This feature can be insecure in some terminals.
.It Fl r
Toggle if line-wrapping mode is enabled, by default it is not enabled.
This uses the terminal width which can be specified with the
DIR diff --git a/webdump.c b/webdump.c
@@ -50,6 +50,7 @@ static int uniqrefs = 0; /* (-d) number unique references */
static int showrefinline = 0; /* (-i) show link reference number inline */
static int showurlinline = 0; /* (-I) show full link reference inline */
static int showrefbottom = 0; /* (-l) show link references at the bottom */
+static int allowosc8 = 0; /* (-o) allow OSC 8 hyperlinks in output */
static int allowlinewrap = 0; /* (-r) line-wrapping */
static int termwidth = 77; /* (-w) terminal width */
static int xresources = 0; /* (-x) write resources line-by-line to fd 3 */
@@ -1585,7 +1586,7 @@ handleinlinelink(void)
char buf[4096], *url;
int r;
- if (!showrefbottom && !showrefinline && !showurlinline && !xresources)
+ if (!showrefbottom && !showrefinline && !showurlinline && !xresources && !allowosc8)
return; /* there is no need to collect the reference */
if (!attr_href.len && !attr_src.len && !attr_data.len)
@@ -1654,6 +1655,7 @@ printlinkrefs(void)
for (i = 0; i < nvisrefs; i++) {
ref = visrefs[i];
+
printf(" %zu. ", ref->linknr);
printuri(ref->url);
printf(" (%s)\n", ref->type);
@@ -1664,6 +1666,7 @@ printlinkrefs(void)
/* hidden links don't have a link number, just count them */
for (i = 0; i < nhiddenrefs; i++) {
ref = hiddenrefs[i];
+
printf(" %zu. ", ref->linknr);
printuri(ref->url);
printf(" (%s)\n", ref->type);
@@ -1822,6 +1825,34 @@ findtag(const char *t)
return bsearch(&find, tags, LEN(tags), sizeof(*tags), findtagcmp);
}
+/* start OSC 8 (hyperlink) sequence */
+static void
+handleosc8(void)
+{
+ struct node *cur;
+
+ if (!allowosc8 || nodes_links[curnode].len == 0)
+ return;
+
+ /* do not show the link if the element is hidden */
+ cur = &nodes[curnode];
+ if (reader_ignore || (cur->tag.displaytype & DisplayNone))
+ return;
+
+ if (!nbytesline) {
+ if (curmarkup)
+ emitmarkup(0);
+ rindent();
+ /* emit code again per line, needed for GNU/less -R */
+ if (curmarkup)
+ emitmarkup(curmarkup);
+ }
+ hflush();
+ fputs("\033]8;;", stdout);
+ printuri(nodes_links[curnode].data);
+ fputs("\033\\", stdout);
+}
+
static void
handleendtag(struct tag *tag)
{
@@ -1832,6 +1863,12 @@ handleendtag(struct tag *tag)
if (reader_ignore)
return;
+ /* end OSC 8 (hyperlink sequence) */
+ if (allowosc8 && nodes_links[curnode].len > 0) {
+ hflush();
+ fputs("\033]8;;\033\\", stdout);
+ }
+
if (tag->displaytype & DisplayQuote) {
hputchar('"');
hflush();
@@ -2220,8 +2257,11 @@ xmltagstartparsed(XMLParser *p, const char *t, size_t tl, int isshort)
/* indent for this tag */
cur->indent = cur->tag.indent;
- /* add link reference, print links and alt text */
+ /* add link reference */
handleinlinelink();
+ /* handle OSC8 start sequence */
+ handleosc8();
+ /* print inline links and alt text */
handleinlinealt();
/* <select><option> */
@@ -2524,7 +2564,7 @@ xmlattrstart(XMLParser *p, const char *t, size_t tl, const char *n,
static void
usage(void)
{
- fprintf(stderr, "%s [-8adiIlrx] [-b basehref] [-s selector] [-u selector] [-w termwidth]\n", argv0);
+ fprintf(stderr, "%s [-8adiIlorx] [-b basehref] [-s selector] [-u selector] [-w termwidth]\n", argv0);
exit(1);
}
@@ -2563,6 +2603,9 @@ main(int argc, char **argv)
case 'l':
showrefbottom = !showrefbottom;
break;
+ case 'o':
+ allowosc8 = !allowosc8;
+ break;
case 'r':
allowlinewrap = !allowlinewrap;
break;