summaryrefslogtreecommitdiff
path: root/src/tool_cb_hdr.c
diff options
context:
space:
mode:
authorDan Fandrich <dan@coneharvesters.com>2021-11-04 22:02:05 -0700
committerDan Fandrich <dan@telarity.com>2022-03-11 17:25:35 -0800
commit3055c4c814e5ea5dfebe41bd827456c8ade12958 (patch)
tree2a44062662853321aab75fea883afe9766439535 /src/tool_cb_hdr.c
parent68b356a1b47e201881c61bd4abfacf9a53a4534f (diff)
downloadcurl-3055c4c814e5ea5dfebe41bd827456c8ade12958.tar.gz
tool_cb_hdr: Turn the Location: into a terminal hyperlink
This turns even relative URLs into clickable hyperlinks in a supported terminal when --styled-output is enabled. Many terminals already turn URLs into clickable links but there is not enough information in a relative URL to do this automatically otherwise.
Diffstat (limited to 'src/tool_cb_hdr.c')
-rw-r--r--src/tool_cb_hdr.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c
index 67ea10447..f1a2e3f2f 100644
--- a/src/tool_cb_hdr.c
+++ b/src/tool_cb_hdr.c
@@ -47,6 +47,15 @@ static char *parse_filename(const char *ptr, size_t len);
bold-off code (21) isn't supported everywhere - like in the mac
Terminal. */
#define BOLDOFF "\x1b[0m"
+/* OSC 8 hyperlink escape sequence */
+#define LINK "\x1b]8;;"
+#define LINKST "\x1b\\"
+#define LINKOFF LINK LINKST
+#endif
+
+#ifdef LINK
+static void write_linked_location(CURL *curl, const char *location,
+ size_t loclen, FILE *stream);
#endif
/*
@@ -204,7 +213,16 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
if(value) {
size_t namelen = value - ptr;
fprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", namelen, ptr);
+#ifndef LINK
fwrite(&value[1], cb - namelen - 1, 1, outs->stream);
+#else
+ if(curl_strnequal("Location", ptr, namelen)) {
+ write_linked_location(per->curl, &value[1], cb - namelen - 1,
+ outs->stream);
+ }
+ else
+ fwrite(&value[1], cb - namelen - 1, 1, outs->stream);
+#endif
}
else
/* not "handled", just show it */
@@ -311,3 +329,85 @@ static char *parse_filename(const char *ptr, size_t len)
return copy;
}
+
+#ifdef LINK
+/*
+ * Treat the Location: header specially, by writing a special escape
+ * sequence that adds a hyperlink to the displayed text. This makes
+ * the absolute URL of the redirect clickable in supported terminals,
+ * which couldn't happen otherwise for relative URLs. The Location:
+ * header is supposed to always be absolute so this theoretically
+ * shouldn't be needed but the real world returns plenty of relative
+ * URLs here.
+ */
+static
+void write_linked_location(CURL *curl, const char *location, size_t loclen,
+ FILE *stream) {
+ /* This would so simple if CURLINFO_REDIRECT_URL were available here */
+ CURLU *u = NULL;
+ char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL;
+ const char *loc = location;
+ size_t llen = loclen;
+
+ /* Strip leading whitespace of the redirect URL */
+ while(llen && *loc == ' ') {
+ ++loc;
+ --llen;
+ }
+
+ /* Strip the trailing end-of-line characters, normally "\r\n" */
+ while(llen && (loc[llen-1] == '\n' || loc[llen-1] == '\r'))
+ --llen;
+
+ /* CURLU makes it easy to handle the relative URL case */
+ u = curl_url();
+ if(!u)
+ goto locout;
+
+ /* Create a NUL-terminated and whitespace-stripped copy of Location: */
+ copyloc = malloc(llen + 1);
+ if(!copyloc)
+ goto locout;
+ memcpy(copyloc, loc, llen);
+ copyloc[llen] = 0;
+
+ /* The original URL to use as a base for a relative redirect URL */
+ if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &locurl))
+ goto locout;
+ if(curl_url_set(u, CURLUPART_URL, locurl, 0))
+ goto locout;
+
+ /* Redirected location. This can be either absolute or relative. */
+ if(curl_url_set(u, CURLUPART_URL, copyloc, 0))
+ goto locout;
+
+ if(curl_url_get(u, CURLUPART_URL, &finalurl, CURLU_NO_DEFAULT_PORT))
+ goto locout;
+
+ if(curl_url_get(u, CURLUPART_SCHEME, &scheme, 0))
+ goto locout;
+
+ if(!strcmp("http", scheme) ||
+ !strcmp("https", scheme) ||
+ !strcmp("ftp", scheme) ||
+ !strcmp("ftps", scheme)) {
+ fprintf(stream, LINK "%s" LINKST "%.*s" LINKOFF,
+ finalurl, loclen, location);
+ goto locdone;
+ }
+
+ /* Not a "safe" URL: don't linkify it */
+
+locout:
+ /* Write the normal output in case of error or unsafe */
+ fwrite(location, loclen, 1, stream);
+
+locdone:
+ if(u) {
+ curl_free(finalurl);
+ curl_free(scheme);
+ curl_url_cleanup(u);
+ free(copyloc);
+ }
+}
+#endif