webdump

HTML to plain-text converter for webpages
git clone git://git.codemadness.org/webdump
Log | Files | Refs | README | LICENSE

commit 682550f60fad5c354456257dc63a213d64862d67
parent fe6d27fcb8b5f7b1b3d3c3343d8d22349d4260b3
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:
Mwebdump.1 | 8++++++--
Mwebdump.c | 49++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 52 insertions(+), 5 deletions(-)

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 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;