~strahinja/slweb

cb512a8b8a31311d0c47f1a744fd4c9d702953f9 — Страхиња Радић 3 months ago 92a411c v0.5
Clearly separate macro definitions from macro calls; update manpage accordingly

Signed-off-by: Страхиња Радић <contact@strahinja.org>
5 files changed, 137 insertions(+), 112 deletions(-)

M defs.h
M examples/links/index.slw
M examples/macros/index.slw
M slweb.1.in
M slweb.c
M defs.h => defs.h +0 -1
@@ 68,7 68,6 @@ typedef struct {
	u8* key;
	u8* value;
	size_t value_size;
	int seen; /* for use with macros */
} KeyValue;

typedef int (*csv_callback_t)(FILE* output, u8** csv_header, u8** csv_register);

M examples/links/index.slw => examples/links/index.slw +2 -2
@@ 2,11 2,11 @@
stylesheet: style.css
site-name: Links test
---
{=icon}<svg style="width:24px;height:24px" viewBox="0 0 24 24">
{=!icon}<svg style="width:24px;height:24px" viewBox="0 0 24 24">
    <path fill="currentColor"
d="M12,17.56L16.07,16.43L16.62,10.33H9.38L9.2,8.3H16.8L17,6.31H7L7.56,12.32H14.45L14.22,14.9L12,15.5L9.78,14.9L9.64,13.24H7.64L7.93,16.43L12,17.56M4.07,3H19.93L18.5,19.2L12,21L5.5,19.2L4.07,3Z"
/>
</svg>{/=icon}
</svg>{/=!icon}

Links [Test][1].


M examples/macros/index.slw => examples/macros/index.slw +2 -2
@@ 2,8 2,8 @@
stylesheet: style.css
---

{=copyright}Copyright &copy; John Doe. <br />
All rights reserved.{/=copyright}
{=!copyright}Copyright &copy; John Doe. <br />
All rights reserved.{/=!copyright}

{header}Header: {=copyright}{/header}


M slweb.1.in => slweb.1.in +4 -3
@@ 572,19 572,20 @@ Directive \fC{include "somefile"}\fP will fork, parse
.IR basedir )
and output the resulting
.SM HTML
as if the option \fC\-\-body\-only\fP was specified. All macros and
as if the option \fC\-\-body\-only\fP was specified. All
.SM YAML
variables will be preserved.
.
.IP \[bu]
.BR Macros .
Macros can be declared using the directive \fC{=macroname}{/=macroname}\fP,
Macros can be declared using the directive \fC{=!macroname}{/=!macroname}\fP,
where
.I macroname
is the name of the macro. Anything between those two tags will then become the
body of a macro. Whenever \fC{=macroname}\fP is subsequently encountered in the
file, it will be replaced by the macro body. Macro definitions can't be nested
nor can they contain macro calls, and doing so will produce an error.
nor can they contain macro calls, and doing so will produce an error. Tags won't
be processed within the body of a macro.
.
.IP \[bu]
.BR "Previous Git commit information" .

M slweb.c => slweb.c +129 -104
@@ 211,7 211,7 @@ endswith(const char* s, const char* what)
}

