diff options
author | Dan Fandrich <dan@coneharvesters.com> | 2021-11-04 22:02:05 -0700 |
---|---|---|
committer | Dan Fandrich <dan@telarity.com> | 2022-03-11 17:25:35 -0800 |
commit | 3055c4c814e5ea5dfebe41bd827456c8ade12958 (patch) | |
tree | 2a44062662853321aab75fea883afe9766439535 /src/tool_cb_hdr.c | |
parent | 68b356a1b47e201881c61bd4abfacf9a53a4534f (diff) | |
download | curl-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.c | 100 |
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 |