From cbbff786b4c347b6ef7c6ae6c18cf9ea09dfc40a Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 4 Nov 2021 22:02:05 -0700 Subject: 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. --- src/tool_cb_hdr.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index 67ea10447..a93d5f2ec 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -47,6 +47,10 @@ 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 /* @@ -204,7 +208,89 @@ 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)) { + /* 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. + */ + + /* This would so simple if CURLINFO_REDIRECT_URL were available here */ + char *location = &value[1]; + size_t llen = cb - namelen - 1; + CURLU *u = NULL; + char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL; + + /* Strip leading whitespace of the redirect URL */ + while(llen && *location == ' ') { + ++location; + --llen; + } + + /* Strip the trailing end-of-line characters, normally "\r\n" */ + while(llen && (location[llen-1] == '\n' || location[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, location, llen); + copyloc[llen] = 0; + + /* The original URL to use as a base for a relative redirect URL */ + if(curl_easy_getinfo(per->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(outs->stream, LINK "%s" LINKST "%.*s" LINKOFF, + finalurl, cb - namelen - 1, &value[1]); + goto locdone; + } + + /* Not a "safe" URL: don't linkify it */ + +locout: + /* Write the normal output in case of error or unsafe */ + fwrite(&value[1], cb - namelen - 1, 1, outs->stream); + +locdone: + if(u) { + curl_url_cleanup(u); + free(copyloc); + free(scheme); + free(finalurl); + } + } + else + fwrite(&value[1], cb - namelen - 1, 1, outs->stream); +#endif } else /* not "handled", just show it */ -- cgit v1.2.1