summary refs log tree commit diff
path: root/bin
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-09-05 13:36:58 -0400
committerJune McEnroe <june@causal.agency>2019-09-05 13:36:58 -0400
commitaadee09d292ced6d04ace5b22e5214ad70f5365a (patch)
tree5650dd725876155c2e774f2b812b3217a73505c1 /bin
parentUse CURL_PREFIX to set flags (diff)
downloadsrc-aadee09d292ced6d04ace5b22e5214ad70f5365a.tar.gz
src-aadee09d292ced6d04ace5b22e5214ad70f5365a.zip
Print title as soon as it's available
Diffstat (limited to 'bin')
-rw-r--r--bin/title.c94
1 files changed, 48 insertions, 46 deletions
diff --git a/bin/title.c b/bin/title.c
index ed9eeccc..37d1d8f5 100644
--- a/bin/title.c
+++ b/bin/title.c
@@ -16,6 +16,7 @@
 
 #include <curl/curl.h>
 #include <err.h>
+#include <locale.h>
 #include <regex.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -23,16 +24,22 @@
 #include <string.h>
 #include <strings.h>
 #include <sysexits.h>
+#include <wchar.h>
 
 static regex_t regex(const char *pattern) {
 	regex_t regex;
 	int error = regcomp(&regex, pattern, REG_EXTENDED);
 	if (!error) return regex;
+
 	char buf[256];
 	regerror(error, &regex, buf, sizeof(buf));
 	errx(EX_SOFTWARE, "regcomp: %s: %s", buf, pattern);
 }
 
+static void showTitle(const char *title) {
+	printf("%s\n", title);
+}
+
 static CURL *curl;
 static bool html;
 static struct {
@@ -40,66 +47,62 @@ static struct {
 	size_t len;
 } body;
 
+static const char ContentType[] = "Content-Type: text/html";
+
 static size_t handleHeader(char *buf, size_t size, size_t nitems, void *user) {
 	(void)user;
 	size_t len = size * nitems;
-	const char ContentType[] = "Content-Type: text/html";
 	if (sizeof(ContentType) - 1 < len) len = sizeof(ContentType) - 1;
 	if (!strncasecmp(buf, ContentType, len)) html = true;
 	return size * nitems;
 }
 
+// HE COMES
+static const char TitlePattern[] = "<title>([^<]*)</title>";
+static regex_t TitleRegex;
+
 static size_t handleBody(char *buf, size_t size, size_t nitems, void *user) {
 	(void)user;
+
 	size_t len = size * nitems;
-	size_t cap = sizeof(body.buf) - body.len;
-	size_t cpy = (len < cap ? len : cap);
-	memcpy(&body.buf[body.len], buf, cpy);
-	body.len += cpy;
-	return len;
-}
+	size_t cap = sizeof(body.buf) - body.len - 1;
+	size_t new = (len < cap ? len : cap);
+	if (!new) return 0;
 
-static const char *TitlePattern = "<title>([^<]*)</title>";
-static regex_t TitleRegex;
+	memcpy(&body.buf[body.len], buf, new);
+	body.len += new;
+	body.buf[body.len] = '\0';
+
+	regmatch_t match[2];
+	if (regexec(&TitleRegex, body.buf, 2, match, 0)) return len;
+	body.buf[match[1].rm_eo] = '\0';
+	showTitle(&body.buf[match[1].rm_so]);
+	return 0;
+}
 
-static bool getTitle(const char *url) {
+static CURLcode fetchTitle(const char *url) {
 	CURLcode code = curl_easy_setopt(curl, CURLOPT_URL, url);
-	if (code) {
-		warnx("CURLOPT_URL: %s", curl_easy_strerror(code));
-		return false;
-	}
+	if (code) return code;
 
 	html = false;
 	curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
-	if ((code = curl_easy_perform(curl))) {
-		warnx("curl_easy_perform: %s", curl_easy_strerror(code));
-		return false;
-	}
-	if (!html) return false;
+	code = curl_easy_perform(curl);
+	if (code) return code;
+	if (!html) return CURLE_OK;
 
 	body.len = 0;
 	curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
-	if ((code = curl_easy_perform(curl))) {
-		warnx("curl_easy_perform: %s", curl_easy_strerror(code));
-		return false;
-	}
-	body.buf[body.len - 1] = '\0';
-
-	regmatch_t match[2];
-	int error = regexec(&TitleRegex, body.buf, 2, match, 0);
-	if (error == REG_NOMATCH) return false;
-	if (error) errx(EX_SOFTWARE, "regexec: %d", error);
-
-	body.buf[match[1].rm_eo] = '\0';
-	char *title = &body.buf[match[1].rm_so];
-
-	printf("%s\n", title);
-	return true;
+	code = curl_easy_perform(curl);
+	if (code == CURLE_WRITE_ERROR) return CURLE_OK;
+	return code;
 }
 
 int main(int argc, char *argv[]) {
 	TitleRegex = regex(TitlePattern);
 
+	setlocale(LC_CTYPE, "");
+	setlinebuf(stdout);
+
 	CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
 	if (code) errx(EX_OSERR, "curl_global_init: %s", curl_easy_strerror(code));
 
@@ -108,31 +111,30 @@ int main(int argc, char *argv[]) {
 
 	curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
+	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3L);
 
 	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handleHeader);
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handleBody);
 
-	setlinebuf(stdout);
-
 	if (argc > 1) {
-		return (getTitle(argv[1]) ? EX_OK : EX_DATAERR);
+		code = fetchTitle(argv[1]);
+		if (!code) return EX_OK;
+		errx(EX_DATAERR, "curl_easy_perform: %s", curl_easy_strerror(code));
 	}
 
-	regex_t urlRegex = regex("https?://[^[:space:]>\"]+");
-
 	char *buf = NULL;
 	size_t cap = 0;
+
+	regex_t urlRegex = regex("https?://[^[:space:]>\"]+");
 	while (0 < getline(&buf, &cap, stdin)) {
 		regmatch_t match = {0};
 		for (char *url = buf; *url; url += match.rm_eo) {
-			int error = regexec(&urlRegex, url, 1, &match, 0);
-			if (error == REG_NOMATCH) break;
-			if (error) errx(EX_SOFTWARE, "regexec: %d", error);
-
+			if (regexec(&urlRegex, url, 1, &match, 0)) break;
 			url[match.rm_eo] = '\0';
-			getTitle(&url[match.rm_so]);
+			code = fetchTitle(&url[match.rm_so]);
+			if (code) warnx("curl_easy_perform: %s", curl_easy_strerror(code));
 			url[match.rm_eo] = ' ';
 		}
 	}
+	if (ferror(stdin)) err(EX_IOERR, "getline");
 }