u8*
get_value(KeyValue* list, const size_t list_count, const u8* key, int* seen)
get_value(KeyValue* list, const size_t list_count, const u8* key)
{
	if (!list)
		return NULL;


@@ 219,14 219,7 @@ get_value(KeyValue* list, const size_t list_count, const u8* key, int* seen)
	while (plist < list + list_count)
	{
		if (!strcmp((char*)plist->key, (char*)key))
		{
			if (seen)
			{
				*seen	    = plist->seen;
				plist->seen = 1;
			}
			return plist->value;
		}
		plist++;
	}
	return NULL;


@@ 502,7 495,7 @@ print_meta_var(FILE* output, u8** tsv_header, u8** tsv_register)
		u8* var_name		    = NULL;
		var_name		    = (u8*)strdup((char*)pvalue + 1);
		*(var_name + value_len - 2) = 0;
		u8* value = get_value(vars, vars_count, var_name, NULL);
		u8* value = get_value(vars, vars_count, var_name);

		if (value)
			print_output(output,


@@ 1121,8 1114,7 @@ read_csv(FILE* output, const char* filename, csv_callback_t callback)
	UBYTE current_header = 0;
	u8* csv_register[MAX_CSV_REGISTERS];
	UBYTE current_register = 0;
	u8* csv_delimiter
		= get_value(vars, vars_count, (u8*)"csv-delimiter", NULL);
	u8* csv_delimiter = get_value(vars, vars_count, (u8*)"csv-delimiter");

	if (!(csv = fopen(filename, "rt")))
		exit(error(ENOENT, (u8*)"csv: No such file: %s", filename));


@@ 1301,7 1293,7 @@ int
process_include(FILE* output, const u8* token,
	const int read_yaml_macros_and_links)
{
	u8* ptoken		= (u8*)strchr((char*)token, ' ');
	u8* ptoken;
	char* include_filename	= NULL;
	char* pinclude_filename = NULL;
	pid_t pid		= 0;


@@ 1309,6 1301,8 @@ process_include(FILE* output, const u8* token,
	int arg_pipe_fds[2];
	int output_pipe_fds[2];

	ptoken = (u8*)strchr((char*)token, ' ');

	if (!input_filename)
		return warning(1, (u8*)"Cannot use 'include' in stdin");



@@ 1426,6 1420,7 @@ process_include(FILE* output, const u8* token,
		result = slweb_parse(output, filename, buffer, 1, 0);

	process_include_child_cleanup:
		fflush(output);
		slweb_cleanup();
		free(filename);
		free(buffer);


@@ 1714,7 1709,7 @@ process_incdir(FILE* output, const u8* token, const u8* link_prefix,
		exit(error(1, (u8*)"incdir: Second argument required"));

	if (*arg == '=')
		macro_body = get_value(macros, macros_count, arg + 1, NULL);
		macro_body = get_value(macros, macros_count, arg + 1);
	else
	{
		u8* parg = arg;


@@ 1740,8 1735,7 @@ process_incdir(FILE* output, const u8* token, const u8* link_prefix,
					(u8*)"incdir: Third argument not "
					     "macro"));
			}
			macro_body = get_value(macros, macros_count, arg + 1,
				NULL);
			macro_body = get_value(macros, macros_count, arg + 1);
		}
	}



@@ 1838,70 1832,66 @@ process_timestamp_cleanup:
}

int
process_macro(FILE* output, const u8* token,
process_macro_def(FILE* output, const u8* token,
	const int read_yaml_macros_and_links, const int end_tag)
{
	if (!end_tag)
	if (end_tag)
	{
		if (IN(state, ST_MACRO_BODY))
			exit(error(1, (u8*)"Macro undefined or nested"));
		fflush(output);
		state &= ~ST_MACRO_BODY;
		return 0;
	}

		int seen       = 0;
		u8* macro_body = get_value(macros, macros_count, token + 1,
			read_yaml_macros_and_links ? NULL : &seen);
	if (IN(state, ST_MACRO_BODY))
		exit(error(1, (u8*)"Cannot nest definitions"));

		if (macro_body && !read_yaml_macros_and_links)
	if (read_yaml_macros_and_links)
	{
		macros_count++;

		if (!macros)
		{
			if (seen)
			{
				u8* eol = (u8*)strrchr((char*)macro_body, '\n');
				size_t macro_body_len
					= strlen((char*)macro_body);
				if (eol)
					output_firstcol = macro_body
							+ macro_body_len
							- (u8*)strrchr(
								(char*)macro_body,
								'\n')
						> 0;
				else
					output_firstcol = !*macro_body
						|| *(macro_body + macro_body_len
							   - 1)
							== '\n';
				print_output(output, "%s", macro_body);
			}
			else
				state |= ST_MACRO_BODY;
			CALLOC(macros, KeyValue, macros_count);
			pmacros = macros;
		}
		else if (!macro_body)
		else
		{
			if (read_yaml_macros_and_links)
			{
				macros_count++;

				if (!macros)
				{
					CALLOC(macros, KeyValue, macros_count);
					pmacros = macros;
				}
				else
				{
					REALLOC(macros, KeyValue,
						macros_count * sizeof(KeyValue));
					pmacros = macros + macros_count - 1;
				}
				CALLOC(pmacros->key, u8, KEYSIZE);
				pmacros->seen = 0;
				strcpy((char*)pmacros->key, (char*)token + 1);
				pmacros->value	    = NULL;
				pmacros->value_size = 0;
			}
			state |= ST_MACRO_BODY;
			REALLOC(macros, KeyValue,
				macros_count * sizeof(KeyValue));
			pmacros = macros + macros_count - 1;
		}
		CALLOC(pmacros->key, u8, KEYSIZE);
		strcpy((char*)pmacros->key, (char*)token + 2);
		pmacros->value	    = NULL;
		pmacros->value_size = 0;
	}
	fflush(output);
	state |= ST_MACRO_BODY;

	return 0;
}

int
process_macro(FILE* output, const u8* token, const int end_tag)
{
	u8* macro_body;
	u8* eol;
	size_t macro_body_len;

	macro_body = get_value(macros, macros_count, token + 1);
	if (!macro_body)
		exit(error(1, (u8*)"Macro '%s' undefined", token + 1));

	eol	       = (u8*)strrchr((char*)macro_body, '\n');
	macro_body_len = strlen((char*)macro_body);
	if (eol)
		output_firstcol = macro_body + macro_body_len
				- (u8*)strrchr((char*)macro_body, '\n')
			> 0;
	else
		state &= ~ST_MACRO_BODY;
		output_firstcol = !*macro_body
			|| *(macro_body + macro_body_len - 1) == '\n';
	print_output(output, "%s", macro_body);

	return 0;
}


@@ 1948,8 1938,11 @@ process_tag(FILE* output, const u8* token, const u8* link_prefix,
	}
	else if (*token == '=') /* {=macro} */
	{
		process_macro(output, token, read_yaml_macros_and_links,
			end_tag);
		if (*(token + 1) == '!')
			process_macro_def(output, token,
				read_yaml_macros_and_links, end_tag);
		else if ((*(token + 1) != '!') && !read_yaml_macros_and_links)
			process_macro(output, token, end_tag);
		*skip_eol = 1;
	}
	else if (!read_yaml_macros_and_links) /* general tags */


@@ 2138,7 2131,7 @@ int
process_link(FILE* output, const u8* link_text, const u8* link_prefix,
	const u8* link_macro_body, const u8* link_id)
{
	u8* url = get_value(links, links_count, link_id, NULL);
	u8* url = get_value(links, links_count, link_id);
	return process_inline_link(output, link_text, link_prefix,
		link_macro_body, url);
}


@@ 2225,7 2218,7 @@ process_image(FILE* output, const u8* image_text, const u8* image_file_prefix,
	const u8* link_prefix, const u8* image_id, const int add_link,
	const int add_figcaption)
{
	u8* url = get_value(links, links_count, image_id, NULL);
	u8* url = get_value(links, links_count, image_id);
	return process_inline_image(output, image_text, image_file_prefix,
		link_prefix, url, add_link, add_figcaption);
}


@@ 2436,14 2429,14 @@ process_formula(FILE* output, const u8* token, const int display_formula)
int
begin_html_and_head(FILE* output)
{
	u8* lang	= get_value(vars, vars_count, (u8*)"lang", NULL);
	u8* site_name	= get_value(vars, vars_count, (u8*)"site-name", NULL);
	u8* site_desc	= get_value(vars, vars_count, (u8*)"site-desc", NULL);
	u8* canonical	= get_value(vars, vars_count, (u8*)"canonical", NULL);
	u8* favicon_url = get_value(vars, vars_count, (u8*)"favicon-url", NULL);
	u8* meta	= get_value(vars, vars_count, (u8*)"meta", NULL);
	u8* feed	= get_value(vars, vars_count, (u8*)"feed", NULL);
	u8* feed_desc	= get_value(vars, vars_count, (u8*)"feed-desc", NULL);
	u8* lang	= get_value(vars, vars_count, (u8*)"lang");
	u8* site_name	= get_value(vars, vars_count, (u8*)"site-name");
	u8* site_desc	= get_value(vars, vars_count, (u8*)"site-desc");
	u8* canonical	= get_value(vars, vars_count, (u8*)"canonical");
	u8* favicon_url = get_value(vars, vars_count, (u8*)"favicon-url");
	u8* meta	= get_value(vars, vars_count, (u8*)"meta");
	u8* feed	= get_value(vars, vars_count, (u8*)"feed");
	u8* feed_desc	= get_value(vars, vars_count, (u8*)"feed-desc");

	print_output(output,
		"<!DOCTYPE html>\n"


@@ 2546,10 2539,10 @@ begin_article(FILE* output, const char* source_filename, const u8* link_prefix,

	if (date && source_filename)
	{
		char* link	    = strip_ext((const char*)source_filename);
		char* real_link	    = NULL;
		u8* permalink_macro = get_value(macros, macros_count,
			(u8*)"permalink", NULL);
		char* link	= strip_ext((const char*)source_filename);
		char* real_link = NULL;
		u8* permalink_macro
			= get_value(macros, macros_count, (u8*)"permalink");
		CALLOC(real_link, char, BUFSIZE);

		if (ext_in_permalink)


@@ 2761,29 2754,27 @@ slweb_parse(FILE* output, const char* source_filename, const u8* buffer,
	if (!buffer)
		exit(error(1, (u8*)"Empty buffer"));

	title = get_value(vars, vars_count, (u8*)"title", NULL);
	title = get_value(vars, vars_count, (u8*)"title");
	title_heading_level
		= get_value(vars, vars_count, (u8*)"title-heading-level", NULL);
	header_text   = get_value(vars, vars_count, (u8*)"header-text", NULL);
	author	      = get_value(vars, vars_count, (u8*)"author", NULL);
	date	      = get_value(vars, vars_count, (u8*)"date", NULL);
	permalink_url = get_value(vars, vars_count, (u8*)"permalink-url", NULL);
		= get_value(vars, vars_count, (u8*)"title-heading-level");
	header_text   = get_value(vars, vars_count, (u8*)"header-text");
	author	      = get_value(vars, vars_count, (u8*)"author");
	date	      = get_value(vars, vars_count, (u8*)"date");
	permalink_url = get_value(vars, vars_count, (u8*)"permalink-url");
	image_file_prefix
		= get_value(vars, vars_count, (u8*)"image-file-prefix", NULL);
	ext_in_permalink
		= get_value(vars, vars_count, (u8*)"ext-in-permalink", NULL);
		= get_value(vars, vars_count, (u8*)"image-file-prefix");
	ext_in_permalink = get_value(vars, vars_count, (u8*)"ext-in-permalink");
	var_add_article_header
		= get_value(vars, vars_count, (u8*)"add-article-header", NULL);
		= get_value(vars, vars_count, (u8*)"add-article-header");
	var_add_image_links
		= get_value(vars, vars_count, (u8*)"add-image-links", NULL);
		= get_value(vars, vars_count, (u8*)"add-image-links");
	add_image_links = !(var_add_image_links && *var_add_image_links == '0');
	var_add_figcaption
		= get_value(vars, vars_count, (u8*)"add-figcaption", NULL);
	var_add_figcaption = get_value(vars, vars_count, (u8*)"add-figcaption");
	add_figcaption = !(var_add_figcaption && *var_add_figcaption == '0');
	var_add_footnote_div
		= get_value(vars, vars_count, (u8*)"add-footnote-div", NULL);
		= get_value(vars, vars_count, (u8*)"add-footnote-div");
	add_footnote_div = var_add_footnote_div && *var_add_footnote_div == '1';
	link_prefix = get_value(vars, vars_count, (u8*)"link-prefix", NULL);
	link_prefix	 = get_value(vars, vars_count, (u8*)"link-prefix");

	CALLOC(line, u8, BUFSIZE);
	token_size = BUFSIZE;


@@ 3645,7 3636,7 @@ do_line:
			break;
		}

		if ((IN(state, ST_TAG)) && pline - 1 && *(pline - 1) == '{')
		if ((IN(state, ST_TAG)) && *(pline - 1) == '{')
		{
			end_tag = 1;
			pline++;


@@ 3666,10 3657,44 @@ do_line:
			break;
		}

		if (IN(state, ST_TAG))
		*ptoken = 0;

		if (IN(state, ST_MACRO_BODY)
			&& !(end_tag && startswith((const char*)token, "=")))
		{
			size_t token_len;
			size_t value_len;

			if (read_yaml_macros_and_links)
			{
				token_len = strlen((char*)token);

				skip_eol  = 1;
				value_len = strlen((char*)pmacros->value);

				if (pmacros->value_size
					< value_len + token_len + 1)
				{
					pmacros->value_size += token_len;
					REALLOC(pmacros->value, u8,
						pmacros->value_size);
				}
				strcat((char*)pmacros->value, "{");
				if (end_tag)
					strcat((char*)pmacros->value, "/");
				strcat(strcat((char*)pmacros->value,
					       (char*)token),
					"}");
			}

			*token = 0;
			ptoken = token;
			state &= ~ST_TAG;
			end_tag = 0;
		}
		else if (IN(state, ST_TAG))
		{
			state &= ~ST_TAG;
			*ptoken = 0;

			process_tag(output, token, link_prefix,
				read_yaml_macros_and_links, &skip_eol, end_tag);


@@ 4176,7 4201,7 @@ do_line:
					process_inline_link(output, link_text,
						link_prefix,
						get_value(macros, macros_count,
							link_macro, NULL),
							link_macro),
						token);
				else
					process_inline_image(output, link_text,


@@ 4250,7 4275,7 @@ do_line:
			if (!read_yaml_macros_and_links)
				process_link(output, link_text, link_prefix,
					get_value(macros, macros_count,
						link_macro, NULL),
						link_macro),
					token);
			RESET_TOKEN(token, ptoken, token_size);
			state &= ~(ST_LINK | ST_LINK_SECOND_ARG);