diff --git a/include/client.h b/include/client.h
index f711eea91c3842ac29a884b38e0d7a0ab99cb7fb..40729073bcaa2c0a172d3375662ac91cef0ea550 100644
--- a/include/client.h
+++ b/include/client.h
@@ -68,4 +68,8 @@
// Returns a user-friendly string describing an error.
const char *gemini_strerr(enum gemini_result r, struct gemini_response *resp);
+// Returns the given URL with the input response set to the specified value.
+// The caller must free the string.
+char *gemini_input_url(const char *url, const char *input);
#endif
diff --git a/src/client.c b/src/client.c
index 67671ccca2a25a460681bdffacb981c7f26c1407..c252c9d9ab1783dd462343ce346cc74611e9ae85 100644
--- a/src/client.c
+++ b/src/client.c
@@ -176,7 +176,7 @@ }
char *endptr;
resp->status = (int)strtol(buf, &endptr, 10);
res = GEMINI_ERR_PROTOCOL;
goto cleanup;
}
@@ -195,14 +195,25 @@ {
if (!resp) {
return;
}
if (resp->fd != -1) {
close(resp->fd);
resp->fd = -1;
}
BIO_free(BIO_pop(resp->bio)); // ssl bio
BIO_free(resp->bio); // buffered bio
resp->bio = NULL;
SSL_free(resp->ssl);
SSL_CTX_free(resp->ssl_ctx);
free(resp->meta);
}
const char *
@@ -230,3 +241,26 @@ return "Protocol error";
}
assert(0);
}
+char *
+gemini_input_url(const char *url, const char *input)
+{
return NULL;
goto cleanup;
goto cleanup;
new_url = NULL;
goto cleanup;
+cleanup:
+}
diff --git a/src/gmnic.c b/src/gmnic.c
index 014211dc3784ba3eadcf1885ef7bcf6fe84d8462..794b94ba2235757864acb81194b8de88220cc6da 100644
--- a/src/gmnic.c
+++ b/src/gmnic.c
@@ -26,10 +26,17 @@ OMIT_HEADERS,
SHOW_HEADERS,
ONLY_HEADERS,
};
INPUT_READ,
INPUT_SUPPRESS,
int c;
switch (c) {
case '4':
assert(0); // TODO
@@ -41,7 +48,21 @@ case 'C':
assert(0); // TODO: Client certificates
break;
case 'd':
assert(0); // TODO: Input
input_mode = INPUT_READ;
input_source = fmemopen(optarg, strlen(optarg), "r");
break;
case 'D':
input_mode = INPUT_READ;
if (strcmp(optarg, "-") == 0) {
input_source = stdin;
} else {
input_source = fopen(optarg, "r");
if (!input_source) {
fprintf(stderr, "Error: open %s: %s",
optarg, strerror(errno));
return 1;
}
}
break;
case 'h':
usage(argv[0]);
@@ -50,10 +71,14 @@ case 'L':
assert(0); // TODO: Follow redirects
break;
case 'i':
headers = SHOW_HEADERS;
header_mode = SHOW_HEADERS;
break;
case 'I':
headers = ONLY_HEADERS;
header_mode = ONLY_HEADERS;
input_mode = INPUT_SUPPRESS;
break;
case 'N':
input_mode = INPUT_SUPPRESS;
break;
default:
fprintf(stderr, "fatal: unknown flag %c", c);
@@ -69,43 +94,105 @@
SSL_load_error_strings();
ERR_load_crypto_strings();
fprintf(stderr, "Error: %s\n", gemini_strerr(r, &resp));
gemini_response_finish(&resp);
return (int)r;
struct gemini_response resp;
enum gemini_result r = gemini_request(url, NULL, &resp);
if (r != GEMINI_OK) {
fprintf(stderr, "Error: %s\n", gemini_strerr(r, &resp));
ret = (int)r;
exit = true;
goto next;
}
char *new_url, *input = NULL;
switch (resp.status / 10) {
case 1: // INPUT
if (input_mode == INPUT_SUPPRESS) {
exit = true;
break;
}
if (fileno(input_source) != -1 &&
isatty(fileno(input_source))) {
fprintf(stderr, "%s: ", resp.meta);
}
printf("%d %s\n", resp.status, resp.meta);
break;
printf("%d %s\n", resp.status, resp.meta);
/* fallthrough */
for (int n = 1; n > 0;) {
char buf[BUFSIZ];
n = BIO_read(resp.bio, buf, BUFSIZ);
size_t s = 0;
ssize_t n = getline(&input, &s, input_source);
if (n == -1) {
fprintf(stderr, "Error: read\n");
return 1;
fprintf(stderr, "Error reading input: %s\n",
feof(input_source) ? "EOF" :
strerror(ferror(input_source)));
r = 1;
exit = true;
break;
}
input[n - 1] = '\0'; // Drop LF
new_url = gemini_input_url(url, input);
free(url);
url = new_url;
goto next;
case 3: // REDIRECT
assert(0); // TODO
case 6: // CLIENT CERTIFICATE REQUIRED
assert(0); // TODO
case 4: // TEMPORARY FAILURE
case 5: // PERMANENT FAILURE
if (header_mode == OMIT_HEADERS) {
fprintf(stderr, "%s: %d %s\n",
resp.status / 10 == 4 ?
"TEMPORARY FAILURE" : "PERMANENT FALIURE",
resp.status, resp.meta);
}
ssize_t w = 0;
while (w < (ssize_t)n) {
ssize_t x = write(STDOUT_FILENO, &buf[w], n - w);
if (x == -1) {
fprintf(stderr, "Error: write: %s\n",
strerror(errno));
exit = true;
break;
case 2: // SUCCESS
exit = true;
break;
}
switch (header_mode) {
case ONLY_HEADERS:
printf("%d %s\n", resp.status, resp.meta);
break;
case SHOW_HEADERS:
printf("%d %s\n", resp.status, resp.meta);
/* fallthrough */
case OMIT_HEADERS:
if (resp.status / 10 != 2) {
break;
}
for (int n = 1; n > 0;) {
char buf[BUFSIZ];
n = BIO_read(resp.bio, buf, BUFSIZ);
if (n == -1) {
fprintf(stderr, "Error: read\n");
return 1;
}
w += x;
ssize_t w = 0;
while (w < (ssize_t)n) {
ssize_t x = write(STDOUT_FILENO, &buf[w], n - w);
if (x == -1) {
fprintf(stderr, "Error: write: %s\n",
strerror(errno));
return 1;
}
w += x;
}
}
break;
}
break;
+next:
gemini_response_finish(&resp);
}
}
text/gemini
This content has been proxied by September (3851b).