slw2gmi

Конвертор из slweb-а у Џемини
git clone https://git.sr.ht/~strahinja/slw2gmi
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

slw2gmi.c (122510B)


      1 /* This program is licensed under the terms of GNU GPL v3 or (at your option)
      2  * any later version. Copyright (C) 2021-2024  Страхиња Радић.
      3  * See the file LICENSE for exact copyright and license details. */
      4 
      5 #include "defs.h"
      6 
      7 static size_t lineno		      = 0;
      8 static size_t colno		      = 1;
      9 static int output_firstcol	      = 1;
     10 static int skip_para_end	      = 0;
     11 static const char* input_filename     = NULL;
     12 static char* input_dirname	      = NULL;
     13 static char* basedir		      = NULL;
     14 static size_t basedir_size	      = 0;
     15 static char* incdir		      = NULL;
     16 static char* global_link_prefix	      = NULL;
     17 static size_t global_link_prefix_size = 0;
     18 static KeyValue* vars		      = NULL;
     19 static KeyValue* pvars		      = NULL;
     20 static size_t vars_count	      = 0;
     21 static KeyValue* macros		      = NULL;
     22 static KeyValue* pmacros	      = NULL;
     23 static size_t macros_count	      = 0;
     24 static KeyValue* links		      = NULL;
     25 static KeyValue* plinks		      = NULL;
     26 static size_t links_count	      = 0;
     27 static KeyValue* paralinks	      = NULL;
     28 static KeyValue* pparalinks	      = NULL;
     29 static size_t paralinks_count	      = 0;
     30 static KeyValue* footnotes	      = NULL;
     31 static KeyValue* pfootnotes	      = NULL;
     32 static size_t footnote_count	      = 0;
     33 static size_t current_footnote	      = 0;
     34 static u8** inline_footnotes	      = NULL;
     35 static size_t inline_footnote_count   = 0;
     36 static size_t current_inline_footnote = 0;
     37 static u8* csv_template		      = NULL;
     38 static size_t csv_template_size	      = 0;
     39 static char* csv_filename	      = NULL;
     40 static long csv_iter		      = 0;
     41 static u8* tsv_template		      = NULL;
     42 static size_t tsv_template_size	      = 0;
     43 static char* tsv_filename	      = NULL;
     44 static long tsv_iter		      = 0;
     45 static ULLONG state		      = ST_NONE;
     46 static int incdir_only_summary	      = 0;
     47 
     48 #define COPYRIGHT                                                            \
     49 	("  This program is licensed under the terms of GNU GPL v3"          \
     50 	 " or (at your option)\n"                                            \
     51 	 "  any later version. Copyright (C) 2021-2024  Strahinya Radich.\n" \
     52 	 "  See the file LICENSE for exact copyright and license "           \
     53 	 "details.")
     54 
     55 #define CHECKEXITNOMEM(ptr, func)                                             \
     56 	do                                                                    \
     57 	{                                                                     \
     58 		if (!ptr)                                                     \
     59 			exit(error(ENOMEM,                                    \
     60 				(u8*)"" #func                                 \
     61 				     ":%d: Memory allocation failed (out of " \
     62 				     "memory?)",                              \
     63 				__LINE__));                                   \
     64 	} while (0)
     65 
     66 #define CALLOC(ptr, ptrtype, nmemb)                   \
     67 	do                                            \
     68 	{                                             \
     69 		ptr = calloc(nmemb, sizeof(ptrtype)); \
     70 		CHECKEXITNOMEM(ptr, calloc);          \
     71 	} while (0)
     72 
     73 #define REALLOC(ptr, ptrtype, newsize)                   \
     74 	do                                               \
     75 	{                                                \
     76 		ptrtype* newptr = realloc(ptr, newsize); \
     77 		CHECKEXITNOMEM(newptr, realloc);         \
     78 		ptr = newptr;                            \
     79 	} while (0)
     80 
     81 #define REALLOCARRAY(ptr, membtype, newcount) \
     82 	REALLOC(ptr, membtype, sizeof(membtype) * newcount);
     83 
     84 #define CHECKCOPY(token, ptoken, token_size, pline)     \
     85 	do                                              \
     86 	{                                               \
     87 		if (ptoken + 2 > token + token_size)    \
     88 		{                                       \
     89 			size_t old_size = token_size;   \
     90 			token_size += BUFSIZE;          \
     91 			REALLOC(token, u8, token_size); \
     92 			ptoken = token + old_size - 1;  \
     93 		}                                       \
     94 		*ptoken++ = *pline++;                   \
     95 	} while (0)
     96 
     97 #define RESET_TOKEN(token, ptoken, token_size)  \
     98 	do                                      \
     99 	{                                       \
    100 		token_size = BUFSIZE;           \
    101 		REALLOC(token, u8, token_size); \
    102 		*token = 0;                     \
    103 		ptoken = token;                 \
    104 	} while (0)
    105 
    106 int slweb_cleanup(void);
    107 int usage(void);
    108 int version(const int full);
    109 
    110 int
    111 version(const int full)
    112 {
    113 	printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
    114 	if (full)
    115 		puts(COPYRIGHT);
    116 	return 0;
    117 }
    118 
    119 int
    120 usage(void)
    121 {
    122 	printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n"
    123 	       "\t%s [-d directory | --basedir directory]"
    124 	       " [-p URL | --global-link-prefix URL] [filename]\n",
    125 		PROGRAMNAME, PROGRAMNAME);
    126 	return 0;
    127 }
    128 
    129 int
    130 error(const int code, const u8* fmt, ...)
    131 {
    132 	char buf[BUFSIZE];
    133 	va_list args;
    134 	va_start(args, fmt);
    135 	vsnprintf(buf, sizeof(buf), (const char*)fmt, args);
    136 	va_end(args);
    137 	fprintf(stderr, "%s:%s:%lu:%lu: %s\n", PROGRAMNAME,
    138 		input_filename ? input_filename : "(stdin)", lineno, colno,
    139 		buf);
    140 	return code;
    141 }
    142 
    143 int
    144 warning(const int code, const u8* fmt, ...)
    145 {
    146 	char buf[BUFSIZE];
    147 	va_list args;
    148 	va_start(args, fmt);
    149 	vsnprintf(buf, sizeof(buf), (const char*)fmt, args);
    150 	va_end(args);
    151 	fprintf(stderr, "Warning: %s\n", buf);
    152 	return code;
    153 }
    154 
    155 int
    156 free_keyvalue(KeyValue** list, const size_t list_count)
    157 {
    158 	if (!list || !*list)
    159 		return 1;
    160 
    161 	for (size_t index = 0; index < list_count; index++)
    162 	{
    163 		KeyValue* current = *list + index;
    164 		free(current->value);
    165 		free(current->key);
    166 	}
    167 	return 0;
    168 }
    169 
    170 int
    171 slweb_cleanup(void)
    172 {
    173 	free(basedir);
    174 	free(input_dirname);
    175 	while (inline_footnote_count--)
    176 		free(inline_footnotes[inline_footnote_count]);
    177 	free(inline_footnotes);
    178 	free_keyvalue(&footnotes, footnote_count);
    179 	free_keyvalue(&paralinks, paralinks_count);
    180 	free_keyvalue(&links, links_count);
    181 	free_keyvalue(&macros, macros_count);
    182 	free_keyvalue(&vars, vars_count);
    183 	free(footnotes);
    184 	free(paralinks);
    185 	free(links);
    186 	free(macros);
    187 	free(vars);
    188 
    189 	return 0;
    190 }
    191 
    192 u8* format_date(const u8* date_arg, const char* timestamp_format);
    193 int simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count,
    194 	KeyValue** pvars);
    195 int slweb_parse(FILE* output, const char* source_filename, const u8* buffer,
    196 	const int read_yaml_macros_and_links);
    197 
    198 int
    199 startswith(const char* s, const char* what)
    200 {
    201 	if (!s || !what)
    202 		return 0;
    203 
    204 	do
    205 	{
    206 		if (*s++ != *what++)
    207 			return 0;
    208 	} while (*s && *what);
    209 
    210 	return !*what;
    211 }
    212 
    213 int
    214 endswith(const char* s, const char* what)
    215 {
    216 	const char* ps	  = s;
    217 	const char* pwhat = what;
    218 
    219 	if (!s || !what)
    220 		return 0;
    221 
    222 	if (!*what)
    223 		return 1;
    224 
    225 	while (*ps)
    226 		ps++;
    227 	while (*pwhat)
    228 		pwhat++;
    229 
    230 	while (ps != s && pwhat != what)
    231 		if (*ps-- != *pwhat--)
    232 			return 0;
    233 
    234 	return *ps == *pwhat && pwhat == what;
    235 }
    236 
    237 int
    238 all_numeric(const u8* start, const u8* end)
    239 {
    240 	const u8* ps = start;
    241 	while (ps != end)
    242 	{
    243 		if (*ps && !isdigit((const int)*ps))
    244 			return 0;
    245 		ps++;
    246 	}
    247 	return 1;
    248 }
    249 
    250 u8*
    251 get_value(KeyValue* list, const size_t list_count, const u8* key,
    252 	KeyValue** cursor)
    253 {
    254 	if (!list)
    255 		return NULL;
    256 	KeyValue* plist = list;
    257 	while (plist < list + list_count)
    258 	{
    259 		if (!strcmp((char*)plist->key, (char*)key))
    260 		{
    261 			if (cursor)
    262 			{
    263 				if (plist + 1 < list + list_count)
    264 					*cursor = plist + 1;
    265 				else
    266 					*cursor = NULL;
    267 			}
    268 			return plist->value;
    269 		}
    270 		plist++;
    271 	}
    272 	if (cursor)
    273 		*cursor = NULL;
    274 	return NULL;
    275 }
    276 
    277 int
    278 set_basedir(char** basedir, size_t* basedir_size, const char* arg)
    279 {
    280 	size_t basedir_len = 0;
    281 	size_t arg_len	   = strlen(arg);
    282 
    283 	if (arg_len < 1)
    284 		exit(error(1, (u8*)"set_basedir: Argument required"));
    285 
    286 	if (arg_len + 1 > *basedir_size)
    287 	{
    288 		*basedir_size = arg_len + 1;
    289 		REALLOC(*basedir, char, *basedir_size);
    290 	}
    291 
    292 	if (strlcpy(*basedir, arg, *basedir_size) >= *basedir_size)
    293 		warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
    294 	basedir_len = strlen(*basedir);
    295 	if (*(*basedir + basedir_len - 1) == '/')
    296 		*(*basedir + basedir_len - 1) = 0;
    297 
    298 	return 0;
    299 }
    300 
    301 int
    302 set_global_link_prefix(char** global_link_prefix,
    303 	size_t* global_link_prefix_size, const char* arg)
    304 {
    305 	size_t arg_len = strlen(arg);
    306 
    307 	if (arg_len < 1)
    308 		exit(error(1, (u8*)"set_global_link_prefix: Argument required"));
    309 
    310 	if (arg_len + 1 > *global_link_prefix_size)
    311 	{
    312 		*global_link_prefix_size = arg_len + 1;
    313 		REALLOC(*global_link_prefix, char, *global_link_prefix_size);
    314 	}
    315 	if (strlcpy(*global_link_prefix, arg, *global_link_prefix_size)
    316 		>= *global_link_prefix_size)
    317 		warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
    318 	*(*global_link_prefix + arg_len) = 0;
    319 
    320 	return 0;
    321 }
    322 
    323 char*
    324 strip_ext(const char* fn)
    325 {
    326 	char* newname	= NULL;
    327 	char* pnewname	= NULL;
    328 	const char* pfn = NULL;
    329 	char* dot	= NULL;
    330 
    331 	dot = strrchr(fn, '.');
    332 
    333 	if (!dot)
    334 		return NULL;
    335 
    336 	CALLOC(newname, char, strlen(fn) + 1);
    337 	pnewname = newname;
    338 	pfn	 = fn;
    339 
    340 	while (pfn != dot && *pfn)
    341 		*pnewname++ = *pfn++;
    342 
    343 	return newname;
    344 }
    345 
    346 int
    347 print_output(FILE* output, const char* fmt, ...)
    348 {
    349 	char buf[BUFSIZE];
    350 	va_list args;
    351 	u8* var_incdir_only_summary = NULL;
    352 	// int incdir_only_summary	    = 0;
    353 
    354 	if (!output || !fmt)
    355 		exit(error(EINVAL, (u8*)"print_output: Invalid argument"));
    356 
    357 	va_start(args, fmt);
    358 
    359 	var_incdir_only_summary
    360 		= get_value(vars, vars_count, (u8*)"incdir-only-summary", NULL);
    361 	incdir_only_summary
    362 		= var_incdir_only_summary && *var_incdir_only_summary == '1';
    363 
    364 	/*if (IN(state, ST_INCDIR) && !IN(state, ST_SUMMARY)
    365 		&& incdir_only_summary)
    366 		return 0;
    367 	else*/
    368 	if (IN(state, ST_CSV_BODY))
    369 	{
    370 		size_t buf_len = 0;
    371 		vsnprintf((char*)buf, BUFSIZE, fmt, args);
    372 		buf_len = strlen(buf);
    373 		if (!csv_template)
    374 		{
    375 			csv_template_size = BUFSIZE;
    376 			CALLOC(csv_template, u8, csv_template_size);
    377 			if (strlcpy((char*)csv_template, buf, csv_template_size)
    378 				>= csv_template_size)
    379 				warning(1, (u8*)"strlcpy:%d: Overflow",
    380 					__LINE__);
    381 		}
    382 		else
    383 		{
    384 			if (strlen((char*)csv_template) + buf_len
    385 				> csv_template_size)
    386 			{
    387 				csv_template_size += BUFSIZE;
    388 				REALLOC(csv_template, u8, csv_template_size);
    389 			}
    390 			if (strlcat((char*)csv_template, buf, csv_template_size)
    391 				> csv_template_size)
    392 				warning(1, (u8*)"strlcat:%d: Overflow",
    393 					__LINE__);
    394 		}
    395 	}
    396 	else if (IN(state, ST_TSV_BODY))
    397 	{
    398 		size_t buf_len = 0;
    399 		vsnprintf((char*)buf, BUFSIZE, fmt, args);
    400 		buf_len = strlen(buf);
    401 		if (!tsv_template)
    402 		{
    403 			tsv_template_size = BUFSIZE;
    404 			CALLOC(tsv_template, u8, tsv_template_size);
    405 			if (strlcpy((char*)tsv_template, buf, tsv_template_size)
    406 				>= tsv_template_size)
    407 				warning(1, (u8*)"strlcpy:%d: Overflow",
    408 					__LINE__);
    409 		}
    410 		else
    411 		{
    412 			if (strlen((char*)tsv_template) + buf_len
    413 				> tsv_template_size)
    414 			{
    415 				tsv_template_size += BUFSIZE;
    416 				REALLOC(tsv_template, u8, tsv_template_size);
    417 			}
    418 			if (strlcat((char*)tsv_template, buf, tsv_template_size)
    419 				> tsv_template_size)
    420 				warning(1, (u8*)"strlcat:%d: Overflow",
    421 					__LINE__);
    422 		}
    423 	}
    424 	else
    425 		vfprintf(output, fmt, args);
    426 	va_end(args);
    427 	return 0;
    428 }
    429 
    430 #define PIPE_READ_INDEX	 0
    431 #define PIPE_WRITE_INDEX 1
    432 
    433 int
    434 print_command(FILE* output, const char* command, const u8* pass_arguments[],
    435 	const u8* pipe_arguments[], const int strip_newlines)
    436 {
    437 	if (!command || !pass_arguments)
    438 		exit(error(EINVAL, (u8*)"print_command: Invalid argument"));
    439 
    440 	pid_t pid   = 0;
    441 	int pstatus = 0;
    442 	int arg_pipe_fds[2];
    443 	int output_pipe_fds[2];
    444 
    445 	fflush(output);
    446 	pipe(arg_pipe_fds);
    447 	pipe(output_pipe_fds);
    448 	pid = fork();
    449 	if (pid == 0)
    450 	{
    451 		close(arg_pipe_fds[PIPE_WRITE_INDEX]);
    452 		close(output_pipe_fds[PIPE_READ_INDEX]);
    453 
    454 		dup2(arg_pipe_fds[PIPE_READ_INDEX], STDIN_FILENO);
    455 		dup2(output_pipe_fds[PIPE_WRITE_INDEX], STDOUT_FILENO);
    456 
    457 		execvp(command, (char* const*)pass_arguments);
    458 		exit(1);
    459 	}
    460 	else if (pid < 0)
    461 		exit(error(errno, (u8*)"print_command: fork failed"));
    462 
    463 	/* Parent */
    464 	close(arg_pipe_fds[PIPE_READ_INDEX]);
    465 	close(output_pipe_fds[PIPE_WRITE_INDEX]);
    466 	FILE* cmd_input	 = fdopen(arg_pipe_fds[PIPE_WRITE_INDEX], "w");
    467 	FILE* cmd_output = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r");
    468 
    469 	if (!cmd_input || !cmd_output)
    470 	{
    471 		perror("fdopen");
    472 		exit(error(1, (u8*)"print_command: fdopen failed"));
    473 	}
    474 
    475 	if (pipe_arguments)
    476 	{
    477 		const u8** ppipe_argument = pipe_arguments;
    478 		while (ppipe_argument && *ppipe_argument)
    479 		{
    480 			fprintf(cmd_input, "%s\n", *ppipe_argument);
    481 			ppipe_argument++;
    482 		}
    483 	}
    484 	fclose(cmd_input);
    485 
    486 	u8* cmd_output_line = NULL;
    487 	CALLOC(cmd_output_line, u8, BUFSIZE);
    488 	while (!feof(cmd_output))
    489 	{
    490 		char* eol = NULL;
    491 
    492 		if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output))
    493 			continue;
    494 
    495 		eol = strchr((char*)cmd_output_line, '\n');
    496 		if (eol)
    497 			*eol = 0;
    498 
    499 		print_output(output, "%s%s", cmd_output_line,
    500 			strip_newlines ? "" : "\n");
    501 	}
    502 	if (!strip_newlines)
    503 		output_firstcol = 1;
    504 	free(cmd_output_line);
    505 
    506 	fclose(cmd_output);
    507 
    508 	kill(pid, SIGKILL);
    509 	pid_t wpid = waitpid(pid, &pstatus, 0);
    510 	if (wpid < 0)
    511 		warning(pstatus,
    512 			(u8*)"Child returned nonzero status, errno = %d",
    513 			errno);
    514 	if (WIFEXITED(pstatus))
    515 		return WEXITSTATUS(pstatus);
    516 
    517 	return wpid;
    518 }
    519 
    520 int
    521 read_file_into_buffer(FILE** input, u8** buffer, size_t* buffer_size,
    522 	const char* input_filename, char** input_dirname)
    523 {
    524 	struct stat fs;
    525 	char* slash = NULL;
    526 
    527 	*input = fopen(input_filename, "r");
    528 	if (!*input)
    529 	{
    530 		perror("fopen");
    531 		return error(ENOENT,
    532 			(u8*)"read_file_into_buffer: fopen failed: %s",
    533 			input_filename);
    534 	}
    535 
    536 	fstat(fileno(*input), &fs);
    537 	if (S_ISDIR(fs.st_mode))
    538 		return error(EISDIR,
    539 			(u8*)"read_file_into_buffer: Is a directory: %s",
    540 			input_filename);
    541 
    542 	free(*buffer);
    543 	*buffer_size = fs.st_size + 1;
    544 	CALLOC(*buffer, u8, *buffer_size);
    545 	fread((void*)*buffer, 1, *buffer_size, *input);
    546 
    547 	free(*input_dirname);
    548 
    549 	slash = strrchr(input_filename, '/');
    550 	if (slash)
    551 	{
    552 		CALLOC(*input_dirname, char, strlen(input_filename) + 1);
    553 		char* pinput_dirname	    = *input_dirname;
    554 		const char* pinput_filename = input_filename;
    555 		while (pinput_filename && *pinput_filename
    556 			&& pinput_filename != slash)
    557 			*pinput_dirname++ = *pinput_filename++;
    558 	}
    559 	else
    560 	{
    561 		CALLOC(*input_dirname, char, 2);
    562 		**input_dirname = '.';
    563 	}
    564 
    565 	fclose(*input);
    566 
    567 	return 0;
    568 }
    569 
    570 int read_csv(FILE* output, const char* filename, csv_callback_t callback);
    571 int read_tsv(FILE* output, const char* filename, tsv_callback_t callback);
    572 
    573 int
    574 process_heading_start(FILE* output, const UBYTE heading_level)
    575 {
    576 	if (!output_firstcol)
    577 		print_output(output, "\n");
    578 	for (size_t i = 0; i < heading_level; i++)
    579 		print_output(output, "#");
    580 	print_output(output, " ");
    581 	output_firstcol = 0;
    582 	return 0;
    583 }
    584 
    585 int
    586 process_heading(FILE* output, const u8* token)
    587 {
    588 	if (!token || strlen((char*)token) < 1)
    589 		warning(1, (u8*)"Empty heading");
    590 
    591 	print_output(output, "%s\n", token ? (char*)token : "");
    592 	output_firstcol = 1;
    593 
    594 	return 0;
    595 }
    596 
    597 int
    598 process_git_log(FILE* output)
    599 {
    600 	if (!input_filename)
    601 		return warning(1, (u8*)"Cannot use 'git-log' in stdin");
    602 
    603 	char* basename	     = NULL;
    604 	size_t basename_size = BUFSIZE;
    605 	CALLOC(basename, char, basename_size);
    606 	char* slash  = strrchr(input_filename, '/');
    607 	pid_t result = 0;
    608 
    609 	if (slash)
    610 	{
    611 		if (strlcpy(basename, slash + 1, basename_size) >= basename_size)
    612 			warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
    613 	}
    614 	else
    615 	{
    616 		if (strlcpy(basename, input_filename, basename_size)
    617 			>= basename_size)
    618 			warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
    619 	}
    620 
    621 	u8* pipe_args[] = {(u8*)basename, NULL};
    622 
    623 	print_output(output, "Previous commit: ");
    624 	result = print_command(output, CMD_GIT_LOG,
    625 		(const u8**)CMD_GIT_LOG_ARGS, (const u8**)pipe_args, 0);
    626 	print_output(output, "\n");
    627 	output_firstcol = 1;
    628 
    629 	free(basename);
    630 
    631 	return result ? warning(result, (u8*)"git-log: Cannot run git") : 0;
    632 }
    633 
    634 #define PRINTTSVIF(format, arg)                                           \
    635 	do                                                                \
    636 	{                                                                 \
    637 		if ((ALL(tsv_state, ST_CS_COND_NONEMPTY)                  \
    638 			    && *tsv_register[conditional_index - 1])      \
    639 			|| (ALL(tsv_state, ST_CS_COND_EMPTY)              \
    640 				&& !*tsv_register[conditional_index - 1]) \
    641 			|| !ANY(tsv_state,                                \
    642 				ST_CS_COND_EMPTY | ST_CS_COND_NONEMPTY))  \
    643 		{                                                         \
    644 			fprintf(output, format, arg);                     \
    645 		}                                                         \
    646 	} while (0)
    647 
    648 int
    649 print_tsv_row(FILE* output, u8** tsv_header, u8** tsv_register)
    650 {
    651 	u8* ptsv_template	= tsv_template;
    652 	UBYTE tsv_state		= ST_CS_NONE;
    653 	UBYTE num		= 0;
    654 	UBYTE conditional_index = 0;
    655 
    656 tsv_template_start:
    657 	if (!*ptsv_template)
    658 		goto tsv_template_done;
    659 	if (IN(tsv_state, ST_CS_ESCAPE))
    660 	{
    661 		PRINTTSVIF("%c", *ptsv_template);
    662 		tsv_state &= ~ST_CS_ESCAPE;
    663 		ptsv_template++;
    664 		goto tsv_template_start;
    665 	}
    666 
    667 	switch (*ptsv_template)
    668 	{
    669 	case '\\':
    670 		tsv_state |= ST_CS_ESCAPE;
    671 		ptsv_template++;
    672 		break;
    673 	case '$':
    674 		if (IN(tsv_state, ST_CS_REGISTER))
    675 		{
    676 			PRINTTSVIF("%c", '$');
    677 			tsv_state &= ~ST_CS_REGISTER;
    678 		}
    679 		else
    680 			tsv_state |= ST_CS_REGISTER;
    681 		ptsv_template++;
    682 		break;
    683 	case '#':
    684 		if (IN(tsv_state, ST_CS_HEADER))
    685 		{
    686 			error(1,
    687 				(u8*)"print_tsv_row: Invalid header register"
    688 				     " mark");
    689 			tsv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER);
    690 		}
    691 		else if (IN(tsv_state, ST_CS_REGISTER))
    692 		{
    693 			tsv_state &= ~ST_CS_REGISTER;
    694 			tsv_state |= ST_CS_HEADER;
    695 		}
    696 		else
    697 			PRINTTSVIF("%c", '#');
    698 		ptsv_template++;
    699 		break;
    700 	case '?':
    701 		if (IN(tsv_state, ST_CS_COND))
    702 		{
    703 			error(1,
    704 				(u8*)"print_tsv_row: Invalid conditional"
    705 				     " mark");
    706 			tsv_state &= ~ST_CS_COND;
    707 		}
    708 		else if (IN(tsv_state, ST_CS_REGISTER))
    709 		{
    710 			tsv_state &= ~ST_CS_REGISTER;
    711 			tsv_state |= ST_CS_COND;
    712 		}
    713 		else
    714 			PRINTTSVIF("%c", '?');
    715 		ptsv_template++;
    716 		break;
    717 	case '!':
    718 		if (ALL(tsv_state, ST_CS_COND | ST_CS_COND_NONEMPTY))
    719 		{
    720 			tsv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY);
    721 			tsv_state |= ST_CS_COND_EMPTY;
    722 		}
    723 		else if (IN(tsv_state, ST_CS_COND))
    724 		{
    725 			error(1,
    726 				(u8*)"print_tsv_row: Empty conditional"
    727 				     " before/without nonempty conditional");
    728 			tsv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY);
    729 		}
    730 		else
    731 			PRINTTSVIF("%c", '!');
    732 		ptsv_template++;
    733 		break;
    734 	case '/':
    735 		if (ALL(tsv_state, ST_CS_COND | ST_CS_COND_EMPTY))
    736 			tsv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY);
    737 		else if (ALL(tsv_state, ST_CS_COND))
    738 			tsv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY);
    739 		else
    740 			PRINTTSVIF("%c", '/');
    741 		ptsv_template++;
    742 		break;
    743 	case '1':
    744 	case '2':
    745 	case '3':
    746 	case '4':
    747 	case '5':
    748 	case '6':
    749 	case '7':
    750 	case '8':
    751 	case '9':
    752 		if (IN(tsv_state, ST_CS_COND))
    753 		{
    754 			conditional_index = *ptsv_template - '0';
    755 			tsv_state &= ~ST_CS_COND;
    756 			tsv_state |= ST_CS_COND_NONEMPTY;
    757 		}
    758 		else if (IN(tsv_state, ST_CS_HEADER))
    759 		{
    760 			num = *ptsv_template - '0';
    761 			PRINTTSVIF("%s", tsv_header[num - 1]);
    762 			tsv_state &= ~ST_CS_HEADER;
    763 		}
    764 		else if (IN(tsv_state, ST_CS_REGISTER))
    765 		{
    766 			num = *ptsv_template - '0';
    767 			PRINTTSVIF("%s", tsv_register[num - 1]);
    768 			tsv_state &= ~ST_CS_REGISTER;
    769 		}
    770 		else
    771 			PRINTTSVIF("%c", *ptsv_template);
    772 		ptsv_template++;
    773 		break;
    774 	default:
    775 		if (IN(tsv_state, ST_CS_HEADER))
    776 		{
    777 			error(1,
    778 				(u8*)"print_tsv_row: Invalid header register"
    779 				     " mark");
    780 			tsv_state &= ~ST_CS_HEADER;
    781 		}
    782 		else if (IN(tsv_state, ST_CS_REGISTER))
    783 		{
    784 			error(1, (u8*)"print_tsv_row: Invalid register mark");
    785 			tsv_state &= ~ST_CS_REGISTER;
    786 		}
    787 		else
    788 			PRINTTSVIF("%c", *ptsv_template);
    789 		ptsv_template++;
    790 	}
    791 	goto tsv_template_start;
    792 tsv_template_done:
    793 	return 0;
    794 }
    795 
    796 int
    797 read_tsv(FILE* output, const char* filename, tsv_callback_t callback)
    798 {
    799 	if (!callback)
    800 		exit(error(EINVAL, (u8*)"read_tsv: Invalid callback argument"));
    801 
    802 	FILE* tsv	  = NULL;
    803 	size_t tsv_lineno = 0;
    804 	u8* bufline	  = NULL;
    805 	u8* pbufline	  = NULL;
    806 	u8* token	  = NULL;
    807 	u8* ptoken	  = NULL;
    808 	size_t token_size = 0;
    809 	u8* tsv_header[MAX_TSV_REGISTERS];
    810 	UBYTE current_header = 0;
    811 	u8* tsv_register[MAX_TSV_REGISTERS];
    812 	UBYTE current_register = 0;
    813 
    814 	if (!(tsv = fopen(filename, "rt")))
    815 	{
    816 		perror("fopen");
    817 		exit(error(ENOENT, (u8*)"read_tsv: fopen failed: %s", filename));
    818 	}
    819 
    820 	CALLOC(bufline, u8, BUFSIZE);
    821 	token_size = BUFSIZE;
    822 	CALLOC(token, u8, token_size);
    823 	for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++)
    824 		CALLOC(tsv_header[i], u8, BUFSIZE);
    825 	for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++)
    826 		CALLOC(tsv_register[i], u8, BUFSIZE);
    827 
    828 	while (!feof(tsv) && (!tsv_iter || tsv_lineno <= tsv_iter))
    829 	{
    830 		u8* eol = NULL;
    831 		if (!fgets((char*)bufline, BUFSIZE, tsv))
    832 			break;
    833 		eol = (u8*)strchr((char*)bufline, '\n');
    834 		if (eol)
    835 			*eol = 0;
    836 
    837 		pbufline = bufline;
    838 		RESET_TOKEN(token, ptoken, token_size);
    839 		current_register = 0;
    840 	tsv_bufline_start:
    841 		if (!*pbufline)
    842 			goto tsv_bufline_done;
    843 		switch (*pbufline)
    844 		{
    845 		case '\t':
    846 			*ptoken = 0;
    847 			if (tsv_lineno > 0
    848 				&& current_register < MAX_TSV_REGISTERS)
    849 			{
    850 				if (strlcpy((char*)tsv_register[current_register],
    851 					    (char*)token, BUFSIZE)
    852 					>= BUFSIZE)
    853 					warning(1, (u8*)"strlcpy:%d: Overflow",
    854 						__LINE__);
    855 				current_register++;
    856 			}
    857 			else if (tsv_lineno <= 0
    858 				&& current_header < MAX_TSV_REGISTERS)
    859 			{
    860 				if (strlcpy((char*)tsv_header[current_header],
    861 					    (char*)token, BUFSIZE)
    862 					>= BUFSIZE)
    863 					warning(1, (u8*)"strlcpy:%d: Overflow",
    864 						__LINE__);
    865 				current_header++;
    866 			}
    867 			RESET_TOKEN(token, ptoken, token_size);
    868 			pbufline++;
    869 			break;
    870 		default:
    871 			CHECKCOPY(token, ptoken, token_size, pbufline);
    872 		}
    873 		goto tsv_bufline_start;
    874 	tsv_bufline_done:
    875 		*ptoken = 0;
    876 		if (tsv_lineno > 0)
    877 		{
    878 			if (current_register < MAX_TSV_REGISTERS)
    879 			{
    880 				if (strlcpy((char*)tsv_register[current_register],
    881 					    (char*)token, BUFSIZE)
    882 					>= BUFSIZE)
    883 					warning(1, (u8*)"strlcpy:%d: Overflow",
    884 						__LINE__);
    885 				current_register++;
    886 			}
    887 		}
    888 		else
    889 		{
    890 			if (current_header < MAX_TSV_REGISTERS)
    891 			{
    892 				if (strlcpy((char*)tsv_header[current_header],
    893 					    (char*)token, BUFSIZE)
    894 					>= BUFSIZE)
    895 					warning(1, (u8*)"strlcpy:%d: Overflow",
    896 						__LINE__);
    897 				current_header++;
    898 			}
    899 		}
    900 		RESET_TOKEN(token, ptoken, token_size);
    901 
    902 		if (tsv_lineno > 0 && pbufline != bufline)
    903 			(*callback)(output, tsv_header, tsv_register);
    904 
    905 		for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++)
    906 			*tsv_register[i] = 0;
    907 		tsv_lineno++;
    908 	}
    909 	fclose(tsv);
    910 	for (UBYTE i = MAX_TSV_REGISTERS; i > 0; i--)
    911 		free(tsv_register[i - 1]);
    912 	for (UBYTE i = MAX_TSV_REGISTERS; i > 0; i--)
    913 		free(tsv_header[i - 1]);
    914 	free(token);
    915 	free(bufline);
    916 
    917 	return 0;
    918 }
    919 
    920 int
    921 process_tsv(FILE* output, const u8* arg_token,
    922 	const int read_yaml_macros_and_links, const int end_tag)
    923 {
    924 	if (end_tag)
    925 	{
    926 		state &= ~ST_TSV_BODY;
    927 
    928 		if (read_yaml_macros_and_links)
    929 			return 0;
    930 
    931 		read_tsv(output, tsv_filename, &print_tsv_row);
    932 
    933 		free(tsv_filename);
    934 		tsv_filename = NULL;
    935 		free(tsv_template);
    936 		tsv_template	  = NULL;
    937 		tsv_template_size = 0;
    938 	}
    939 	else
    940 	{
    941 		if (ANY(state, ST_CSV_BODY | ST_TSV_BODY))
    942 			exit(error(1,
    943 				(u8*)"process_tsv: Can't nest csv/tsv "
    944 				     "directives"));
    945 
    946 		state |= ST_TSV_BODY;
    947 
    948 		if (read_yaml_macros_and_links)
    949 			return 0;
    950 
    951 		tsv_iter    = 0;
    952 		u8* saveptr = NULL;
    953 		u8* args    = NULL;
    954 
    955 		(void)strtok_r((char*)arg_token, " ", (char**)&saveptr);
    956 		args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
    957 		if (!args)
    958 			exit(error(EINVAL,
    959 				(u8*)"process_tsv: Arguments required"));
    960 		size_t args_len = strlen((char*)args);
    961 		if (*args != '"' || *(args + args_len - 1) != '"')
    962 			exit(error(EINVAL,
    963 				(u8*)"process_tsv: First argument must be a "
    964 				     "string"));
    965 		if (!tsv_filename)
    966 			CALLOC(tsv_filename, char, BUFSIZE);
    967 		u8* args_base = (u8*)strdup((char*)args + 1);
    968 		*(args_base + strlen((char*)args_base) - 1) = 0;
    969 		snprintf(tsv_filename, BUFSIZE, "%s/%s.tsv", input_dirname,
    970 			(char*)args_base);
    971 		free(args_base);
    972 		args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
    973 		if (args)
    974 		{
    975 			errno	 = 0;
    976 			tsv_iter = strtol((char*)args, NULL, 10);
    977 			if (errno)
    978 				exit(error(errno,
    979 					(u8*)"process_tsv: Invalid argument "
    980 					     "'%s'",
    981 					args));
    982 		}
    983 	}
    984 
    985 	return 0;
    986 }
    987 
    988 #define PRINTCSVIF(format, arg)                                           \
    989 	do                                                                \
    990 	{                                                                 \
    991 		if ((ALL(csv_state, ST_CS_COND_NONEMPTY)                  \
    992 			    && *csv_register[conditional_index - 1])      \
    993 			|| (ALL(csv_state, ST_CS_COND_EMPTY)              \
    994 				&& !*csv_register[conditional_index - 1]) \
    995 			|| !ANY(csv_state,                                \
    996 				ST_CS_COND_EMPTY | ST_CS_COND_NONEMPTY))  \
    997 		{                                                         \
    998 			fprintf(output, format, arg);                     \
    999 		}                                                         \
   1000 	} while (0)
   1001 
   1002 int
   1003 print_csv_row(FILE* output, u8** csv_header, u8** csv_register)
   1004 {
   1005 	u8* pcsv_template	= csv_template;
   1006 	UBYTE csv_state		= ST_CS_NONE;
   1007 	UBYTE num		= 0;
   1008 	UBYTE conditional_index = 0;
   1009 
   1010 csv_template_start:
   1011 	if (!*pcsv_template)
   1012 		goto csv_template_done;
   1013 	if (IN(csv_state, ST_CS_ESCAPE))
   1014 	{
   1015 		PRINTCSVIF("%c", *pcsv_template);
   1016 		csv_state &= ~ST_CS_ESCAPE;
   1017 		pcsv_template++;
   1018 		goto csv_template_start;
   1019 	}
   1020 
   1021 	switch (*pcsv_template)
   1022 	{
   1023 	case '\\':
   1024 		csv_state |= ST_CS_ESCAPE;
   1025 		pcsv_template++;
   1026 		break;
   1027 	case '$':
   1028 		if (IN(csv_state, ST_CS_REGISTER))
   1029 		{
   1030 			PRINTCSVIF("%c", '$');
   1031 			csv_state &= ~ST_CS_REGISTER;
   1032 		}
   1033 		else
   1034 			csv_state |= ST_CS_REGISTER;
   1035 		pcsv_template++;
   1036 		break;
   1037 	case '#':
   1038 		if (IN(csv_state, ST_CS_HEADER))
   1039 		{
   1040 			error(1, (u8*)"print_csv_row: Invalid header register mark");
   1041 			csv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER);
   1042 		}
   1043 		else if (IN(csv_state, ST_CS_REGISTER))
   1044 		{
   1045 			csv_state &= ~ST_CS_REGISTER;
   1046 			csv_state |= ST_CS_HEADER;
   1047 		}
   1048 		else
   1049 			PRINTCSVIF("%c", '#');
   1050 		pcsv_template++;
   1051 		break;
   1052 	case '?':
   1053 		if (IN(csv_state, ST_CS_COND))
   1054 		{
   1055 			error(1, (u8*)"print_csv_row: Invalid conditional mark");
   1056 			csv_state &= ~ST_CS_COND;
   1057 		}
   1058 		else if (IN(csv_state, ST_CS_REGISTER))
   1059 		{
   1060 			csv_state &= ~ST_CS_REGISTER;
   1061 			csv_state |= ST_CS_COND;
   1062 		}
   1063 		else
   1064 			PRINTCSVIF("%c", '?');
   1065 		pcsv_template++;
   1066 		break;
   1067 	case '!':
   1068 		if (ALL(csv_state, ST_CS_COND | ST_CS_COND_NONEMPTY))
   1069 		{
   1070 			csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY);
   1071 			csv_state |= ST_CS_COND_EMPTY;
   1072 		}
   1073 		else if (IN(csv_state, ST_CS_COND))
   1074 		{
   1075 			error(1,
   1076 				(u8*)"print_csv_row: Empty conditional "
   1077 				     "before/without nonempty"
   1078 				     " conditional");
   1079 			csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY);
   1080 		}
   1081 		else
   1082 			PRINTCSVIF("%c", '!');
   1083 		pcsv_template++;
   1084 		break;
   1085 	case '/':
   1086 		if (ALL(csv_state, ST_CS_COND | ST_CS_COND_EMPTY))
   1087 			csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY);
   1088 		else if (ALL(csv_state, ST_CS_COND))
   1089 			csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY);
   1090 		else
   1091 			PRINTCSVIF("%c", '/');
   1092 		pcsv_template++;
   1093 		break;
   1094 	case '1':
   1095 	case '2':
   1096 	case '3':
   1097 	case '4':
   1098 	case '5':
   1099 	case '6':
   1100 	case '7':
   1101 	case '8':
   1102 	case '9':
   1103 		if (IN(csv_state, ST_CS_COND))
   1104 		{
   1105 			conditional_index = *pcsv_template - '0';
   1106 			csv_state &= ~ST_CS_COND;
   1107 			csv_state |= ST_CS_COND_NONEMPTY;
   1108 		}
   1109 		else if (IN(csv_state, ST_CS_HEADER))
   1110 		{
   1111 			num = *pcsv_template - '0';
   1112 			PRINTCSVIF("%s", csv_header[num - 1]);
   1113 			csv_state &= ~ST_CS_HEADER;
   1114 		}
   1115 		else if (IN(csv_state, ST_CS_REGISTER))
   1116 		{
   1117 			num = *pcsv_template - '0';
   1118 			PRINTCSVIF("%s", csv_register[num - 1]);
   1119 			csv_state &= ~ST_CS_REGISTER;
   1120 		}
   1121 		else
   1122 			PRINTCSVIF("%c", *pcsv_template);
   1123 		pcsv_template++;
   1124 		break;
   1125 	default:
   1126 		if (IN(csv_state, ST_CS_HEADER))
   1127 		{
   1128 			error(1, (u8*)"print_csv_row: Invalid header register mark");
   1129 			csv_state &= ~ST_CS_HEADER;
   1130 		}
   1131 		else if (IN(csv_state, ST_CS_REGISTER))
   1132 		{
   1133 			error(1, (u8*)"print_csv_row: Invalid register mark");
   1134 			csv_state &= ~ST_CS_REGISTER;
   1135 		}
   1136 		else
   1137 			PRINTCSVIF("%c", *pcsv_template);
   1138 		pcsv_template++;
   1139 	}
   1140 	goto csv_template_start;
   1141 csv_template_done:
   1142 	return 0;
   1143 }
   1144 
   1145 int
   1146 read_csv(FILE* output, const char* filename, csv_callback_t callback)
   1147 {
   1148 	if (!callback)
   1149 		exit(error(EINVAL, (u8*)"read_csv: Invalid callback argument"));
   1150 
   1151 	FILE* csv	  = NULL;
   1152 	size_t csv_lineno = 0;
   1153 	u8* bufline	  = NULL;
   1154 	u8* pbufline	  = NULL;
   1155 	u8* token	  = NULL;
   1156 	u8* ptoken	  = NULL;
   1157 	size_t token_size = 0;
   1158 	UBYTE csv_state	  = ST_CS_NONE;
   1159 	u8* csv_header[MAX_CSV_REGISTERS];
   1160 	UBYTE current_header = 0;
   1161 	u8* csv_register[MAX_CSV_REGISTERS];
   1162 	UBYTE current_register = 0;
   1163 	u8* csv_delimiter
   1164 		= get_value(vars, vars_count, (u8*)"csv-delimiter", NULL);
   1165 
   1166 	if (!(csv = fopen(filename, "rt")))
   1167 	{
   1168 		perror("fopen");
   1169 		exit(error(ENOENT, (u8*)"read_csv: fopen failed: %s", filename));
   1170 	}
   1171 
   1172 	CALLOC(bufline, u8, BUFSIZE);
   1173 	token_size = BUFSIZE;
   1174 	CALLOC(token, u8, token_size);
   1175 	for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++)
   1176 		CALLOC(csv_header[i], u8, BUFSIZE);
   1177 	for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++)
   1178 		CALLOC(csv_register[i], u8, BUFSIZE);
   1179 
   1180 	while (!feof(csv) && (!csv_iter || csv_lineno <= csv_iter))
   1181 	{
   1182 		u8* eol = NULL;
   1183 		if (!fgets((char*)bufline, BUFSIZE, csv))
   1184 			break;
   1185 		eol = (u8*)strchr((char*)bufline, '\n');
   1186 		if (eol)
   1187 			*eol = 0;
   1188 
   1189 		pbufline = bufline;
   1190 		RESET_TOKEN(token, ptoken, token_size);
   1191 		current_register = 0;
   1192 	csv_bufline_start:
   1193 		if (!*pbufline)
   1194 			goto csv_bufline_done;
   1195 		switch (*pbufline)
   1196 		{
   1197 		case '"':
   1198 			if (IN(csv_state, ST_CS_QUOTE))
   1199 				csv_state &= ~ST_CS_QUOTE;
   1200 			else
   1201 				csv_state |= ST_CS_QUOTE;
   1202 			pbufline++;
   1203 			break;
   1204 		case ';':
   1205 		case ',':
   1206 			if (IN(csv_state, ST_CS_QUOTE))
   1207 				CHECKCOPY(token, ptoken, token_size, pbufline);
   1208 			else
   1209 			{
   1210 				*ptoken = 0;
   1211 				if (csv_lineno > 0
   1212 					&& current_register < MAX_CSV_REGISTERS)
   1213 				{
   1214 					if (strlcpy((char*)csv_register
   1215 							    [current_register],
   1216 						    (char*)token, BUFSIZE)
   1217 						>= BUFSIZE)
   1218 						warning(1, (u8*)"strlcpy:%d: Overflow",
   1219 							__LINE__);
   1220 					current_register++;
   1221 				}
   1222 				else if (csv_lineno <= 0
   1223 					&& current_header < MAX_CSV_REGISTERS)
   1224 				{
   1225 					if (strlcpy((char*)csv_header
   1226 							    [current_header],
   1227 						    (char*)token, BUFSIZE)
   1228 						>= BUFSIZE)
   1229 						warning(1, (u8*)"strlcpy:%d: Overflow",
   1230 							__LINE__);
   1231 					current_header++;
   1232 				}
   1233 				RESET_TOKEN(token, ptoken, token_size);
   1234 				pbufline++;
   1235 			}
   1236 			break;
   1237 		default:
   1238 			if (IN(csv_state, ST_CS_QUOTE))
   1239 				CHECKCOPY(token, ptoken, token_size, pbufline);
   1240 			else if (csv_delimiter && *pbufline == *csv_delimiter)
   1241 			{
   1242 				*ptoken = 0;
   1243 				if (csv_lineno > 0
   1244 					&& current_register < MAX_CSV_REGISTERS)
   1245 				{
   1246 					if (strlcpy((char*)csv_register
   1247 							    [current_register],
   1248 						    (char*)token, BUFSIZE)
   1249 						>= BUFSIZE)
   1250 						warning(1, (u8*)"strlcpy:%d: Overflow",
   1251 							__LINE__);
   1252 					current_register++;
   1253 				}
   1254 				else if (csv_lineno <= 0
   1255 					&& current_header < MAX_CSV_REGISTERS)
   1256 				{
   1257 					if (strlcpy((char*)csv_header
   1258 							    [current_header],
   1259 						    (char*)token, BUFSIZE)
   1260 						>= BUFSIZE)
   1261 						warning(1, (u8*)"strlcpy:%d: Overflow",
   1262 							__LINE__);
   1263 					current_header++;
   1264 				}
   1265 				RESET_TOKEN(token, ptoken, token_size);
   1266 				pbufline++;
   1267 			}
   1268 			else
   1269 				CHECKCOPY(token, ptoken, token_size, pbufline);
   1270 		}
   1271 		goto csv_bufline_start;
   1272 	csv_bufline_done:
   1273 		*ptoken = 0;
   1274 		if (csv_lineno > 0)
   1275 		{
   1276 			if (current_register < MAX_CSV_REGISTERS)
   1277 			{
   1278 				if (strlcpy((char*)csv_register[current_register],
   1279 					    (char*)token, BUFSIZE)
   1280 					>= BUFSIZE)
   1281 					warning(1, (u8*)"strlcpy:%d: Overflow",
   1282 						__LINE__);
   1283 				current_register++;
   1284 			}
   1285 		}
   1286 		else
   1287 		{
   1288 			if (current_header < MAX_CSV_REGISTERS)
   1289 			{
   1290 				if (strlcpy((char*)csv_header[current_header],
   1291 					    (char*)token, BUFSIZE)
   1292 					>= BUFSIZE)
   1293 					warning(1, (u8*)"strlcpy:%d: Overflow",
   1294 						__LINE__);
   1295 				current_header++;
   1296 			}
   1297 		}
   1298 		RESET_TOKEN(token, ptoken, token_size);
   1299 
   1300 		if (csv_lineno > 0 && pbufline != bufline)
   1301 			(*callback)(output, csv_header, csv_register);
   1302 
   1303 		for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++)
   1304 			*csv_register[i] = 0;
   1305 		csv_lineno++;
   1306 	}
   1307 	fclose(csv);
   1308 	for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--)
   1309 		free(csv_register[i - 1]);
   1310 	for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--)
   1311 		free(csv_header[i - 1]);
   1312 	free(token);
   1313 	free(bufline);
   1314 
   1315 	return 0;
   1316 }
   1317 
   1318 int
   1319 process_csv(FILE* output, const u8* arg_token,
   1320 	const int read_yaml_macros_and_links, const int end_tag)
   1321 {
   1322 	if (end_tag)
   1323 	{
   1324 		state &= ~ST_CSV_BODY;
   1325 
   1326 		if (read_yaml_macros_and_links)
   1327 			return 0;
   1328 
   1329 		read_csv(output, csv_filename, &print_csv_row);
   1330 
   1331 		free(csv_filename);
   1332 		csv_filename = NULL;
   1333 		free(csv_template);
   1334 		csv_template	  = NULL;
   1335 		csv_template_size = 0;
   1336 	}
   1337 	else
   1338 	{
   1339 		if (ANY(state, ST_CSV_BODY | ST_TSV_BODY))
   1340 			exit(error(1,
   1341 				(u8*)"read_csv: Can't nest csv/tsv"
   1342 				     " directives"));
   1343 
   1344 		state |= ST_CSV_BODY;
   1345 
   1346 		if (read_yaml_macros_and_links)
   1347 			return 0;
   1348 
   1349 		csv_iter    = 0;
   1350 		u8* saveptr = NULL;
   1351 		u8* args    = NULL;
   1352 
   1353 		(void)strtok_r((char*)arg_token, " ", (char**)&saveptr);
   1354 		args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
   1355 		if (!args)
   1356 			exit(error(EINVAL, (u8*)"read_csv: Arguments required"));
   1357 		size_t args_len = strlen((char*)args);
   1358 		if (*args != '"' || *(args + args_len - 1) != '"')
   1359 			exit(error(EINVAL,
   1360 				(u8*)"read_csv: First argument must be a "
   1361 				     "string"));
   1362 		if (!csv_filename)
   1363 			CALLOC(csv_filename, char, BUFSIZE);
   1364 		u8* args_base = (u8*)strdup((char*)args + 1);
   1365 		*(args_base + strlen((char*)args_base) - 1) = 0;
   1366 		snprintf(csv_filename, BUFSIZE, "%s/%s.csv", input_dirname,
   1367 			(char*)args_base);
   1368 		free(args_base);
   1369 		args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
   1370 		if (args)
   1371 		{
   1372 			errno	 = 0;
   1373 			csv_iter = strtol((char*)args, NULL, 10);
   1374 			if (errno)
   1375 				exit(error(errno,
   1376 					(u8*)"read_csv: Invalid argument '%s'",
   1377 					args));
   1378 		}
   1379 	}
   1380 
   1381 	return 0;
   1382 }
   1383 
   1384 int
   1385 process_include(FILE* output, const u8* token,
   1386 	const int read_yaml_macros_and_links)
   1387 {
   1388 	u8* ptoken;
   1389 	char* include_filename	= NULL;
   1390 	char* pinclude_filename = NULL;
   1391 	pid_t pid		= 0;
   1392 	int pstatus		= 0;
   1393 	int arg_pipe_fds[2];
   1394 	int output_pipe_fds[2];
   1395 
   1396 	ptoken = (u8*)strchr((char*)token, ' ');
   1397 
   1398 	if (!input_filename)
   1399 		return warning(1, (u8*)"Cannot use 'include' in stdin");
   1400 
   1401 	if (!ptoken)
   1402 		exit(error(1,
   1403 			(u8*)"process_include: Directive 'include' requires"
   1404 			     " an argument"));
   1405 
   1406 	fflush(output);
   1407 	pipe(arg_pipe_fds);
   1408 	pipe(output_pipe_fds);
   1409 	pid = fork();
   1410 
   1411 	if (pid > 0) /* parent */
   1412 	{
   1413 		FILE* child_output    = NULL;
   1414 		u8* child_output_line = NULL;
   1415 		close(arg_pipe_fds[PIPE_READ_INDEX]);
   1416 		close(output_pipe_fds[PIPE_WRITE_INDEX]);
   1417 		if (!(child_output
   1418 			    = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r")))
   1419 		{
   1420 			wait(&pstatus);
   1421 			perror("fdopen");
   1422 			exit(error(1, (u8*)"process_include: fdopen failed"));
   1423 		}
   1424 
   1425 		CALLOC(child_output_line, u8, BUFSIZE);
   1426 		while (!feof(child_output))
   1427 		{
   1428 			char* eol = NULL;
   1429 
   1430 			if (!fgets((char*)child_output_line, BUFSIZE,
   1431 				    child_output))
   1432 				continue;
   1433 
   1434 			eol = strchr((char*)child_output_line, '\n');
   1435 			if (eol)
   1436 				*eol = 0;
   1437 			simple_parse_yaml_line(child_output_line, &vars,
   1438 				&vars_count, &pvars);
   1439 		}
   1440 		free(child_output_line);
   1441 		fclose(child_output);
   1442 
   1443 		wait(&pstatus);
   1444 	}
   1445 	else if (pid == 0) /* child */
   1446 	{
   1447 		char* filename	   = NULL;
   1448 		FILE* input	   = NULL;
   1449 		FILE* output	   = stdout;
   1450 		u8* buffer	   = NULL;
   1451 		size_t buffer_size = 0;
   1452 		int result	   = 0;
   1453 		FILE* child_output = NULL;
   1454 
   1455 		close(arg_pipe_fds[PIPE_WRITE_INDEX]);
   1456 		close(output_pipe_fds[PIPE_READ_INDEX]);
   1457 		if (!(child_output
   1458 			    = fdopen(output_pipe_fds[PIPE_WRITE_INDEX], "w")))
   1459 		{
   1460 			perror("fdopen");
   1461 			exit(error(1, (u8*)"process_include: fdopen failed"));
   1462 		}
   1463 
   1464 		CALLOC(include_filename, char, BUFSIZE);
   1465 		pinclude_filename = include_filename;
   1466 		ptoken++;
   1467 		while (ptoken && *ptoken)
   1468 			if (*ptoken != '"')
   1469 				*pinclude_filename++ = *ptoken++;
   1470 			else
   1471 				ptoken++;
   1472 
   1473 		if (!strcmp(basedir, "."))
   1474 			set_basedir(&basedir, &basedir_size, input_dirname);
   1475 
   1476 		CALLOC(filename, char, BUFSIZE);
   1477 		snprintf(filename, BUFSIZE, "%s/%s.slw", basedir,
   1478 			include_filename);
   1479 		free(include_filename);
   1480 
   1481 		read_file_into_buffer(&input, &buffer, &buffer_size, filename,
   1482 			&input_dirname);
   1483 
   1484 		free_keyvalue(&links, links_count);
   1485 		free(links);
   1486 		links	    = NULL;
   1487 		links_count = 0;
   1488 
   1489 		free_keyvalue(&paralinks, paralinks_count);
   1490 		free(paralinks);
   1491 		paralinks	= NULL;
   1492 		paralinks_count = 0;
   1493 
   1494 		free_keyvalue(&footnotes, footnote_count);
   1495 		free(footnotes);
   1496 		footnotes	 = NULL;
   1497 		footnote_count	 = 0;
   1498 		current_footnote = 0;
   1499 
   1500 		while (inline_footnote_count--)
   1501 			free(inline_footnotes[inline_footnote_count]);
   1502 		free(inline_footnotes);
   1503 		inline_footnotes	= NULL;
   1504 		inline_footnote_count	= 0;
   1505 		current_inline_footnote = 0;
   1506 		state			= ST_NONE;
   1507 
   1508 		/* First pass: read YAML, macros and links */
   1509 		result = slweb_parse(output, filename, buffer, 1);
   1510 
   1511 		if (result || read_yaml_macros_and_links)
   1512 			goto process_include_child_cleanup;
   1513 
   1514 		state			= ST_NONE;
   1515 		current_footnote	= 0;
   1516 		current_inline_footnote = 0;
   1517 
   1518 		/* Second pass: parse and output */
   1519 		result = slweb_parse(output, filename, buffer, 0);
   1520 
   1521 	process_include_child_cleanup:
   1522 		fflush(output);
   1523 		slweb_cleanup();
   1524 		free(filename);
   1525 		free(buffer);
   1526 		fclose(child_output);
   1527 		exit(result);
   1528 	}
   1529 	else
   1530 		exit(error(1, (u8*)"process_include: fork failed"));
   1531 
   1532 	return 0;
   1533 }
   1534 
   1535 int
   1536 process_list_start(FILE* output)
   1537 {
   1538 	return 0;
   1539 }
   1540 
   1541 int
   1542 process_list_item_start(FILE* output)
   1543 {
   1544 	print_output(output, "%s* ", output_firstcol ? "" : "\n");
   1545 	state |= ST_PARA_OPEN;
   1546 	return 0;
   1547 }
   1548 
   1549 int process_paralinks(FILE* output, const u8* link_prefix);
   1550 
   1551 int
   1552 process_list_item_end(FILE* output, const u8* link_prefix)
   1553 {
   1554 	if (IN(state, ST_PARA_OPEN))
   1555 	{
   1556 		state &= ~ST_PARA_OPEN;
   1557 		print_output(output, "\n\n");
   1558 		output_firstcol = 1;
   1559 		process_paralinks(output, link_prefix);
   1560 	}
   1561 	return 0;
   1562 }
   1563 
   1564 int
   1565 process_list_end(FILE* output)
   1566 {
   1567 	return 0;
   1568 }
   1569 
   1570 int
   1571 process_numlist_start(FILE* output)
   1572 {
   1573 	return 0;
   1574 }
   1575 
   1576 int
   1577 process_numlist_end(FILE* output)
   1578 {
   1579 	return 0;
   1580 }
   1581 
   1582 int
   1583 filter_subdirs(const struct dirent* node)
   1584 {
   1585 	if (!node
   1586 		|| ((*node->d_name == '.')
   1587 			&& (!*(node->d_name + 1)
   1588 				|| ((*(node->d_name + 1) == '.')
   1589 					&& !(*(node->d_name + 2))))))
   1590 		return 0;
   1591 
   1592 	struct stat st;
   1593 	char* nodename = NULL;
   1594 
   1595 	CALLOC(nodename, char, BUFSIZE);
   1596 	snprintf(nodename, BUFSIZE, "%s/%s", incdir, node->d_name);
   1597 
   1598 	if (lstat(nodename, &st) < 0 || !S_ISDIR(st.st_mode))
   1599 	{
   1600 		free(nodename);
   1601 		return 0;
   1602 	}
   1603 
   1604 	free(nodename);
   1605 
   1606 	return 1;
   1607 }
   1608 
   1609 int
   1610 filter_slw(const struct dirent* node)
   1611 {
   1612 	if (!node
   1613 		|| ((*node->d_name == '.')
   1614 			&& (!*(node->d_name + 1)
   1615 				|| ((*(node->d_name + 1) == '.')
   1616 					&& !(*(node->d_name + 2))))))
   1617 		return 0;
   1618 
   1619 	size_t node_len = strlen(node->d_name);
   1620 	size_t slw_len	= strlen(".slw");
   1621 
   1622 	if (slw_len >= node_len)
   1623 		return 0;
   1624 	return !strcmp(node->d_name + strlen(node->d_name) - slw_len, ".slw");
   1625 }
   1626 
   1627 int
   1628 reverse_alphacompare(const struct dirent** a, const struct dirent** b)
   1629 {
   1630 	if (!a || !b)
   1631 		return 0;
   1632 
   1633 	return -1 * strcmp((*a)->d_name, (*b)->d_name);
   1634 }
   1635 
   1636 int process_timestamp(FILE* output, const u8* link_prefix, const char* link,
   1637 	const u8* permalink_macro, const u8* date, const u8* title,
   1638 	const int ext_in_permalink);
   1639 
   1640 int
   1641 process_incdir_subdir(FILE* output, const char* subdirname,
   1642 	const u8* link_prefix, const int details_open, const u8* macro_body,
   1643 	const u8* footer_permalink_text, const char* permalink_url,
   1644 	const int ext_in_permalink, const int list_only)
   1645 {
   1646 	struct dirent** namelist;
   1647 	struct dirent** pnamelist;
   1648 	long names_total = 0;
   1649 	long names_output;
   1650 	char* abs_subdirname = NULL;
   1651 
   1652 	CALLOC(abs_subdirname, char, BUFSIZE);
   1653 	snprintf(abs_subdirname, BUFSIZE, "%s/%s/%s", basedir, incdir,
   1654 		subdirname);
   1655 
   1656 	if ((names_total = scandir(abs_subdirname, &namelist, &filter_slw,
   1657 		     &reverse_alphacompare))
   1658 		< 0)
   1659 	{
   1660 		perror("scandir");
   1661 		exit(error(errno,
   1662 			(u8*)"process_incdir_subdir: scandir '%s' failed",
   1663 			abs_subdirname));
   1664 	}
   1665 
   1666 	pnamelist    = namelist;
   1667 	names_output = 0;
   1668 	while (names_output < names_total)
   1669 	{
   1670 		int pstatus = 0;
   1671 
   1672 		fflush(output);
   1673 		pid_t pid = fork();
   1674 
   1675 		if (pid > 0)
   1676 			wait(&pstatus);
   1677 		else if (pid == 0)
   1678 		{
   1679 			FILE* input	   = NULL;
   1680 			FILE* output	   = stdout;
   1681 			u8* buffer	   = NULL;
   1682 			size_t buffer_size = 0;
   1683 			int result	   = 0;
   1684 			ULLONG saved_state = ST_NONE;
   1685 			char* filename	   = NULL;
   1686 			char* abs_filename = NULL;
   1687 			char* link	   = NULL;
   1688 			u8* title	   = NULL;
   1689 			u8* date	   = NULL;
   1690 
   1691 			if (!strcmp(basedir, "."))
   1692 				set_basedir(&basedir, &basedir_size,
   1693 					abs_subdirname);
   1694 			CALLOC(filename, char, BUFSIZE);
   1695 			CALLOC(abs_filename, char, BUFSIZE);
   1696 			CALLOC(link, char, BUFSIZE);
   1697 			snprintf(filename, BUFSIZE, "%s/%s", subdirname,
   1698 				(*pnamelist)->d_name);
   1699 			snprintf(abs_filename, BUFSIZE, "%s/%s", abs_subdirname,
   1700 				(*pnamelist)->d_name);
   1701 			link = strip_ext(filename);
   1702 
   1703 			read_file_into_buffer(&input, &buffer, &buffer_size,
   1704 				abs_filename, &input_dirname);
   1705 
   1706 			free_keyvalue(&links, links_count);
   1707 			free(links);
   1708 			links	    = NULL;
   1709 			links_count = 0;
   1710 
   1711 			free_keyvalue(&paralinks, paralinks_count);
   1712 			free(paralinks);
   1713 			paralinks	= 0;
   1714 			paralinks_count = 0;
   1715 
   1716 			free_keyvalue(&footnotes, footnote_count);
   1717 			free(footnotes);
   1718 			footnotes	 = NULL;
   1719 			footnote_count	 = 0;
   1720 			current_footnote = 0;
   1721 
   1722 			while (inline_footnote_count--)
   1723 				free(inline_footnotes[inline_footnote_count]);
   1724 			free(inline_footnotes);
   1725 			inline_footnotes	= NULL;
   1726 			inline_footnote_count	= 0;
   1727 			current_inline_footnote = 0;
   1728 
   1729 			saved_state	= state;
   1730 			state		= ST_INCDIR;
   1731 			output_firstcol = 1;
   1732 			skip_para_end	= 0;
   1733 
   1734 			/* First pass: read YAML, macros and links */
   1735 			result = slweb_parse(output, abs_filename, buffer, 1);
   1736 			if (result)
   1737 			{
   1738 				warning(1,
   1739 					(u8*)"process_incdir_subdir:"
   1740 					     " slweb_parse error");
   1741 				goto incdir_subdir_child_cleanup;
   1742 			}
   1743 
   1744 			title = get_value(vars, vars_count, (u8*)"title", NULL);
   1745 			date  = get_value(vars, vars_count, (u8*)"date", NULL);
   1746 
   1747 			process_timestamp(output, link_prefix, link, NULL, date,
   1748 				title, ext_in_permalink);
   1749 
   1750 			state = saved_state;
   1751 
   1752 		incdir_subdir_child_cleanup:
   1753 			fflush(output);
   1754 			slweb_cleanup();
   1755 			free(link);
   1756 			free(abs_filename);
   1757 			free(filename);
   1758 			free(buffer);
   1759 			free(abs_subdirname);
   1760 			while (names_total--)
   1761 				free(namelist[names_total]);
   1762 			free(namelist);
   1763 			exit(result);
   1764 		}
   1765 		else
   1766 			exit(error(1,
   1767 				(u8*)"process_incdir_subdir: fork failed"));
   1768 
   1769 		/*if (!list_only)
   1770 			print_output(output, "</article>\n");*/
   1771 
   1772 		pnamelist++;
   1773 		names_output++;
   1774 	}
   1775 
   1776 	while (names_total--)
   1777 		free(namelist[names_total]);
   1778 	free(namelist);
   1779 	free(abs_subdirname);
   1780 
   1781 	/*if (list_only)
   1782 		; / *print_output(output, "</ul>\n");* /
   1783 	else
   1784 		print_output(output, "</div>\n</details>\n</li>\n");*/
   1785 
   1786 	output_firstcol = 0;
   1787 	return 0;
   1788 }
   1789 
   1790 int
   1791 process_incdir(FILE* output, const u8* token, const u8* link_prefix,
   1792 	const u8* footer_permalink_text, const char* permalink_url,
   1793 	const int ext_in_permalink, const int read_yaml_macros_and_links)
   1794 {
   1795 	u8* saveptr = NULL;
   1796 	/* skipping the first token (incdir) */
   1797 	u8* arg		= NULL;
   1798 	size_t arg_len	= 0;
   1799 	long num	= 5;
   1800 	u8* macro_body	= NULL;
   1801 	int names_total = 0;
   1802 	struct dirent** namelist;
   1803 	struct dirent** pnamelist;
   1804 	long names_output;
   1805 	int details_open = 1;
   1806 	int list_only	 = 0;
   1807 	int pass;
   1808 	u8* parg = NULL;
   1809 
   1810 	if (read_yaml_macros_and_links)
   1811 		return 0;
   1812 
   1813 	(void)strtok_r((char*)token, " ", (char**)&saveptr);
   1814 	arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
   1815 	if (!arg)
   1816 		exit(error(1, (u8*)"process_incdir: Arguments required"));
   1817 
   1818 	arg_len = strlen((char*)arg);
   1819 
   1820 	if (*arg != '"' || *(arg + arg_len - 1) != '"')
   1821 		exit(error(1, (u8*)"process_incdir: First argument not string"));
   1822 
   1823 	incdir			       = strdup((char*)(arg + 1));
   1824 	*(incdir + strlen(incdir) - 1) = 0; /* trim ending " */
   1825 
   1826 	pass = 0;
   1827 
   1828 incdir_next_arg:
   1829 	arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
   1830 	if (!arg)
   1831 		goto incdir_done_args;
   1832 
   1833 	parg = arg;
   1834 	if (*parg == '=')
   1835 		macro_body = get_value(macros, macros_count, arg + 1, NULL);
   1836 	else if (!strcmp((const char*)arg, "listonly"))
   1837 		list_only = 1;
   1838 	else
   1839 	{
   1840 		while (parg && *parg)
   1841 		{
   1842 			if (*parg < '0' || *parg > '9')
   1843 				exit(error(1,
   1844 					(u8*)"process_incdir: Invalid "
   1845 					     "argument"));
   1846 			parg++;
   1847 		}
   1848 		errno = 0;
   1849 		num   = strtol((char*)arg, NULL, 10);
   1850 		if (errno)
   1851 			exit(error(errno,
   1852 				(u8*)"process_incdir: Invalid parameter "
   1853 				     "'num'"));
   1854 	}
   1855 	pass++;
   1856 	if (pass < 3)
   1857 		goto incdir_next_arg;
   1858 
   1859 incdir_done_args:
   1860 	/*print_output(output, "<ul class=\"incdir\">\n")*/;
   1861 
   1862 	if ((names_total = scandir(incdir, &namelist, &filter_subdirs,
   1863 		     &reverse_alphacompare))
   1864 		< 0)
   1865 	{
   1866 		perror("scandir");
   1867 		exit(error(errno, (u8*)"process_incdir: scandir '%s' failed",
   1868 			incdir));
   1869 	}
   1870 
   1871 	pnamelist    = namelist;
   1872 	names_output = 0;
   1873 	while (names_output < MIN(names_total, num))
   1874 	{
   1875 		/*if (list_only)
   1876 			print_output(output, "<li>%s\n",
   1877 			(*pnamelist)->d_name);*/
   1878 		process_incdir_subdir(output, (*pnamelist)->d_name, link_prefix,
   1879 			details_open, macro_body, footer_permalink_text,
   1880 			permalink_url, ext_in_permalink, list_only);
   1881 		details_open = 0;
   1882 		pnamelist++;
   1883 		names_output++;
   1884 		/*if (list_only)
   1885 			print_output(output, "</li>\n");*/
   1886 	}
   1887 	/*print_output(output, "\n");*/
   1888 	output_firstcol = 1;
   1889 
   1890 	while (names_total--)
   1891 		free(namelist[names_total]);
   1892 	free(namelist);
   1893 	free(incdir);
   1894 
   1895 	return 0;
   1896 }
   1897 
   1898 int url_is_local(const char* url);
   1899 
   1900 u8*
   1901 format_date(const u8* date_arg, const char* timestamp_format)
   1902 {
   1903 	const char* ptimestamp_format = NULL;
   1904 	u8* day			      = NULL;
   1905 	u8* month		      = NULL;
   1906 	u8* year		      = NULL;
   1907 	u8* formatted_date	      = NULL;
   1908 	u8* ptr			      = NULL;
   1909 	u8* date		      = NULL;
   1910 
   1911 	date = (u8*)strdup((const char*)date_arg);
   1912 	if (!date)
   1913 		goto format_date_cleanup;
   1914 
   1915 	CALLOC(formatted_date, u8, DATEBUFSIZE);
   1916 	ptr  = NULL;
   1917 	year = (u8*)strtok_r((char*)date, "-", (char**)&ptr);
   1918 	if (!year)
   1919 		goto format_date_cleanup;
   1920 	month = (u8*)strtok_r(NULL, "-", (char**)&ptr);
   1921 	if (!month)
   1922 		goto format_date_cleanup;
   1923 	day = (u8*)strtok_r(NULL, "T", (char**)&ptr);
   1924 	if (!day)
   1925 		goto format_date_cleanup;
   1926 	ptimestamp_format = timestamp_format;
   1927 	while (*ptimestamp_format)
   1928 	{
   1929 		if (*ptimestamp_format == 'd' || *ptimestamp_format == 'D')
   1930 		{
   1931 			if (strlcat((char*)formatted_date, (char*)day,
   1932 				    DATEBUFSIZE)
   1933 				> DATEBUFSIZE)
   1934 				warning(1, (u8*)"strlcat:%d: Overflow",
   1935 					__LINE__);
   1936 		}
   1937 		else if (*ptimestamp_format == 'm' || *ptimestamp_format == 'M')
   1938 		{
   1939 			if (strlcat((char*)formatted_date, (char*)month,
   1940 				    DATEBUFSIZE)
   1941 				> DATEBUFSIZE)
   1942 				warning(1, (u8*)"strlcat:%d: Overflow",
   1943 					__LINE__);
   1944 		}
   1945 		else if (*ptimestamp_format == 'y' || *ptimestamp_format == 'Y')
   1946 		{
   1947 			if (strlcat((char*)formatted_date, (char*)year,
   1948 				    DATEBUFSIZE)
   1949 				> DATEBUFSIZE)
   1950 				warning(1, (u8*)"strlcat:%d: Overflow",
   1951 					__LINE__);
   1952 		}
   1953 		else
   1954 			*(formatted_date + strlen((char*)formatted_date))
   1955 				= *ptimestamp_format;
   1956 
   1957 		ptimestamp_format++;
   1958 	}
   1959 	return formatted_date;
   1960 
   1961 format_date_cleanup:
   1962 	free(formatted_date);
   1963 	free(date);
   1964 
   1965 	return NULL;
   1966 }
   1967 
   1968 int
   1969 process_timestamp(FILE* output, const u8* link_prefix, const char* link,
   1970 	const u8* permalink_macro, const u8* date, const u8* title,
   1971 	const int ext_in_permalink)
   1972 {
   1973 	char* in_filename  = NULL;
   1974 	char* in_line	   = NULL;
   1975 	u8* formatted_date = NULL;
   1976 
   1977 	formatted_date = format_date(date, timestamp_format);
   1978 	if (!formatted_date)
   1979 		goto process_timestamp_cleanup;
   1980 
   1981 	print_output(output, "=> %s%s%s%s\t%s %s\n",
   1982 		url_is_local(link) && global_link_prefix
   1983 			? (const char*)global_link_prefix
   1984 			: "",
   1985 		url_is_local(link) && link_prefix ? (const char*)link_prefix
   1986 						  : "",
   1987 		link, ext_in_permalink ? timestamp_output_ext : "",
   1988 		formatted_date, title ? (const char*)title : "");
   1989 	output_firstcol = 1;
   1990 
   1991 process_timestamp_cleanup:
   1992 	free(formatted_date);
   1993 	free(in_line);
   1994 	free(in_filename);
   1995 
   1996 	return 0;
   1997 }
   1998 
   1999 int
   2000 process_macro_def(FILE* output, const u8* token,
   2001 	const int read_yaml_macros_and_links, const int end_tag)
   2002 {
   2003 	if (end_tag)
   2004 	{
   2005 		fflush(output);
   2006 		state &= ~ST_MACRO_BODY;
   2007 		return 0;
   2008 	}
   2009 
   2010 	if (IN(state, ST_MACRO_BODY))
   2011 		exit(error(1,
   2012 			(u8*)"process_macro_def: Cannot nest definitions"));
   2013 
   2014 	if (read_yaml_macros_and_links)
   2015 	{
   2016 		macros_count++;
   2017 
   2018 		if (!macros)
   2019 		{
   2020 			CALLOC(macros, KeyValue, macros_count);
   2021 			pmacros = macros;
   2022 		}
   2023 		else
   2024 		{
   2025 			REALLOC(macros, KeyValue,
   2026 				macros_count * sizeof(KeyValue));
   2027 			pmacros = macros + macros_count - 1;
   2028 		}
   2029 		CALLOC(pmacros->key, u8, KEYSIZE);
   2030 		if (strlcpy((char*)pmacros->key, (char*)token + 2, KEYSIZE)
   2031 			>= KEYSIZE)
   2032 			warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
   2033 		pmacros->value	    = NULL;
   2034 		pmacros->value_size = 0;
   2035 	}
   2036 	fflush(output);
   2037 	state |= ST_MACRO_BODY;
   2038 
   2039 	return 0;
   2040 }
   2041 
   2042 int
   2043 process_macro(FILE* output, const u8* token, const int end_tag)
   2044 {
   2045 	u8* macro_body;
   2046 	u8* eol;
   2047 	size_t macro_body_len;
   2048 
   2049 	macro_body = get_value(macros, macros_count, token + 1, NULL);
   2050 	if (!macro_body)
   2051 		exit(error(1, (u8*)"process_macro: Macro '%s' undefined",
   2052 			token + 1));
   2053 
   2054 	eol	       = (u8*)strrchr((char*)macro_body, '\n');
   2055 	macro_body_len = strlen((char*)macro_body);
   2056 	if (eol)
   2057 		output_firstcol = macro_body + macro_body_len
   2058 				- (u8*)strrchr((char*)macro_body, '\n')
   2059 			> 0;
   2060 	else
   2061 		output_firstcol = !*macro_body
   2062 			|| *(macro_body + macro_body_len - 1) == '\n';
   2063 	print_output(output, "%s", macro_body);
   2064 
   2065 	return 0;
   2066 }
   2067 
   2068 int process_para_end(FILE* output, const u8* link_prefix,
   2069 	const int previous_line_blank, const int read_yaml_macros_and_links);
   2070 
   2071 int
   2072 process_tag(FILE* output, const u8* token, const char* source_filename,
   2073 	const u8* link_prefix, const int previous_line_blank,
   2074 	const int first_line_in_doc, const u8* footer_permalink_text,
   2075 	const char* permalink_url, const int ext_in_permalink,
   2076 	const int read_yaml_macros_and_links, int* skip_eol, const int end_tag)
   2077 {
   2078 	if (!token || strlen((char*)token) < 1)
   2079 		return warning(1, (u8*)"%s:%ld:%ld: Empty tag name",
   2080 			input_filename, lineno, colno);
   2081 
   2082 	if (!strcmp((char*)token, "git-log")
   2083 		&& !read_yaml_macros_and_links) /* {git-log} */
   2084 	{
   2085 		process_git_log(output);
   2086 	}
   2087 	else if (startswith((char*)token, "csv")) /* {csv} */
   2088 		process_csv(output, token, read_yaml_macros_and_links, end_tag);
   2089 	else if (startswith((char*)token, "tsv")) /* {csv} */
   2090 		process_tsv(output, token, read_yaml_macros_and_links, end_tag);
   2091 	else if (startswith((char*)token, "include")) /* {include} */
   2092 	{
   2093 		process_include(output, token, read_yaml_macros_and_links);
   2094 		*skip_eol = 1;
   2095 	}
   2096 	else if (startswith((char*)token, "incdir")) /* {incdir} */
   2097 	{
   2098 		process_incdir(output, token, link_prefix,
   2099 			footer_permalink_text, permalink_url, ext_in_permalink,
   2100 			read_yaml_macros_and_links);
   2101 		*skip_eol = 1;
   2102 	}
   2103 	else if (*token == '=') /* {=macro} */
   2104 	{
   2105 		if (*(token + 1) == '!')
   2106 			process_macro_def(output, token,
   2107 				read_yaml_macros_and_links, end_tag);
   2108 		else if ((*(token + 1) != '!') && !read_yaml_macros_and_links)
   2109 			process_macro(output, token, end_tag);
   2110 		*skip_eol = 1;
   2111 	}
   2112 	else if (startswith((char*)token, "p") && !read_yaml_macros_and_links)
   2113 	{
   2114 		skip_para_end = 1;
   2115 		*skip_eol     = 1;
   2116 		if (end_tag)
   2117 		{
   2118 			process_para_end(output, link_prefix,
   2119 				previous_line_blank,
   2120 				read_yaml_macros_and_links);
   2121 			output_firstcol = 1;
   2122 		}
   2123 	}
   2124 
   2125 	return 0;
   2126 }
   2127 
   2128 int
   2129 process_bold(FILE* output, int end_tag)
   2130 {
   2131 	return 0;
   2132 }
   2133 
   2134 int
   2135 process_italic(FILE* output, int end_tag)
   2136 {
   2137 	return 0;
   2138 }
   2139 
   2140 int
   2141 process_code(FILE* output, int end_tag)
   2142 {
   2143 	return 0;
   2144 }
   2145 
   2146 int
   2147 process_blockquote(FILE* output, int end_tag)
   2148 {
   2149 	if (end_tag)
   2150 		print_output(output, "\n\n");
   2151 	else
   2152 		print_output(output, "%s>", output_firstcol ? "" : "\n");
   2153 	return 0;
   2154 }
   2155 
   2156 int
   2157 process_kbd(FILE* output, int end_tag)
   2158 {
   2159 	return 0;
   2160 }
   2161 
   2162 int
   2163 process_madeby(FILE* output)
   2164 {
   2165 	print_output(output,
   2166 		"Generated by slw2gmi © %s Strahinya Radich.\n"
   2167 		"=> %s slw2gmi\n",
   2168 		COPYRIGHTYEAR, MADEBY_URL);
   2169 
   2170 	output_firstcol = 1;
   2171 	return 0;
   2172 }
   2173 
   2174 int
   2175 process_strike(FILE* output, int end_tag)
   2176 {
   2177 	print_output(output, "~");
   2178 	return 0;
   2179 }
   2180 
   2181 int
   2182 process_table_start(FILE* output)
   2183 {
   2184 	return 0;
   2185 }
   2186 
   2187 int
   2188 process_table_header_cell(FILE* output)
   2189 {
   2190 	return 0;
   2191 }
   2192 
   2193 int
   2194 process_table_header_end(FILE* output)
   2195 {
   2196 	print_output(output, "\n");
   2197 	return 0;
   2198 }
   2199 
   2200 int
   2201 process_table_body_start(FILE* output, int start_row)
   2202 {
   2203 	return 0;
   2204 }
   2205 
   2206 int
   2207 process_table_body_row_start(FILE* output)
   2208 {
   2209 	return 0;
   2210 }
   2211 
   2212 int
   2213 process_table_body_cell(FILE* output)
   2214 {
   2215 	return 0;
   2216 }
   2217 
   2218 int
   2219 process_table_body_row_end(FILE* output)
   2220 {
   2221 	print_output(output, "\n");
   2222 	return 0;
   2223 }
   2224 
   2225 int
   2226 process_table_end(FILE* output)
   2227 {
   2228 	return 0;
   2229 }
   2230 
   2231 int
   2232 url_is_local(const char* url)
   2233 {
   2234 	return !(startswith(url, "http://") || startswith(url, "https://")
   2235 		|| startswith(url, "gemini://") || startswith(url, "ftp://")
   2236 		|| startswith(url, "ftps://") || startswith(url, "mailto://"));
   2237 }
   2238 
   2239 int
   2240 process_inline_link(FILE* output, const u8* link_text)
   2241 {
   2242 	print_output(output, "%s", link_text);
   2243 	return 0;
   2244 }
   2245 
   2246 int
   2247 process_link(FILE* output, const u8* link_text)
   2248 {
   2249 	return process_inline_link(output, link_text);
   2250 }
   2251 
   2252 int
   2253 process_inline_image(FILE* output, const u8* image_text)
   2254 {
   2255 	print_output(output, "%s ", image_text);
   2256 	return 0;
   2257 }
   2258 
   2259 int
   2260 process_image(FILE* output, const u8* image_text)
   2261 {
   2262 	return process_inline_image(output, image_text);
   2263 }
   2264 
   2265 int
   2266 process_paralinks(FILE* output, const u8* link_prefix)
   2267 {
   2268 	KeyValue* pparalink = paralinks;
   2269 	if (!skip_para_end)
   2270 	{
   2271 		/*
   2272 		 *if (!output_firstcol && (paralinks_count > 0))
   2273 		 *    print_output(output, "\n");
   2274 		 */
   2275 
   2276 		/*print_output(output, "\n");*/
   2277 	}
   2278 	// fprintf(stderr, "PL_count=%ld\n", paralinks_count);
   2279 	while (pparalink < paralinks + paralinks_count)
   2280 	{
   2281 		if (pparalink->key)
   2282 		{
   2283 			/*fprintf(stderr, "PL{%s=%s}\n", pparalink->key,
   2284 				pparalink->value);*/
   2285 			if (!output_firstcol)
   2286 				print_output(output, "\n");
   2287 			print_output(output, "=> %s%s%s\t%s\n",
   2288 				url_is_local((const char*)pparalink->key)
   2289 						&& global_link_prefix
   2290 					? (const char*)global_link_prefix
   2291 					: "",
   2292 				url_is_local((const char*)pparalink->key)
   2293 						&& link_prefix
   2294 					? (const char*)link_prefix
   2295 					: "",
   2296 				pparalink->key, pparalink->value);
   2297 			output_firstcol = 1;
   2298 		}
   2299 		pparalink++;
   2300 	}
   2301 	if (paralinks_count > 0)
   2302 		print_output(output, "\n");
   2303 
   2304 	free_keyvalue(&paralinks, paralinks_count);
   2305 	free(paralinks);
   2306 	paralinks	= NULL;
   2307 	paralinks_count = 0;
   2308 	pparalinks	= paralinks;
   2309 
   2310 	return 0;
   2311 }
   2312 
   2313 int
   2314 process_para_end(FILE* output, const u8* link_prefix,
   2315 	const int previous_line_blank, const int read_yaml_macros_and_links)
   2316 {
   2317 	if (!read_yaml_macros_and_links && !skip_para_end
   2318 		&& !previous_line_blank)
   2319 		print_output(output, "\n");
   2320 	process_paralinks(output, link_prefix);
   2321 
   2322 	return 0;
   2323 }
   2324 
   2325 int
   2326 process_line_start(FILE* output, const u8* line, const u8* link_prefix,
   2327 	const int first_line_in_doc, const int previous_line_blank,
   2328 	int* previous_line_block, const int read_yaml_macros_and_links,
   2329 	const int list_para, const int new_para_ok)
   2330 {
   2331 	if ((first_line_in_doc || previous_line_blank || *previous_line_block)
   2332 		&& !(ANY(state, ST_BLOCKQUOTE | ST_PRE)))
   2333 	{
   2334 		if (!list_para)
   2335 		{
   2336 			if (IN(state, ST_LIST))
   2337 			{
   2338 				state &= ~ST_LIST;
   2339 				if (!read_yaml_macros_and_links)
   2340 				{
   2341 					process_list_item_end(output,
   2342 						link_prefix);
   2343 					process_list_end(output);
   2344 				}
   2345 			}
   2346 
   2347 			if (IN(state, ST_NUMLIST))
   2348 			{
   2349 				state &= ~ST_NUMLIST;
   2350 				if (!read_yaml_macros_and_links)
   2351 				{
   2352 					process_list_item_end(output,
   2353 						link_prefix);
   2354 					process_numlist_end(output);
   2355 				}
   2356 			}
   2357 
   2358 			if (IN(state, ST_FOOTNOTE_TEXT))
   2359 			{
   2360 				if (!read_yaml_macros_and_links
   2361 					&& (IN(state, ST_PARA_OPEN)))
   2362 				{
   2363 					process_para_end(output, link_prefix,
   2364 						previous_line_blank,
   2365 						read_yaml_macros_and_links);
   2366 					output_firstcol = 1;
   2367 				}
   2368 				state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN);
   2369 			}
   2370 		}
   2371 		if (new_para_ok
   2372 			&& !ANY(state,
   2373 				ST_TABLE | ST_TABLE_HEADER | ST_TABLE_LINE))
   2374 		{
   2375 			if (!read_yaml_macros_and_links)
   2376 			{
   2377 				print_output(output, "%s",
   2378 					output_firstcol && previous_line_blank
   2379 						? ""
   2380 						: "\n");
   2381 				*previous_line_block = 0;
   2382 			}
   2383 			output_firstcol = 1;
   2384 			skip_para_end	= 0;
   2385 			state |= ST_PARA_OPEN;
   2386 		}
   2387 	}
   2388 	return 0;
   2389 }
   2390 
   2391 int
   2392 process_text_token(FILE* output, const u8* line, const u8* link_prefix,
   2393 	const int first_line_in_doc, const int previous_line_blank,
   2394 	int* previous_line_block, const int processed_start_of_line,
   2395 	const int read_yaml_macros_and_links, const int list_para, u8** token,
   2396 	u8** ptoken, size_t* token_size, const int add_enclosing_paragraph,
   2397 	const int new_para_ok)
   2398 {
   2399 	if (!(IN(state, ST_YAML)))
   2400 	{
   2401 		if (add_enclosing_paragraph && !processed_start_of_line)
   2402 			process_line_start(output, line, link_prefix,
   2403 				first_line_in_doc, previous_line_blank,
   2404 				previous_line_block, read_yaml_macros_and_links,
   2405 				list_para, new_para_ok);
   2406 		**ptoken = 0;
   2407 		if (**token && !read_yaml_macros_and_links
   2408 			&& !(IN(state, ST_MACRO_BODY)))
   2409 		{
   2410 			print_output(output, "%s", *token);
   2411 			u8* eol = (u8*)strrchr((char*)*token, '\n');
   2412 			if (eol)
   2413 				output_firstcol
   2414 					= *token + strlen((char*)*token) - eol
   2415 					> 0;
   2416 			else
   2417 				output_firstcol = 0;
   2418 		}
   2419 	}
   2420 	RESET_TOKEN(*token, *ptoken, *token_size);
   2421 	return 0;
   2422 }
   2423 
   2424 int
   2425 process_inline_footnote(const u8* token, const int read_yaml_macros_and_links,
   2426 	FILE* output)
   2427 {
   2428 	current_inline_footnote++;
   2429 
   2430 	if (read_yaml_macros_and_links)
   2431 	{
   2432 		size_t token_len = strlen((char*)token);
   2433 
   2434 		inline_footnote_count++;
   2435 		if (inline_footnote_count == 1 && footnote_count > 0)
   2436 			warning(1,
   2437 				(u8*)"Both inline and regular footnotes "
   2438 				     "present");
   2439 		else if (!inline_footnotes)
   2440 		{
   2441 			inline_footnote_count = 1;
   2442 			CALLOC(inline_footnotes, u8*, inline_footnote_count);
   2443 		}
   2444 		else
   2445 			REALLOC(inline_footnotes, u8*,
   2446 				sizeof(u8*) * inline_footnote_count);
   2447 		CALLOC(inline_footnotes[inline_footnote_count - 1], u8,
   2448 			token_len + 1);
   2449 
   2450 		if (strlcpy((char*)inline_footnotes[inline_footnote_count - 1],
   2451 			    (char*)token, token_len + 1)
   2452 			>= token_len + 1)
   2453 			warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
   2454 		*(inline_footnotes[inline_footnote_count - 1] + token_len) = 0;
   2455 	}
   2456 	else
   2457 	{
   2458 		print_output(output, "(%d)", current_inline_footnote);
   2459 		output_firstcol = 0;
   2460 	}
   2461 
   2462 	return 0;
   2463 }
   2464 
   2465 int
   2466 process_footnote(FILE* output, const u8* token, const int footnote_definition,
   2467 	const int footnote_output)
   2468 {
   2469 	current_footnote++;
   2470 
   2471 	if (footnote_definition)
   2472 	{
   2473 		footnote_count++;
   2474 		if (footnote_count == 1 && inline_footnote_count > 0)
   2475 			warning(1,
   2476 				(u8*)"Both inline and regular footnotes "
   2477 				     "present");
   2478 		else if (!footnotes)
   2479 		{
   2480 			CALLOC(footnotes, KeyValue, footnote_count);
   2481 			pfootnotes = footnotes;
   2482 		}
   2483 		else
   2484 		{
   2485 			REALLOCARRAY(footnotes, KeyValue, footnote_count);
   2486 			pfootnotes = footnotes + footnote_count - 1;
   2487 		}
   2488 		CALLOC(pfootnotes->key, u8, KEYSIZE);
   2489 		if (strlcpy((char*)pfootnotes->key, (char*)token, KEYSIZE)
   2490 			>= KEYSIZE)
   2491 			warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
   2492 		pfootnotes->value      = NULL;
   2493 		pfootnotes->value_size = 0;
   2494 	}
   2495 
   2496 	if (footnote_output)
   2497 	{
   2498 		print_output(output, "(%d)", current_footnote);
   2499 		output_firstcol = 0;
   2500 	}
   2501 
   2502 	return 0;
   2503 }
   2504 
   2505 int
   2506 process_horizontal_rule(FILE* output, const u8* link_prefix,
   2507 	const int previous_line_blank, const int read_yaml_macros_and_links)
   2508 {
   2509 	/* Temporarily break paragraph as hr is para-level */
   2510 	if (IN(state, ST_PARA_OPEN))
   2511 		process_para_end(output, link_prefix, previous_line_blank,
   2512 			read_yaml_macros_and_links);
   2513 	print_output(output, "————————————————————\n");
   2514 	if (IN(state, ST_PARA_OPEN))
   2515 		print_output(output, "\n");
   2516 	return 0;
   2517 }
   2518 
   2519 int
   2520 process_formula(FILE* output, const u8* token, int display_formula)
   2521 {
   2522 	return 0;
   2523 }
   2524 
   2525 int
   2526 begin_article(FILE* output, const char* source_filename, const u8* link_prefix,
   2527 	const int add_article_header, const u8* author, const u8* title,
   2528 	const u8* header_text, const char* title_heading_level, u8* date,
   2529 	const int ext_in_permalink, int first_line_in_doc,
   2530 	int* previous_line_blank, const char* permalink_url)
   2531 {
   2532 	if (title)
   2533 	{
   2534 		process_heading_start(output, 1);
   2535 		process_heading(output, title);
   2536 	}
   2537 	int save_incdir = IN(state, ST_INCDIR);
   2538 	state &= ~ST_INCDIR;
   2539 
   2540 	if (header_text)
   2541 		print_output(output, "%s\n\n", (char*)header_text);
   2542 
   2543 	if (author)
   2544 		print_output(output, "%s\n\n", author);
   2545 
   2546 	if (date && source_filename)
   2547 	{
   2548 		char* link = strip_ext((const char*)source_filename);
   2549 
   2550 		if (ext_in_permalink
   2551 			&& strlcat(link, timestamp_output_ext, sizeof link)
   2552 				> sizeof link)
   2553 			warning(1, (u8*)"strlcat:%d: Overflow", __LINE__);
   2554 
   2555 		free(link);
   2556 	}
   2557 
   2558 	/*if (author || date || header_text || title)
   2559 		print_output(output, "\n");*/
   2560 
   2561 	output_firstcol	     = author || date || header_text || title;
   2562 	*previous_line_blank = author || header_text || title;
   2563 
   2564 	if (save_incdir)
   2565 		state |= ST_INCDIR;
   2566 
   2567 	return 0;
   2568 }
   2569 
   2570 int
   2571 end_footnotes(FILE* output, const u8* link_prefix, const int add_footnote_div,
   2572 	const int previous_line_blank, const int read_yaml_macros_and_links)
   2573 {
   2574 	size_t footnote = 0;
   2575 
   2576 	if (IN(state, ST_PARA_OPEN))
   2577 	{
   2578 		output_firstcol = 1;
   2579 		state &= ~ST_PARA_OPEN;
   2580 		if (paralinks_count > 0)
   2581 			process_paralinks(output, link_prefix);
   2582 	}
   2583 
   2584 	process_horizontal_rule(output, link_prefix, previous_line_blank,
   2585 		read_yaml_macros_and_links);
   2586 	output_firstcol = 1;
   2587 
   2588 	for (footnote = 0; footnote < inline_footnote_count; footnote++)
   2589 	{
   2590 		print_output(output, "%d. %s\n\n", footnote + 1,
   2591 			(char*)inline_footnotes[footnote]);
   2592 		output_firstcol = 1;
   2593 	}
   2594 
   2595 	KeyValue* pfootnote = footnotes;
   2596 	footnote	    = 0;
   2597 	while (pfootnote && footnote < footnote_count)
   2598 	{
   2599 		print_output(output, "%d. %s\n\n", footnote + 1,
   2600 			(char*)pfootnote->value);
   2601 		output_firstcol = 1;
   2602 		pfootnote++;
   2603 		footnote++;
   2604 	}
   2605 
   2606 	return 0;
   2607 }
   2608 
   2609 int
   2610 simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count,
   2611 	KeyValue** pvars)
   2612 {
   2613 	u8* token	= NULL;
   2614 	u8* ptoken	= NULL;
   2615 	u8* var_key	= NULL;
   2616 	const u8* pline = line;
   2617 	int in_arg	= 0;
   2618 
   2619 	if (!line || !vars || !vars_count || !pvars)
   2620 		exit(error(EINVAL,
   2621 			(u8*)"simple_parse_yaml_line: Invalid argument"));
   2622 
   2623 	CALLOC(token, u8, BUFSIZE);
   2624 	ptoken	= token;
   2625 	*ptoken = 0;
   2626 	while (*pline)
   2627 	{
   2628 		if (!in_arg && *pline == ':')
   2629 		{
   2630 			in_arg	= 1;
   2631 			*ptoken = 0;
   2632 			var_key = (u8*)strdup((char*)token);
   2633 			if (!var_key)
   2634 				exit(error(ENOMEM,
   2635 					(u8*)"simple_parse_yaml_line: strdup "
   2636 					     "failed (out of "
   2637 					     "memory?)"));
   2638 			ptoken	= token;
   2639 			*ptoken = 0;
   2640 			pline++;
   2641 		}
   2642 		else if (*pline == ' ' || *pline == '\t')
   2643 			pline++;
   2644 		else
   2645 			*ptoken++ = *pline++;
   2646 	}
   2647 	if (*token)
   2648 		*ptoken = 0;
   2649 	if (var_key && *var_key && *token)
   2650 	{
   2651 		int found      = 0;
   2652 		KeyValue* cvar = NULL;
   2653 
   2654 		cvar = *vars;
   2655 		while (!found && cvar != *vars + *vars_count)
   2656 		{
   2657 			if (!strcmp((const char*)cvar->key, (const char*)var_key)
   2658 				&& cvar->value_size
   2659 				&& !strcmp((const char*)cvar->value,
   2660 					(const char*)token))
   2661 				found = 1;
   2662 			cvar++;
   2663 		}
   2664 		if (!found)
   2665 		{
   2666 			(*vars_count)++;
   2667 			if (!*vars)
   2668 			{
   2669 				CALLOC(*vars, KeyValue, *vars_count);
   2670 				*pvars = *vars;
   2671 			}
   2672 			else
   2673 			{
   2674 				REALLOCARRAY(*vars, KeyValue, *vars_count);
   2675 				*pvars = *vars + *vars_count - 1;
   2676 			}
   2677 			CALLOC((*pvars)->key, u8, KEYSIZE);
   2678 			if (strlcpy((char*)(*pvars)->key, (char*)var_key,
   2679 				    KEYSIZE)
   2680 				>= KEYSIZE)
   2681 				warning(1, (u8*)"strlcpy:%d: Overflow",
   2682 					__LINE__);
   2683 			(*pvars)->value_size = strlen((char*)token) + 1;
   2684 			CALLOC((*pvars)->value, u8, (*pvars)->value_size);
   2685 			if (strlcpy((char*)(*pvars)->value, (char*)token,
   2686 				    (*pvars)->value_size)
   2687 				>= (*pvars)->value_size)
   2688 				warning(1, (u8*)"strlcpy:%d: Overflow",
   2689 					__LINE__);
   2690 		}
   2691 	}
   2692 	free(var_key);
   2693 	free(token);
   2694 	return 0;
   2695 }
   2696 
   2697 int
   2698 slweb_parse(FILE* output, const char* source_filename, const u8* buffer,
   2699 	const int read_yaml_macros_and_links)
   2700 {
   2701 	u8* title		   = NULL;
   2702 	u8* title_heading_level	   = NULL;
   2703 	u8* header_text		   = NULL;
   2704 	u8* author		   = NULL;
   2705 	u8* date		   = NULL;
   2706 	u8* permalink_url	   = NULL;
   2707 	u8* ext_in_permalink	   = NULL;
   2708 	u8* var_add_article_header = NULL;
   2709 	/*u8* var_add_image_links		  = NULL;
   2710 	u8* var_add_figcaption		  = NULL;*/
   2711 	u8* var_add_footnote_div    = NULL;
   2712 	u8* footer_permalink_text   = NULL;
   2713 	u8* var_incdir_only_summary = NULL;
   2714 	/*u8* image_file_prefix		  = NULL;*/
   2715 	u8* link_prefix			  = NULL;
   2716 	const u8* pbuffer		  = NULL;
   2717 	u8* line			  = NULL;
   2718 	u8* pline			  = NULL;
   2719 	size_t line_len			  = 0;
   2720 	size_t line_size		  = 0;
   2721 	u8* token			  = NULL;
   2722 	u8* ptoken			  = NULL;
   2723 	size_t token_size		  = 0;
   2724 	u8* link_text			  = NULL;
   2725 	size_t link_size		  = 0;
   2726 	u8* link_macro			  = NULL;
   2727 	UBYTE heading_level		  = 0;
   2728 	int end_tag			  = 0;
   2729 	int first_line_in_doc		  = 1;
   2730 	int skip_change_first_line_in_doc = 0;
   2731 	int skip_eol			  = 0;
   2732 	int keep_token			  = 0;
   2733 	int previous_line_blank		  = 0;
   2734 	int previous_line_block		  = 0;
   2735 	int processed_start_of_line	  = 0;
   2736 	/*int add_image_links		  = 1;
   2737 	int add_figcaption		  = 1;*/
   2738 	int add_footnote_div	   = 0;
   2739 	int list_para		   = 0;
   2740 	int footnote_at_line_start = 0;
   2741 	size_t pline_len	   = 0;
   2742 	int madeby_present	   = 0;
   2743 	u8* eol			   = NULL;
   2744 
   2745 	if (!buffer)
   2746 		exit(error(1, (u8*)"slweb_parse: Empty buffer"));
   2747 
   2748 	title = get_value(vars, vars_count, (u8*)"title", NULL);
   2749 	title_heading_level
   2750 		= get_value(vars, vars_count, (u8*)"title-heading-level", NULL);
   2751 	header_text   = get_value(vars, vars_count, (u8*)"header-text", NULL);
   2752 	author	      = get_value(vars, vars_count, (u8*)"author", NULL);
   2753 	date	      = get_value(vars, vars_count, (u8*)"date", NULL);
   2754 	permalink_url = get_value(vars, vars_count, (u8*)"permalink-url", NULL);
   2755 	/*image_file_prefix
   2756 		= get_value(vars, vars_count, (u8*)"image-file-prefix", NULL);*/
   2757 	ext_in_permalink
   2758 		= get_value(vars, vars_count, (u8*)"ext-in-permalink", NULL);
   2759 	var_add_article_header
   2760 		= get_value(vars, vars_count, (u8*)"add-article-header", NULL);
   2761 	/*var_add_image_links
   2762 		= get_value(vars, vars_count, (u8*)"add-image-links", NULL);
   2763 	add_image_links = !(var_add_image_links && *var_add_image_links == '0');
   2764 	var_add_figcaption
   2765 		= get_value(vars, vars_count, (u8*)"add-figcaption", NULL);
   2766 	add_figcaption = !(var_add_figcaption && *var_add_figcaption == '0');*/
   2767 	var_add_footnote_div
   2768 		= get_value(vars, vars_count, (u8*)"add-footnote-div", NULL);
   2769 	add_footnote_div = var_add_footnote_div && *var_add_footnote_div == '1';
   2770 	link_prefix = get_value(vars, vars_count, (u8*)"link-prefix", NULL);
   2771 	footer_permalink_text = get_value(vars, vars_count,
   2772 		(u8*)"incdir-footer-permalink-text", NULL);
   2773 	var_incdir_only_summary
   2774 		= get_value(vars, vars_count, (u8*)"incdir-only-summary", NULL);
   2775 	incdir_only_summary
   2776 		= var_incdir_only_summary && *var_incdir_only_summary == '1';
   2777 
   2778 	line_size = BUFSIZE;
   2779 	CALLOC(line, u8, line_size);
   2780 	token_size = BUFSIZE;
   2781 	CALLOC(token, u8, BUFSIZE);
   2782 	CALLOC(link_macro, u8, BUFSIZE);
   2783 
   2784 	pbuffer	   = buffer;
   2785 	pvars	   = vars;
   2786 	pmacros	   = macros;
   2787 	plinks	   = links;
   2788 	pparalinks = paralinks;
   2789 	pfootnotes = footnotes;
   2790 	lineno	   = 0;
   2791 
   2792 	if (!read_yaml_macros_and_links)
   2793 		begin_article(output, source_filename, link_prefix,
   2794 			var_add_article_header && *var_add_article_header == '1',
   2795 			author, title, header_text, (char*)title_heading_level,
   2796 			date, ext_in_permalink && *ext_in_permalink != '0',
   2797 			first_line_in_doc, &previous_line_blank,
   2798 			(char*)permalink_url);
   2799 
   2800 	RESET_TOKEN(token, ptoken, token_size);
   2801 
   2802 	output_firstcol = 1;
   2803 
   2804 do_buffer:
   2805 	eol = (u8*)strchr((char*)pbuffer, '\n');
   2806 	if (!eol)
   2807 		goto done_buffer;
   2808 
   2809 	pline = line;
   2810 	while (pbuffer != eol)
   2811 		CHECKCOPY(line, pline, line_size, pbuffer);
   2812 
   2813 	if (pbuffer == eol)
   2814 		pbuffer++;
   2815 
   2816 	*pline	 = 0;
   2817 	pline	 = line;
   2818 	line_len = strlen((char*)line);
   2819 
   2820 	lineno++;
   2821 	colno			= 1;
   2822 	processed_start_of_line = 0;
   2823 	skip_eol		= 0;
   2824 	list_para		= 0;
   2825 
   2826 do_line:
   2827 	if (!pline || !*pline)
   2828 		goto done_line;
   2829 	switch (*pline)
   2830 	{
   2831 	case '-':
   2832 		if (ANY(state,
   2833 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   2834 				    | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL))
   2835 		{
   2836 			CHECKCOPY(token, ptoken, token_size, pline);
   2837 			colno++;
   2838 		}
   2839 		else if (IN(state, ST_HTML_TAG))
   2840 		{
   2841 			pline++;
   2842 			colno++;
   2843 			break;
   2844 		}
   2845 		else if (colno == 1 && strlen((char*)pline) > 2
   2846 			&& startswith((char*)pline, "---"))
   2847 		{
   2848 			skip_eol = 1;
   2849 
   2850 			if (!(IN(state, ST_YAML)) && (lineno > 1)
   2851 				&& !read_yaml_macros_and_links)
   2852 			{
   2853 				process_horizontal_rule(output, link_prefix,
   2854 					previous_line_blank,
   2855 					read_yaml_macros_and_links);
   2856 				output_firstcol = 1;
   2857 			}
   2858 			else
   2859 			{
   2860 				if (lineno == 1)
   2861 					state |= ST_YAML;
   2862 				else
   2863 					state &= ~ST_YAML;
   2864 
   2865 				skip_change_first_line_in_doc = 1;
   2866 			}
   2867 			pline = NULL;
   2868 		}
   2869 		else if (colno == 1 && *(pline + 1) == ' ')
   2870 		{
   2871 			if (IN(state, ST_NUMLIST))
   2872 			{
   2873 				state &= ~ST_NUMLIST;
   2874 				if (!read_yaml_macros_and_links)
   2875 				{
   2876 					process_list_item_end(output,
   2877 						link_prefix);
   2878 					process_numlist_end(output);
   2879 				}
   2880 			}
   2881 			if (!read_yaml_macros_and_links)
   2882 			{
   2883 				if (!(IN(state, ST_LIST)))
   2884 					process_list_start(output);
   2885 				else
   2886 					process_list_item_end(output,
   2887 						link_prefix);
   2888 				process_list_item_start(output);
   2889 			}
   2890 
   2891 			processed_start_of_line = 1;
   2892 			skip_eol		= 0;
   2893 
   2894 			state |= ST_LIST;
   2895 
   2896 			pline += 2;
   2897 			colno += 2;
   2898 		}
   2899 		else if (IN(state, ST_SUMMARY) && *(pline + 1) == '/')
   2900 		{
   2901 			/* Output existing text up to -/ */
   2902 			process_text_token(output, line, link_prefix,
   2903 				first_line_in_doc, previous_line_blank,
   2904 				&previous_line_block, processed_start_of_line,
   2905 				read_yaml_macros_and_links, list_para, &token,
   2906 				&ptoken, &token_size, 1, 1);
   2907 			processed_start_of_line = 1;
   2908 
   2909 			state &= ~ST_SUMMARY;
   2910 
   2911 			pline += 2;
   2912 			colno += 2;
   2913 		}
   2914 		else
   2915 		{
   2916 			CHECKCOPY(token, ptoken, token_size, pline);
   2917 			colno++;
   2918 		}
   2919 		break;
   2920 
   2921 	case ':':
   2922 		if (IN(state, ST_YAML)
   2923 			&& !(ANY(state,
   2924 				ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   2925 					| ST_HEADING | ST_IMAGE | ST_MACRO_BODY
   2926 					| ST_PRE | ST_TAG | ST_YAML_VAL))
   2927 			&& read_yaml_macros_and_links)
   2928 		{
   2929 			*ptoken = 0;
   2930 
   2931 			vars_count++;
   2932 
   2933 			if (!vars)
   2934 			{
   2935 				CALLOC(vars, KeyValue, vars_count);
   2936 				pvars = vars;
   2937 			}
   2938 			else
   2939 			{
   2940 				REALLOCARRAY(vars, KeyValue, vars_count);
   2941 				pvars = vars + vars_count - 1;
   2942 			}
   2943 			CALLOC(pvars->key, u8, KEYSIZE);
   2944 			if (strlcpy((char*)pvars->key, (char*)token, KEYSIZE)
   2945 				>= KEYSIZE)
   2946 				warning(1, (u8*)"strlcpy:%d: Overflow",
   2947 					__LINE__);
   2948 			pvars->value	  = NULL;
   2949 			pvars->value_size = 0;
   2950 
   2951 			state |= ST_YAML_VAL;
   2952 			RESET_TOKEN(token, ptoken, token_size);
   2953 			pline++;
   2954 			colno++;
   2955 
   2956 			while (pline && (*pline == ' ' || *pline == '\t'))
   2957 			{
   2958 				pline++;
   2959 				colno++;
   2960 			}
   2961 		}
   2962 		else if (IN(state, ST_HTML_TAG))
   2963 		{
   2964 			pline++;
   2965 			colno++;
   2966 			break;
   2967 		}
   2968 		else
   2969 		{
   2970 			CHECKCOPY(token, ptoken, token_size, pline);
   2971 			colno++;
   2972 		}
   2973 		break;
   2974 
   2975 	case '~':
   2976 		if (ANY(state,
   2977 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   2978 				    | ST_HTML_TAG | ST_IMAGE | ST_KBD
   2979 				    | ST_LINK_SECOND_ARG
   2980 				    | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY
   2981 				    | ST_PRE | ST_TAG | ST_YAML))
   2982 		{
   2983 			CHECKCOPY(token, ptoken, token_size, pline);
   2984 			colno++;
   2985 			break;
   2986 		}
   2987 
   2988 		if (strlen((char*)pline) > 1 && *(pline + 1) == '~')
   2989 		{
   2990 			u8* entity = (u8*)" ";
   2991 			/* Handle ~~ within footnotes, headings
   2992 			 * and link text specially */
   2993 			if (ANY(state,
   2994 				    ST_INLINE_FOOTNOTE | ST_HEADING
   2995 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   2996 			{
   2997 				size_t entity_len = strlen((char*)entity);
   2998 				size_t token_len  = 0;
   2999 
   3000 				*ptoken	  = 0;
   3001 				token_len = strlen((char*)token);
   3002 
   3003 				if (token_len + entity_len < BUFSIZE)
   3004 				{
   3005 					if (strlcat((char*)token, (char*)entity,
   3006 						    token_size)
   3007 						> token_size)
   3008 						warning(1, (u8*)"strlcat:%d: Overflow",
   3009 							__LINE__);
   3010 					ptoken += entity_len;
   3011 				}
   3012 			}
   3013 			else
   3014 			{
   3015 				/* Output existing text up to ~~ */
   3016 				process_text_token(output, line, link_prefix,
   3017 					first_line_in_doc, previous_line_blank,
   3018 					&previous_line_block,
   3019 					processed_start_of_line,
   3020 					read_yaml_macros_and_links, list_para,
   3021 					&token, &ptoken, &token_size, 1, 1);
   3022 				processed_start_of_line = 1;
   3023 
   3024 				if (!read_yaml_macros_and_links
   3025 					&& !(ANY(state,
   3026 						ST_PRE | ST_CODE | ST_HEADING)))
   3027 					print_output(output, " ");
   3028 			}
   3029 
   3030 			pline += 2;
   3031 			colno += 2;
   3032 		}
   3033 		else if (strlen((char*)pline) > 1 && *(pline + 1) == '-')
   3034 		{
   3035 			u8* entity = (u8*)"-";
   3036 			/* Handle ~- within footnotes, headings
   3037 			 * and link text specially */
   3038 			if (ANY(state,
   3039 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3040 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3041 			{
   3042 				size_t entity_len = strlen((char*)entity);
   3043 				size_t token_len  = 0;
   3044 
   3045 				*ptoken	  = 0;
   3046 				token_len = strlen((char*)token);
   3047 
   3048 				if (token_len + entity_len < BUFSIZE)
   3049 				{
   3050 					if (strlcat((char*)token, (char*)entity,
   3051 						    token_size)
   3052 						> token_size)
   3053 						warning(1, (u8*)"strlcat:%d: Overflow",
   3054 							__LINE__);
   3055 					ptoken += entity_len;
   3056 				}
   3057 			}
   3058 			else
   3059 			{
   3060 				/* Output existing text up to ~- */
   3061 				process_text_token(output, line, link_prefix,
   3062 					first_line_in_doc, previous_line_blank,
   3063 					&previous_line_block,
   3064 					processed_start_of_line,
   3065 					read_yaml_macros_and_links, list_para,
   3066 					&token, &ptoken, &token_size, 1, 1);
   3067 				processed_start_of_line = 1;
   3068 
   3069 				if (!read_yaml_macros_and_links
   3070 					&& !(ANY(state,
   3071 						ST_PRE | ST_CODE | ST_HEADING)))
   3072 					print_output(output, "-");
   3073 			}
   3074 
   3075 			pline += 2;
   3076 			colno += 2;
   3077 		}
   3078 		else
   3079 		{
   3080 			/* Handle ~ within footnotes, headings
   3081 			 * and link text specially */
   3082 			if (ANY(state,
   3083 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3084 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3085 			{
   3086 				u8* tag = IN(state, ST_STRIKE) ? (u8*)"</s>"
   3087 							       : (u8*)"<s>";
   3088 				size_t tag_len	 = strlen((char*)tag);
   3089 				size_t token_len = 0;
   3090 
   3091 				*ptoken	  = 0;
   3092 				token_len = strlen((char*)token);
   3093 
   3094 				if (token_len + tag_len < BUFSIZE)
   3095 				{
   3096 					if (strlcat((char*)token, (char*)tag,
   3097 						    token_size)
   3098 						> token_size)
   3099 						warning(1, (u8*)"strlcat:%d: Overflow",
   3100 							__LINE__);
   3101 					ptoken += tag_len;
   3102 				}
   3103 			}
   3104 			else
   3105 			{
   3106 				/* Output existing text up to ~ */
   3107 				process_text_token(output, line, link_prefix,
   3108 					first_line_in_doc, previous_line_blank,
   3109 					&previous_line_block,
   3110 					processed_start_of_line,
   3111 					read_yaml_macros_and_links, list_para,
   3112 					&token, &ptoken, &token_size, 1, 1);
   3113 				processed_start_of_line = 1;
   3114 
   3115 				if (!read_yaml_macros_and_links
   3116 					&& !(ANY(state,
   3117 						ST_PRE | ST_CODE | ST_HEADING)))
   3118 					process_strike(output,
   3119 						(IN(state, ST_STRIKE)));
   3120 			}
   3121 
   3122 			state ^= ST_STRIKE;
   3123 			pline++;
   3124 			colno++;
   3125 		}
   3126 		break;
   3127 
   3128 	case '`':
   3129 		if (ANY(state,
   3130 			    ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE
   3131 				    | ST_MACRO_BODY))
   3132 		{
   3133 			CHECKCOPY(token, ptoken, token_size, pline);
   3134 			colno++;
   3135 			break;
   3136 		}
   3137 		else if (IN(state, ST_HTML_TAG))
   3138 		{
   3139 			pline++;
   3140 			colno++;
   3141 			break;
   3142 		}
   3143 
   3144 		if (colno == 1 && strlen((char*)pline) > 2
   3145 			&& startswith((char*)pline, "```"))
   3146 		{
   3147 			state ^= ST_PRE;
   3148 
   3149 			if (!read_yaml_macros_and_links)
   3150 			{
   3151 				print_output(output, "%s```%s",
   3152 					IN(state, ST_PRE) && !output_firstcol
   3153 						? "\n"
   3154 						: "",
   3155 					IN(state, ST_PRE) ? "" : "\n\n");
   3156 				output_firstcol = !IN(state, ST_PRE);
   3157 			}
   3158 
   3159 			/* Skip the rest of the line (language) */
   3160 			pline = NULL;
   3161 		}
   3162 		else if (!ANY(state, ST_PRE | ST_TAG | ST_YAML))
   3163 		{
   3164 			if (!ANY(state,
   3165 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3166 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3167 			{
   3168 				/* Output existing text up to ` */
   3169 				process_text_token(output, line, link_prefix,
   3170 					first_line_in_doc, previous_line_blank,
   3171 					&previous_line_block,
   3172 					processed_start_of_line,
   3173 					read_yaml_macros_and_links, list_para,
   3174 					&token, &ptoken, &token_size, 1, 1);
   3175 				processed_start_of_line = 1;
   3176 			}
   3177 
   3178 			state ^= ST_CODE;
   3179 
   3180 			pline++;
   3181 			colno++;
   3182 		}
   3183 		else
   3184 		{
   3185 			CHECKCOPY(token, ptoken, token_size, pline);
   3186 			colno++;
   3187 		}
   3188 		break;
   3189 
   3190 	case '&':
   3191 		if (ANY(state,
   3192 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   3193 				    | ST_HTML_TAG | ST_IMAGE | ST_KBD
   3194 				    | ST_LINK_SECOND_ARG | ST_LINK_SECOND_ARG_END
   3195 				    | ST_MACRO_BODY
   3196 				    /*| ST_PRE*/
   3197 				    | ST_TAG | ST_YAML))
   3198 		{
   3199 			CHECKCOPY(token, ptoken, token_size, pline);
   3200 			colno++;
   3201 		}
   3202 		else if (!IN(state, ST_ENT) && *(pline + 1)
   3203 			&& isalpha(*(pline + 1)) && isascii(*(pline + 1)))
   3204 		{
   3205 			state |= ST_ENT;
   3206 			pline++;
   3207 			colno++;
   3208 		}
   3209 		else
   3210 		{
   3211 			CHECKCOPY(token, ptoken, token_size, pline);
   3212 			colno++;
   3213 		}
   3214 		break;
   3215 
   3216 	case ';':
   3217 		if (IN(state, ST_ENT))
   3218 		{
   3219 			state &= ~ST_ENT;
   3220 			*ptoken = 0;
   3221 			if (!read_yaml_macros_and_links)
   3222 			{
   3223 				/* TODO: Use map */
   3224 				if (endswith((char*)token, "amp"))
   3225 				{
   3226 					token[strlen((char*)token)
   3227 						- strlen("amp")]
   3228 						= 0;
   3229 					strcat((char*)token, "&");
   3230 				}
   3231 				else if (endswith((char*)token, "lt"))
   3232 				{
   3233 					token[strlen((char*)token) - strlen("lt")]
   3234 						= 0;
   3235 					strcat((char*)token, "<");
   3236 				}
   3237 				else if (endswith((char*)token, "gt"))
   3238 				{
   3239 					token[strlen((char*)token) - strlen("gt")]
   3240 						= 0;
   3241 					strcat((char*)token, ">");
   3242 				}
   3243 				else if (endswith((char*)token, "quot"))
   3244 				{
   3245 					token[strlen((char*)token)
   3246 						- strlen("quot")]
   3247 						= 0;
   3248 					strcat((char*)token, "\"");
   3249 				}
   3250 				else if (endswith((char*)token, "ldquo"))
   3251 				{
   3252 					token[strlen((char*)token)
   3253 						- strlen("ldquo")]
   3254 						= 0;
   3255 					strcat((char*)token, "“");
   3256 				}
   3257 				else if (endswith((char*)token, "rdquo"))
   3258 				{
   3259 					token[strlen((char*)token)
   3260 						- strlen("rdquo")]
   3261 						= 0;
   3262 					strcat((char*)token, "”");
   3263 				}
   3264 			}
   3265 			ptoken = token + strlen((char*)token);
   3266 			pline++;
   3267 			colno++;
   3268 		}
   3269 		else
   3270 		{
   3271 			CHECKCOPY(token, ptoken, token_size, pline);
   3272 			colno++;
   3273 		}
   3274 		break;
   3275 
   3276 	case '#':
   3277 		if (IN(state, ST_HTML_TAG))
   3278 		{
   3279 			pline++;
   3280 			colno++;
   3281 			break;
   3282 		}
   3283 		if (colno == 1
   3284 			&& !(ANY(state,
   3285 				ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   3286 					| ST_MACRO_BODY | ST_PRE | ST_YAML)))
   3287 		{
   3288 			if (IN(state, ST_LIST))
   3289 			{
   3290 				state &= ~ST_LIST;
   3291 				if (!read_yaml_macros_and_links)
   3292 				{
   3293 					process_list_item_end(output,
   3294 						link_prefix);
   3295 					process_list_end(output);
   3296 				}
   3297 			}
   3298 
   3299 			if (IN(state, ST_NUMLIST))
   3300 			{
   3301 				state &= ~ST_NUMLIST;
   3302 				if (!read_yaml_macros_and_links)
   3303 				{
   3304 					process_list_item_end(output,
   3305 						link_prefix);
   3306 					process_numlist_end(output);
   3307 				}
   3308 			}
   3309 
   3310 			state |= ST_HEADING;
   3311 			heading_level = 1;
   3312 			pline++;
   3313 			colno++;
   3314 		}
   3315 		else if (IN(state, ST_HEADING) && *(pline - 1) == '#')
   3316 		{
   3317 			if (heading_level < MAX_HEADING_LEVEL)
   3318 				heading_level++;
   3319 			pline++;
   3320 			colno++;
   3321 		}
   3322 		else
   3323 		{
   3324 			CHECKCOPY(token, ptoken, token_size, pline);
   3325 			colno++;
   3326 		}
   3327 		break;
   3328 
   3329 	case '_':
   3330 		if (ANY(state,
   3331 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   3332 				    | ST_HTML_TAG | ST_IMAGE | ST_LINK_SECOND_ARG
   3333 				    | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY
   3334 				    | ST_PRE | ST_TAG | ST_YAML))
   3335 		{
   3336 			CHECKCOPY(token, ptoken, token_size, pline);
   3337 			colno++;
   3338 			break;
   3339 		}
   3340 		else if (IN(state, ST_HTML_TAG))
   3341 		{
   3342 			pline++;
   3343 			colno++;
   3344 			break;
   3345 		}
   3346 
   3347 		if (strlen((char*)pline) > 1 && *(pline + 1) == '_')
   3348 		{
   3349 			/* Handle __ within footnotes, headings
   3350 			 * and link text specially */
   3351 			if (!ANY(state,
   3352 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3353 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3354 			{
   3355 				/* Output existing text up to __ */
   3356 				process_text_token(output, line, link_prefix,
   3357 					first_line_in_doc, previous_line_blank,
   3358 					&previous_line_block,
   3359 					processed_start_of_line,
   3360 					read_yaml_macros_and_links, list_para,
   3361 					&token, &ptoken, &token_size, 1, 1);
   3362 				processed_start_of_line = 1;
   3363 
   3364 				if (!read_yaml_macros_and_links
   3365 					&& !(ANY(state,
   3366 						ST_PRE | ST_CODE | ST_HEADING)))
   3367 					process_bold(output, IN(state, ST_BOLD));
   3368 			}
   3369 
   3370 			state ^= ST_BOLD;
   3371 			pline += 2;
   3372 			colno += 2;
   3373 		}
   3374 		else
   3375 		{
   3376 			/* Handle _ within footnotes, headings
   3377 			 * and link text specially */
   3378 			if (!ANY(state,
   3379 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3380 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3381 			{
   3382 				/* Output existing text up to _ */
   3383 				process_text_token(output, line, link_prefix,
   3384 					first_line_in_doc, previous_line_blank,
   3385 					&previous_line_block,
   3386 					processed_start_of_line,
   3387 					read_yaml_macros_and_links, list_para,
   3388 					&token, &ptoken, &token_size, 1, 1);
   3389 				processed_start_of_line = 1;
   3390 
   3391 				if (!read_yaml_macros_and_links
   3392 					&& !(ANY(state,
   3393 						ST_PRE | ST_CODE | ST_HEADING)))
   3394 					process_italic(output,
   3395 						IN(state, ST_ITALIC));
   3396 			}
   3397 
   3398 			state ^= ST_ITALIC;
   3399 			pline++;
   3400 			colno++;
   3401 		}
   3402 		break;
   3403 
   3404 	case 'o':
   3405 		if (ANY(state,
   3406 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   3407 				    | ST_HTML_TAG | ST_IMAGE | ST_MACRO_BODY
   3408 				    | ST_PRE | ST_YAML))
   3409 		{
   3410 			CHECKCOPY(token, ptoken, token_size, pline);
   3411 			colno++;
   3412 			break;
   3413 		}
   3414 		else if (IN(state, ST_HTML_TAG))
   3415 		{
   3416 			pline++;
   3417 			colno++;
   3418 			break;
   3419 		}
   3420 
   3421 		if (colno == 1 && strlen((char*)pline) > 1
   3422 			&& *(pline + 1) == ' ')
   3423 		{
   3424 			if (IN(state, ST_NUMLIST))
   3425 			{
   3426 				state &= ~ST_NUMLIST;
   3427 				if (!read_yaml_macros_and_links)
   3428 				{
   3429 					process_list_item_end(output,
   3430 						link_prefix);
   3431 					process_numlist_end(output);
   3432 				}
   3433 			}
   3434 			if (!read_yaml_macros_and_links)
   3435 			{
   3436 				if (!(IN(state, ST_LIST)))
   3437 					process_list_start(output);
   3438 				else
   3439 					process_list_item_end(output,
   3440 						link_prefix);
   3441 				process_list_item_start(output);
   3442 			}
   3443 
   3444 			processed_start_of_line = 1;
   3445 			skip_eol		= 0;
   3446 
   3447 			state |= ST_LIST;
   3448 
   3449 			pline += 2;
   3450 			colno += 2;
   3451 		}
   3452 		else
   3453 		{
   3454 			CHECKCOPY(token, ptoken, token_size, pline);
   3455 			colno++;
   3456 		}
   3457 		break;
   3458 
   3459 	case '*':
   3460 		if (ANY(state,
   3461 			    ST_CODE | ST_DISPLAY_FORMULA
   3462 				    | ST_FORMULA
   3463 				    /*| ST_HTML_TAG */
   3464 				    | ST_IMAGE | ST_MACRO_BODY | ST_PRE
   3465 				    | ST_YAML))
   3466 		{
   3467 			CHECKCOPY(token, ptoken, token_size, pline);
   3468 			colno++;
   3469 			break;
   3470 		}
   3471 		else if (IN(state, ST_HTML_TAG))
   3472 		{
   3473 			pline++;
   3474 			colno++;
   3475 			break;
   3476 		}
   3477 
   3478 		pline_len = strlen((char*)pline);
   3479 		if (colno == 1 && pline_len > 1 && *(pline + 1) == '[')
   3480 		{
   3481 			process_line_start(output, line, link_prefix,
   3482 				first_line_in_doc, previous_line_blank,
   3483 				&previous_line_block,
   3484 				read_yaml_macros_and_links, list_para, 1);
   3485 
   3486 			/* Ignore abbreviations (for now) */
   3487 			pline = NULL;
   3488 		}
   3489 		else if (colno == 1 && pline_len > 2
   3490 			&& startswith((char*)pline, "***"))
   3491 		{
   3492 			skip_eol = 1;
   3493 			if (!read_yaml_macros_and_links)
   3494 			{
   3495 				process_horizontal_rule(output, link_prefix,
   3496 					previous_line_blank,
   3497 					read_yaml_macros_and_links);
   3498 				output_firstcol = 1;
   3499 			}
   3500 			pline = NULL;
   3501 		}
   3502 		else if (colno == 1 && strlen((char*)pline) > 1
   3503 			&& *(pline + 1) == ' ')
   3504 		{
   3505 			if (IN(state, ST_NUMLIST))
   3506 			{
   3507 				state &= ~ST_NUMLIST;
   3508 				if (!read_yaml_macros_and_links)
   3509 				{
   3510 					process_list_item_end(output,
   3511 						link_prefix);
   3512 					process_numlist_end(output);
   3513 				}
   3514 			}
   3515 			if (!read_yaml_macros_and_links)
   3516 			{
   3517 				if (!(IN(state, ST_LIST)))
   3518 					process_list_start(output);
   3519 				else
   3520 					process_list_item_end(output,
   3521 						link_prefix);
   3522 				process_list_item_start(output);
   3523 			}
   3524 
   3525 			processed_start_of_line = 1;
   3526 			skip_eol		= 0;
   3527 
   3528 			state |= ST_LIST;
   3529 
   3530 			pline += 2;
   3531 			colno += 2;
   3532 		}
   3533 		else if (pline_len > 1 && *(pline + 1) == '*')
   3534 		{
   3535 			/* Handle ** within footnotes, headings
   3536 			 * and link text specially */
   3537 			if (!ANY(state,
   3538 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3539 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3540 			{
   3541 				/* Output existing text up to * */
   3542 				process_text_token(output, line, link_prefix,
   3543 					first_line_in_doc, previous_line_blank,
   3544 					&previous_line_block,
   3545 					processed_start_of_line,
   3546 					read_yaml_macros_and_links, list_para,
   3547 					&token, &ptoken, &token_size, 1, 1);
   3548 				processed_start_of_line = 1;
   3549 
   3550 				if (!read_yaml_macros_and_links
   3551 					&& !(ANY(state,
   3552 						ST_PRE | ST_CODE | ST_HEADING)))
   3553 					process_bold(output, IN(state, ST_BOLD));
   3554 			}
   3555 
   3556 			state ^= ST_BOLD;
   3557 			pline += 2;
   3558 			colno += 2;
   3559 		}
   3560 		else
   3561 		{
   3562 			/* Handle * within footnotes, headings
   3563 			 * and link text specially */
   3564 			if (!ANY(state,
   3565 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3566 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3567 
   3568 			{
   3569 				/* Output existing text up to * */
   3570 				process_text_token(output, line, link_prefix,
   3571 					first_line_in_doc, previous_line_blank,
   3572 					&previous_line_block,
   3573 					processed_start_of_line,
   3574 					read_yaml_macros_and_links, list_para,
   3575 					&token, &ptoken, &token_size, 1, 1);
   3576 				processed_start_of_line = 1;
   3577 
   3578 				if (!read_yaml_macros_and_links
   3579 					&& !(ANY(state,
   3580 						ST_PRE | ST_CODE | ST_HEADING)))
   3581 					process_italic(output,
   3582 						IN(state, ST_ITALIC));
   3583 			}
   3584 
   3585 			state ^= ST_ITALIC;
   3586 			pline++;
   3587 			colno++;
   3588 		}
   3589 		break;
   3590 
   3591 	case ' ':
   3592 		if (ANY(state,
   3593 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE
   3594 				    | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL))
   3595 		{
   3596 			CHECKCOPY(token, ptoken, token_size, pline);
   3597 			colno++;
   3598 			break;
   3599 		}
   3600 
   3601 		if ((IN(state, ST_HEADING)) && !(IN(state, ST_HEADING_TEXT)))
   3602 		{
   3603 			if (!read_yaml_macros_and_links)
   3604 				process_heading_start(output, heading_level);
   3605 			state |= ST_HEADING_TEXT;
   3606 			pline++;
   3607 			colno++;
   3608 			break;
   3609 		}
   3610 
   3611 		if (colno == 1 && strlen((char*)pline) > 1
   3612 			&& startswith((char*)pline, "  "))
   3613 		{
   3614 			list_para = 1;
   3615 			process_text_token(output, line, link_prefix,
   3616 				first_line_in_doc, previous_line_blank,
   3617 				&previous_line_block, processed_start_of_line,
   3618 				read_yaml_macros_and_links, list_para, &token,
   3619 				&ptoken, &token_size, 1, 1);
   3620 			processed_start_of_line = 1;
   3621 			while (*pline == ' ' || *pline == '\t')
   3622 			{
   3623 				pline++;
   3624 				colno++;
   3625 			}
   3626 			break;
   3627 		}
   3628 
   3629 		if (strlen((char*)pline) == 2 && *(pline + 1) == ' ')
   3630 		{
   3631 			*ptoken = 0;
   3632 			if (strlcat((char*)ptoken, "\n",
   3633 				    token_size - (ptoken - token))
   3634 				> token_size - (ptoken - token))
   3635 				warning(1, (u8*)"strlcat:%d: Overflow",
   3636 					__LINE__);
   3637 			ptoken++;
   3638 			output_firstcol = 1;
   3639 			pline += 2;
   3640 			colno++;
   3641 		}
   3642 		else if (IN(state, ST_LINK_MACRO))
   3643 		{
   3644 			*ptoken = 0;
   3645 			if (strlcpy((char*)link_macro, (char*)token, BUFSIZE)
   3646 				>= BUFSIZE)
   3647 				warning(1, (u8*)"strlcpy:%d: Overflow",
   3648 					__LINE__);
   3649 			*(link_macro + strlen((char*)token)) = 0;
   3650 			RESET_TOKEN(token, ptoken, token_size);
   3651 			state &= ~ST_LINK_MACRO;
   3652 		}
   3653 		else if (!(IN(state, ST_HEADING) && *(pline - 1) == '#'))
   3654 			CHECKCOPY(token, ptoken, token_size, pline);
   3655 		colno++;
   3656 		break;
   3657 
   3658 	case '{':
   3659 		if (ANY(state,
   3660 			    ST_CODE | ST_DISPLAY_FORMULA
   3661 				    | ST_FORMULA
   3662 				    /*| ST_HTML_TAG*/
   3663 				    | ST_PRE | ST_YAML | ST_YAML_VAL))
   3664 		{
   3665 			CHECKCOPY(token, ptoken, token_size, pline);
   3666 			colno++;
   3667 			break;
   3668 		}
   3669 		else if (IN(state, ST_HTML_TAG))
   3670 		{
   3671 			pline++;
   3672 			colno++;
   3673 			break;
   3674 		}
   3675 
   3676 		if (IN(state, ST_MACRO_BODY))
   3677 		{
   3678 			*ptoken		 = 0;
   3679 			size_t token_len = strlen((char*)token);
   3680 
   3681 			skip_eol = 1;
   3682 			if (read_yaml_macros_and_links)
   3683 			{
   3684 				if (!macros)
   3685 				{
   3686 					macros_count = 1;
   3687 					CALLOC(macros, KeyValue, macros_count);
   3688 					pmacros = macros;
   3689 				}
   3690 				if (pmacros->value)
   3691 				{
   3692 					size_t value_len
   3693 						= strlen((char*)pmacros->value);
   3694 
   3695 					if (pmacros->value_size
   3696 						< value_len + token_len + 1)
   3697 					{
   3698 						pmacros->value_size
   3699 							+= token_len;
   3700 						REALLOC(pmacros->value, u8,
   3701 							pmacros->value_size);
   3702 					}
   3703 					if (strlcat((char*)pmacros->value,
   3704 						    (char*)token,
   3705 						    pmacros->value_size)
   3706 						> pmacros->value_size)
   3707 						warning(1, (u8*)"strlcat:%d: Overflow",
   3708 							__LINE__);
   3709 				}
   3710 				else
   3711 				{
   3712 					pmacros->value_size = token_len + 1;
   3713 					CALLOC(pmacros->value, u8,
   3714 						pmacros->value_size);
   3715 					if (strlcpy((char*)pmacros->value,
   3716 						    (char*)token,
   3717 						    pmacros->value_size)
   3718 						>= pmacros->value_size)
   3719 						warning(1, (u8*)"strlcpy:%d: Overflow",
   3720 							__LINE__);
   3721 				}
   3722 			}
   3723 		}
   3724 		else
   3725 		{
   3726 			/* Output existing text up to { */
   3727 			process_text_token(output, line, link_prefix,
   3728 				first_line_in_doc, previous_line_blank,
   3729 				&previous_line_block, processed_start_of_line,
   3730 				read_yaml_macros_and_links, list_para, &token,
   3731 				&ptoken, &token_size, 0, 1);
   3732 			processed_start_of_line = 1;
   3733 		}
   3734 
   3735 		state |= ST_TAG;
   3736 		RESET_TOKEN(token, ptoken, token_size);
   3737 
   3738 		pline++;
   3739 		colno++;
   3740 		break;
   3741 
   3742 	case '/':
   3743 		if (ANY(state,
   3744 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE
   3745 				    | ST_PRE | ST_HEADING | ST_YAML_VAL))
   3746 		{
   3747 			CHECKCOPY(token, ptoken, token_size, pline);
   3748 			colno++;
   3749 			break;
   3750 		}
   3751 		else if (IN(state, ST_HTML_TAG))
   3752 		{
   3753 			if (pline != line && *(pline - 1) == '<')
   3754 				end_tag = 1;
   3755 			pline++;
   3756 		}
   3757 		else if ((IN(state, ST_TAG)) && pline != line
   3758 			&& *(pline - 1) == '{')
   3759 		{
   3760 			end_tag = 1;
   3761 			pline++;
   3762 		}
   3763 		else if (*(pline + 1) == '-')
   3764 		{
   3765 			/* Output existing text up to /- */
   3766 			process_text_token(output, line, link_prefix,
   3767 				first_line_in_doc, previous_line_blank,
   3768 				&previous_line_block, processed_start_of_line,
   3769 				read_yaml_macros_and_links, list_para, &token,
   3770 				&ptoken, &token_size, 1, 1);
   3771 			processed_start_of_line = 1;
   3772 
   3773 			state |= ST_SUMMARY;
   3774 
   3775 			pline += 2;
   3776 			colno++;
   3777 		}
   3778 		else
   3779 			CHECKCOPY(token, ptoken, token_size, pline);
   3780 
   3781 		colno++;
   3782 		break;
   3783 
   3784 	case '}':
   3785 		if (ANY(state,
   3786 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE
   3787 				    | ST_PRE | ST_YAML | ST_YAML_VAL))
   3788 		{
   3789 			CHECKCOPY(token, ptoken, token_size, pline);
   3790 			colno++;
   3791 			break;
   3792 		}
   3793 
   3794 		if (IN(state, ST_TAG))
   3795 		{
   3796 			state &= ~ST_TAG;
   3797 			*ptoken = 0;
   3798 
   3799 			if (!strcmp((char*)token, "made-by"))
   3800 				madeby_present = 1;
   3801 			else
   3802 				process_tag(output, token, source_filename,
   3803 					link_prefix, previous_line_blank,
   3804 					first_line_in_doc,
   3805 					footer_permalink_text,
   3806 					(const char*)permalink_url,
   3807 					ext_in_permalink
   3808 						&& *ext_in_permalink != '0',
   3809 					read_yaml_macros_and_links, &skip_eol,
   3810 					end_tag);
   3811 
   3812 			RESET_TOKEN(token, ptoken, token_size);
   3813 			end_tag = 0;
   3814 		}
   3815 
   3816 		pline++;
   3817 		colno++;
   3818 		break;
   3819 
   3820 	case '|':
   3821 		if (ANY(state,
   3822 			    ST_CODE | ST_DISPLAY_FORMULA
   3823 				    | ST_FORMULA
   3824 				    /*| ST_HTML_TAG*/
   3825 				    | ST_IMAGE | ST_PRE | ST_TAG | ST_YAML))
   3826 		{
   3827 			CHECKCOPY(token, ptoken, token_size, pline);
   3828 			colno++;
   3829 			break;
   3830 		}
   3831 		else if (IN(state, ST_HTML_TAG))
   3832 		{
   3833 			pline++;
   3834 			colno++;
   3835 			break;
   3836 		}
   3837 
   3838 		if (strlen((char*)pline) > 1 && *(pline + 1) == '|')
   3839 		{
   3840 			/* Handle || within footnotes, headings and link
   3841 			 * text specially */
   3842 			if (!ANY(state,
   3843 				    ST_INLINE_FOOTNOTE | ST_HEADING
   3844 					    | ST_FOOTNOTE_TEXT | ST_LINK))
   3845 			{
   3846 				/* Output existing text up to || */
   3847 				process_text_token(output, line, link_prefix,
   3848 					first_line_in_doc, previous_line_blank,
   3849 					&previous_line_block,
   3850 					processed_start_of_line,
   3851 					read_yaml_macros_and_links, list_para,
   3852 					&token, &ptoken, &token_size, 1, 1);
   3853 				processed_start_of_line = 1;
   3854 			}
   3855 
   3856 			state ^= ST_KBD;
   3857 
   3858 			pline += 2;
   3859 			colno += 2;
   3860 		}
   3861 		/* Partial tables (for templating) */
   3862 		else if (!(IN(state, ST_PRE)) && colno == 1
   3863 			&& strlen((char*)pline) > 1 && *(pline + 1) == '@')
   3864 		{
   3865 			skip_eol = 1;
   3866 
   3867 			pline += 2;
   3868 			colno += 2;
   3869 
   3870 			switch (*pline)
   3871 			{
   3872 			case '\\':
   3873 				if (!read_yaml_macros_and_links)
   3874 					process_table_start(output);
   3875 				pline = NULL;
   3876 				break;
   3877 
   3878 			case '#':
   3879 				state |= ST_TABLE_HEADER;
   3880 				pline++;
   3881 				colno++;
   3882 				break;
   3883 
   3884 			case '-':
   3885 				state &= ~ST_TABLE_HEADER;
   3886 				if (!read_yaml_macros_and_links)
   3887 					process_table_body_start(output, 0);
   3888 				pline = NULL;
   3889 				break;
   3890 
   3891 			case ' ':
   3892 				state |= ST_TABLE;
   3893 				if (!read_yaml_macros_and_links)
   3894 					process_table_body_row_start(output);
   3895 				break;
   3896 
   3897 			case '/':
   3898 				state &= ~ST_TABLE;
   3899 				if (!read_yaml_macros_and_links)
   3900 				{
   3901 					process_table_end(output);
   3902 					output_firstcol = 1;
   3903 				}
   3904 				pline = NULL;
   3905 				break;
   3906 
   3907 			default:
   3908 				warning(1, (u8*)"Invalid partial table line");
   3909 				CHECKCOPY(token, ptoken, token_size, pline);
   3910 				colno++;
   3911 			}
   3912 		}
   3913 		else if (!(IN(state, ST_PRE)) && colno == 1)
   3914 		{
   3915 			skip_eol = 1;
   3916 
   3917 			if (IN(state, ST_TABLE_HEADER))
   3918 			{
   3919 				state &= ~ST_TABLE_HEADER;
   3920 				state |= ST_TABLE_LINE;
   3921 				pline = NULL;
   3922 				break;
   3923 			}
   3924 
   3925 			if (IN(state, ST_TABLE_LINE))
   3926 			{
   3927 				state &= ~ST_TABLE_LINE;
   3928 				state |= ST_TABLE;
   3929 				if (!read_yaml_macros_and_links)
   3930 					process_table_body_start(output, 1);
   3931 			}
   3932 			else if (IN(state, ST_TABLE))
   3933 			{
   3934 				if (!read_yaml_macros_and_links)
   3935 					process_table_body_row_start(output);
   3936 			}
   3937 			else
   3938 			{
   3939 				state |= ST_TABLE_HEADER;
   3940 				if (!read_yaml_macros_and_links)
   3941 					process_table_start(output);
   3942 			}
   3943 
   3944 			pline++;
   3945 			colno++;
   3946 		}
   3947 		else if (IN(state, ST_TABLE_HEADER))
   3948 		{
   3949 			*ptoken = 0;
   3950 			if (!read_yaml_macros_and_links)
   3951 			{
   3952 				print_output(output, "%s", token);
   3953 				if (*token)
   3954 					output_firstcol = 0;
   3955 				if (strlen((char*)pline) > 1)
   3956 					process_table_header_cell(output);
   3957 				else
   3958 					process_table_header_end(output);
   3959 			}
   3960 			RESET_TOKEN(token, ptoken, token_size);
   3961 			pline++;
   3962 			colno++;
   3963 		}
   3964 		else if (IN(state, ST_TABLE))
   3965 		{
   3966 			*ptoken = 0;
   3967 			if (!read_yaml_macros_and_links)
   3968 			{
   3969 				print_output(output, "%s", token);
   3970 				if (*token)
   3971 					output_firstcol = 0;
   3972 				if (strlen((char*)pline) > 1)
   3973 					process_table_body_cell(output);
   3974 				else
   3975 					process_table_body_row_end(output);
   3976 			}
   3977 			RESET_TOKEN(token, ptoken, token_size);
   3978 			pline++;
   3979 			colno++;
   3980 		}
   3981 		else
   3982 		{
   3983 			CHECKCOPY(token, ptoken, token_size, pline);
   3984 			colno++;
   3985 		}
   3986 		break;
   3987 
   3988 	case '<':
   3989 		if (read_yaml_macros_and_links
   3990 			|| ANY(state,
   3991 				ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   3992 					| ST_KBD | ST_IMAGE | ST_PRE))
   3993 		{
   3994 			CHECKCOPY(token, ptoken, token_size, pline);
   3995 			colno++;
   3996 			break;
   3997 		}
   3998 
   3999 		state |= ST_HTML_TAG;
   4000 		pline++;
   4001 		colno++;
   4002 		break;
   4003 
   4004 	case '>':
   4005 		if (IN(state, ST_HTML_TAG))
   4006 		{
   4007 			state &= ~ST_HTML_TAG;
   4008 			pline++;
   4009 			colno++;
   4010 			break;
   4011 		}
   4012 
   4013 		if (ANY(state,
   4014 			    ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE
   4015 				    | ST_MACRO_BODY))
   4016 		{
   4017 			CHECKCOPY(token, ptoken, token_size, pline);
   4018 			colno++;
   4019 			break;
   4020 		}
   4021 
   4022 		if (!read_yaml_macros_and_links
   4023 			&& !ANY(state, ST_CODE | ST_HEADING | ST_PRE)
   4024 			&& colno == 1)
   4025 		{
   4026 			if (!read_yaml_macros_and_links
   4027 				&& !(IN(state, ST_BLOCKQUOTE)))
   4028 			{
   4029 				if (IN(state, ST_PARA_OPEN))
   4030 				{
   4031 					print_output(output, "\n");
   4032 					state &= ~ST_PARA_OPEN;
   4033 				}
   4034 				process_blockquote(output, 0);
   4035 				output_firstcol = 0;
   4036 			}
   4037 
   4038 			state |= ST_BLOCKQUOTE;
   4039 
   4040 			pline++;
   4041 			colno++;
   4042 		}
   4043 		else
   4044 		{
   4045 			CHECKCOPY(token, ptoken, token_size, pline);
   4046 			colno++;
   4047 		}
   4048 		break;
   4049 
   4050 	case '!':
   4051 		if (ANY(state,
   4052 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   4053 				    | ST_FOOTNOTE_TEXT | ST_HEADING | ST_IMAGE
   4054 				    | ST_INLINE_FOOTNOTE | ST_LINK
   4055 				    | ST_MACRO_BODY | ST_PRE))
   4056 		{
   4057 			CHECKCOPY(token, ptoken, token_size, pline);
   4058 			colno++;
   4059 			break;
   4060 		}
   4061 		else if (IN(state, ST_HTML_TAG))
   4062 		{
   4063 			pline++;
   4064 			colno++;
   4065 			break;
   4066 		}
   4067 
   4068 		if (strlen((char*)pline) > 1 && *(pline + 1) == '[')
   4069 		{
   4070 			/* Output existing text up to ! */
   4071 			*ptoken = 0;
   4072 			process_text_token(output, line, link_prefix,
   4073 				first_line_in_doc, previous_line_blank,
   4074 				&previous_line_block, processed_start_of_line,
   4075 				read_yaml_macros_and_links, list_para, &token,
   4076 				&ptoken, &token_size, 0, 1);
   4077 			processed_start_of_line = 1;
   4078 
   4079 			RESET_TOKEN(token, ptoken, token_size);
   4080 
   4081 			state |= ST_IMAGE;
   4082 			keep_token = 1;
   4083 			pline += 2;
   4084 			colno += 2;
   4085 		}
   4086 		else
   4087 		{
   4088 			CHECKCOPY(token, ptoken, token_size, pline);
   4089 			colno++;
   4090 		}
   4091 
   4092 		break;
   4093 
   4094 	case '=':
   4095 		if (ANY(state,
   4096 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   4097 				    | ST_MACRO_BODY | ST_PRE | ST_TAG))
   4098 		{
   4099 			CHECKCOPY(token, ptoken, token_size, pline);
   4100 			colno++;
   4101 			break;
   4102 		}
   4103 		else if (IN(state, ST_HTML_TAG))
   4104 		{
   4105 			pline++;
   4106 			colno++;
   4107 			break;
   4108 		}
   4109 
   4110 		if (colno != 1 && *(pline - 1) == '[')
   4111 		{
   4112 			state |= ST_LINK_MACRO;
   4113 			pline++;
   4114 			colno++;
   4115 		}
   4116 		else
   4117 		{
   4118 			CHECKCOPY(token, ptoken, token_size, pline);
   4119 			colno++;
   4120 		}
   4121 		break;
   4122 
   4123 	case '[':
   4124 		pline_len = strlen((char*)pline);
   4125 
   4126 		if (ANY(state,
   4127 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   4128 				    | ST_HEADING | ST_IMAGE | ST_MACRO_BODY
   4129 				    | ST_PRE))
   4130 		{
   4131 			CHECKCOPY(token, ptoken, token_size, pline);
   4132 			colno++;
   4133 			break;
   4134 		}
   4135 		else if (IN(state, ST_HTML_TAG))
   4136 		{
   4137 			pline++;
   4138 			colno++;
   4139 			break;
   4140 		}
   4141 
   4142 		if (IN(state, ST_FOOTNOTE_TEXT))
   4143 		{
   4144 			if (!read_yaml_macros_and_links
   4145 				&& (IN(state, ST_PARA_OPEN)))
   4146 			{
   4147 				process_para_end(output, link_prefix,
   4148 					previous_line_blank,
   4149 					read_yaml_macros_and_links);
   4150 				output_firstcol = 1;
   4151 			}
   4152 			state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN);
   4153 		}
   4154 
   4155 		if (pline_len > 1 && *(pline + 1) == '^')
   4156 		{
   4157 			footnote_at_line_start = colno == 1;
   4158 			if (*token)
   4159 			{
   4160 				/* Output existing text up to [^ */
   4161 				*ptoken = 0;
   4162 				process_text_token(output, line, link_prefix,
   4163 					first_line_in_doc, previous_line_blank,
   4164 					&previous_line_block,
   4165 					processed_start_of_line,
   4166 					read_yaml_macros_and_links, list_para,
   4167 					&token, &ptoken, &token_size, 1, 1);
   4168 			}
   4169 			processed_start_of_line = 1;
   4170 
   4171 			RESET_TOKEN(token, ptoken, token_size);
   4172 			state |= ST_FOOTNOTE;
   4173 			pline += 2;
   4174 			colno += 2;
   4175 			break;
   4176 		}
   4177 
   4178 		if (*token)
   4179 		{
   4180 			/* Output existing text up to [ */
   4181 			*ptoken = 0;
   4182 			process_text_token(output, line, link_prefix,
   4183 				first_line_in_doc, previous_line_blank,
   4184 				&previous_line_block, processed_start_of_line,
   4185 				read_yaml_macros_and_links, list_para, &token,
   4186 				&ptoken, &token_size, 1, 1);
   4187 		}
   4188 		processed_start_of_line = 1;
   4189 
   4190 		RESET_TOKEN(token, ptoken, token_size);
   4191 		state |= ST_LINK;
   4192 		keep_token  = 1;
   4193 		*link_macro = 0;
   4194 		pline++;
   4195 		colno++;
   4196 
   4197 		if (*pline == '(')
   4198 		{
   4199 			*ptoken = 0;
   4200 			state |= ST_LINK_SPAN;
   4201 			pline++;
   4202 			colno++;
   4203 		}
   4204 
   4205 		break;
   4206 
   4207 	case '(':
   4208 		if (ANY(state,
   4209 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   4210 				    | ST_FOOTNOTE_TEXT | ST_HEADING
   4211 				    | ST_INLINE_FOOTNOTE | ST_LINK_SECOND_ARG_END
   4212 				    | ST_MACRO_BODY | ST_PRE))
   4213 			CHECKCOPY(token, ptoken, token_size, pline);
   4214 		else if (*link_macro && (IN(state, ST_LINK)))
   4215 		{
   4216 			/*
   4217 			 *                                u8*
   4218 			 * tag = (u8*)"<span>"; size_t tag_len =
   4219 			 * strlen(tag); size_t token_len = 0;
   4220 			 *
   4221 			 *                                *ptoken
   4222 			 * = 0; token_len = strlen(token);
   4223 			 *
   4224 			 *                                if
   4225 			 * (token_len + tag_len < BUFSIZE)
   4226 			 *                                {
   4227 			 *                                        strncat((char*)token,
   4228 			 * tag, BUFSIZE-token_len-1); ptoken +=
   4229 			 * tag_len;
   4230 			 *                                }
   4231 			 */
   4232 
   4233 			state |= ST_LINK_SPAN;
   4234 			pline++;
   4235 		}
   4236 		else
   4237 			CHECKCOPY(token, ptoken, token_size, pline);
   4238 		colno++;
   4239 		break;
   4240 
   4241 	case ')':
   4242 		if (ANY(state,
   4243 			    ST_DISPLAY_FORMULA | ST_FORMULA
   4244 				    | ST_LINK_SECOND_ARG_END))
   4245 		{
   4246 			CHECKCOPY(token, ptoken, token_size, pline);
   4247 			colno++;
   4248 			break;
   4249 		}
   4250 		else if (IN(state, ST_HTML_TAG))
   4251 		{
   4252 			pline++;
   4253 			colno++;
   4254 			break;
   4255 		}
   4256 
   4257 		pline_len = strlen((char*)pline);
   4258 
   4259 		if (IN(state, ST_LINK_SPAN) && pline_len > 1
   4260 			&& *(pline + 1) == ']')
   4261 		{
   4262 			state &= ~ST_LINK_SPAN;
   4263 			pline++;
   4264 		}
   4265 		else if (ANY(state, ST_LINK_SECOND_ARG | ST_IMAGE_SECOND_ARG))
   4266 		{
   4267 			*ptoken = 0;
   4268 			if (!read_yaml_macros_and_links)
   4269 			{
   4270 				paralinks_count++;
   4271 				if (!paralinks)
   4272 				{
   4273 					CALLOC(paralinks, KeyValue,
   4274 						paralinks_count);
   4275 					pparalinks = paralinks;
   4276 				}
   4277 				else
   4278 				{
   4279 					REALLOCARRAY(paralinks, KeyValue,
   4280 						paralinks_count);
   4281 					pparalinks = paralinks + paralinks_count
   4282 						- 1;
   4283 				}
   4284 				pparalinks->key = (u8*)strdup((char*)token);
   4285 				pparalinks->value_size
   4286 					= strlen((char*)link_text) + 1;
   4287 				pparalinks->value
   4288 					= (u8*)strdup((char*)link_text);
   4289 
   4290 				if (output_firstcol
   4291 					&& ((*(line + line_len - 1) == ']')
   4292 						|| *(line + line_len - 1)
   4293 							== ')'))
   4294 				{
   4295 					skip_eol      = 1;
   4296 					skip_para_end = 1;
   4297 					pline	      = NULL;
   4298 				}
   4299 				else
   4300 				{
   4301 					if (IN(state, ST_LINK_SECOND_ARG))
   4302 						process_inline_link(output,
   4303 							link_text);
   4304 					else
   4305 						process_inline_image(output,
   4306 							link_text);
   4307 					output_firstcol = !*link_text;
   4308 				}
   4309 			}
   4310 			RESET_TOKEN(token, ptoken, token_size);
   4311 			state &= ~(ST_LINK | ST_LINK_SECOND_ARG | ST_IMAGE
   4312 				| ST_IMAGE_SECOND_ARG);
   4313 			/* Difference from slweb: could be NULL */
   4314 			if (pline)
   4315 				pline++;
   4316 		}
   4317 		else
   4318 			CHECKCOPY(token, ptoken, token_size, pline);
   4319 		colno++;
   4320 		break;
   4321 
   4322 	case ']':
   4323 		if (ANY(state,
   4324 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   4325 				    | ST_HEADING | ST_MACRO_BODY | ST_PRE))
   4326 		{
   4327 			CHECKCOPY(token, ptoken, token_size, pline);
   4328 			colno++;
   4329 			break;
   4330 		}
   4331 		else if (IN(state, ST_HTML_TAG))
   4332 		{
   4333 			pline++;
   4334 			colno++;
   4335 			break;
   4336 		}
   4337 
   4338 		*ptoken = 0;
   4339 		if (IN(state, ST_INLINE_FOOTNOTE))
   4340 		{
   4341 			process_inline_footnote(token,
   4342 				read_yaml_macros_and_links, output);
   4343 
   4344 			keep_token = 0;
   4345 			RESET_TOKEN(token, ptoken, token_size);
   4346 			state &= ~ST_INLINE_FOOTNOTE;
   4347 			pline++;
   4348 			colno++;
   4349 		}
   4350 		else if (IN(state, ST_FOOTNOTE))
   4351 		{
   4352 			int footnote_definition = (strlen((char*)pline) > 1)
   4353 				&& (*(pline + 1) == ':')
   4354 				&& footnote_at_line_start;
   4355 
   4356 			process_footnote(output, token,
   4357 				footnote_definition
   4358 					&& read_yaml_macros_and_links,
   4359 				!footnote_definition
   4360 					&& !read_yaml_macros_and_links);
   4361 
   4362 			RESET_TOKEN(token, ptoken, token_size);
   4363 			state &= ~ST_FOOTNOTE;
   4364 			footnote_at_line_start = 0;
   4365 			pline++;
   4366 			colno++;
   4367 			if (footnote_definition)
   4368 			{
   4369 				pline++;
   4370 				colno++;
   4371 				while (*pline == ' ' || *pline == '\t')
   4372 				{
   4373 					pline++;
   4374 					colno++;
   4375 				}
   4376 				state |= ST_FOOTNOTE_TEXT;
   4377 			}
   4378 		}
   4379 		else if (IN(state, ST_LINK_SECOND_ARG))
   4380 		{
   4381 			if (!read_yaml_macros_and_links)
   4382 			{
   4383 				u8* url = get_value(links, links_count, token,
   4384 					NULL);
   4385 				if (!url)
   4386 					exit(error(EINVAL,
   4387 						(u8*)"slweb_parse: get_"
   4388 						     "value: "
   4389 						     "Invalid "
   4390 						     "argumen"
   4391 						     "t"));
   4392 				paralinks_count++;
   4393 				if (!paralinks)
   4394 				{
   4395 					CALLOC(paralinks, KeyValue,
   4396 						paralinks_count);
   4397 					pparalinks = paralinks;
   4398 				}
   4399 				else
   4400 				{
   4401 					REALLOCARRAY(paralinks, KeyValue,
   4402 						paralinks_count);
   4403 					pparalinks = paralinks + paralinks_count
   4404 						- 1;
   4405 				}
   4406 				pparalinks->key = (u8*)strdup((char*)url);
   4407 				pparalinks->value_size
   4408 					= strlen((char*)link_text) + 1;
   4409 				pparalinks->value
   4410 					= (u8*)strdup((char*)link_text);
   4411 
   4412 				if (output_firstcol
   4413 					&& ((*(line + line_len - 1) == ']')
   4414 						|| *(line + line_len - 1)
   4415 							== ')'))
   4416 				{
   4417 					skip_eol = 1;
   4418 					pline	 = NULL;
   4419 				}
   4420 				else
   4421 				{
   4422 					process_link(output, link_text);
   4423 					output_firstcol = !*link_text;
   4424 				}
   4425 			}
   4426 			RESET_TOKEN(token, ptoken, token_size);
   4427 			state &= ~(ST_LINK | ST_LINK_SECOND_ARG);
   4428 			/* Difference from slweb: could be NULL */
   4429 			if (pline)
   4430 			{
   4431 				pline++;
   4432 				colno++;
   4433 			}
   4434 		}
   4435 		else if (IN(state, ST_IMAGE_SECOND_ARG))
   4436 		{
   4437 			if (!read_yaml_macros_and_links)
   4438 			{
   4439 				u8* url = get_value(links, links_count, token,
   4440 					NULL);
   4441 				if (!url)
   4442 					exit(error(EINVAL,
   4443 						(u8*)"slweb_parse: get_"
   4444 						     "value: "
   4445 						     "Invalid "
   4446 						     "argumen"
   4447 						     "t"));
   4448 				paralinks_count++;
   4449 				if (!paralinks)
   4450 				{
   4451 					CALLOC(paralinks, KeyValue,
   4452 						paralinks_count);
   4453 					pparalinks = paralinks;
   4454 				}
   4455 				else
   4456 				{
   4457 					REALLOCARRAY(paralinks, KeyValue,
   4458 						paralinks_count);
   4459 					pparalinks = paralinks + paralinks_count
   4460 						- 1;
   4461 				}
   4462 				pparalinks->key = (u8*)strdup((char*)url);
   4463 				pparalinks->value_size
   4464 					= strlen((char*)link_text) + 1;
   4465 				pparalinks->value
   4466 					= (u8*)strdup((char*)link_text);
   4467 
   4468 				if (output_firstcol
   4469 					&& ((*(line + line_len - 1) == ']')
   4470 						|| *(line + line_len - 1)
   4471 							== ')'))
   4472 				{
   4473 					skip_eol = 1;
   4474 					pline	 = NULL;
   4475 				}
   4476 				else
   4477 				{
   4478 					process_image(output, link_text);
   4479 					output_firstcol = !*link_text;
   4480 				}
   4481 			}
   4482 			RESET_TOKEN(token, ptoken, token_size);
   4483 			state &= ~(ST_IMAGE | ST_IMAGE_SECOND_ARG);
   4484 			/* Difference from slweb: could be NULL */
   4485 			if (pline)
   4486 			{
   4487 				pline++;
   4488 				colno++;
   4489 			}
   4490 		}
   4491 		else if (strlen((char*)pline) > 1)
   4492 		{
   4493 			size_t token_len = 0;
   4494 			switch (*(pline + 1))
   4495 			{
   4496 			case ':':
   4497 				links_count++;
   4498 
   4499 				if (!links)
   4500 				{
   4501 					CALLOC(links, KeyValue, links_count);
   4502 					plinks = links;
   4503 				}
   4504 				else
   4505 				{
   4506 					REALLOCARRAY(links, KeyValue,
   4507 						links_count);
   4508 					plinks = links + links_count - 1;
   4509 				}
   4510 				CALLOC(plinks->key, u8, KEYSIZE);
   4511 				if (strlcpy((char*)plinks->key, (char*)token,
   4512 					    KEYSIZE)
   4513 					>= KEYSIZE)
   4514 					warning(1, (u8*)"strlcpy:%d: Overflow",
   4515 						__LINE__);
   4516 				plinks->value	   = NULL;
   4517 				plinks->value_size = 0;
   4518 				pline += 2;
   4519 				colno += 2;
   4520 				while (pline
   4521 					&& (*pline == ' ' || *pline == '\t'))
   4522 				{
   4523 					pline++;
   4524 					colno++;
   4525 				}
   4526 				RESET_TOKEN(token, ptoken, token_size);
   4527 				keep_token = 0;
   4528 				state |= ST_LINK_SECOND_ARG_END;
   4529 				break;
   4530 
   4531 			case '[':
   4532 			case '(':
   4533 				token_len = strlen((char*)token);
   4534 				if (link_text && token_len + 1 > link_size)
   4535 				{
   4536 					link_size = token_len + 1;
   4537 					REALLOC(link_text, u8, link_size);
   4538 				}
   4539 				else if (!link_text)
   4540 				{
   4541 					link_size = token_len + 1;
   4542 					CALLOC(link_text, u8, link_size);
   4543 				}
   4544 				if (strlcpy((char*)link_text, (char*)token,
   4545 					    link_size)
   4546 					>= link_size)
   4547 					warning(1, (u8*)"strlcpy:%d: Overflow",
   4548 						__LINE__);
   4549 				*(link_text + token_len) = 0;
   4550 				pline += 2;
   4551 				colno += 2;
   4552 				RESET_TOKEN(token, ptoken, token_size);
   4553 				keep_token = 0;
   4554 				if (IN(state, ST_LINK))
   4555 					state |= ST_LINK_SECOND_ARG;
   4556 				else
   4557 					state |= ST_IMAGE_SECOND_ARG;
   4558 				break;
   4559 			default:
   4560 				CHECKCOPY(token, ptoken, token_size, pline);
   4561 				colno++;
   4562 				state &= ~(ST_LINK | ST_IMAGE);
   4563 				break;
   4564 			}
   4565 		}
   4566 		else
   4567 		{
   4568 			CHECKCOPY(token, ptoken, token_size, pline);
   4569 			colno++;
   4570 		}
   4571 		break;
   4572 
   4573 	case '^':
   4574 		if (ANY(state,
   4575 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   4576 				    | ST_FOOTNOTE_TEXT | ST_IMAGE
   4577 				    | ST_INLINE_FOOTNOTE | ST_PRE | ST_YAML))
   4578 		{
   4579 			CHECKCOPY(token, ptoken, token_size, pline);
   4580 			colno++;
   4581 			break;
   4582 		}
   4583 		else if (IN(state, ST_HTML_TAG))
   4584 		{
   4585 			pline++;
   4586 			colno++;
   4587 			break;
   4588 		}
   4589 
   4590 		if (strlen((char*)pline) > 1 && *(pline + 1) == '[')
   4591 		{
   4592 			/* Output existing text up to ^[ */
   4593 			*ptoken = 0;
   4594 			process_text_token(output, line, link_prefix,
   4595 				first_line_in_doc, previous_line_blank,
   4596 				&previous_line_block, processed_start_of_line,
   4597 				read_yaml_macros_and_links, list_para, &token,
   4598 				&ptoken, &token_size, 1, 1);
   4599 			processed_start_of_line = 1;
   4600 
   4601 			RESET_TOKEN(token, ptoken, token_size);
   4602 			state |= ST_INLINE_FOOTNOTE;
   4603 			keep_token = 1;
   4604 			pline += 2;
   4605 			colno += 2;
   4606 			break;
   4607 		}
   4608 
   4609 		CHECKCOPY(token, ptoken, token_size, pline);
   4610 		colno++;
   4611 		break;
   4612 
   4613 	case '\\':
   4614 		*ptoken = 0;
   4615 		if (ANY(state,
   4616 			    ST_DISPLAY_FORMULA
   4617 				    | ST_FORMULA
   4618 				    /*| ST_HTML_TAG*/
   4619 				    | ST_IMAGE | ST_LINK_SECOND_ARG
   4620 				    | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY))
   4621 		{
   4622 			CHECKCOPY(token, ptoken, token_size, pline);
   4623 			colno++;
   4624 			break;
   4625 		}
   4626 		else if (IN(state, ST_HTML_TAG))
   4627 		{
   4628 			pline++;
   4629 			colno++;
   4630 			break;
   4631 		}
   4632 
   4633 		if (strlen((char*)pline) > 1)
   4634 		{
   4635 			pline++;
   4636 			colno++;
   4637 
   4638 			CHECKCOPY(token, ptoken, token_size, pline);
   4639 		}
   4640 		else
   4641 		{
   4642 			pline = NULL;
   4643 			break;
   4644 		}
   4645 
   4646 		colno++;
   4647 		break;
   4648 
   4649 	case '$':
   4650 		if (ANY(state,
   4651 			    ST_CODE | ST_CSV_BODY | ST_IMAGE | ST_PRE
   4652 				    | ST_TSV_BODY | ST_YAML))
   4653 		{
   4654 			CHECKCOPY(token, ptoken, token_size, pline);
   4655 			colno++;
   4656 			break;
   4657 		}
   4658 		else if (IN(state, ST_HTML_TAG))
   4659 		{
   4660 			pline++;
   4661 			colno++;
   4662 			break;
   4663 		}
   4664 
   4665 		if (strlen((char*)pline) > 1 && *(pline + 1) == '$')
   4666 		{
   4667 			if (IN(state, ST_FORMULA))
   4668 				exit(error(1,
   4669 					(u8*)"slweb_parse: Display formula "
   4670 					     "within an"
   4671 					     " open formula"));
   4672 
   4673 			if (!(IN(state, ST_DISPLAY_FORMULA)))
   4674 			{
   4675 				/* Output existing text up to $$ */
   4676 				process_text_token(output, line, link_prefix,
   4677 					first_line_in_doc, previous_line_blank,
   4678 					&previous_line_block,
   4679 					processed_start_of_line,
   4680 					read_yaml_macros_and_links, list_para,
   4681 					&token, &ptoken, &token_size, 1, 1);
   4682 			}
   4683 			processed_start_of_line = 1;
   4684 
   4685 			state ^= ST_DISPLAY_FORMULA;
   4686 
   4687 			if (IN(state, ST_DISPLAY_FORMULA))
   4688 				keep_token = 1;
   4689 			else
   4690 			{
   4691 				*ptoken = 0;
   4692 
   4693 				if (!read_yaml_macros_and_links)
   4694 					process_formula(output, token, 1);
   4695 
   4696 				keep_token = 0;
   4697 				RESET_TOKEN(token, ptoken, token_size);
   4698 			}
   4699 
   4700 			pline += 2;
   4701 			colno += 2;
   4702 		}
   4703 		else
   4704 		{
   4705 			if (IN(state, ST_DISPLAY_FORMULA))
   4706 				exit(error(1,
   4707 					(u8*)"slweb_parse: Formula within an "
   4708 					     "open"
   4709 					     " display formula"));
   4710 
   4711 			if (!(IN(state, ST_FORMULA)))
   4712 			{
   4713 				/* Output existing text up to $ */
   4714 				process_text_token(output, line, link_prefix,
   4715 					first_line_in_doc, previous_line_blank,
   4716 					&previous_line_block,
   4717 					processed_start_of_line,
   4718 					read_yaml_macros_and_links, list_para,
   4719 					&token, &ptoken, &token_size, 1, 1);
   4720 			}
   4721 			processed_start_of_line = 1;
   4722 
   4723 			state ^= ST_FORMULA;
   4724 
   4725 			if (IN(state, ST_FORMULA))
   4726 				keep_token = 1;
   4727 			else
   4728 			{
   4729 				*ptoken = 0;
   4730 
   4731 				if (!read_yaml_macros_and_links)
   4732 					process_formula(output, token, 0);
   4733 
   4734 				keep_token = 0;
   4735 				RESET_TOKEN(token, ptoken, token_size);
   4736 			}
   4737 
   4738 			pline++;
   4739 			colno++;
   4740 		}
   4741 		break;
   4742 
   4743 	case '1': /* FALLTHROUGH */
   4744 	case '2':
   4745 	case '3':
   4746 	case '4':
   4747 	case '5':
   4748 	case '6':
   4749 	case '7':
   4750 	case '8':
   4751 	case '9':
   4752 	case '0':
   4753 		if (ANY(state,
   4754 			    ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
   4755 				    | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL))
   4756 		{
   4757 			CHECKCOPY(token, ptoken, token_size, pline);
   4758 			colno++;
   4759 		}
   4760 		else if (IN(state, ST_HTML_TAG))
   4761 		{
   4762 			pline++;
   4763 			colno++;
   4764 			break;
   4765 		}
   4766 		else if ((colno == 1 || all_numeric(line, pline))
   4767 			&& strlen((char*)pline) > 1
   4768 			&& (*(pline + 1) == '.' || *(pline + 1) == ')'))
   4769 		{
   4770 			ptoken	= token;
   4771 			*ptoken = 0;
   4772 
   4773 			if (IN(state, ST_LIST))
   4774 			{
   4775 				state &= ~ST_LIST;
   4776 				if (!read_yaml_macros_and_links)
   4777 				{
   4778 					process_list_item_end(output,
   4779 						link_prefix);
   4780 					process_list_end(output);
   4781 				}
   4782 			}
   4783 			if (!read_yaml_macros_and_links)
   4784 			{
   4785 				if (!(IN(state, ST_NUMLIST)))
   4786 					process_numlist_start(output);
   4787 				else
   4788 					process_list_item_end(output,
   4789 						link_prefix);
   4790 				process_list_item_start(output);
   4791 			}
   4792 
   4793 			processed_start_of_line = 1;
   4794 			skip_eol		= 0;
   4795 
   4796 			state |= ST_NUMLIST;
   4797 
   4798 			pline += 2;
   4799 			colno += 2;
   4800 		}
   4801 		else
   4802 		{
   4803 			CHECKCOPY(token, ptoken, token_size, pline);
   4804 			colno++;
   4805 		}
   4806 		break;
   4807 
   4808 	default:
   4809 		if (IN(state, ST_HTML_TAG))
   4810 		{
   4811 			pline++;
   4812 			colno++;
   4813 		}
   4814 		else
   4815 		{
   4816 			CHECKCOPY(token, ptoken, token_size, pline);
   4817 			colno++;
   4818 		}
   4819 	}
   4820 	goto do_line;
   4821 
   4822 done_line:
   4823 	*ptoken = 0;
   4824 
   4825 	if (*token && read_yaml_macros_and_links && (IN(state, ST_YAML_VAL)))
   4826 	{
   4827 		*ptoken = 0;
   4828 		if (!vars)
   4829 		{
   4830 			vars_count = 1;
   4831 			CALLOC(vars, KeyValue, vars_count);
   4832 			pvars = vars;
   4833 		}
   4834 		pvars->value_size = strlen((char*)token) + 1;
   4835 		CALLOC(pvars->value, u8, pvars->value_size);
   4836 		if (strlcpy((char*)pvars->value, (char*)token, pvars->value_size)
   4837 			>= pvars->value_size)
   4838 			warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__);
   4839 	}
   4840 	else if (keep_token)
   4841 	{
   4842 		if (ptoken + 2 > token + token_size)
   4843 		{
   4844 			size_t old_size = token_size;
   4845 			token_size += BUFSIZE;
   4846 			REALLOC(token, u8, token_size);
   4847 			ptoken = token + old_size - 1;
   4848 		}
   4849 		if (strlcat((char*)token,
   4850 			    ANY(state, ST_IMAGE | ST_LINK) ? " " : "\n",
   4851 			    token_size)
   4852 			> token_size)
   4853 			warning(1, (u8*)"strlcat:%d: Overflow", __LINE__);
   4854 		ptoken++;
   4855 		*ptoken = 0;
   4856 	}
   4857 	else
   4858 	{
   4859 		if (*token)
   4860 		{
   4861 			*ptoken		 = 0;
   4862 			size_t token_len = strlen((char*)token);
   4863 			if (IN(state, ST_MACRO_BODY))
   4864 			{
   4865 				skip_eol = 1;
   4866 				if (!read_yaml_macros_and_links)
   4867 					goto skip_macro_read;
   4868 				if (!macros)
   4869 				{
   4870 					macros_count = 1;
   4871 					CALLOC(macros, KeyValue, macros_count);
   4872 					pmacros = macros;
   4873 				}
   4874 				if (pmacros->value)
   4875 				{
   4876 					size_t value_len
   4877 						= strlen((char*)pmacros->value);
   4878 
   4879 					if (pmacros->value_size
   4880 						< value_len + token_len + 1)
   4881 					{
   4882 						pmacros->value_size += BUFSIZE;
   4883 						REALLOC(pmacros->value, u8,
   4884 							pmacros->value_size);
   4885 					}
   4886 					if (strlcat((char*)pmacros->value,
   4887 						    (char*)token,
   4888 						    pmacros->value_size)
   4889 						> pmacros->value_size)
   4890 						warning(1, (u8*)"strlcat:%d: Overflow",
   4891 							__LINE__);
   4892 					if (strlcat((char*)pmacros->value, " ",
   4893 						    pmacros->value_size)
   4894 						> pmacros->value_size)
   4895 						warning(1, (u8*)"strlcat:%d: Overflow",
   4896 							__LINE__);
   4897 				}
   4898 				else
   4899 				{
   4900 					pmacros->value_size = BUFSIZE;
   4901 					CALLOC(pmacros->value, u8,
   4902 						pmacros->value_size);
   4903 					if (strlcpy((char*)pmacros->value,
   4904 						    (char*)token,
   4905 						    pmacros->value_size)
   4906 						>= pmacros->value_size)
   4907 						warning(1, (u8*)"strlcpy:%d: Overflow",
   4908 							__LINE__);
   4909 					if (strlcat((char*)pmacros->value, " ",
   4910 						    pmacros->value_size)
   4911 						> pmacros->value_size)
   4912 						warning(1, (u8*)"strlcat:%d: Overflow",
   4913 							__LINE__);
   4914 				}
   4915 			skip_macro_read:;
   4916 			}
   4917 			else if (IN(state, ST_FOOTNOTE_TEXT))
   4918 			{
   4919 				skip_eol = 1;
   4920 				if (!read_yaml_macros_and_links)
   4921 					goto skip_footnote_read;
   4922 				if (!footnotes)
   4923 				{
   4924 					footnote_count = 1;
   4925 					CALLOC(footnotes, KeyValue,
   4926 						footnote_count);
   4927 					pfootnotes = footnotes;
   4928 				}
   4929 				if (pfootnotes->value)
   4930 				{
   4931 					size_t value_len = strlen(
   4932 						(char*)pfootnotes->value);
   4933 
   4934 					if (pfootnotes->value_size
   4935 						< value_len + token_len + 1)
   4936 					{
   4937 						pfootnotes->value_size
   4938 							+= BUFSIZE;
   4939 						REALLOC(pfootnotes->value, u8,
   4940 							pfootnotes->value_size);
   4941 					}
   4942 					if (strlcat((char*)pfootnotes->value,
   4943 						    (char*)token,
   4944 						    pfootnotes->value_size)
   4945 						> pfootnotes->value_size)
   4946 						warning(1, (u8*)"strlcat:%d: Overflow",
   4947 							__LINE__);
   4948 					if (strlcat((char*)pfootnotes->value,
   4949 						    " ", pfootnotes->value_size)
   4950 						> pfootnotes->value_size)
   4951 						warning(1, (u8*)"strlcat:%d: Overflow",
   4952 							__LINE__);
   4953 				}
   4954 				else
   4955 				{
   4956 					pfootnotes->value_size = BUFSIZE;
   4957 					CALLOC(pfootnotes->value, u8,
   4958 						pfootnotes->value_size);
   4959 					if (strlcpy((char*)pfootnotes->value,
   4960 						    (char*)token,
   4961 						    pfootnotes->value_size)
   4962 						>= pfootnotes->value_size)
   4963 						warning(1, (u8*)"strlcpy:%d: Overflow",
   4964 							__LINE__);
   4965 					if (strlcat((char*)pfootnotes->value,
   4966 						    " ", pfootnotes->value_size)
   4967 						> pfootnotes->value_size)
   4968 						warning(1, (u8*)"strlcat:%d: Overflow",
   4969 							__LINE__);
   4970 				}
   4971 				*token = 0;
   4972 				ptoken = token;
   4973 			skip_footnote_read:;
   4974 			}
   4975 			else if (ANY(state,
   4976 					 ST_LINK_SECOND_ARG
   4977 						 | ST_LINK_SECOND_ARG_END))
   4978 			{
   4979 				if (read_yaml_macros_and_links)
   4980 				{
   4981 					if (!links)
   4982 					{
   4983 						links_count = 1;
   4984 						CALLOC(links, KeyValue,
   4985 							links_count);
   4986 						plinks = links;
   4987 					}
   4988 					plinks->value_size
   4989 						= strlen((char*)token) + 1;
   4990 					CALLOC(plinks->value, u8,
   4991 						plinks->value_size);
   4992 					if (strlcpy((char*)plinks->value,
   4993 						    (char*)token,
   4994 						    plinks->value_size)
   4995 						>= plinks->value_size)
   4996 						warning(1, (u8*)"strlcpy:%d: Overflow",
   4997 							__LINE__);
   4998 				}
   4999 			}
   5000 			else if (IN(state, ST_HEADING))
   5001 			{
   5002 				state &= ~(ST_HEADING | ST_HEADING_TEXT);
   5003 				if (!read_yaml_macros_and_links)
   5004 					process_heading(output, token);
   5005 				first_line_in_doc = 0;
   5006 				RESET_TOKEN(token, ptoken, token_size);
   5007 				heading_level = 0;
   5008 			}
   5009 			else if (!ANY(state,
   5010 					 ST_DISPLAY_FORMULA | ST_FORMULA
   5011 						 | ST_IMAGE | ST_LINK))
   5012 				process_text_token(output, line, link_prefix,
   5013 					first_line_in_doc, previous_line_blank,
   5014 					&previous_line_block,
   5015 					processed_start_of_line,
   5016 					read_yaml_macros_and_links, list_para,
   5017 					&token, &ptoken, &token_size, 1, 1);
   5018 		}
   5019 
   5020 		if (!ANY(state,
   5021 			    ST_PRE | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG
   5022 				    | ST_LINK_SECOND_ARG_END)
   5023 			&& (!*pbuffer || *pbuffer == '\n'))
   5024 		{
   5025 			if (IN(state, ST_PARA_OPEN))
   5026 			{
   5027 				/*if (!read_yaml_macros_and_links)
   5028 				{
   5029 					print_output(output, "\n;");
   5030 					output_firstcol = 1;
   5031 				}*/
   5032 				if (IN(state, ST_LIST))
   5033 					skip_eol = 1;
   5034 				state &= ~ST_PARA_OPEN;
   5035 			}
   5036 
   5037 			if (!*pbuffer && (IN(state, ST_LIST)))
   5038 			{
   5039 				if (!read_yaml_macros_and_links)
   5040 				{
   5041 					process_list_item_end(output,
   5042 						link_prefix);
   5043 					process_list_end(output);
   5044 				}
   5045 				state &= ~ST_LIST;
   5046 			}
   5047 
   5048 			if (!*pbuffer && (IN(state, ST_NUMLIST)))
   5049 			{
   5050 				if (!read_yaml_macros_and_links)
   5051 				{
   5052 					process_list_item_end(output,
   5053 						link_prefix);
   5054 					process_numlist_end(output);
   5055 				}
   5056 				state &= ~ST_NUMLIST;
   5057 			}
   5058 
   5059 			if (IN(state, ST_PARA_OPEN))
   5060 			{
   5061 				if (!read_yaml_macros_and_links)
   5062 				{
   5063 					process_para_end(output, link_prefix,
   5064 						previous_line_blank,
   5065 						read_yaml_macros_and_links);
   5066 					output_firstcol = 1;
   5067 				}
   5068 
   5069 				if (IN(state, ST_LIST))
   5070 					skip_eol = 1;
   5071 
   5072 				state &= ~ST_PARA_OPEN;
   5073 			}
   5074 
   5075 			if (!*pbuffer && (IN(state, ST_FOOTNOTE_TEXT)))
   5076 			{
   5077 				state &= ~ST_FOOTNOTE_TEXT;
   5078 
   5079 				*ptoken		 = 0;
   5080 				size_t token_len = strlen((char*)token);
   5081 
   5082 				skip_eol = 1;
   5083 				if (!read_yaml_macros_and_links)
   5084 					goto skip_footnote_text_read;
   5085 				if (!footnotes)
   5086 				{
   5087 					footnote_count = 1;
   5088 					CALLOC(footnotes, KeyValue,
   5089 						footnote_count);
   5090 					pfootnotes = footnotes;
   5091 				}
   5092 				if (pfootnotes->value)
   5093 				{
   5094 					size_t value_len = strlen(
   5095 						(char*)pfootnotes->value);
   5096 
   5097 					if (pfootnotes->value_size
   5098 						< value_len + token_len + 1)
   5099 					{
   5100 						pfootnotes->value_size
   5101 							= value_len + token_len
   5102 							+ 1;
   5103 						REALLOC(pfootnotes->value, u8,
   5104 							pfootnotes->value_size);
   5105 					}
   5106 					if (strlcat((char*)pfootnotes->value,
   5107 						    (char*)token,
   5108 						    pfootnotes->value_size)
   5109 						> pfootnotes->value_size)
   5110 						warning(1, (u8*)"strlcat:%d: Overflow",
   5111 							__LINE__);
   5112 				}
   5113 				else
   5114 				{
   5115 					pfootnotes->value_size = token_len + 1;
   5116 					CALLOC(pfootnotes->value, u8,
   5117 						pfootnotes->value_size);
   5118 					if (strlcpy((char*)pfootnotes->value,
   5119 						    (char*)token,
   5120 						    pfootnotes->value_size)
   5121 						>= pfootnotes->value_size)
   5122 						warning(1, (u8*)"strlcpy:%d: Overflow",
   5123 							__LINE__);
   5124 				}
   5125 				*token = 0;
   5126 				ptoken = token;
   5127 			skip_footnote_text_read:;
   5128 			}
   5129 		}
   5130 
   5131 		if (IN(state, ST_BLOCKQUOTE) && (!*pbuffer || *pbuffer != '>'))
   5132 		{
   5133 			state &= ~ST_BLOCKQUOTE;
   5134 			if (!read_yaml_macros_and_links)
   5135 			{
   5136 				process_blockquote(output, 1);
   5137 				previous_line_block = 1;
   5138 				output_firstcol	    = 1;
   5139 			}
   5140 		}
   5141 
   5142 		if (ANY(state, ST_TABLE_HEADER | ST_TABLE_LINE)
   5143 			&& (!line_len || !*pbuffer))
   5144 		{
   5145 			if (!read_yaml_macros_and_links)
   5146 			{
   5147 				warning(1, (u8*)"Malformed table");
   5148 				process_table_end(output);
   5149 				output_firstcol = 1;
   5150 			}
   5151 			state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE);
   5152 		}
   5153 
   5154 		if ((IN(state, ST_TABLE)) && (!line_len || !*pbuffer))
   5155 		{
   5156 			if (!read_yaml_macros_and_links)
   5157 			{
   5158 				process_table_end(output);
   5159 				output_firstcol = 1;
   5160 			}
   5161 			state &= ~ST_TABLE;
   5162 		}
   5163 	}
   5164 
   5165 	if (!skip_eol && !keep_token && !read_yaml_macros_and_links
   5166 		&& !ANY(state,
   5167 			ST_YAML | ST_YAML_VAL | ST_LINK_SECOND_ARG
   5168 				| ST_LINK_SECOND_ARG_END))
   5169 	{
   5170 		print_output(output, "%s",
   5171 			IN(state, ST_PRE) ? "\n" : (output_firstcol ? "" : " "));
   5172 		output_firstcol = (IN(state, ST_PRE)) || output_firstcol;
   5173 	}
   5174 
   5175 	if (!keep_token)
   5176 		RESET_TOKEN(token, ptoken, token_size);
   5177 
   5178 	if (!skip_change_first_line_in_doc && !(IN(state, ST_YAML)))
   5179 		first_line_in_doc = 0;
   5180 
   5181 	skip_change_first_line_in_doc = 0;
   5182 	if (line_len == 0)
   5183 	{
   5184 		if (!previous_line_blank)
   5185 		{
   5186 			if (!read_yaml_macros_and_links
   5187 				&& IN(state, ST_PARA_OPEN))
   5188 			{
   5189 				if (IN(state, ST_LIST))
   5190 					process_list_item_end(output,
   5191 						link_prefix);
   5192 				else
   5193 				{
   5194 					process_para_end(output, link_prefix,
   5195 						previous_line_blank,
   5196 						read_yaml_macros_and_links);
   5197 					output_firstcol = 1;
   5198 				}
   5199 			}
   5200 			else
   5201 			{
   5202 				process_para_end(output, link_prefix,
   5203 					previous_line_blank,
   5204 					read_yaml_macros_and_links);
   5205 				output_firstcol = 1;
   5206 			}
   5207 		}
   5208 		else
   5209 		{
   5210 			process_para_end(output, link_prefix,
   5211 				previous_line_blank,
   5212 				read_yaml_macros_and_links);
   5213 			output_firstcol = 1;
   5214 		}
   5215 		previous_line_blank = 1;
   5216 	}
   5217 	else
   5218 		previous_line_blank = 0;
   5219 
   5220 	/* Lasts until the end of line */
   5221 	state &= ~(ST_YAML_VAL | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG
   5222 		| ST_LINK_SECOND_ARG_END);
   5223 
   5224 	if (pbuffer && *pbuffer)
   5225 		goto do_buffer;
   5226 done_buffer:
   5227 	if (!read_yaml_macros_and_links)
   5228 	{
   5229 		if (footnote_count > 0 || inline_footnote_count > 0)
   5230 			end_footnotes(output, link_prefix, add_footnote_div,
   5231 				previous_line_blank,
   5232 				read_yaml_macros_and_links);
   5233 
   5234 		if (paralinks_count > 0)
   5235 			process_paralinks(output, link_prefix);
   5236 
   5237 		if (madeby_present)
   5238 		{
   5239 			process_madeby(output);
   5240 			output_firstcol = 1;
   5241 		}
   5242 	}
   5243 
   5244 	free(link_text);
   5245 	free(link_macro);
   5246 	free(token);
   5247 	free(line);
   5248 
   5249 	return 0;
   5250 }
   5251 
   5252 int
   5253 main(const int argc, const char** argv)
   5254 {
   5255 	const char* arg;
   5256 	Command cmd	   = CMD_NONE;
   5257 	int result	   = 0;
   5258 	FILE* input	   = NULL;
   5259 	FILE* output	   = stdout;
   5260 	u8* buffer	   = NULL;
   5261 	size_t buffer_size = 0;
   5262 
   5263 	basedir_size = 2;
   5264 	CALLOC(basedir, char, basedir_size);
   5265 	*basedir = '.';
   5266 
   5267 	while ((arg = *++argv))
   5268 	{
   5269 		if (*arg == '-')
   5270 		{
   5271 			arg++;
   5272 			char c = *arg++;
   5273 			int result;
   5274 
   5275 			if (c == '-')
   5276 			{
   5277 				if (!strcmp(arg, "full-version"))
   5278 					cmd = CMD_FULL_VERSION;
   5279 				else if (!strcmp(arg, "version"))
   5280 					cmd = CMD_VERSION;
   5281 				else if (startswith(arg, "basedir"))
   5282 				{
   5283 					arg += strlen("basedir");
   5284 					result = set_basedir(&basedir,
   5285 						&basedir_size, arg);
   5286 					if (result)
   5287 						return result;
   5288 				}
   5289 				else if (startswith(arg, "global-link-prefix"))
   5290 				{
   5291 					arg += strlen("global-link-prefix");
   5292 					result = set_global_link_prefix(
   5293 						&global_link_prefix,
   5294 						&global_link_prefix_size, arg);
   5295 					if (result)
   5296 						return result;
   5297 				}
   5298 				else if (!strcmp(arg, "help"))
   5299 					return usage();
   5300 				else
   5301 				{
   5302 					error(EINVAL,
   5303 						(u8*)"main: Invalid "
   5304 						     "argument: --%s",
   5305 						arg);
   5306 					return usage();
   5307 				}
   5308 			}
   5309 			else
   5310 			{
   5311 				switch (c)
   5312 				{
   5313 				case 'd':
   5314 					cmd = CMD_BASEDIR;
   5315 					break;
   5316 				case 'h':
   5317 					return usage();
   5318 					break;
   5319 				case 'p':
   5320 					cmd = CMD_LINK_PREFIX;
   5321 					break;
   5322 				case 'V':
   5323 					cmd = CMD_FULL_VERSION;
   5324 					break;
   5325 				case 'v':
   5326 					cmd = CMD_VERSION;
   5327 					break;
   5328 				default:
   5329 					error(EINVAL,
   5330 						(u8*)"main: Invalid "
   5331 						     "argument: -%c",
   5332 						c);
   5333 					return usage();
   5334 				}
   5335 			}
   5336 		}
   5337 		else
   5338 		{
   5339 			int result;
   5340 
   5341 			if (cmd == CMD_BASEDIR)
   5342 			{
   5343 				result = set_basedir(&basedir, &basedir_size,
   5344 					arg);
   5345 				if (result)
   5346 					return result;
   5347 			}
   5348 			else if (cmd == CMD_LINK_PREFIX)
   5349 			{
   5350 				result = set_global_link_prefix(&global_link_prefix,
   5351 					&global_link_prefix_size, arg);
   5352 				if (result)
   5353 					return result;
   5354 			}
   5355 			else
   5356 				input_filename = arg;
   5357 			cmd = CMD_NONE;
   5358 		}
   5359 	}
   5360 
   5361 	if (cmd == CMD_BASEDIR)
   5362 		return error(1, (u8*)"main: set_basedir: Argument required");
   5363 
   5364 	if (cmd == CMD_FULL_VERSION)
   5365 		return version(1);
   5366 	else if (cmd == CMD_VERSION)
   5367 		return version(0);
   5368 
   5369 	if (input_filename)
   5370 	{
   5371 		int result = read_file_into_buffer(&input, &buffer,
   5372 			&buffer_size, input_filename, &input_dirname);
   5373 		if (result)
   5374 			exit(result);
   5375 	}
   5376 	else
   5377 	{
   5378 		u8* bufline	   = NULL;
   5379 		size_t buffer_len  = 0;
   5380 		size_t buffer_size = 0;
   5381 		size_t bufline_len = 0;
   5382 
   5383 		input = stdin;
   5384 
   5385 		CALLOC(input_dirname, char, 2);
   5386 		*input_dirname = '.';
   5387 
   5388 		CALLOC(bufline, u8, BUFSIZE);
   5389 		buffer_size = BUFSIZE;
   5390 		CALLOC(buffer, u8, buffer_size);
   5391 
   5392 		while (!feof(input))
   5393 		{
   5394 			u8* eol = NULL;
   5395 			if (!fgets((char*)bufline, BUFSIZE - 1, input))
   5396 				break;
   5397 			eol = (u8*)strchr((char*)bufline, '\n');
   5398 			if (eol)
   5399 				*(eol + 1) = 0;
   5400 			bufline_len = strlen((char*)bufline);
   5401 			if (buffer_len + bufline_len + 1 > buffer_size)
   5402 			{
   5403 				buffer_size += BUFSIZE;
   5404 				REALLOC(buffer, u8, buffer_size);
   5405 			}
   5406 			if (strlcat((char*)buffer, (char*)bufline, buffer_size)
   5407 				> buffer_size)
   5408 				warning(1, (u8*)"strlcat:%d: Overflow",
   5409 					__LINE__);
   5410 			buffer_len += bufline_len;
   5411 		}
   5412 		free(bufline);
   5413 	}
   5414 
   5415 	vars		      = NULL;
   5416 	vars_count	      = 0;
   5417 	macros		      = NULL;
   5418 	macros_count	      = 0;
   5419 	links		      = NULL;
   5420 	links_count	      = 0;
   5421 	paralinks	      = NULL;
   5422 	paralinks_count	      = 0;
   5423 	footnotes	      = NULL;
   5424 	footnote_count	      = 0;
   5425 	inline_footnotes      = NULL;
   5426 	inline_footnote_count = 0;
   5427 
   5428 	/* First pass: read YAML, macros and links */
   5429 	result = slweb_parse(output, input_filename, buffer, 1);
   5430 
   5431 	if (result)
   5432 	{
   5433 		slweb_cleanup();
   5434 		free(buffer);
   5435 		return result;
   5436 	}
   5437 
   5438 	state			= ST_NONE;
   5439 	current_footnote	= 0;
   5440 	current_inline_footnote = 0;
   5441 
   5442 	/* Second pass: parse and output */
   5443 	result = slweb_parse(output, input_filename, buffer, 0);
   5444 
   5445 	slweb_cleanup();
   5446 	free(buffer);
   5447 
   5448 	return result;
   5449 }