~callum/gmsfn

3808ce81f0092ba51d2bf1d3ec4608c2046dbd35 — Callum Brown 1 year, 3 months ago 638dcee
Tidy up
1 files changed, 94 insertions(+), 68 deletions(-)

M src/gemlogger.c
M src/gemlogger.c => src/gemlogger.c +94 -68
@@ 97,28 97,16 @@ bad_response(enum gemini_result r, struct gemini_response *resp)
}


/* Determine whether the given token is a link and has a url */
const bool
token_is_good_link(struct gemini_token tok)
{
	if (tok.token == GEMINI_LINK) {
		return (strlen(tok.link.url) != 0) && (tok.link.url != NULL);
	} else {
		return false;
	}
}


/* Return url or file path up to and including the last '/' */
/* Return string up to and including the last '/' */
char *
strip_filename(const char *url)
strip_filename(const char *path)
{
	size_t last_slash;
	for (size_t i = 0; i < strlen(url); ++i) {
		if (url[i] == '/')
	size_t last_slash = 0;
	for (size_t i = 0; i < strlen(path); ++i) {
		if (path[i] == '/')
			last_slash = ++i;
	}
	return strndup(url, last_slash);
	return strndup(path, last_slash);
}




@@ 126,15 114,15 @@ strip_filename(const char *url)
char *
get_absolute_url(char *url, char *base_url)
{
	bool not_absolute = false, colon = false, slash = false;
	bool absolute = true, colon = false, slash = false;
	size_t i = 0;
	while (i < strlen(url) && !(not_absolute)) {
	while ((i < strlen(url)) && absolute) {
		switch (url[i]) {
		case ':':
			if (i == 0 || colon) {
				/* If colon is first char then no scheme */
				/* 2 colons but no slashes means no scheme */
				not_absolute = true;
				/* If colon is first char, or if there has already been a
					colon, then no scheme */
				absolute = false;
				break;
			} else {
				/* This is the first colon */


@@ 152,19 140,20 @@ get_absolute_url(char *url, char *base_url)
				break;
			} else {
				/* This slash is before any colon, so no scheme */
				not_absolute = true;
				absolute = false;
				break;
			}
		default:
			/* If there has been a colon but this character is not a slash,
				then the url is not absolute. Break the loop. */
			if (colon)
				not_absolute = true;
				absolute = false;
		}
		++i;
	}
	/* remove leading slashes */
	/* Remove leading slashes */
	while (*url && (*url == '/')) ++url;
	/* Prepend base_url to url */
	char *abs_url = malloc(strlen(base_url) + strlen(url) + 1);
	strcpy(abs_url, base_url);
	return strcat(abs_url, url);


@@ 172,6 161,42 @@ get_absolute_url(char *url, char *base_url)


int
make_response_from_file(struct gemini_response *resp, FILE *fp) {
	BIO *file = BIO_new_fp(fp, BIO_CLOSE);
	resp->bio = BIO_new(BIO_f_buffer());
	BIO_push(resp->bio, file);
	resp->meta = strdup("text/gemini");
	resp->status = GEMINI_STATUS_SUCCESS;
	resp->fd = -1;
	resp->ssl = NULL;
	resp->ssl_ctx = NULL;
	return 0;
}


const bool
token_is_heading(struct gemini_token tok, int heading_level)
{
	if (tok.token == GEMINI_HEADING) {
		return tok.heading.level == heading_level;
	} else {
		return false;
	}
}


const bool
token_is_good_link(struct gemini_token tok)
{
	if (tok.token == GEMINI_LINK) {
		return (strlen(tok.link.url) != 0) && (tok.link.url != NULL);
	} else {
		return false;
	}
}


int
main(int argc, char *argv[])
{
	/* Must have exactly one argument */


@@ 186,6 211,7 @@ main(int argc, char *argv[])
		fprintf(stderr, "Failed to access config file %s.\n", argv[1]);
		return 1;
	}
	char *config_dir = strip_filename(argv[1]);

	/* Setup for using gmni */
	struct addrinfo hints = {0};


@@ 200,70 226,64 @@ main(int argc, char *argv[])
	gemini_tofu_init(&cfg.tofu, opts.ssl_ctx, &tofu_callback, &cfg);

	/* Make a gmni response from the config file */
	BIO *file = BIO_new_fp(config_fp, BIO_CLOSE);
	struct gemini_response config_resp;
	config_resp.bio = BIO_new(BIO_f_buffer());
	BIO_push(config_resp.bio, file);
	config_resp.meta = strdup("text/gemini");
	config_resp.status = GEMINI_STATUS_SUCCESS;
	config_resp.fd = -1;
	config_resp.ssl = NULL;
	config_resp.ssl_ctx = NULL;
	make_response_from_file(&config_resp, config_fp);

	/* Parse config file */
	FILE *out_fp = stdout;
	bool good_output_file = true;
	bool absolute = true, good_output_file = false;
	char *out_path;
	FILE *out_fp = NULL;
	struct gemini_parser config_parser;
	gemini_parser_init(&config_parser, config_resp.bio);
	struct gemini_token config_tok;
	gemini_parser_init(&config_parser, config_resp.bio);

	while (gemini_parser_next(&config_parser, &config_tok) == 0) {
		if (config_tok.token == GEMINI_HEADING) {
			if (config_tok.heading.level == 3) {
				/* Change output file */
				char *path = trim_ws(config_tok.heading.title);
				char *out_path;
				/* If path is not absolute then prepend the path to the
					directory the config file is in */
				if (path[0] != '/') {
					char *config_dir = strip_filename(argv[1]);
					out_path = malloc(strlen(config_dir) + strlen(path) + 1);
					strcpy(out_path, config_dir);
					strcat(out_path, path);
				} else {
					out_path = path;
				}
				FILE *new_out_fp = fopen(out_path, "w+");
				if (!new_out_fp) {
					printf("Bad output file: %s\n", out_path);
					free(out_path);
					/* Skip links until a new output file is given */
					good_output_file = false;
					continue;
				}
		/* Change output file */
		if (token_is_heading(config_tok, 3)) {
			/* Clear up from previous output file */
			if (out_fp)
				fclose(out_fp);
			if (!absolute)
				free(out_path);
				out_fp = new_out_fp;
				good_output_file = true;

			/* Get path to the output file. If it does not start with '/'
				assume relative to the directory the config file is in */
			char *path = trim_ws(config_tok.heading.title);
			if ((absolute = path[0] == '/')) {
				out_path = path;
			} else {
				out_path = malloc(strlen(config_dir) + strlen(path) + 1);
				strcpy(out_path, config_dir);
				strcat(out_path, path);
			}

			out_fp = fopen(out_path, "w");
			if (!out_fp) {
				printf("Bad output file: %s\n", out_path);
				/* Skip links until a new output file is given */
				good_output_file = false;
				continue;
			}
			good_output_file = true;

		/* Make requests and output links */
		} else if (good_output_file && token_is_good_link(config_tok)) {
			/* Make requests and output links */
			/* Get gemlog index page */
			struct gemini_response resp;
			enum gemini_result r = gemini_request(config_tok.link.url, &opts, &resp);
			if (bad_response(r, &resp)) {
				fprintf(stderr, "Could not access %s.\n", config_tok.link.url);
				gemini_response_finish(&resp);
				/* Go to next link in config file */
				continue;
			}

			char *base_url = strip_filename(config_tok.link.url);
			char *abs_url;

			/* Parse gemlog index page */
			char *abs_url, *base_url = strip_filename(config_tok.link.url);
			struct gemini_parser p;
			gemini_parser_init(&p, resp.bio);
			struct gemini_token tok;
			gemini_parser_init(&p, resp.bio);

			while (gemini_parser_next(&p, &tok) == 0) {
				if (token_is_good_link(tok)) {
					abs_url = get_absolute_url(tok.link.url, base_url);


@@ 278,13 298,19 @@ main(int argc, char *argv[])
					free(abs_url);
				}
			}

			gemini_token_finish(&tok);
			gemini_parser_finish(&p);
			gemini_response_finish(&resp);
		}
	}

	fclose(out_fp);
	/* Clear up from previous output file */
	if (out_fp)
		fclose(out_fp);
	if (!absolute)
		free(out_path);

	gemini_token_finish(&config_tok);
	gemini_parser_finish(&config_parser);
	gemini_response_finish(&config_resp);