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.
This commit is contained in:
Dan Fandrich 2021-11-04 22:02:05 -07:00 committed by Dan Fandrich
parent 68b356a1b4
commit 3055c4c814

View File

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