arcnyxx.net.git

snail.c

espurr
/* snail - markdown compiler
 * Copyright (C) 2024 ArcNyxx <me@arcnyxx.net>
 * see LICENCE file for licensing information */

#include <stdio.h>
#include <string.h>

static void enc(char *str);
static int link(char *str);
static int info(char *str);
static int code(char *str);
static int nochange(char *str);

static void
enc(char *str)
{
	for (int i = 0; str[i] != '\0'; ++i)
		switch (str[i]) {
		case '&':
			fputs("&amp;", stdout);
			break;
		case '<':
			fputs("&lt;", stdout);
			break;
		case '>':
			fputs("&gt;", stdout);
			break;
		default:
			putchar(str[i]);
		}
}

static int
link(char *str)
{
	if (str[1] != '{') {
		putchar('@');
		return 0;
	}

	char *first = strchr(str, '}');
	if (first == NULL || first[1] != '{') {
		putchar('@');
		return 0;
	}

	char *second = strchr(&first[1], '}');
	if (second == NULL) {
		putchar('@');
		return 0;
	}
	first[0] = '\0';
	second[0] = '\0';

	const char *target = "";
	if (!strstr(&str[2], "arcnyxx.net"))
		target = " target='_blank'";

	fputs("<a href='", stdout);
	enc(&str[2]);
	printf("'%s>", target);
	enc(&first[2]);
	fputs("</a>", stdout);

	return second - str;
}

static int
info(char *str)
{
	if (str[1] != '{') {
		putchar('%');
		return 0;
	}

	char *first = strchr(str, '}');
	if (first == NULL || first[1] != '{') {
		putchar('%');
		return 0;
	}

	char *second = strchr(&first[1], '}');
	if (second == NULL) {
		putchar('%');
		return 0;
	}
	first[0] = '\0';
	second[0] = '\0';

	fputs("<span class='n' title='", stdout);
	enc(&str[2]);
	fputs("'>", stdout);
	enc(&first[2]);
	fputs("</span>", stdout);

	return second - str;
}

static int
code(char *str)
{
	char *end;
	if ((end = strchr(&str[1], '`')) == NULL) {
		putchar('`');
		return 0;
	}
	end[0] = '\0';

	size_t ret = end - str;

	char class[11] = "";
	if (str[1] == '\\') {
		if (str[2] == '\\') {
			++str;
		} else {
			str += 2;
			snprintf(class, sizeof(class), " class='%c'", str[0]);
		}
	}

	fprintf(stdout, "<code%s>", class);
	enc(str + 1);
	fputs("</code>", stdout);
	return ret;
}

static int
nochange(char *str)
{
	char *end;
	if ((end = strchr(&str[1], '|')) == NULL || &str[1] == end) {
		putchar('|');
		return &str[1] == end;
	}
	end[0] = '\0';
	fputs(&str[1], stdout);
	return end - str;
}

int
main(int argc, char **argv)
{
	if (argc != 2)
		return 1;
	for (int i = 0; argv[1][i] != '\0'; ++i) {
		switch (argv[1][i]) {
		case '@':
			i += link(&argv[1][i]);
			break;
		case '%':
			i += info(&argv[1][i]);
			break;
		case '`':
			i += code(&argv[1][i]);
			break;
		case '|':
			i += nochange(&argv[1][i]);
			break;
		case '&':
			fputs("&amp;", stdout);
			break;
		case '<':
			fputs("&lt;", stdout);
			break;
		case '>':
			fputs("&gt;", stdout);
			break;
		default:
			putchar(argv[1][i]);
		}
	}
}