mirror of
https://github.com/curl/curl.git
synced 2025-04-12 16:20:35 +08:00
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:
parent
68b356a1b4
commit
3055c4c814
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user