slweb

Једноставни генератор статичких веб страна
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 042376a1d1e7bcb3af3c1ba380fb77613b25e022
родитељ c995f21f0a965431529127161692bfaa63aeca83
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Fri, 13 Aug 2021 14:55:35 +0200

Reformatted code using clang-format; using goto instead of while in places to decrease indentation

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
Mindex.html | 2+-
Mslweb.c | 8049++++++++++++++++++++++++++++++++++++++++---------------------------------------
измењених датотека: 2, додавања: 4128(+), брисања: 3923(-)

diff --git a/index.html b/index.html @@ -152,7 +152,7 @@ this program. If not, see &lt;<a href="https://www.gnu.org/licenses">https://www <div id="git-log"> Previous commit: -index.slw e0dbba5 2021-08-11 17:31:02 +0200 (Страхиња Радић) (HEAD -> master, origin/master, origin/HEAD) +index.slw c995f21 2021-08-12 09:06:45 +0200 (Страхиња Радић) (HEAD -> master, origin/master, origin/HEAD) </div><!--git-log--> diff --git a/slweb.c b/slweb.c @@ -2,4320 +2,4525 @@ #include "defs.h" -static size_t lineno = 0; -static size_t colno = 1; -static BOOL output_firstcol = TRUE; -static char* input_filename = NULL; -static char* input_dirname = NULL; -static char* basedir = NULL; -static size_t basedir_size = 0; -static char* incdir = NULL; -static char* global_link_prefix = NULL; +static size_t lineno = 0; +static size_t colno = 1; +static BOOL output_firstcol = TRUE; +static char* input_filename = NULL; +static char* input_dirname = NULL; +static char* basedir = NULL; +static size_t basedir_size = 0; +static char* incdir = NULL; +static char* global_link_prefix = NULL; static size_t global_link_prefix_size = 0; -static KeyValue* vars = NULL; -static KeyValue* pvars = NULL; -static size_t vars_count = 0; -static KeyValue* macros = NULL; -static KeyValue* pmacros = NULL; -static size_t macros_count = 0; -static KeyValue* links = NULL; -static KeyValue* plinks = NULL; -static size_t links_count = 0; -static KeyValue* footnotes = NULL; -static KeyValue* pfootnotes = NULL; -static size_t footnote_count = 0; -static size_t current_footnote = 0; -static u8** inline_footnotes = NULL; +static KeyValue* vars = NULL; +static KeyValue* pvars = NULL; +static size_t vars_count = 0; +static KeyValue* macros = NULL; +static KeyValue* pmacros = NULL; +static size_t macros_count = 0; +static KeyValue* links = NULL; +static KeyValue* plinks = NULL; +static size_t links_count = 0; +static KeyValue* footnotes = NULL; +static KeyValue* pfootnotes = NULL; +static size_t footnote_count = 0; +static size_t current_footnote = 0; +static u8** inline_footnotes = NULL; static size_t inline_footnote_count = 0; static size_t current_inline_footnote = 0; -static u8* csv_template = NULL; -static size_t csv_template_size = 0; -static char* csv_filename = NULL; -static long csv_iter = 0; -static ULLONG state = ST_NONE; - -#define CHECKEXITNOMEM(ptr) do { if (!ptr) exit(error(ENOMEM, \ - (u8*)"Memory allocation failed (out of memory?)")); } while (0) - -#define CALLOC(ptr, ptrtype, nmemb) do { ptr = calloc(nmemb, sizeof(ptrtype)); \ - CHECKEXITNOMEM(ptr); } while (0) - -#define REALLOC(ptr, ptrtype, newsize) do { ptrtype* newptr = realloc(ptr, newsize); \ - CHECKEXITNOMEM(newptr); \ - ptr = newptr; } while (0) +static u8* csv_template = NULL; +static size_t csv_template_size = 0; +static char* csv_filename = NULL; +static long csv_iter = 0; +static ULLONG state = ST_NONE; + +#define CHECKEXITNOMEM(ptr) \ + do \ + { \ + if (!ptr) \ + exit(error(ENOMEM, \ + (u8*)"Memory allocation failed (out of " \ + "memory?)")); \ + } while (0) + +#define CALLOC(ptr, ptrtype, nmemb) \ + do \ + { \ + ptr = calloc(nmemb, sizeof(ptrtype)); \ + CHECKEXITNOMEM(ptr); \ + } while (0) + +#define REALLOC(ptr, ptrtype, newsize) \ + do \ + { \ + ptrtype* newptr = realloc(ptr, newsize); \ + CHECKEXITNOMEM(newptr); \ + ptr = newptr; \ + } while (0) #define REALLOCARRAY(ptr, membtype, newcount) \ - REALLOC(ptr, membtype, sizeof(membtype) * newcount); - -#define CHECKCOPY(token, ptoken, token_size, pline) do { \ - if (ptoken + 2 > token + token_size) \ - { \ - size_t old_size = token_size; \ - token_size += BUFSIZE; \ - REALLOC(token, u8, token_size); \ - ptoken = token + old_size - 1; \ - } \ - *ptoken++ = *pline++; } while (0) - -#define RESET_TOKEN(token, ptoken, token_size) do { \ - token_size = BUFSIZE; \ - REALLOC(token, u8, token_size); \ - *token = 0; ptoken = token; } while (0) - -#define ALL(var, mask) ( ((var) & (mask)) == (mask) ) -#define ANY(var, mask) ( (var) & (mask) ) + REALLOC(ptr, membtype, sizeof(membtype) * newcount); + +#define CHECKCOPY(token, ptoken, token_size, pline) \ + do \ + { \ + if (ptoken + 2 > token + token_size) \ + { \ + size_t old_size = token_size; \ + token_size += BUFSIZE; \ + REALLOC(token, u8, token_size); \ + ptoken = token + old_size - 1; \ + } \ + *ptoken++ = *pline++; \ + } while (0) + +#define RESET_TOKEN(token, ptoken, token_size) \ + do \ + { \ + token_size = BUFSIZE; \ + REALLOC(token, u8, token_size); \ + *token = 0; \ + ptoken = token; \ + } while (0) + +#define ALL(var, mask) (((var) & (mask)) == (mask)) +#define ANY(var, mask) ((var) & (mask)) int version() { - printf("%s v%s\n", PROGRAMNAME, VERSION); - return 0; + printf("%s v%s\n", PROGRAMNAME, VERSION); + return 0; } int usage() { - printf("Usage: %s [-b|--body-only] [-d|--basedir <dir>] [-h|--help]" - " [-v|--version] [filename]\n", PROGRAMNAME); - return 0; + printf("Usage: %s [-b|--body-only] [-d|--basedir <dir>] [-h|--help]" + " [-v|--version] [filename]\n", + PROGRAMNAME); + return 0; } int error(int code, u8* fmt, ...) { - char buf[BUFSIZE]; - va_list args; - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), (const char*)fmt, args); - va_end(args); - fprintf(stderr, "%s:%s:%lu:%lu: %s\n", PROGRAMNAME, - input_filename ? input_filename : "(stdin)", - lineno, colno, buf); - return code; + char buf[BUFSIZE]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), (const char*)fmt, args); + va_end(args); + fprintf(stderr, "%s:%s:%lu:%lu: %s\n", PROGRAMNAME, + input_filename ? input_filename : "(stdin)", lineno, colno, + buf); + return code; } int warning(int code, u8* fmt, ...) { - char buf[BUFSIZE]; - va_list args; - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), (const char*)fmt, args); - va_end(args); - fprintf(stderr, "Warning: %s\n", buf); - return code; + char buf[BUFSIZE]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), (const char*)fmt, args); + va_end(args); + fprintf(stderr, "Warning: %s\n", buf); + return code; } int free_keyvalue(KeyValue** list, size_t list_count) { - if (!list || !*list) - return 1; - - for (size_t index = 0; index < list_count; index++) - { - KeyValue* current = *list + index; - if (current->value) - free(current->value); - if (current->key) - free(current->key); - } - return 0; + if (!list || !*list) + return 1; + + for (size_t index = 0; index < list_count; index++) + { + KeyValue* current = *list + index; + if (current->value) + free(current->value); + if (current->key) + free(current->key); + } + return 0; } int slweb_cleanup() { - if (basedir) - free(basedir); - free(input_dirname); - while (inline_footnote_count--) - free(inline_footnotes[inline_footnote_count]); - if (inline_footnotes) - free(inline_footnotes); - free_keyvalue(&footnotes, footnote_count); - free_keyvalue(&links, links_count); - free_keyvalue(&macros, macros_count); - free_keyvalue(&vars, vars_count); - if (footnotes) - free(footnotes); - if (links) - free(links); - if (macros) - free(macros); - if (vars) - free(vars); - - return 0; + if (basedir) + free(basedir); + free(input_dirname); + while (inline_footnote_count--) + free(inline_footnotes[inline_footnote_count]); + if (inline_footnotes) + free(inline_footnotes); + free_keyvalue(&footnotes, footnote_count); + free_keyvalue(&links, links_count); + free_keyvalue(&macros, macros_count); + free_keyvalue(&vars, vars_count); + if (footnotes) + free(footnotes); + if (links) + free(links); + if (macros) + free(macros); + if (vars) + free(vars); + + return 0; } -int slweb_parse(u8* buffer, FILE* output, BOOL body_only, - BOOL read_yaml_macros_and_links); +int +slweb_parse(u8* buffer, FILE* output, BOOL body_only, + BOOL read_yaml_macros_and_links); BOOL startswith(const char* s, const char* what) { - if (!s || !what) - return FALSE; + if (!s || !what) + return FALSE; - do - { - if (*s++ != *what++) - return FALSE; - } - while (*s && *what); + do + { + if (*s++ != *what++) + return FALSE; + } while (*s && *what); - return !*what; + return !*what; } u8* get_value(KeyValue* list, size_t list_count, u8* key, BOOL* seen) { - KeyValue* plist = list; - while (plist < list + list_count) - { - if (!strcmp((char*)plist->key, (char*)key)) - { - if (seen) - { - *seen = plist->seen; - plist->seen = TRUE; - } - return plist->value; - } - plist++; - } - return NULL; + KeyValue* plist = list; + while (plist < list + list_count) + { + if (!strcmp((char*)plist->key, (char*)key)) + { + if (seen) + { + *seen = plist->seen; + plist->seen = TRUE; + } + return plist->value; + } + plist++; + } + return NULL; } int set_basedir(char* arg, char** basedir, size_t* basedir_size) { - size_t basedir_len = 0; - size_t arg_len = strlen(arg); - - if (arg_len < 1) - exit(error(1, (u8*)"--basedir: Argument required")); - - if (arg_len + 1 > *basedir_size) - { - *basedir_size = arg_len+1; - REALLOC(*basedir, char, *basedir_size); - } - strncpy(*basedir, arg, *basedir_size-1); - *(*basedir + arg_len) = 0; - basedir_len = strlen(*basedir); - if (*(*basedir + basedir_len - 1) == '/') - *(*basedir + basedir_len - 1) = 0; - - return 0; + size_t basedir_len = 0; + size_t arg_len = strlen(arg); + + if (arg_len < 1) + exit(error(1, (u8*)"--basedir: Argument required")); + + if (arg_len + 1 > *basedir_size) + { + *basedir_size = arg_len + 1; + REALLOC(*basedir, char, *basedir_size); + } + strncpy(*basedir, arg, *basedir_size - 1); + *(*basedir + arg_len) = 0; + basedir_len = strlen(*basedir); + if (*(*basedir + basedir_len - 1) == '/') + *(*basedir + basedir_len - 1) = 0; + + return 0; } int -set_global_link_prefix(char* arg, char** global_link_prefix, - size_t* global_link_prefix_size) +set_global_link_prefix( + char* arg, char** global_link_prefix, size_t* global_link_prefix_size) { - size_t arg_len = strlen(arg); + size_t arg_len = strlen(arg); - if (arg_len < 1) - exit(error(1, (u8*)"--global-link-prefix: Argument required")); + if (arg_len < 1) + exit(error(1, (u8*)"--global-link-prefix: Argument required")); - if (arg_len + 1 > *global_link_prefix_size) - { - *global_link_prefix_size = arg_len+1; - REALLOC(*global_link_prefix, char, *global_link_prefix_size); - } - strncpy(*global_link_prefix, arg, *global_link_prefix_size-1); - *(*global_link_prefix + arg_len) = 0; + if (arg_len + 1 > *global_link_prefix_size) + { + *global_link_prefix_size = arg_len + 1; + REALLOC(*global_link_prefix, char, *global_link_prefix_size); + } + strncpy(*global_link_prefix, arg, *global_link_prefix_size - 1); + *(*global_link_prefix + arg_len) = 0; - return 0; + return 0; } char* strip_ext(const char* fn) { - char* newname = NULL; - char* pnewname = NULL; - const char* pfn = NULL; - char* dot = NULL; + char* newname = NULL; + char* pnewname = NULL; + const char* pfn = NULL; + char* dot = NULL; - dot = strrchr(fn, '.'); + dot = strrchr(fn, '.'); - if (!dot) - return NULL; + if (!dot) + return NULL; - CALLOC(newname, char, strlen(fn)+1); - pnewname = newname; - pfn = fn; + CALLOC(newname, char, strlen(fn) + 1); + pnewname = newname; + pfn = fn; - while (pfn != dot && *pfn) - *pnewname++ = *pfn++; + while (pfn != dot && *pfn) + *pnewname++ = *pfn++; - return newname; + return newname; } int print_output(FILE* output, char* fmt, ...) { - char buf[BUFSIZE]; - va_list args; - - if (!output || !fmt) - exit(error(EINVAL, (u8*)"print_output: Invalid argument")); - - va_start(args, fmt); - if (state & ST_CSV_BODY) - { - size_t buf_len = 0; - vsnprintf((char*)buf, BUFSIZE, fmt, args); - buf_len = strlen(buf); - if (!csv_template) - { - csv_template_size = BUFSIZE; - CALLOC(csv_template, u8, csv_template_size); - strncpy((char*)csv_template, buf, BUFSIZE-1); - *(csv_template + buf_len) = 0; - } - else - { - if (strlen((char*)csv_template) + buf_len > csv_template_size) - { - csv_template_size += BUFSIZE; - REALLOC(csv_template, u8, csv_template_size); - } - strncat((char*)csv_template, buf, csv_template_size - - strlen((char*)csv_template) - 1); - } - } - else - vfprintf(output, fmt, args); - va_end(args); - return 0; + char buf[BUFSIZE]; + va_list args; + + if (!output || !fmt) + exit(error(EINVAL, (u8*)"print_output: Invalid argument")); + + va_start(args, fmt); + if (state & ST_CSV_BODY) + { + size_t buf_len = 0; + vsnprintf((char*)buf, BUFSIZE, fmt, args); + buf_len = strlen(buf); + if (!csv_template) + { + csv_template_size = BUFSIZE; + CALLOC(csv_template, u8, csv_template_size); + strncpy((char*)csv_template, buf, BUFSIZE - 1); + *(csv_template + buf_len) = 0; + } + else + { + if (strlen((char*)csv_template) + buf_len + > csv_template_size) + { + csv_template_size += BUFSIZE; + REALLOC(csv_template, u8, csv_template_size); + } + strncat((char*)csv_template, buf, + csv_template_size - strlen((char*)csv_template) + - 1); + } + } + else + vfprintf(output, fmt, args); + va_end(args); + return 0; } -#define PIPE_READ_INDEX 0 +#define PIPE_READ_INDEX 0 #define PIPE_WRITE_INDEX 1 int -print_command(const char* command, - const u8* pass_arguments[], const u8* pipe_arguments[], - FILE* output, BOOL strip_newlines) +print_command(const char* command, const u8* pass_arguments[], + const u8* pipe_arguments[], FILE* output, BOOL strip_newlines) { - if (!command || !pass_arguments) - exit(error(EINVAL, (u8*)"print_command: Invalid argument")); - - pid_t pid = 0; - int arg_pipe_fds[2]; - int output_pipe_fds[2]; - int pstatus = 0; - - pipe(arg_pipe_fds); - pipe(output_pipe_fds); - pid = fork(); - if (pid == 0) - { - close(arg_pipe_fds[PIPE_WRITE_INDEX]); - close(output_pipe_fds[PIPE_READ_INDEX]); - - prctl(PR_SET_PDEATHSIG, SIGTERM); - - dup2(arg_pipe_fds[PIPE_READ_INDEX], STDIN_FILENO); - dup2(output_pipe_fds[PIPE_WRITE_INDEX], STDOUT_FILENO); - - execvp(command, (char* const*)pass_arguments); - exit(1); - } - else if (pid < 0) - exit(error(errno, (u8*)"Fork failed")); - - /* Parent */ - close(arg_pipe_fds[PIPE_READ_INDEX]); - close(output_pipe_fds[PIPE_WRITE_INDEX]); - FILE* cmd_input = fdopen(arg_pipe_fds[PIPE_WRITE_INDEX], "w"); - FILE* cmd_output = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r"); - - if (!cmd_input || !cmd_output) - exit(error(1, (u8*)"Cannot fdopen")); - - if (pipe_arguments) - { - const u8** ppipe_argument = pipe_arguments; - while (ppipe_argument && *ppipe_argument) - { - fprintf(cmd_input, "%s\n", *ppipe_argument); - ppipe_argument++; - } - } - fclose(cmd_input); - - u8* cmd_output_line = NULL; - CALLOC(cmd_output_line, u8, BUFSIZE); - while (!feof(cmd_output)) - { - if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output)) - continue; - - char* eol = strchr((char*)cmd_output_line, '\n'); - if (eol) - *eol = 0; - - print_output(output, "%s%s", cmd_output_line, - strip_newlines ? "" : "\n"); - } - if (!strip_newlines) - output_firstcol = TRUE; - free(cmd_output_line); - - fclose(cmd_output); - - kill(pid, SIGKILL); - pid_t wpid = waitpid(pid, &pstatus, 0); - if (wpid < 0) - warning(pstatus, (u8*)"Child returned nonzero status, errno = %d", - errno); - if (WIFEXITED(pstatus)) - return WEXITSTATUS(pstatus); - - return wpid; + if (!command || !pass_arguments) + exit(error(EINVAL, (u8*)"print_command: Invalid argument")); + + pid_t pid = 0; + int arg_pipe_fds[2]; + int output_pipe_fds[2]; + int pstatus = 0; + + pipe(arg_pipe_fds); + pipe(output_pipe_fds); + pid = fork(); + if (pid == 0) + { + close(arg_pipe_fds[PIPE_WRITE_INDEX]); + close(output_pipe_fds[PIPE_READ_INDEX]); + + prctl(PR_SET_PDEATHSIG, SIGTERM); + + dup2(arg_pipe_fds[PIPE_READ_INDEX], STDIN_FILENO); + dup2(output_pipe_fds[PIPE_WRITE_INDEX], STDOUT_FILENO); + + execvp(command, (char* const*)pass_arguments); + exit(1); + } + else if (pid < 0) + exit(error(errno, (u8*)"Fork failed")); + + /* Parent */ + close(arg_pipe_fds[PIPE_READ_INDEX]); + close(output_pipe_fds[PIPE_WRITE_INDEX]); + FILE* cmd_input = fdopen(arg_pipe_fds[PIPE_WRITE_INDEX], "w"); + FILE* cmd_output = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r"); + + if (!cmd_input || !cmd_output) + exit(error(1, (u8*)"Cannot fdopen")); + + if (pipe_arguments) + { + const u8** ppipe_argument = pipe_arguments; + while (ppipe_argument && *ppipe_argument) + { + fprintf(cmd_input, "%s\n", *ppipe_argument); + ppipe_argument++; + } + } + fclose(cmd_input); + + u8* cmd_output_line = NULL; + CALLOC(cmd_output_line, u8, BUFSIZE); + while (!feof(cmd_output)) + { + if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output)) + continue; + + char* eol = strchr((char*)cmd_output_line, '\n'); + if (eol) + *eol = 0; + + print_output(output, "%s%s", cmd_output_line, + strip_newlines ? "" : "\n"); + } + if (!strip_newlines) + output_firstcol = TRUE; + free(cmd_output_line); + + fclose(cmd_output); + + kill(pid, SIGKILL); + pid_t wpid = waitpid(pid, &pstatus, 0); + if (wpid < 0) + warning(pstatus, + (u8*)"Child returned nonzero status, errno = %d", + errno); + if (WIFEXITED(pstatus)) + return WEXITSTATUS(pstatus); + + return wpid; } int -read_file_into_buffer(u8** buffer, size_t* buffer_size, char* input_filename, - char** input_dirname, FILE** input) +read_file_into_buffer(u8** buffer, size_t* buffer_size, char* input_filename, + char** input_dirname, FILE** input) { - struct stat fs; - char* slash = NULL; - - *input = fopen(input_filename, "r"); - if (!*input) - return error(ENOENT, (u8*)"No such file: %s", input_filename); - - fstat(fileno(*input), &fs); - if (*buffer) - free(*buffer); - *buffer_size = fs.st_size+1; - CALLOC(*buffer, u8, *buffer_size); - fread((void*)*buffer, 1, *buffer_size, *input); - - if (*input_dirname) - free(*input_dirname); - - slash = strrchr(input_filename, '/'); - if (slash) - { - CALLOC(*input_dirname, char, strlen(input_filename)+1); - char* pinput_dirname = *input_dirname; - char* pinput_filename = input_filename; - while (pinput_filename && *pinput_filename - && pinput_filename != slash) - *pinput_dirname++ = *pinput_filename++; - } - else - { - CALLOC(*input_dirname, char, 2); - **input_dirname = '.'; - } - - fclose(*input); - - return 0; + struct stat fs; + char* slash = NULL; + + *input = fopen(input_filename, "r"); + if (!*input) + return error(ENOENT, (u8*)"No such file: %s", input_filename); + + fstat(fileno(*input), &fs); + if (*buffer) + free(*buffer); + *buffer_size = fs.st_size + 1; + CALLOC(*buffer, u8, *buffer_size); + fread((void*)*buffer, 1, *buffer_size, *input); + + if (*input_dirname) + free(*input_dirname); + + slash = strrchr(input_filename, '/'); + if (slash) + { + CALLOC(*input_dirname, char, strlen(input_filename) + 1); + char* pinput_dirname = *input_dirname; + char* pinput_filename = input_filename; + while (pinput_filename && *pinput_filename + && pinput_filename != slash) + *pinput_dirname++ = *pinput_filename++; + } + else + { + CALLOC(*input_dirname, char, 2); + **input_dirname = '.'; + } + + fclose(*input); + + return 0; } -int read_csv(FILE* output, const char* filename, csv_callback_t callback); +int +read_csv(FILE* output, const char* filename, csv_callback_t callback); int print_meta_var(FILE* output, u8** csv_header, u8** csv_register) { - if (*csv_register && *(csv_register+1)) - { - u8* pvalue = *(csv_register+1); - size_t value_len = strlen((char*)pvalue); - if (*pvalue == '%' && *(pvalue+value_len-1) == '%') - { - u8* var_name = NULL; - var_name = (u8*)strdup((char*)pvalue+1); - *(var_name+value_len-2) = 0; - u8* value = get_value(vars, vars_count, var_name, NULL); - - if (value) - print_output(output, "<meta name=\"%s\" content=\"%s\" />\n", - *csv_register, value); - - free(var_name); - } - else - print_output(output, "<meta name=\"%s\" content=\"%s\" />\n", - *csv_register, *(csv_register+1)); - } - return 0; + if (!*csv_register || !*(csv_register + 1)) + return 0; + u8* pvalue = *(csv_register + 1); + size_t value_len = strlen((char*)pvalue); + if (*pvalue == '%' && *(pvalue + value_len - 1) == '%') + { + u8* var_name = NULL; + var_name = (u8*)strdup((char*)pvalue + 1); + *(var_name + value_len - 2) = 0; + u8* value = get_value(vars, vars_count, var_name, NULL); + + if (value) + print_output(output, + "<meta name=\"%s\" content=\"%s\" />\n", + *csv_register, value); + + free(var_name); + } + else + print_output(output, "<meta name=\"%s\" content=\"%s\" />\n", + *csv_register, *(csv_register + 1)); + return 0; } int process_heading_start(FILE* output, UBYTE heading_level, ULONG heading_count) { - print_output(output, "<h%d id=\"heading-%lu\">", heading_level, heading_count); - return 0; + print_output(output, "<h%d id=\"heading-%lu\">", heading_level, + heading_count); + return 0; } int process_heading(u8* token, FILE* output, UBYTE heading_level) { - if (!token || strlen((char*)token) < 1) - warning(1, (u8*)"Empty heading"); + if (!token || strlen((char*)token) < 1) + warning(1, (u8*)"Empty heading"); - print_output(output, "%s</h%d>", - token ? (char*)token : "", heading_level); + print_output( + output, "%s</h%d>", token ? (char*)token : "", heading_level); - return 0; + return 0; } int process_git_log(FILE* output) { - if (!input_filename) - return warning(1, (u8*)"Cannot use 'git-log' in stdin"); + if (!input_filename) + return warning(1, (u8*)"Cannot use 'git-log' in stdin"); - char* basename = NULL; - size_t basename_size = strlen(input_filename)+1; - CALLOC(basename, char, basename_size); - char* slash = strrchr(input_filename, '/'); - pid_t result = 0; + char* basename = NULL; + size_t basename_size = strlen(input_filename) + 1; + CALLOC(basename, char, basename_size); + char* slash = strrchr(input_filename, '/'); + pid_t result = 0; - if (slash) - strncpy(basename, slash+1, basename_size-1); - else - strncpy(basename, input_filename, basename_size-1); + if (slash) + strncpy(basename, slash + 1, basename_size - 1); + else + strncpy(basename, input_filename, basename_size - 1); - u8* pipe_args[] = { (u8*)basename, NULL }; + u8* pipe_args[] = {(u8*)basename, NULL}; - print_output(output, "<div id=\"git-log\">\nPrevious commit:\n"); - result = print_command(CMD_GIT_LOG, - (const u8**)CMD_GIT_LOG_ARGS, - (const u8**)pipe_args, output, FALSE); + print_output(output, "<div id=\"git-log\">\nPrevious commit:\n"); + result = print_command(CMD_GIT_LOG, (const u8**)CMD_GIT_LOG_ARGS, + (const u8**)pipe_args, output, FALSE); - print_output(output, "</div><!--git-log-->\n"); - output_firstcol = TRUE; + print_output(output, "</div><!--git-log-->\n"); + output_firstcol = TRUE; - free(basename); + free(basename); - return result ? warning(result, (u8*)"git-log: Cannot run git") : 0; + return result ? warning(result, (u8*)"git-log: Cannot run git") : 0; } -#define PRINTIF(format, arg) do { \ - if ((ALL(csv_state, ST_CS_COND_NONEMPTY) \ - && *csv_register[conditional_index-1]) \ - || (ALL(csv_state, ST_CS_COND_EMPTY) \ - && !*csv_register[conditional_index-1]) \ - || !ANY(csv_state, ST_CS_COND_EMPTY | ST_CS_COND_NONEMPTY)) \ - { fprintf(output, format, arg); } } while (0) +#define PRINTIF(format, arg) \ + do \ + { \ + if ((ALL(csv_state, ST_CS_COND_NONEMPTY) \ + && *csv_register[conditional_index - 1]) \ + || (ALL(csv_state, ST_CS_COND_EMPTY) \ + && !*csv_register[conditional_index - 1]) \ + || !ANY(csv_state, \ + ST_CS_COND_EMPTY | ST_CS_COND_NONEMPTY)) \ + { \ + fprintf(output, format, arg); \ + } \ + } while (0) int print_csv_row(FILE* output, u8** csv_header, u8** csv_register) { - u8* pcsv_template = csv_template; - UBYTE csv_state = ST_CS_NONE; - UBYTE num = 0; - UBYTE conditional_index = 0; - - while (*pcsv_template) - { - if (csv_state & ST_CS_ESCAPE) - { - PRINTIF("%c", *pcsv_template); - csv_state &= ~ST_CS_ESCAPE; - pcsv_template++; - continue; - } - - switch (*pcsv_template) - { - case '\\': - csv_state |= ST_CS_ESCAPE; - pcsv_template++; - break; - case '$': - if (csv_state & ST_CS_REGISTER) - { - PRINTIF("%c", '$'); - csv_state &= ~ST_CS_REGISTER; - } - else - csv_state |= ST_CS_REGISTER; - pcsv_template++; - break; - case '#': - if (csv_state & ST_CS_HEADER) - { - error(1, (u8*)"csv: Invalid header register mark"); - csv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER); - } - else if (csv_state & ST_CS_REGISTER) - { - csv_state &= ~ST_CS_REGISTER; - csv_state |= ST_CS_HEADER; - } - else - PRINTIF("%c", '#'); - pcsv_template++; - break; - case '?': - if (csv_state & ST_CS_COND) - { - error(1, (u8*)"csv: Invalid conditional mark"); - csv_state &= ~ST_CS_COND; - } - else if (csv_state & ST_CS_REGISTER) - { - csv_state &= ~ST_CS_REGISTER; - csv_state |= ST_CS_COND; - } - else - PRINTIF("%c", '?'); - pcsv_template++; - break; - case '!': - if (ALL(csv_state, ST_CS_COND | ST_CS_COND_NONEMPTY)) - { - csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); - csv_state |= ST_CS_COND_EMPTY; - } - else if (csv_state & ST_CS_COND) - { - error(1, (u8*)"Empty conditional before/without nonempty" - " conditional"); - csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); - } - else - PRINTIF("%c", '!'); - pcsv_template++; - break; - case '/': - if (ALL(csv_state, ST_CS_COND | ST_CS_COND_EMPTY)) - csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); - else if (ALL(csv_state, ST_CS_COND)) - csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); - else - PRINTIF("%c", '/'); - pcsv_template++; - break; - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': - if (csv_state & ST_CS_COND) - { - conditional_index = *pcsv_template - '0'; - csv_state &= ~ST_CS_COND; - csv_state |= ST_CS_COND_NONEMPTY; - } - else if (csv_state & ST_CS_HEADER) - { - num = *pcsv_template - '0'; - PRINTIF("%s", csv_header[num-1]); - csv_state &= ~ST_CS_HEADER; - } - else if (csv_state & ST_CS_REGISTER) - { - num = *pcsv_template - '0'; - PRINTIF("%s", csv_register[num-1]); - csv_state &= ~ST_CS_REGISTER; - } - else - PRINTIF("%c", *pcsv_template); - pcsv_template++; - break; - default: - if (csv_state & ST_CS_HEADER) - { - error(1, (u8*)"csv: Invalid header register mark"); - csv_state &= ~ST_CS_HEADER; - } - else if (csv_state & ST_CS_REGISTER) - { - error(1, (u8*)"csv: Invalid register mark"); - csv_state &= ~ST_CS_REGISTER; - } - else - PRINTIF("%c", *pcsv_template); - pcsv_template++; - } - } - return 0; + u8* pcsv_template = csv_template; + UBYTE csv_state = ST_CS_NONE; + UBYTE num = 0; + UBYTE conditional_index = 0; + +csv_template_start: + if (!*pcsv_template) + goto csv_template_done; + if (csv_state & ST_CS_ESCAPE) + { + PRINTIF("%c", *pcsv_template); + csv_state &= ~ST_CS_ESCAPE; + pcsv_template++; + goto csv_template_start; + } + + switch (*pcsv_template) + { + case '\\': + csv_state |= ST_CS_ESCAPE; + pcsv_template++; + break; + case '$': + if (csv_state & ST_CS_REGISTER) + { + PRINTIF("%c", '$'); + csv_state &= ~ST_CS_REGISTER; + } + else + csv_state |= ST_CS_REGISTER; + pcsv_template++; + break; + case '#': + if (csv_state & ST_CS_HEADER) + { + error(1, (u8*)"csv: Invalid header register mark"); + csv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER); + } + else if (csv_state & ST_CS_REGISTER) + { + csv_state &= ~ST_CS_REGISTER; + csv_state |= ST_CS_HEADER; + } + else + PRINTIF("%c", '#'); + pcsv_template++; + break; + case '?': + if (csv_state & ST_CS_COND) + { + error(1, (u8*)"csv: Invalid conditional mark"); + csv_state &= ~ST_CS_COND; + } + else if (csv_state & ST_CS_REGISTER) + { + csv_state &= ~ST_CS_REGISTER; + csv_state |= ST_CS_COND; + } + else + PRINTIF("%c", '?'); + pcsv_template++; + break; + case '!': + if (ALL(csv_state, ST_CS_COND | ST_CS_COND_NONEMPTY)) + { + csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); + csv_state |= ST_CS_COND_EMPTY; + } + else if (csv_state & ST_CS_COND) + { + error(1, + (u8*)"Empty conditional before/without nonempty" + " conditional"); + csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); + } + else + PRINTIF("%c", '!'); + pcsv_template++; + break; + case '/': + if (ALL(csv_state, ST_CS_COND | ST_CS_COND_EMPTY)) + csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); + else if (ALL(csv_state, ST_CS_COND)) + csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); + else + PRINTIF("%c", '/'); + pcsv_template++; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (csv_state & ST_CS_COND) + { + conditional_index = *pcsv_template - '0'; + csv_state &= ~ST_CS_COND; + csv_state |= ST_CS_COND_NONEMPTY; + } + else if (csv_state & ST_CS_HEADER) + { + num = *pcsv_template - '0'; + PRINTIF("%s", csv_header[num - 1]); + csv_state &= ~ST_CS_HEADER; + } + else if (csv_state & ST_CS_REGISTER) + { + num = *pcsv_template - '0'; + PRINTIF("%s", csv_register[num - 1]); + csv_state &= ~ST_CS_REGISTER; + } + else + PRINTIF("%c", *pcsv_template); + pcsv_template++; + break; + default: + if (csv_state & ST_CS_HEADER) + { + error(1, (u8*)"csv: Invalid header register mark"); + csv_state &= ~ST_CS_HEADER; + } + else if (csv_state & ST_CS_REGISTER) + { + error(1, (u8*)"csv: Invalid register mark"); + csv_state &= ~ST_CS_REGISTER; + } + else + PRINTIF("%c", *pcsv_template); + pcsv_template++; + } + goto csv_template_start; +csv_template_done: + return 0; } int read_csv(FILE* output, const char* filename, csv_callback_t callback) { - if (!callback) - exit(error(EINVAL, (u8*)"read_csv: Invalid callback argument")); - - FILE* csv = NULL; - size_t csv_lineno = 0; - u8* bufline = NULL; - u8* pbufline = NULL; - u8* token = NULL; - u8* ptoken = NULL; - size_t token_size = 0; - UBYTE csv_state = ST_CS_NONE; - u8* csv_header[MAX_CSV_REGISTERS]; - UBYTE current_header = 0; - u8* csv_register[MAX_CSV_REGISTERS]; - UBYTE current_register = 0; - u8* csv_delimiter = get_value(vars, vars_count, - (u8*)"csv-delimiter", NULL); - - if (!(csv = fopen(filename, "rt"))) - exit(error(ENOENT, (u8*)"csv: No such file: %s", filename)); - - CALLOC(bufline, u8, BUFSIZE); - token_size = BUFSIZE; - CALLOC(token, u8, token_size); - for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) - CALLOC(csv_header[i], u8, BUFSIZE); - for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) - CALLOC(csv_register[i], u8, BUFSIZE); - - while (!feof(csv) && (!csv_iter || csv_lineno <= csv_iter)) - { - u8* eol = NULL; - if (!fgets((char*)bufline, BUFSIZE, csv)) - break; - eol = (u8*)strchr((char*)bufline, '\n'); - if (eol) - *eol = 0; - - pbufline = bufline; - RESET_TOKEN(token, ptoken, token_size); - current_register = 0; - while (*pbufline) - { - switch (*pbufline) - { - case '"': - if (csv_state & ST_CS_QUOTE) - csv_state &= ~ST_CS_QUOTE; - else - csv_state |= ST_CS_QUOTE; - pbufline++; - break; - case ';': - case ',': - if (csv_state & ST_CS_QUOTE) - CHECKCOPY(token, ptoken, token_size, pbufline); - else - { - *ptoken = 0; - if (csv_lineno > 0) - { - if (current_register < MAX_CSV_REGISTERS) - strncpy((char*)csv_register[current_register++], - (char*)token, BUFSIZE-1); - } - else - { - if (current_header < MAX_CSV_REGISTERS) - strncpy((char*)csv_header[current_header++], - (char*)token, BUFSIZE-1); - } - RESET_TOKEN(token, ptoken, token_size); - pbufline++; - } - break; - default: - if (csv_state & ST_CS_QUOTE) - CHECKCOPY(token, ptoken, token_size, pbufline); - else - { - if (csv_delimiter && *pbufline == *csv_delimiter) - { - *ptoken = 0; - if (csv_lineno > 0) - { - if (current_register < MAX_CSV_REGISTERS) - strncpy((char*)csv_register[current_register++], - (char*)token, BUFSIZE-1); - } - else - { - if (current_header < MAX_CSV_REGISTERS) - strncpy((char*)csv_header[current_header++], - (char*)token, BUFSIZE-1); - } - RESET_TOKEN(token, ptoken, token_size); - pbufline++; - } - else - CHECKCOPY(token, ptoken, token_size, pbufline); - } - } - } - *ptoken = 0; - if (csv_lineno > 0) - { - if (current_register < MAX_CSV_REGISTERS) - strncpy((char*)csv_register[current_register++], - (char*)token, BUFSIZE-1); - } - else - { - if (current_header < MAX_CSV_REGISTERS) - strncpy((char*)csv_header[current_header++], - (char*)token, BUFSIZE-1); - } - RESET_TOKEN(token, ptoken, token_size); - - if (csv_lineno > 0 && pbufline != bufline) - (*callback)(output, csv_header, csv_register); - - for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) - *csv_register[i] = 0; - csv_lineno++; - } - fclose(csv); - for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--) - free(csv_register[i-1]); - for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--) - free(csv_header[i-1]); - free(token); - free(bufline); - - return 0; + if (!callback) + exit(error(EINVAL, (u8*)"read_csv: Invalid callback argument")); + + FILE* csv = NULL; + size_t csv_lineno = 0; + u8* bufline = NULL; + u8* pbufline = NULL; + u8* token = NULL; + u8* ptoken = NULL; + size_t token_size = 0; + UBYTE csv_state = ST_CS_NONE; + u8* csv_header[MAX_CSV_REGISTERS]; + UBYTE current_header = 0; + u8* csv_register[MAX_CSV_REGISTERS]; + UBYTE current_register = 0; + u8* csv_delimiter + = get_value(vars, vars_count, (u8*)"csv-delimiter", NULL); + + if (!(csv = fopen(filename, "rt"))) + exit(error(ENOENT, (u8*)"csv: No such file: %s", filename)); + + CALLOC(bufline, u8, BUFSIZE); + token_size = BUFSIZE; + CALLOC(token, u8, token_size); + for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) + CALLOC(csv_header[i], u8, BUFSIZE); + for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) + CALLOC(csv_register[i], u8, BUFSIZE); + + while (!feof(csv) && (!csv_iter || csv_lineno <= csv_iter)) + { + u8* eol = NULL; + if (!fgets((char*)bufline, BUFSIZE, csv)) + break; + eol = (u8*)strchr((char*)bufline, '\n'); + if (eol) + *eol = 0; + + pbufline = bufline; + RESET_TOKEN(token, ptoken, token_size); + current_register = 0; + csv_bufline_start: + if (!*pbufline) + goto csv_bufline_done; + switch (*pbufline) + { + case '"': + if (csv_state & ST_CS_QUOTE) + csv_state &= ~ST_CS_QUOTE; + else + csv_state |= ST_CS_QUOTE; + pbufline++; + break; + case ';': + case ',': + if (csv_state & ST_CS_QUOTE) + CHECKCOPY(token, ptoken, token_size, pbufline); + else + { + *ptoken = 0; + if (csv_lineno > 0 + && current_register < MAX_CSV_REGISTERS) + strncpy((char*)csv_register + [current_register++], + (char*)token, BUFSIZE - 1); + else if (csv_lineno <= 0 + && current_header < MAX_CSV_REGISTERS) + strncpy((char*)csv_header[current_header++], + (char*)token, BUFSIZE - 1); + RESET_TOKEN(token, ptoken, token_size); + pbufline++; + } + break; + default: + if (csv_state & ST_CS_QUOTE) + CHECKCOPY(token, ptoken, token_size, pbufline); + else if (csv_delimiter && *pbufline == *csv_delimiter) + { + *ptoken = 0; + if (csv_lineno > 0 + && current_register < MAX_CSV_REGISTERS) + strncpy((char*)csv_register + [current_register++], + (char*)token, BUFSIZE - 1); + else if (csv_lineno <= 0 + && current_header < MAX_CSV_REGISTERS) + strncpy((char*)csv_header[current_header++], + (char*)token, BUFSIZE - 1); + RESET_TOKEN(token, ptoken, token_size); + pbufline++; + } + else + CHECKCOPY(token, ptoken, token_size, pbufline); + } + goto csv_bufline_start; + csv_bufline_done: + *ptoken = 0; + if (csv_lineno > 0) + { + if (current_register < MAX_CSV_REGISTERS) + strncpy((char*)csv_register[current_register++], + (char*)token, BUFSIZE - 1); + } + else + { + if (current_header < MAX_CSV_REGISTERS) + strncpy((char*)csv_header[current_header++], + (char*)token, BUFSIZE - 1); + } + RESET_TOKEN(token, ptoken, token_size); + + if (csv_lineno > 0 && pbufline != bufline) + (*callback)(output, csv_header, csv_register); + + for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) + *csv_register[i] = 0; + csv_lineno++; + } + fclose(csv); + for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--) + free(csv_register[i - 1]); + for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--) + free(csv_header[i - 1]); + free(token); + free(bufline); + + return 0; } int -process_csv(u8* arg_token, FILE* output, BOOL read_yaml_macros_and_links, - BOOL end_tag) +process_csv(u8* arg_token, FILE* output, BOOL read_yaml_macros_and_links, + BOOL end_tag) { - if (end_tag) - { - state &= ~ST_CSV_BODY; - - if (read_yaml_macros_and_links) - return 0; - - read_csv(output, csv_filename, &print_csv_row); - - free(csv_filename); - csv_filename = NULL; - free(csv_template); - csv_template = NULL; - csv_template_size = 0; - } - else - { - if (state & ST_CSV_BODY) - exit(error(1, (u8*)"Can't nest csv directives")); - - state |= ST_CSV_BODY; - - if (read_yaml_macros_and_links) - return 0; - - csv_iter = 0; - u8* saveptr = NULL; - u8* args = (u8*)strtok_r((char*)arg_token, " ", (char**)&saveptr); - args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); - if (!args) - exit(error(EINVAL, (u8*)"csv: Arguments required")); - size_t args_len = strlen((char*)args); - if (*args != '"' || *(args + args_len - 1) != '"') - exit(error(EINVAL, (u8*)"csv: First argument must be a string")); - if (!csv_filename) - CALLOC(csv_filename, u8, BUFSIZE); - u8* args_base = (u8*)strdup((char*)args+1); - *(args_base + strlen((char*)args_base) - 1) = 0; - snprintf(csv_filename, BUFSIZE, "%s/%s.csv", input_dirname, (char*)args_base); - free(args_base); - args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); - if (args) - { - errno = 0; - csv_iter = strtol((char*)args, NULL, 10); - if (errno) - exit(error(errno, (u8*)"csv: Invalid argument '%s'", args)); - } - } - - return 0; + if (end_tag) + { + state &= ~ST_CSV_BODY; + + if (read_yaml_macros_and_links) + return 0; + + read_csv(output, csv_filename, &print_csv_row); + + free(csv_filename); + csv_filename = NULL; + free(csv_template); + csv_template = NULL; + csv_template_size = 0; + } + else + { + if (state & ST_CSV_BODY) + exit(error(1, (u8*)"Can't nest csv directives")); + + state |= ST_CSV_BODY; + + if (read_yaml_macros_and_links) + return 0; + + csv_iter = 0; + u8* saveptr = NULL; + u8* args = (u8*)strtok_r( + (char*)arg_token, " ", (char**)&saveptr); + args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); + if (!args) + exit(error(EINVAL, (u8*)"csv: Arguments required")); + size_t args_len = strlen((char*)args); + if (*args != '"' || *(args + args_len - 1) != '"') + exit(error(EINVAL, + (u8*)"csv: First argument must be a string")); + if (!csv_filename) + CALLOC(csv_filename, u8, BUFSIZE); + u8* args_base = (u8*)strdup((char*)args + 1); + *(args_base + strlen((char*)args_base) - 1) = 0; + snprintf(csv_filename, BUFSIZE, "%s/%s.csv", input_dirname, + (char*)args_base); + free(args_base); + args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); + if (args) + { + errno = 0; + csv_iter = strtol((char*)args, NULL, 10); + if (errno) + exit(error(errno, + (u8*)"csv: Invalid argument '%s'", + args)); + } + } + + return 0; } int process_include(u8* token, FILE* output, BOOL read_yaml_macros_and_links) { - if (read_yaml_macros_and_links) - return 0; - - if (!input_filename) - return warning(1, (u8*)"Cannot use 'include' in stdin"); - - u8* ptoken = (u8*)strchr((char*)token, ' '); - char* include_filename = NULL; - char* pinclude_filename = NULL; - - if (!ptoken) - exit(error(1, (u8*)"Directive 'include' requires" - " an argument")); - - fflush(output); - pid_t pid = fork(); - int pstatus = 0; - - if (pid > 0) - wait(&pstatus); - else if (pid == 0) - { - prctl(PR_SET_PDEATHSIG, SIGTERM); - - CALLOC(include_filename, char, BUFSIZE); - pinclude_filename = include_filename; - ptoken++; - while (ptoken && *ptoken) - if (*ptoken != '"') - *pinclude_filename++ = *ptoken++; - else - ptoken++; - - if (!strcmp(basedir, ".")) - set_basedir(input_dirname, &basedir, &basedir_size); - - CALLOC(input_filename, char, BUFSIZE); - snprintf(input_filename, BUFSIZE, "%s/%s.slw", basedir, include_filename); - free(include_filename); - - FILE* input = NULL; - FILE* output = stdout; - u8* buffer = NULL; - size_t buffer_size = 0; - int result = 0; - ULLONG saved_state = ST_NONE; - - read_file_into_buffer(&buffer, &buffer_size, input_filename, - &input_dirname, &input); - - free_keyvalue(&links, links_count); - free(links); - links = NULL; - links_count = 0; - - free_keyvalue(&footnotes, footnote_count); - free(footnotes); - footnotes = NULL; - footnote_count = 0; - current_footnote = 0; - - while (inline_footnote_count--) - free(inline_footnotes[inline_footnote_count]); - if (inline_footnotes) - free(inline_footnotes); - inline_footnotes = NULL; - inline_footnote_count = 0; - current_inline_footnote = 0; - - saved_state = state; - state = ST_NONE; - - /* First pass: read YAML, macros and links */ - result = slweb_parse(buffer, output, TRUE, TRUE); - - if (result) - { - slweb_cleanup(); - free(buffer); - return result; - } - state = ST_NONE; - current_footnote = 0; - current_inline_footnote = 0; - - /* Second pass: parse and output */ - result = slweb_parse(buffer, output, TRUE, FALSE); - - state = saved_state; - - slweb_cleanup(); - free(buffer); - exit(result); - } - else - exit(error(1, (u8*)"Fork failed")); - - return 0; + if (read_yaml_macros_and_links) + return 0; + + if (!input_filename) + return warning(1, (u8*)"Cannot use 'include' in stdin"); + + u8* ptoken = (u8*)strchr((char*)token, ' '); + char* include_filename = NULL; + char* pinclude_filename = NULL; + + if (!ptoken) + exit(error(1, + (u8*)"Directive 'include' requires" + " an argument")); + + fflush(output); + pid_t pid = fork(); + int pstatus = 0; + + if (pid > 0) + wait(&pstatus); + else if (pid == 0) + { + prctl(PR_SET_PDEATHSIG, SIGTERM); + + CALLOC(include_filename, char, BUFSIZE); + pinclude_filename = include_filename; + ptoken++; + while (ptoken && *ptoken) + if (*ptoken != '"') + *pinclude_filename++ = *ptoken++; + else + ptoken++; + + if (!strcmp(basedir, ".")) + set_basedir(input_dirname, &basedir, &basedir_size); + + CALLOC(input_filename, char, BUFSIZE); + snprintf(input_filename, BUFSIZE, "%s/%s.slw", basedir, + include_filename); + free(include_filename); + + FILE* input = NULL; + FILE* output = stdout; + u8* buffer = NULL; + size_t buffer_size = 0; + int result = 0; + ULLONG saved_state = ST_NONE; + + read_file_into_buffer(&buffer, &buffer_size, input_filename, + &input_dirname, &input); + + free_keyvalue(&links, links_count); + free(links); + links = NULL; + links_count = 0; + + free_keyvalue(&footnotes, footnote_count); + free(footnotes); + footnotes = NULL; + footnote_count = 0; + current_footnote = 0; + + while (inline_footnote_count--) + free(inline_footnotes[inline_footnote_count]); + if (inline_footnotes) + free(inline_footnotes); + inline_footnotes = NULL; + inline_footnote_count = 0; + current_inline_footnote = 0; + + saved_state = state; + state = ST_NONE; + + /* First pass: read YAML, macros and links */ + result = slweb_parse(buffer, output, TRUE, TRUE); + + if (result) + { + slweb_cleanup(); + free(buffer); + return result; + } + state = ST_NONE; + current_footnote = 0; + current_inline_footnote = 0; + + /* Second pass: parse and output */ + result = slweb_parse(buffer, output, TRUE, FALSE); + + state = saved_state; + + slweb_cleanup(); + free(buffer); + exit(result); + } + else + exit(error(1, (u8*)"Fork failed")); + + return 0; } int process_list_start(FILE* output) { - fprintf(output, "<ul>"); - return 0; + fprintf(output, "<ul>"); + return 0; } int process_list_item_start(FILE* output) { - fprintf(output, "%s<li><p>", output_firstcol ? "" : "\n"); - return 0; + fprintf(output, "%s<li><p>", output_firstcol ? "" : "\n"); + return 0; } int process_list_item_end(FILE* output) { - if (state & ST_PARA_OPEN) - { - state &= ~ST_PARA_OPEN; - fprintf(output, "</p>"); - output_firstcol = FALSE; - } - fprintf(output, "</li>"); - output_firstcol = FALSE; - return 0; + if (state & ST_PARA_OPEN) + { + state &= ~ST_PARA_OPEN; + fprintf(output, "</p>"); + output_firstcol = FALSE; + } + fprintf(output, "</li>"); + output_firstcol = FALSE; + return 0; } int process_list_end(FILE* output) { - fprintf(output, "</ul>\n"); - output_firstcol = TRUE; - return 0; + fprintf(output, "</ul>\n"); + output_firstcol = TRUE; + return 0; } int process_numlist_start(FILE* output) { - fprintf(output, "<ol>"); - output_firstcol = FALSE; - return 0; + fprintf(output, "<ol>"); + output_firstcol = FALSE; + return 0; } int process_numlist_end(FILE* output) { - fprintf(output, "</ol>\n"); - output_firstcol = TRUE; - return 0; + fprintf(output, "</ol>\n"); + output_firstcol = TRUE; + return 0; } int filter_subdirs(const struct dirent* node) { - if (!node || ((*node->d_name == '.') - && (!*(node->d_name+1) - || ((*(node->d_name+1) == '.') - && !(*(node->d_name+2)))))) - return 0; - - struct stat st; - char* nodename = NULL; - - CALLOC(nodename, char, BUFSIZE); - snprintf(nodename, BUFSIZE, "%s/%s", incdir, node->d_name); - - if (lstat(nodename, &st) < 0 || !S_ISDIR(st.st_mode)) - { - free(nodename); - return 0; - } - - free(nodename); - - return 1; + if (!node + || ((*node->d_name == '.') + && (!*(node->d_name + 1) + || ((*(node->d_name + 1) == '.') + && !(*(node->d_name + 2)))))) + return 0; + + struct stat st; + char* nodename = NULL; + + CALLOC(nodename, char, BUFSIZE); + snprintf(nodename, BUFSIZE, "%s/%s", incdir, node->d_name); + + if (lstat(nodename, &st) < 0 || !S_ISDIR(st.st_mode)) + { + free(nodename); + return 0; + } + + free(nodename); + + return 1; } int filter_slw(const struct dirent* node) { - if (!node || ((*node->d_name == '.') - && (!*(node->d_name+1) - || ((*(node->d_name+1) == '.') - && !(*(node->d_name+2)))))) - return 0; - - size_t node_len = strlen(node->d_name); - size_t slw_len = strlen(".slw"); - - if (slw_len >= node_len) - return 0; - return !strcmp(node->d_name + strlen(node->d_name) - slw_len, ".slw"); + if (!node + || ((*node->d_name == '.') + && (!*(node->d_name + 1) + || ((*(node->d_name + 1) == '.') + && !(*(node->d_name + 2)))))) + return 0; + + size_t node_len = strlen(node->d_name); + size_t slw_len = strlen(".slw"); + + if (slw_len >= node_len) + return 0; + return !strcmp(node->d_name + strlen(node->d_name) - slw_len, ".slw"); } -int +int reverse_alphacompare(const struct dirent** a, const struct dirent** b) { - if (!a || !b) - return 0; + if (!a || !b) + return 0; - return -1 * strcmp((*a)->d_name, (*b)->d_name); + return -1 * strcmp((*a)->d_name, (*b)->d_name); } int -process_timestamp(FILE* output, const u8* link_prefix, const char* link, - u8* permalink_macro, u8* date); +process_timestamp(FILE* output, const u8* link_prefix, const char* link, + u8* permalink_macro, u8* date); int process_incdir_subdir(const char* subdirname, const u8* link_prefix, - FILE* output, BOOL details_open, u8* macro_body) + FILE* output, BOOL details_open, u8* macro_body) { - print_output(output, "<li>\n<details%s>\n<summary>", - details_open ? " open" : ""); - if (macro_body) - fprintf(stdout, "%s", macro_body); - print_output(output, "%s</summary>\n<div>\n", subdirname); - - struct dirent** namelist; - struct dirent** pnamelist; - long names_total = 0; - long names_output; - char* abs_subdirname = NULL; - - CALLOC(abs_subdirname, char, BUFSIZE); - snprintf(abs_subdirname, BUFSIZE, "%s/%s/%s", basedir, incdir, subdirname); - - if ((names_total = scandir(abs_subdirname, &namelist, &filter_slw, - &reverse_alphacompare)) < 0) - { - perror("scandir"); - exit(error(errno, (u8*)"incdir_subdir: scandir '%s' error", - abs_subdirname)); - } - - pnamelist = namelist; - names_output = 0; - while (names_output < names_total && pnamelist && *pnamelist) - { - int pstatus = 0; - /* - *char* basename = strip_ext((*pnamelist)->d_name); - *char* link = NULL; - *CALLOC(link, char, BUFSIZE); - *snprintf(link, BUFSIZE, "%s/%s", subdirname, basename); - */ - - fflush(output); - pid_t pid = fork(); - - if (pid > 0) - wait(&pstatus); - else if (pid == 0) - { - set_basedir(abs_subdirname, &basedir, &basedir_size); - snprintf(input_filename, BUFSIZE, "%s/%s", abs_subdirname, - (*pnamelist)->d_name); - - FILE* input = NULL; - FILE* output = stdout; - u8* buffer = NULL; - size_t buffer_size = 0; - int result = 0; - ULLONG saved_state = ST_NONE; - - read_file_into_buffer(&buffer, &buffer_size, input_filename, - &input_dirname, &input); - - free_keyvalue(&links, links_count); - free(links); - links = NULL; - links_count = 0; - - free_keyvalue(&footnotes, footnote_count); - free(footnotes); - footnotes = NULL; - footnote_count = 0; - current_footnote = 0; - - while (inline_footnote_count--) - free(inline_footnotes[inline_footnote_count]); - if (inline_footnotes) - free(inline_footnotes); - inline_footnotes = NULL; - inline_footnote_count = 0; - current_inline_footnote = 0; - - saved_state = state; - state = ST_NONE; - - /* First pass: read YAML, macros and links */ - result = slweb_parse(buffer, output, TRUE, TRUE); - - if (result) - { - slweb_cleanup(); - free(buffer); - free(abs_subdirname); - while (names_total--) - free(namelist[names_total]); - free(namelist); - return result; - } - - state = ST_NONE; - current_footnote = 0; - current_inline_footnote = 0; - - /* Second pass: parse and output */ - result = slweb_parse(buffer, output, TRUE, FALSE); - - state = saved_state; - - fflush(output); - slweb_cleanup(); - free(buffer); - free(abs_subdirname); - while (names_total--) - free(namelist[names_total]); - free(namelist); - exit(result); - } - else - exit(error(1, (u8*)"Fork failed")); - - pnamelist++; - names_output++; - } - - while (names_total--) - free(namelist[names_total]); - free(namelist); - free(abs_subdirname); - - print_output(output, "</div>\n</details>\n</li>\n"); - output_firstcol = FALSE; - return 0; + print_output(output, "<li>\n<details%s>\n<summary>", + details_open ? " open" : ""); + if (macro_body) + fprintf(stdout, "%s", macro_body); + print_output(output, "%s</summary>\n<div>\n", subdirname); + + struct dirent** namelist; + struct dirent** pnamelist; + long names_total = 0; + long names_output; + char* abs_subdirname = NULL; + + CALLOC(abs_subdirname, char, BUFSIZE); + snprintf(abs_subdirname, BUFSIZE, "%s/%s/%s", basedir, incdir, + subdirname); + + if ((names_total = scandir(abs_subdirname, &namelist, &filter_slw, + &reverse_alphacompare)) + < 0) + { + perror("scandir"); + exit(error(errno, (u8*)"incdir_subdir: scandir '%s' error", + abs_subdirname)); + } + + pnamelist = namelist; + names_output = 0; + while (names_output < names_total && pnamelist && *pnamelist) + { + int pstatus = 0; + /* + *char* basename = strip_ext((*pnamelist)->d_name); + *char* link = NULL; + *CALLOC(link, char, BUFSIZE); + *snprintf(link, BUFSIZE, "%s/%s", subdirname, basename); + */ + + fflush(output); + pid_t pid = fork(); + + if (pid > 0) + wait(&pstatus); + else if (pid == 0) + { + set_basedir(abs_subdirname, &basedir, &basedir_size); + snprintf(input_filename, BUFSIZE, "%s/%s", + abs_subdirname, (*pnamelist)->d_name); + + FILE* input = NULL; + FILE* output = stdout; + u8* buffer = NULL; + size_t buffer_size = 0; + int result = 0; + ULLONG saved_state = ST_NONE; + + read_file_into_buffer(&buffer, &buffer_size, + input_filename, &input_dirname, &input); + + free_keyvalue(&links, links_count); + free(links); + links = NULL; + links_count = 0; + + free_keyvalue(&footnotes, footnote_count); + free(footnotes); + footnotes = NULL; + footnote_count = 0; + current_footnote = 0; + + while (inline_footnote_count--) + free(inline_footnotes[inline_footnote_count]); + if (inline_footnotes) + free(inline_footnotes); + inline_footnotes = NULL; + inline_footnote_count = 0; + current_inline_footnote = 0; + + saved_state = state; + state = ST_NONE; + + /* First pass: read YAML, macros and links */ + result = slweb_parse(buffer, output, TRUE, TRUE); + + if (result) + { + slweb_cleanup(); + free(buffer); + free(abs_subdirname); + while (names_total--) + free(namelist[names_total]); + free(namelist); + return result; + } + + state = ST_NONE; + current_footnote = 0; + current_inline_footnote = 0; + + /* Second pass: parse and output */ + result = slweb_parse(buffer, output, TRUE, FALSE); + + state = saved_state; + + fflush(output); + slweb_cleanup(); + free(buffer); + free(abs_subdirname); + while (names_total--) + free(namelist[names_total]); + free(namelist); + exit(result); + } + else + exit(error(1, (u8*)"Fork failed")); + + pnamelist++; + names_output++; + } + + while (names_total--) + free(namelist[names_total]); + free(namelist); + free(abs_subdirname); + + print_output(output, "</div>\n</details>\n</li>\n"); + output_firstcol = FALSE; + return 0; } int -process_incdir(u8* token, const u8* link_prefix, FILE* output, - BOOL read_yaml_macros_and_links) +process_incdir(u8* token, const u8* link_prefix, FILE* output, + BOOL read_yaml_macros_and_links) { - if (read_yaml_macros_and_links) - return 0; - - u8* saveptr = NULL; - /* skipping the first token (incdir) */ - u8* arg = (u8*)strtok_r((char*)token, " ", - (char**)&saveptr); - size_t arg_len = 0; - long num = 5; - u8* macro_body = NULL; - int names_total = 0; - struct dirent** namelist; - struct dirent** pnamelist; - long names_output; - BOOL details_open = TRUE; - - - arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); - if (!arg) - exit(error(1, (u8*)"incdir: Arguments required")); - - arg_len = strlen((char*)arg); - - if (*arg != '"' || *(arg + arg_len - 1) != '"') - exit(error(1, (u8*)"incdir: First argument not string")); - - incdir = strdup((char*)(arg+1)); - *(incdir + strlen(incdir) - 1) = 0; - - arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); - if (!arg) - exit(error(1, (u8*)"incdir: Second argument required")); - - if (*arg == '=') - macro_body = get_value(macros, macros_count, arg+1, NULL); - else - { - u8* parg = arg; - while (parg && *parg) - { - if (*parg < '0' || *parg > '9') - exit(error(1, (u8*)"incdir: Non-numeric argument")); - parg++; - } - errno = 0; - num = strtol((char*)arg, NULL, 10); - if (errno) - exit(error(errno, (u8*)"incdir: Invalid parameter 'num'")); - arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); - if (arg) - { - if (*arg != '=') - { - free(incdir); - exit(error(1, (u8*)"incdir: Third argument not macro")); - } - macro_body = get_value(macros, macros_count, arg+1, NULL); - } - } - - print_output(output, "<ul class=\"incdir\">\n"); - - if ((names_total = scandir(incdir, &namelist, &filter_subdirs, - &reverse_alphacompare)) < 0) - { - perror("scandir"); - exit(error(errno, (u8*)"incdir: scandir '%s' error", incdir)); - } - - pnamelist = namelist; - names_output = 0; - while (names_output < num && pnamelist && *pnamelist) - { - process_incdir_subdir((*pnamelist)->d_name, link_prefix, output, - details_open, macro_body); - details_open = FALSE; - pnamelist++; - names_output++; - } - while (names_total--) - free(namelist[names_total]); - free(namelist); - free(incdir); - - print_output(output, "</ul>\n"); - output_firstcol = TRUE; - - return 0; + if (read_yaml_macros_and_links) + return 0; + + u8* saveptr = NULL; + /* skipping the first token (incdir) */ + u8* arg = (u8*)strtok_r((char*)token, " ", (char**)&saveptr); + size_t arg_len = 0; + long num = 5; + u8* macro_body = NULL; + int names_total = 0; + struct dirent** namelist; + struct dirent** pnamelist; + long names_output; + BOOL details_open = TRUE; + + arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); + if (!arg) + exit(error(1, (u8*)"incdir: Arguments required")); + + arg_len = strlen((char*)arg); + + if (*arg != '"' || *(arg + arg_len - 1) != '"') + exit(error(1, (u8*)"incdir: First argument not string")); + + incdir = strdup((char*)(arg + 1)); + *(incdir + strlen(incdir) - 1) = 0; + + arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); + if (!arg) + exit(error(1, (u8*)"incdir: Second argument required")); + + if (*arg == '=') + macro_body = get_value(macros, macros_count, arg + 1, NULL); + else + { + u8* parg = arg; + while (parg && *parg) + { + if (*parg < '0' || *parg > '9') + exit(error(1, + (u8*)"incdir: Non-numeric argument")); + parg++; + } + errno = 0; + num = strtol((char*)arg, NULL, 10); + if (errno) + exit(error( + errno, (u8*)"incdir: Invalid parameter 'num'")); + arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); + if (arg) + { + if (*arg != '=') + { + free(incdir); + exit(error(1, + (u8*)"incdir: Third argument not " + "macro")); + } + macro_body = get_value( + macros, macros_count, arg + 1, NULL); + } + } + + print_output(output, "<ul class=\"incdir\">\n"); + + if ((names_total = scandir( + incdir, &namelist, &filter_subdirs, &reverse_alphacompare)) + < 0) + { + perror("scandir"); + exit(error(errno, (u8*)"incdir: scandir '%s' error", incdir)); + } + + pnamelist = namelist; + names_output = 0; + while (names_output < num && pnamelist && *pnamelist) + { + process_incdir_subdir((*pnamelist)->d_name, link_prefix, output, + details_open, macro_body); + details_open = FALSE; + pnamelist++; + names_output++; + } + while (names_total--) + free(namelist[names_total]); + free(namelist); + free(incdir); + + print_output(output, "</ul>\n"); + output_firstcol = TRUE; + + return 0; } BOOL url_is_local(const char* url); int -process_timestamp(FILE* output, const u8* link_prefix, const char* link, - u8* permalink_macro, u8* date) +process_timestamp(FILE* output, const u8* link_prefix, const char* link, + u8* permalink_macro, u8* date) { - u8* day = NULL; - u8* month = NULL; - u8* year = NULL; - u8* formatted_date = NULL; - u8* ptr = NULL; - const char* ptimestamp_format = NULL; - char* in_filename = NULL; - char* in_line = NULL; - - CALLOC(formatted_date, u8, DATEBUFSIZE); - ptr = NULL; - year = (u8*)strtok_r((char*)date, "-", (char**)&ptr); - if (year) - { - month = (u8*)strtok_r(NULL, "-", (char**)&ptr); - if (month) - { - day = (u8*)strtok_r(NULL, "T", (char**)&ptr); - if (day) - { - ptimestamp_format = timestamp_format; - while (*ptimestamp_format) - { - if (*ptimestamp_format == 'd' - || *ptimestamp_format == 'D') - strncat((char*)formatted_date, (char*)day, - DATEBUFSIZE-strlen((char*)formatted_date)-1); - else if (*ptimestamp_format == 'm' - || *ptimestamp_format == 'M') - strncat((char*)formatted_date, (char*)month, - DATEBUFSIZE-strlen((char*)formatted_date)-1); - else if (*ptimestamp_format == 'y' - || *ptimestamp_format == 'Y') - strncat((char*)formatted_date, (char*)year, - DATEBUFSIZE-strlen((char*)formatted_date)-1); - else - *(formatted_date + strlen((char*)formatted_date)) - = *ptimestamp_format; - - ptimestamp_format++; - } - print_output(output, "<a href=\"%s%s%s\"" - " class=\"timestamp\">%s%s</a>\n", - url_is_local(link) && global_link_prefix - ? (const char*)global_link_prefix : "", - url_is_local(link) && link_prefix - ? (const char*)link_prefix : "", - link, - permalink_macro ? (char*)permalink_macro : "", - formatted_date); - output_firstcol = TRUE; - } - } - } - - free(formatted_date); - free(in_line); - free(in_filename); - - return 0; + u8* day = NULL; + u8* month = NULL; + u8* year = NULL; + u8* formatted_date = NULL; + u8* ptr = NULL; + const char* ptimestamp_format = NULL; + char* in_filename = NULL; + char* in_line = NULL; + + CALLOC(formatted_date, u8, DATEBUFSIZE); + ptr = NULL; + year = (u8*)strtok_r((char*)date, "-", (char**)&ptr); + if (!year) + goto process_timestamp_cleanup; + month = (u8*)strtok_r(NULL, "-", (char**)&ptr); + if (!month) + goto process_timestamp_cleanup; + day = (u8*)strtok_r(NULL, "T", (char**)&ptr); + if (!day) + goto process_timestamp_cleanup; + ptimestamp_format = timestamp_format; + while (*ptimestamp_format) + { + if (*ptimestamp_format == 'd' || *ptimestamp_format == 'D') + strncat((char*)formatted_date, (char*)day, + DATEBUFSIZE - strlen((char*)formatted_date) - 1); + else if (*ptimestamp_format == 'm' || *ptimestamp_format == 'M') + strncat((char*)formatted_date, (char*)month, + DATEBUFSIZE - strlen((char*)formatted_date) - 1); + else if (*ptimestamp_format == 'y' || *ptimestamp_format == 'Y') + strncat((char*)formatted_date, (char*)year, + DATEBUFSIZE - strlen((char*)formatted_date) - 1); + else + *(formatted_date + strlen((char*)formatted_date)) + = *ptimestamp_format; + + ptimestamp_format++; + } + print_output(output, + "<a href=\"%s%s%s\"" + " class=\"timestamp\">%s%s</a>\n", + url_is_local(link) && global_link_prefix + ? (const char*)global_link_prefix + : "", + url_is_local(link) && link_prefix ? (const char*)link_prefix + : "", + link, permalink_macro ? (char*)permalink_macro : "", + formatted_date); + output_firstcol = TRUE; +process_timestamp_cleanup: + + free(formatted_date); + free(in_line); + free(in_filename); + + return 0; } int -process_macro(u8* token, FILE* output, BOOL read_yaml_macros_and_links, - BOOL end_tag) +process_macro( + u8* token, FILE* output, BOOL read_yaml_macros_and_links, BOOL end_tag) { - if (!end_tag) - { - if (state & ST_MACRO_BODY) - exit(error(1, (u8*)"Macro undefined or nested")); - - BOOL seen = FALSE; - u8* macro_body = get_value(macros, macros_count, token+1, - read_yaml_macros_and_links ? NULL : &seen); - - if (macro_body) - { - if (!read_yaml_macros_and_links) - { - if (seen) - { - u8* eol = (u8*)strrchr((char*)macro_body, '\n'); - size_t macro_body_len = strlen((char*)macro_body); - if (eol) - output_firstcol = macro_body + macro_body_len - - (u8*)strrchr((char*)macro_body, '\n') > 0; - else - output_firstcol = !*macro_body - || *(macro_body+macro_body_len-1) == '\n'; - print_output(output, "%s", macro_body); - } - else - state |= ST_MACRO_BODY; - } - } - else - { - if (read_yaml_macros_and_links) - { - macros_count++; - - if (!macros) - { - CALLOC(macros, KeyValue, macros_count); - pmacros = macros; - } - else - { - REALLOC(macros, KeyValue, macros_count * sizeof(KeyValue)); - pmacros = macros + macros_count - 1; - } - CALLOC(pmacros->key, u8, KEYSIZE); - pmacros->seen = FALSE; - strncpy((char*)pmacros->key, (char*)token+1, KEYSIZE-1); - pmacros->value = NULL; - pmacros->value_size = 0; - } - state |= ST_MACRO_BODY; - } - } - else - state &= ~ST_MACRO_BODY; - - return 0; + if (!end_tag) + { + if (state & ST_MACRO_BODY) + exit(error(1, (u8*)"Macro undefined or nested")); + + BOOL seen = FALSE; + u8* macro_body = get_value(macros, macros_count, token + 1, + read_yaml_macros_and_links ? NULL : &seen); + + if (macro_body && !read_yaml_macros_and_links) + { + if (seen) + { + u8* eol = (u8*)strrchr((char*)macro_body, '\n'); + size_t macro_body_len + = strlen((char*)macro_body); + if (eol) + output_firstcol = macro_body + + macro_body_len + - (u8*)strrchr( + (char*)macro_body, + '\n') + > 0; + else + output_firstcol = !*macro_body + || *(macro_body + macro_body_len + - 1) + == '\n'; + print_output(output, "%s", macro_body); + } + else + state |= ST_MACRO_BODY; + } + else if (!macro_body) + { + if (read_yaml_macros_and_links) + { + macros_count++; + + if (!macros) + { + CALLOC(macros, KeyValue, macros_count); + pmacros = macros; + } + else + { + REALLOC(macros, KeyValue, + macros_count * sizeof(KeyValue)); + pmacros = macros + macros_count - 1; + } + CALLOC(pmacros->key, u8, KEYSIZE); + pmacros->seen = FALSE; + strncpy((char*)pmacros->key, (char*)token + 1, + KEYSIZE - 1); + pmacros->value = NULL; + pmacros->value_size = 0; + } + state |= ST_MACRO_BODY; + } + } + else + state &= ~ST_MACRO_BODY; + + return 0; } int -process_tag(u8* token, const u8* link_prefix, FILE* output, - BOOL read_yaml_macros_and_links, BOOL* skip_eol, BOOL end_tag) +process_tag(u8* token, const u8* link_prefix, FILE* output, + BOOL read_yaml_macros_and_links, BOOL* skip_eol, BOOL end_tag) { - if (!token || strlen((char*)token) < 1) - return warning(1, (u8*)"%s:%ld:%ld: Empty tag name", - input_filename, lineno, colno); - - if (!strcmp((char*)token, "git-log") - && !read_yaml_macros_and_links) /* {git-log} */ - { - process_git_log(output); - } - else if (!strcmp((char*)token, "made-by") - && !read_yaml_macros_and_links) /* {made-by} */ - { - print_output(output, "<div id=\"made-by\">\n" - "Generated by <a href=\"%s\">" - "slweb</a>\n" - "© %s Strahinya Radich.\n" - "</div><!--made-by-->\n", - MADEBY_URL, - COPYRIGHTYEAR); - output_firstcol = TRUE; - } - else if (startswith((char*)token, "csv")) /* {csv} */ - { - process_csv(token, output, read_yaml_macros_and_links, end_tag); - } - else if (startswith((char*)token, "include")) /* {include} */ - { - process_include(token, output, read_yaml_macros_and_links); - *skip_eol = TRUE; - } - else if (startswith((char*)token, "incdir")) /* {incdir} */ - { - process_incdir(token, link_prefix, output, read_yaml_macros_and_links); - *skip_eol = TRUE; - } - else if (*token == '=') /* {=macro} */ - { - process_macro(token, output, read_yaml_macros_and_links, end_tag); - *skip_eol = TRUE; - } - else if (!read_yaml_macros_and_links) /* general tags */ - { - print_output(output, "<"); - if (end_tag) - print_output(output, "/"); - - if (*token == '.' || *token == '#') - { - print_output(output, "div"); - } - - while (*token && *token != '#' && *token != '.') - print_output(output, "%c", *token++); - - if (!end_tag) - { - if (*token == '#') - { - token++; - print_output(output, " id=\""); - while (*token && *token != '.') - print_output(output, "%c", *token++); - print_output(output, "\""); - if (*token == '.') - { - token++; - print_output(output, " class=\""); - while (*token) - print_output(output, "%c", *token++); - print_output(output, "\""); - } - } - else if (*token == '.') - { - token++; - print_output(output, " class=\""); - while (*token && *token != '#') - print_output(output, "%c", *token++); - print_output(output, "\""); - if (*token == '#') - { - token++; - print_output(output, " id=\""); - while (*token) - print_output(output, "%c", *token++); - print_output(output, "\""); - } - } - } - print_output(output, ">"); - output_firstcol = FALSE; - } - - return 0; + if (!token || strlen((char*)token) < 1) + return warning(1, (u8*)"%s:%ld:%ld: Empty tag name", + input_filename, lineno, colno); + + if (!strcmp((char*)token, "git-log") + && !read_yaml_macros_and_links) /* {git-log} */ + { + process_git_log(output); + } + else if (!strcmp((char*)token, "made-by") + && !read_yaml_macros_and_links) /* {made-by} */ + { + print_output(output, + "<div id=\"made-by\">\n" + "Generated by <a href=\"%s\">" + "slweb</a>\n" + "© %s Strahinya Radich.\n" + "</div><!--made-by-->\n", + MADEBY_URL, COPYRIGHTYEAR); + output_firstcol = TRUE; + } + else if (startswith((char*)token, "csv")) /* {csv} */ + { + process_csv(token, output, read_yaml_macros_and_links, end_tag); + } + else if (startswith((char*)token, "include")) /* {include} */ + { + process_include(token, output, read_yaml_macros_and_links); + *skip_eol = TRUE; + } + else if (startswith((char*)token, "incdir")) /* {incdir} */ + { + process_incdir( + token, link_prefix, output, read_yaml_macros_and_links); + *skip_eol = TRUE; + } + else if (*token == '=') /* {=macro} */ + { + process_macro( + token, output, read_yaml_macros_and_links, end_tag); + *skip_eol = TRUE; + } + else if (!read_yaml_macros_and_links) /* general tags */ + { + print_output(output, "<"); + if (end_tag) + print_output(output, "/"); + + if (*token == '.' || *token == '#') + { + print_output(output, "div"); + } + + while (*token && *token != '#' && *token != '.') + print_output(output, "%c", *token++); + + if (!end_tag) + { + if (*token == '#') + { + token++; + print_output(output, " id=\""); + while (*token && *token != '.') + print_output(output, "%c", *token++); + print_output(output, "\""); + if (*token == '.') + { + token++; + print_output(output, " class=\""); + while (*token) + print_output( + output, "%c", *token++); + print_output(output, "\""); + } + } + else if (*token == '.') + { + token++; + print_output(output, " class=\""); + while (*token && *token != '#') + print_output(output, "%c", *token++); + print_output(output, "\""); + if (*token == '#') + { + token++; + print_output(output, " id=\""); + while (*token) + print_output( + output, "%c", *token++); + print_output(output, "\""); + } + } + } + print_output(output, ">"); + output_firstcol = FALSE; + } + + return 0; } int process_bold(FILE* output, BOOL end_tag) { - print_output(output, "<%sstrong>", end_tag ? "/" : ""); - return 0; + print_output(output, "<%sstrong>", end_tag ? "/" : ""); + return 0; } int process_italic(FILE* output, BOOL end_tag) { - print_output(output, "<%sem>", end_tag ? "/" : ""); - return 0; + print_output(output, "<%sem>", end_tag ? "/" : ""); + return 0; } int process_code(FILE* output, BOOL end_tag) { - print_output(output, "<%scode>", end_tag ? "/" : ""); - return 0; + print_output(output, "<%scode>", end_tag ? "/" : ""); + return 0; } int process_blockquote(FILE* output, BOOL end_tag) { - print_output(output, "<%sblockquote>", end_tag ? "/" : ""); - return 0; + print_output(output, "<%sblockquote>", end_tag ? "/" : ""); + return 0; } int process_kbd(FILE* output, BOOL end_tag) { - print_output(output, "<%skbd>", end_tag ? "/" : ""); - return 0; + print_output(output, "<%skbd>", end_tag ? "/" : ""); + return 0; } int process_strike(FILE* output, BOOL end_tag) { - print_output(output, "<%ss>", end_tag ? "/" : ""); - return 0; + print_output(output, "<%ss>", end_tag ? "/" : ""); + return 0; } int process_table_start(FILE* output) { - print_output(output, "<table>\n" - "<thead>\n<tr><th>"); - return 0; + print_output(output, + "<table>\n" + "<thead>\n<tr><th>"); + return 0; } int process_table_header_cell(FILE* output) { - print_output(output, "</th><th>"); - return 0; + print_output(output, "</th><th>"); + return 0; } -int +int process_table_header_end(FILE* output) { - print_output(output, "</th></tr>\n</thead>\n"); - return 0; + print_output(output, "</th></tr>\n</thead>\n"); + return 0; } int process_table_body_start(FILE* output, BOOL start_row) { - print_output(output, "<tbody>\n"); - if (start_row) - print_output(output, "<tr><td>"); - return 0; + print_output(output, "<tbody>\n"); + if (start_row) + print_output(output, "<tr><td>"); + return 0; } int process_table_body_row_start(FILE* output) { - print_output(output, "<tr><td>"); - return 0; + print_output(output, "<tr><td>"); + return 0; } int process_table_body_cell(FILE* output) { - print_output(output, "</td><td>"); - return 0; + print_output(output, "</td><td>"); + return 0; } int process_table_body_row_end(FILE* output) { - print_output(output, "</td></tr>\n"); - return 0; + print_output(output, "</td></tr>\n"); + return 0; } int process_table_end(FILE* output) { - print_output(output, "</tbody>\n</table>\n"); - return 0; + print_output(output, "</tbody>\n</table>\n"); + return 0; } BOOL url_is_local(const char* url) { - return !(startswith(url, "http://") - || startswith(url, "https://") - || startswith(url, "gemini://") - || startswith(url, "ftp://") - || startswith(url, "ftps://") - || startswith(url, "mailto://")); + return !(startswith(url, "http://") || startswith(url, "https://") + || startswith(url, "gemini://") || startswith(url, "ftp://") + || startswith(url, "ftps://") || startswith(url, "mailto://")); } int get_realpath(char** realpath, char* relativeto, char* path) { - u8* command = NULL; - FILE* cmd_output = NULL; - - CALLOC(command, u8, BUFSIZE); - strncpy(*realpath, ".", BUFSIZE-1); - snprintf((char*)command, BUFSIZE-1, "realpath --relative-to=%s %s", - relativeto, path); - - cmd_output = popen((char*)command, "r"); - if (cmd_output) - { - u8* cmd_output_line = NULL; - CALLOC(cmd_output_line, u8, BUFSIZE); - while (!feof(cmd_output)) - { - if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output)) - continue; - - char* eol = strchr((char*)cmd_output_line, '\n'); - if (eol) - *eol = 0; - - strncpy(*realpath, (char*)cmd_output_line, BUFSIZE-1); - } - pclose(cmd_output); - free(cmd_output_line); - } - else - warning(1, (u8*)"get_realpath: Cannot popen"); - - free(command); - - return 0; + u8* command = NULL; + FILE* cmd_output = NULL; + + CALLOC(command, u8, BUFSIZE); + strncpy(*realpath, ".", BUFSIZE - 1); + snprintf((char*)command, BUFSIZE - 1, "realpath --relative-to=%s %s", + relativeto, path); + + cmd_output = popen((char*)command, "r"); + if (cmd_output) + { + u8* cmd_output_line = NULL; + CALLOC(cmd_output_line, u8, BUFSIZE); + while (!feof(cmd_output)) + { + if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output)) + continue; + + char* eol = strchr((char*)cmd_output_line, '\n'); + if (eol) + *eol = 0; + + strncpy(*realpath, (char*)cmd_output_line, BUFSIZE - 1); + } + pclose(cmd_output); + free(cmd_output_line); + } + else + warning(1, (u8*)"get_realpath: Cannot popen"); + + free(command); + + return 0; } int -process_inline_link(u8* link_text, const u8* link_prefix, - u8* link_macro_body, u8* link_url, FILE* output) +process_inline_link(u8* link_text, const u8* link_prefix, u8* link_macro_body, + u8* link_url, FILE* output) { - print_output(output, "<a href=\"%s%s%s\">%s%s</a>", - global_link_prefix ? (const char*)global_link_prefix : "", - link_prefix ? (const char*)link_prefix : "", - link_url ? (char*)link_url : "", - link_macro_body ? (char*)link_macro_body : "", - link_text); - output_firstcol = FALSE; - - return 0; + print_output(output, "<a href=\"%s%s%s\">%s%s</a>", + global_link_prefix ? (const char*)global_link_prefix : "", + link_prefix ? (const char*)link_prefix : "", + link_url ? (char*)link_url : "", + link_macro_body ? (char*)link_macro_body : "", link_text); + output_firstcol = FALSE; + + return 0; } int -process_link(u8* link_text, const u8* link_prefix, - u8* link_macro_body, u8* link_id, FILE* output) +process_link(u8* link_text, const u8* link_prefix, u8* link_macro_body, + u8* link_id, FILE* output) { - u8* url = get_value(links, links_count, link_id, NULL); - return process_inline_link(link_text, link_prefix, link_macro_body, url, - output); + u8* url = get_value(links, links_count, link_id, NULL); + return process_inline_link( + link_text, link_prefix, link_macro_body, url, output); } int -process_inline_image(u8* image_text, const u8* link_prefix, - u8* image_url, FILE* output, BOOL add_link, BOOL add_figcaption) +process_inline_image(u8* image_text, const u8* link_prefix, u8* image_url, + FILE* output, BOOL add_link, BOOL add_figcaption) { - if (add_figcaption) - print_output(output, "<figure>\n"); - - if (add_link) - print_output(output, "<a href=\"%s%s%s\" title=\"%s\" class=\"image\"" - " target=\"_blank\">", - global_link_prefix ? (const char*)global_link_prefix : "", - link_prefix ? (const char*)link_prefix : "", - image_url ? (char*)image_url : "", - image_text); - - print_output(output, "<img src=\"%s\" alt=\"%s\" />", - image_url ? (char*)image_url : "", - image_text); - output_firstcol = FALSE; - - if (add_link) - print_output(output, "</a>"); - - if (add_figcaption) - { - print_output(output, "<figcaption>%s</figcaption>\n</figure>\n", - image_text); - output_firstcol = TRUE; - } - return 0; + if (add_figcaption) + print_output(output, "<figure>\n"); + + if (add_link) + print_output(output, + "<a href=\"%s%s%s\" title=\"%s\" class=\"image\"" + " target=\"_blank\">", + global_link_prefix ? (const char*)global_link_prefix + : "", + link_prefix ? (const char*)link_prefix : "", + image_url ? (char*)image_url : "", image_text); + + print_output(output, "<img src=\"%s\" alt=\"%s\" />", + image_url ? (char*)image_url : "", image_text); + output_firstcol = FALSE; + + if (add_link) + print_output(output, "</a>"); + + if (add_figcaption) + { + print_output(output, "<figcaption>%s</figcaption>\n</figure>\n", + image_text); + output_firstcol = TRUE; + } + return 0; } int -process_image(u8* image_text, const u8* link_prefix, - u8* image_id, FILE* output, BOOL add_link, BOOL add_figcaption) +process_image(u8* image_text, const u8* link_prefix, u8* image_id, FILE* output, + BOOL add_link, BOOL add_figcaption) { - u8* url = get_value(links, links_count, image_id, NULL); - return process_inline_image(image_text, link_prefix, url, output, add_link, - add_figcaption); + u8* url = get_value(links, links_count, image_id, NULL); + return process_inline_image( + image_text, link_prefix, url, output, add_link, add_figcaption); } int -process_line_start(u8* line, const u8* link_prefix, - BOOL first_line_in_doc, BOOL previous_line_blank, - BOOL read_yaml_macros_and_links, BOOL list_para, FILE* output, - u8** token, u8** ptoken) +process_line_start(u8* line, const u8* link_prefix, BOOL first_line_in_doc, + BOOL previous_line_blank, BOOL read_yaml_macros_and_links, + BOOL list_para, FILE* output, u8** token, u8** ptoken) { - if ((first_line_in_doc || previous_line_blank) - && !(ANY(state, ST_BLOCKQUOTE | ST_PRE))) - { - if (!list_para) - { - if (state & ST_LIST) - { - state &= ~ST_LIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_list_end(output); - } - } - - if (state & ST_NUMLIST) - { - state &= ~ST_NUMLIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_numlist_end(output); - } - } - - if (state & ST_FOOTNOTE_TEXT) - { - if (!read_yaml_macros_and_links && (state & ST_PARA_OPEN)) - { - print_output(output, "</p>\n"); - output_firstcol = TRUE; - } - state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN); - } - } - if (!ANY(state, ST_TABLE | ST_TABLE_HEADER | ST_TABLE_LINE)) - { - if (!read_yaml_macros_and_links) - { - print_output(output, "<p>"); - output_firstcol = FALSE; - } - state |= ST_PARA_OPEN; - } - } - return 0; + if ((first_line_in_doc || previous_line_blank) + && !(ANY(state, ST_BLOCKQUOTE | ST_PRE))) + { + if (!list_para) + { + if (state & ST_LIST) + { + state &= ~ST_LIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_list_end(output); + } + } + + if (state & ST_NUMLIST) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_numlist_end(output); + } + } + + if (state & ST_FOOTNOTE_TEXT) + { + if (!read_yaml_macros_and_links + && (state & ST_PARA_OPEN)) + { + print_output(output, "</p>\n"); + output_firstcol = TRUE; + } + state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN); + } + } + if (!ANY(state, ST_TABLE | ST_TABLE_HEADER | ST_TABLE_LINE)) + { + if (!read_yaml_macros_and_links) + { + print_output(output, "<p>"); + output_firstcol = FALSE; + } + state |= ST_PARA_OPEN; + } + } + return 0; } int -process_text_token(u8* line, const u8* link_prefix, - BOOL first_line_in_doc, BOOL previous_line_blank, BOOL - processed_start_of_line, BOOL read_yaml_macros_and_links, - BOOL list_para, FILE* output, u8** token, u8** ptoken, - size_t* token_size, BOOL add_enclosing_paragraph) +process_text_token(u8* line, const u8* link_prefix, BOOL first_line_in_doc, + BOOL previous_line_blank, BOOL processed_start_of_line, + BOOL read_yaml_macros_and_links, BOOL list_para, FILE* output, + u8** token, u8** ptoken, size_t* token_size, + BOOL add_enclosing_paragraph) { - if (!(state & ST_YAML)) - { - if (add_enclosing_paragraph && !processed_start_of_line) - process_line_start(line, link_prefix, first_line_in_doc, - previous_line_blank, read_yaml_macros_and_links, list_para, - output, token, ptoken); - **ptoken = 0; - if (**token && !read_yaml_macros_and_links - && !(state & ST_MACRO_BODY)) - { - print_output(output, "%s", *token); - u8* eol = (u8*)strrchr((char*)*token, '\n'); - if (eol) - output_firstcol = *token + strlen((char*)*token) - eol > 0; - else - output_firstcol = FALSE; - } - } - RESET_TOKEN(*token, *ptoken, *token_size); - return 0; + if (!(state & ST_YAML)) + { + if (add_enclosing_paragraph && !processed_start_of_line) + process_line_start(line, link_prefix, first_line_in_doc, + previous_line_blank, read_yaml_macros_and_links, + list_para, output, token, ptoken); + **ptoken = 0; + if (**token && !read_yaml_macros_and_links + && !(state & ST_MACRO_BODY)) + { + print_output(output, "%s", *token); + u8* eol = (u8*)strrchr((char*)*token, '\n'); + if (eol) + output_firstcol + = *token + strlen((char*)*token) - eol + > 0; + else + output_firstcol = FALSE; + } + } + RESET_TOKEN(*token, *ptoken, *token_size); + return 0; } int -process_inline_footnote(u8* token, BOOL read_yaml_macros_and_links, - FILE* output) +process_inline_footnote(u8* token, BOOL read_yaml_macros_and_links, FILE* output) { - current_inline_footnote++; - - if (read_yaml_macros_and_links) - { - size_t token_len = strlen((char*)token); - - inline_footnote_count++; - if (inline_footnote_count == 1 && footnote_count > 0) - warning(1, (u8*)"Both inline and regular footnotes present"); - else if (!inline_footnotes) - { - inline_footnote_count = 1; - CALLOC(inline_footnotes, u8*, inline_footnote_count); - } - else - REALLOC(inline_footnotes, u8*, sizeof(u8*) * inline_footnote_count); - CALLOC(inline_footnotes[inline_footnote_count-1], u8, token_len+1); - - strncpy((char*)inline_footnotes[inline_footnote_count-1], (char*)token, - token_len); - *(inline_footnotes[inline_footnote_count-1] + token_len) = 0; - } - else - { - print_output(output, "<a href=\"#inline-footnote-%d\" " - "id=\"inline-footnote-text-%d\"><sup>%d</sup></a>", - current_inline_footnote, current_inline_footnote, - current_inline_footnote); - output_firstcol = FALSE; - } - - return 0; + current_inline_footnote++; + + if (read_yaml_macros_and_links) + { + size_t token_len = strlen((char*)token); + + inline_footnote_count++; + if (inline_footnote_count == 1 && footnote_count > 0) + warning(1, + (u8*)"Both inline and regular footnotes " + "present"); + else if (!inline_footnotes) + { + inline_footnote_count = 1; + CALLOC(inline_footnotes, u8*, inline_footnote_count); + } + else + REALLOC(inline_footnotes, u8*, + sizeof(u8*) * inline_footnote_count); + CALLOC(inline_footnotes[inline_footnote_count - 1], u8, + token_len + 1); + + strncpy((char*)inline_footnotes[inline_footnote_count - 1], + (char*)token, token_len); + *(inline_footnotes[inline_footnote_count - 1] + token_len) = 0; + } + else + { + print_output(output, + "<a href=\"#inline-footnote-%d\" " + "id=\"inline-footnote-text-%d\"><sup>%d</sup></a>", + current_inline_footnote, current_inline_footnote, + current_inline_footnote); + output_firstcol = FALSE; + } + + return 0; } int -process_footnote(u8* token, BOOL footnote_definition, BOOL footnote_output, - FILE* output) +process_footnote( + u8* token, BOOL footnote_definition, BOOL footnote_output, FILE* output) { - current_footnote++; - - if (footnote_definition) - { - footnote_count++; - if (footnote_count == 1 && inline_footnote_count > 0) - warning(1, (u8*)"Both inline and regular footnotes present"); - else if (!footnotes) - { - CALLOC(footnotes, KeyValue, footnote_count); - pfootnotes = footnotes; - } - else - { - REALLOCARRAY(footnotes, KeyValue, footnote_count); - pfootnotes = footnotes + footnote_count - 1; - } - CALLOC(pfootnotes->key, u8, KEYSIZE); - strncpy((char*)pfootnotes->key, (char*)token, KEYSIZE-1); - pfootnotes->value = NULL; - pfootnotes->value_size = 0; - } - - if (footnote_output) - { - print_output(output, "<a href=\"#footnote-%d\" id=\"footnote-text-%d\">" - "<sup>%d</sup></a>", - current_footnote, current_footnote, current_footnote); - output_firstcol = FALSE; - } - - return 0; + current_footnote++; + + if (footnote_definition) + { + footnote_count++; + if (footnote_count == 1 && inline_footnote_count > 0) + warning(1, + (u8*)"Both inline and regular footnotes " + "present"); + else if (!footnotes) + { + CALLOC(footnotes, KeyValue, footnote_count); + pfootnotes = footnotes; + } + else + { + REALLOCARRAY(footnotes, KeyValue, footnote_count); + pfootnotes = footnotes + footnote_count - 1; + } + CALLOC(pfootnotes->key, u8, KEYSIZE); + strncpy((char*)pfootnotes->key, (char*)token, KEYSIZE - 1); + pfootnotes->value = NULL; + pfootnotes->value_size = 0; + } + + if (footnote_output) + { + print_output(output, + "<a href=\"#footnote-%d\" id=\"footnote-text-%d\">" + "<sup>%d</sup></a>", + current_footnote, current_footnote, current_footnote); + output_firstcol = FALSE; + } + + return 0; } int process_horizontal_rule(FILE* output) { - /* Temporarily break paragraph as hr is para-level */ - if (state & ST_PARA_OPEN) - print_output(output, "</p>\n"); - print_output(output, "<hr />\n"); - if (state & ST_PARA_OPEN) - print_output(output, "<p>\n"); - output_firstcol = TRUE; - return 0; + /* Temporarily break paragraph as hr is para-level */ + if (state & ST_PARA_OPEN) + print_output(output, "</p>\n"); + print_output(output, "<hr />\n"); + if (state & ST_PARA_OPEN) + print_output(output, "<p>\n"); + output_firstcol = TRUE; + return 0; } int process_formula(FILE* output, const u8* token, BOOL display_formula) { - int result = 0; - const u8* pipe_args[] = { token, NULL}; - - result = print_command(CMD_KATEX, - display_formula - ? (const u8**)CMD_KATEX_DISPLAY_ARGS - : (const u8**)CMD_KATEX_INLINE_ARGS, - (const u8**)pipe_args, output, TRUE); - - if (result) - { - print_output(output, "%s$%s$%s", - display_formula ? "$" : "", - token, - display_formula ? "$" : ""); - output_firstcol = FALSE; - } - - return result; + int result = 0; + const u8* pipe_args[] = {token, NULL}; + + result = print_command(CMD_KATEX, + display_formula ? (const u8**)CMD_KATEX_DISPLAY_ARGS + : (const u8**)CMD_KATEX_INLINE_ARGS, + (const u8**)pipe_args, output, TRUE); + + if (result) + { + print_output(output, "%s$%s$%s", display_formula ? "$" : "", + token, display_formula ? "$" : ""); + output_firstcol = FALSE; + } + + return result; } int begin_html_and_head(FILE* output) { - u8* lang = get_value(vars, vars_count, (u8*)"lang", NULL); - u8* site_name = get_value(vars, vars_count, (u8*)"site-name", NULL); - u8* site_desc = get_value(vars, vars_count, (u8*)"site-desc", NULL); - u8* canonical = get_value(vars, vars_count, (u8*)"canonical", NULL); - u8* favicon_url = get_value(vars, vars_count, (u8*)"favicon-url", NULL); - u8* meta = get_value(vars, vars_count, (u8*)"meta", NULL); - u8* feed = get_value(vars, vars_count, (u8*)"feed", NULL); - u8* feed_desc = get_value(vars, vars_count, (u8*)"feed-desc", NULL); - - print_output(output, "<!DOCTYPE html>\n" - "<html lang=\"%s\">\n" - "<head>\n" - "<title>%s</title>\n" - "<meta charset=\"utf-8\" />\n", - lang ? (char*)lang : "en", - site_name ? (char*)site_name : ""); - - char* favicon = NULL; - CALLOC(favicon, char, BUFSIZE); - snprintf(favicon, BUFSIZE-1, "%s/favicon.ico", basedir); - if (!access(favicon, R_OK)) - print_output(output, "<link rel=\"shortcut icon\" type=\"image/x-icon\"" - " href=\"%s\" />\n", - favicon_url ? (char*)favicon_url : "/favicon.ico"); - free(favicon); - - if (meta) - { - char* filename = NULL; - CALLOC(filename, char, BUFSIZE); - snprintf(filename, BUFSIZE, "%s/%s", input_dirname, (char*)meta); - read_csv(output, filename, &print_meta_var); - free(filename); - } - - if (canonical && *canonical) - print_output(output, "<link rel=\"canonical\" href=\"%s\" />\n", - (char*)canonical); - - if (feed && *feed && feed_desc && *feed_desc) - print_output(output, "<link rel=\"alternate\"" - " type=\"application/rss+xml\" href=\"%s\" title=\"%s\" />\n", - (char*)feed, (char*)feed_desc); - - if (site_desc && *site_desc) - print_output(output, "<meta name=\"description\" content=\"%s\" />\n", - (char*)site_desc); - - print_output(output, "<meta name=\"viewport\" content=\"width=device-width," - " initial-scale=1\" />\n<meta name=\"generator\" content=\"slweb\" />\n"); - output_firstcol = TRUE; - return 0; + u8* lang = get_value(vars, vars_count, (u8*)"lang", NULL); + u8* site_name = get_value(vars, vars_count, (u8*)"site-name", NULL); + u8* site_desc = get_value(vars, vars_count, (u8*)"site-desc", NULL); + u8* canonical = get_value(vars, vars_count, (u8*)"canonical", NULL); + u8* favicon_url = get_value(vars, vars_count, (u8*)"favicon-url", NULL); + u8* meta = get_value(vars, vars_count, (u8*)"meta", NULL); + u8* feed = get_value(vars, vars_count, (u8*)"feed", NULL); + u8* feed_desc = get_value(vars, vars_count, (u8*)"feed-desc", NULL); + + print_output(output, + "<!DOCTYPE html>\n" + "<html lang=\"%s\">\n" + "<head>\n" + "<title>%s</title>\n" + "<meta charset=\"utf-8\" />\n", + lang ? (char*)lang : "en", site_name ? (char*)site_name : ""); + + char* favicon = NULL; + CALLOC(favicon, char, BUFSIZE); + snprintf(favicon, BUFSIZE - 1, "%s/favicon.ico", basedir); + if (!access(favicon, R_OK)) + print_output(output, + "<link rel=\"shortcut icon\" type=\"image/x-icon\"" + " href=\"%s\" />\n", + favicon_url ? (char*)favicon_url : "/favicon.ico"); + free(favicon); + + if (meta) + { + char* filename = NULL; + CALLOC(filename, char, BUFSIZE); + snprintf(filename, BUFSIZE, "%s/%s", input_dirname, (char*)meta); + read_csv(output, filename, &print_meta_var); + free(filename); + } + + if (canonical && *canonical) + print_output(output, "<link rel=\"canonical\" href=\"%s\" />\n", + (char*)canonical); + + if (feed && *feed && feed_desc && *feed_desc) + print_output(output, + "<link rel=\"alternate\"" + " type=\"application/rss+xml\" href=\"%s\" " + "title=\"%s\" />\n", + (char*)feed, (char*)feed_desc); + + if (site_desc && *site_desc) + print_output(output, + "<meta name=\"description\" content=\"%s\" />\n", + (char*)site_desc); + + print_output(output, + "<meta name=\"viewport\" content=\"width=device-width," + " initial-scale=1\" />\n<meta name=\"generator\" " + "content=\"slweb\" />\n"); + output_firstcol = TRUE; + return 0; } int add_css(FILE* output) { - if (!vars) - return -1; - KeyValue* pvars = vars; - while (pvars < vars + vars_count) - { - if (!strcmp((char*)pvars->key, "stylesheet")) - { - print_output(output, "<link rel=\"stylesheet\" href=\"%s\" />\n", - pvars->value); - output_firstcol = TRUE; - } - pvars++; - } - return 0; + if (!vars) + return -1; + KeyValue* pvars = vars; + while (pvars < vars + vars_count) + { + if (!strcmp((char*)pvars->key, "stylesheet")) + { + print_output(output, + "<link rel=\"stylesheet\" href=\"%s\" />\n", + pvars->value); + output_firstcol = TRUE; + } + pvars++; + } + return 0; } int end_head_start_body(FILE* output) { - print_output(output, "</head>\n<body>\n"); - output_firstcol = TRUE; + print_output(output, "</head>\n<body>\n"); + output_firstcol = TRUE; - return 0; + return 0; } int -begin_article(FILE* output, const u8* link_prefix, - const BOOL add_article_header, const u8* author, - const u8* title, const u8* header_text, - const char* title_heading_level, u8* date, - const BOOL ext_in_permalink, const char* permalink_url) +begin_article(FILE* output, const u8* link_prefix, + const BOOL add_article_header, const u8* author, const u8* title, + const u8* header_text, const char* title_heading_level, u8* date, + const BOOL ext_in_permalink, const char* permalink_url) { - if (author || date || header_text || title) - print_output(output, "<header>\n"); - - if (title) - print_output(output, "<h%s>%s</h%s>\n", - title_heading_level ? title_heading_level : "2", - (char*)title, - title_heading_level ? title_heading_level : "2"); - - if (header_text) - print_output(output, "<p>%s</p>\n", (char*)header_text); - - if (author) - print_output(output, "<address>%s</address>\n", author); - - - if (date && input_filename) - { - char* link = strip_ext(input_filename); - u8* samedir_permalink = get_value(vars, vars_count, - (u8*)"samedir-permalink", NULL); - char* real_link = NULL; - u8* permalink_macro = get_value(macros, macros_count, - (u8*)"permalink", NULL); - CALLOC(real_link, char, BUFSIZE); - - if (ext_in_permalink) - strncat(link, timestamp_output_ext, BUFSIZE-strlen(link)-1); - - if (permalink_url) - process_timestamp(output, link_prefix, permalink_url, - permalink_macro, date); - else if (samedir_permalink && !strcmp((char*)samedir_permalink, "1")) - { - get_realpath(&real_link, input_dirname, link); - process_timestamp(output, link_prefix, real_link, permalink_macro, - date); - } - else - process_timestamp(output, link_prefix, link, permalink_macro, date); - - free(real_link); - free(link); - } - - if (author || date || header_text || title) - print_output(output, "</header>\n"); - - output_firstcol = author || date || header_text || title; - - return 0; + if (author || date || header_text || title) + print_output(output, "<header>\n"); + + if (title) + print_output(output, "<h%s>%s</h%s>\n", + title_heading_level ? title_heading_level : "2", + (char*)title, + title_heading_level ? title_heading_level : "2"); + + if (header_text) + print_output(output, "<p>%s</p>\n", (char*)header_text); + + if (author) + print_output(output, "<address>%s</address>\n", author); + + if (date && input_filename) + { + char* link = strip_ext(input_filename); + u8* samedir_permalink = get_value( + vars, vars_count, (u8*)"samedir-permalink", NULL); + char* real_link = NULL; + u8* permalink_macro = get_value( + macros, macros_count, (u8*)"permalink", NULL); + CALLOC(real_link, char, BUFSIZE); + + if (ext_in_permalink) + strncat(link, timestamp_output_ext, + BUFSIZE - strlen(link) - 1); + + if (permalink_url) + process_timestamp(output, link_prefix, permalink_url, + permalink_macro, date); + else if (samedir_permalink + && !strcmp((char*)samedir_permalink, "1")) + { + get_realpath(&real_link, input_dirname, link); + process_timestamp(output, link_prefix, real_link, + permalink_macro, date); + } + else + process_timestamp(output, link_prefix, link, + permalink_macro, date); + + free(real_link); + free(link); + } + + if (author || date || header_text || title) + print_output(output, "</header>\n"); + + output_firstcol = author || date || header_text || title; + + return 0; } int end_footnotes(FILE* output, BOOL add_footnote_div) { - size_t footnote = 0; - - if (state & ST_PARA_OPEN) - { - print_output(output, "</p>\n"); - output_firstcol = TRUE; - state &= ~ST_PARA_OPEN; - } - - if (add_footnote_div) - { - print_output(output, "<div class=\"footnotes\">\n"); - output_firstcol = TRUE; - } - - process_horizontal_rule(output); - - for (footnote = 0; footnote < inline_footnote_count; footnote++) - { - print_output(output, "<p id=\"inline-footnote-%d\">" - "<a href=\"#inline-footnote-text-%d\">%d.</a> %s</p>\n", - footnote+1, footnote+1, footnote+1, - (char*)inline_footnotes[footnote]); - output_firstcol = TRUE; - } - - KeyValue* pfootnote = footnotes; - footnote = 0; - while (pfootnote && footnote < footnote_count) - { - print_output(output, "<p id=\"footnote-%d\">" - "<a href=\"#footnote-text-%d\">%d.</a> %s</p>\n", - footnote+1, footnote+1, footnote+1, - (char*)pfootnote->value); - output_firstcol = TRUE; - pfootnote++; - footnote++; - } - - if (add_footnote_div) - { - print_output(output, "</div><!--footnotes-->\n"); - output_firstcol = TRUE; - } - - return 0; + size_t footnote = 0; + + if (state & ST_PARA_OPEN) + { + print_output(output, "</p>\n"); + output_firstcol = TRUE; + state &= ~ST_PARA_OPEN; + } + + if (add_footnote_div) + { + print_output(output, "<div class=\"footnotes\">\n"); + output_firstcol = TRUE; + } + + process_horizontal_rule(output); + + for (footnote = 0; footnote < inline_footnote_count; footnote++) + { + print_output(output, + "<p id=\"inline-footnote-%d\">" + "<a href=\"#inline-footnote-text-%d\">%d.</a> %s</p>\n", + footnote + 1, footnote + 1, footnote + 1, + (char*)inline_footnotes[footnote]); + output_firstcol = TRUE; + } + + KeyValue* pfootnote = footnotes; + footnote = 0; + while (pfootnote && footnote < footnote_count) + { + print_output(output, + "<p id=\"footnote-%d\">" + "<a href=\"#footnote-text-%d\">%d.</a> %s</p>\n", + footnote + 1, footnote + 1, footnote + 1, + (char*)pfootnote->value); + output_firstcol = TRUE; + pfootnote++; + footnote++; + } + + if (add_footnote_div) + { + print_output(output, "</div><!--footnotes-->\n"); + output_firstcol = TRUE; + } + + return 0; } int end_body_and_html(FILE* output) { - print_output(output, "</body>\n</html>\n"); - output_firstcol = TRUE; - return 0; + print_output(output, "</body>\n</html>\n"); + output_firstcol = TRUE; + return 0; } int slweb_parse(u8* buffer, FILE* output, BOOL body_only, - BOOL read_yaml_macros_and_links) + BOOL read_yaml_macros_and_links) { - u8* title = NULL; - u8* title_heading_level = NULL; - u8* header_text = NULL; - u8* author = NULL; - u8* date = NULL; - u8* permalink_url = NULL; - u8* ext_in_permalink = NULL; - u8* var_add_article_header = NULL; - u8* var_add_image_links = NULL; - u8* var_add_figcaption = NULL; - u8* var_add_footnote_div = NULL; - u8* link_prefix = NULL; - u8* pbuffer = NULL; - u8* line = NULL; - u8* pline = NULL; - size_t line_len = 0; - u8* token = NULL; - u8* ptoken = NULL; - size_t token_size = 0; - u8* link_text = NULL; - size_t link_size = 0; - u8* link_macro = NULL; - UBYTE heading_level = 0; - ULONG heading_count = 0; - BOOL end_tag = FALSE; - BOOL first_line_in_doc = TRUE; - BOOL skip_change_first_line_in_doc = FALSE; - BOOL skip_eol = FALSE; - BOOL keep_token = FALSE; - BOOL previous_line_blank = FALSE; - BOOL processed_start_of_line = FALSE; - BOOL add_image_links = TRUE; - BOOL add_figcaption = TRUE; - BOOL add_footnote_div = FALSE; - BOOL list_para = FALSE; - BOOL footnote_at_line_start = FALSE; - size_t pline_len = 0; - - if (!buffer) - exit(error(1, (u8*)"Empty buffer")); - - title = get_value(vars, vars_count, (u8*)"title", NULL); - title_heading_level = get_value(vars, vars_count, (u8*)"title-heading-level", NULL); - header_text = get_value(vars, vars_count, (u8*)"header-text", NULL); - author = get_value(vars, vars_count, (u8*)"author", NULL); - date = get_value(vars, vars_count, (u8*)"date", NULL); - permalink_url = get_value(vars, vars_count, (u8*)"permalink-url", NULL); - ext_in_permalink = get_value(vars, vars_count, (u8*)"ext-in-permalink", NULL); - var_add_article_header = get_value(vars, vars_count, (u8*)"add-article-header", NULL); - var_add_image_links = get_value(vars, vars_count, (u8*)"add-image-links", NULL); - add_image_links = !(var_add_image_links && *var_add_image_links == '0'); - var_add_figcaption = get_value(vars, vars_count, (u8*)"add-figcaption", NULL); - add_figcaption = !(var_add_figcaption && *var_add_figcaption == '0'); - var_add_footnote_div = get_value(vars, vars_count, (u8*)"add-footnote-div", NULL); - add_footnote_div = var_add_footnote_div && *var_add_footnote_div == '1'; - link_prefix = get_value(vars, vars_count, (u8*)"link-prefix", NULL); - - CALLOC(line, u8, BUFSIZE); - token_size = BUFSIZE; - CALLOC(token, u8, BUFSIZE); - CALLOC(link_macro, u8, BUFSIZE); - - pbuffer = buffer; - pvars = vars; - pmacros = macros; - plinks = links; - pfootnotes = footnotes; - lineno = 0; - - if (!read_yaml_macros_and_links && !body_only) - { - begin_html_and_head(output); - add_css(output); - end_head_start_body(output); - } - - if (!read_yaml_macros_and_links) - begin_article(output, link_prefix, - var_add_article_header && *var_add_article_header == '1', - author, title, header_text, (char*)title_heading_level, date, - ext_in_permalink && *ext_in_permalink != '0', - (char*)permalink_url); - - RESET_TOKEN(token, ptoken, token_size); - - do - { - u8* eol = (u8*)strchr((char*)pbuffer, '\n'); - if (!eol) - break; - - pline = line; - while (pbuffer != eol) - *pline++ = *pbuffer++; - pbuffer++; - *pline = 0; - pline = line; - line_len = strlen((char*)line); - - lineno++; - colno = 1; - output_firstcol = TRUE; - processed_start_of_line = FALSE; - skip_eol = FALSE; - /*list_item = FALSE;*/ - list_para = FALSE; - - while (pline && *pline) - { - switch (*pline) - { - case '-': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - else if (colno == 1 - && strlen((char*)pline) > 2 - && startswith((char*)pline, "---")) - { - skip_eol = TRUE; - - if (!(state & ST_YAML) && lineno > 1 - && !read_yaml_macros_and_links) - process_horizontal_rule(output); - else - { - if (lineno == 1) - state |= ST_YAML; - else - state &= ~ST_YAML; - - skip_change_first_line_in_doc = TRUE; - } - pline = NULL; - } - else if (colno == 1 - && strlen((char*)pline) > 1 - && *(pline+1) == ' ') - { - if (state & ST_NUMLIST) - { - state &= ~ST_NUMLIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_numlist_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_LIST)) - process_list_start(output); - else - process_list_item_end(output); - process_list_item_start(output); - state |= ST_PARA_OPEN; - } - - processed_start_of_line = TRUE; - skip_eol = FALSE; - - state |= ST_LIST; - - pline += 2; - colno += 2; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case ':': - if (state & ST_YAML - && !(ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HEADING | ST_IMAGE | ST_MACRO_BODY - | ST_PRE | ST_TAG | ST_YAML_VAL)) - && read_yaml_macros_and_links) - { - *ptoken = 0; - - vars_count++; - - if (!vars) - { - CALLOC(vars, KeyValue, vars_count); - pvars = vars; - } - else - { - REALLOCARRAY(vars, KeyValue, vars_count); - pvars = vars + vars_count - 1; - } - CALLOC(pvars->key, u8, KEYSIZE); - strncpy((char*)pvars->key, (char*)token, KEYSIZE-1); - pvars->value = NULL; - pvars->value_size = 0; - - state |= ST_YAML_VAL; - RESET_TOKEN(token, ptoken, token_size); - pline++; - colno++; - - while (pline && (*pline == ' ' || *pline == '\t')) - { - pline++; - colno++; - } - } - else { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '~': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HTML_TAG | ST_IMAGE | ST_LINK_SECOND_ARG - | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY - | ST_PRE | ST_TAG | ST_YAML)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (strlen((char*)pline) > 1 && *(pline+1) == '~') - { - /* Handle ~~ within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - u8* entity = (u8*)"&nbsp;"; - size_t entity_len = strlen((char*)entity); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + entity_len < BUFSIZE) - { - strncat((char*)token, (char*)entity, - BUFSIZE-token_len-1); - ptoken += entity_len; - } - } - else - { - /* Output existing text up to ~~ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_CODE | ST_HEADING))) - print_output(output, "&nbsp;"); - } - - pline += 2; - colno += 2; - } - else - { - /* Handle ~ within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - u8* tag = state & ST_STRIKE - ? (u8*)"</s>" - : (u8*)"<s>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; - } - } - else - { - /* Output existing text up to ~ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_CODE | ST_HEADING))) - process_strike(output, state & ST_STRIKE); - } - - state ^= ST_STRIKE; - pline++; - colno++; - } - break; - - - case '`': - if (ANY(state, ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE - | ST_MACRO_BODY)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (colno == 1 - && strlen((char*)pline) > 2 - && startswith((char*)pline, "```")) - { - state ^= ST_PRE; - - if (!read_yaml_macros_and_links) - { - if (state & ST_PRE) - print_output(output, "<pre>"); - else - print_output(output, "</pre>"); - output_firstcol = FALSE; - } - - /* Skip the rest of the line (language) */ - pline = NULL; - } - else if (!ANY(state, ST_PRE | ST_TAG | ST_YAML)) - { - /* Handle ` within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - u8* tag = state & ST_CODE - ? (u8*)"</code>" - : (u8*)"<code>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; - } - } - else - { - /* Output existing text up to ` */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_HEADING))) - process_code(output, state & ST_CODE); - } - - state ^= ST_CODE; - - pline++; - colno++; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '#': - if (colno == 1 - && !(ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_MACRO_BODY | ST_PRE | ST_YAML))) - { - if (state & ST_LIST) - { - state &= ~ST_LIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_list_end(output); - } - } - - if (state & ST_NUMLIST) - { - state &= ~ST_NUMLIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_numlist_end(output); - } - } - - state |= ST_HEADING; - heading_level = 1; - pline++; - colno++; - } - else if (state & ST_HEADING && *(pline-1) == '#') - { - if (heading_level < MAX_HEADING_LEVEL) - heading_level++; - pline++; - colno++; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '_': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HTML_TAG | ST_IMAGE | ST_LINK_SECOND_ARG - | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY | ST_PRE - | ST_TAG | ST_YAML)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (strlen((char*)pline) > 1 && *(pline+1) == '_') - { - /* Handle __ within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - u8* tag = state & ST_BOLD - ? (u8*)"</strong>" - : (u8*)"<strong>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; - } - } - else - { - /* Output existing text up to __ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_CODE | ST_HEADING))) - process_bold(output, state & ST_BOLD); - } - - state ^= ST_BOLD; - pline += 2; - colno += 2; - } - else - { - /* Handle _ within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - u8* tag = state & ST_ITALIC - ? (u8*)"</em>" - : (u8*)"<em>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; - } - } - else - { - /* Output existing text up to _ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_CODE | ST_HEADING))) - process_italic(output, state & ST_ITALIC); - } - - state ^= ST_ITALIC; - pline++; - colno++; - } - break; - - case 'o': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HTML_TAG | ST_IMAGE | ST_MACRO_BODY | ST_PRE - | ST_YAML)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (colno == 1 - && strlen((char*)pline) > 1 - && *(pline+1) == ' ') - { - if (state & ST_NUMLIST) - { - state &= ~ST_NUMLIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_numlist_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_LIST)) - process_list_start(output); - else - process_list_item_end(output); - process_list_item_start(output); - state |= ST_PARA_OPEN; - } - - processed_start_of_line = TRUE; - skip_eol = FALSE; - - state |= ST_LIST; - - pline += 2; - colno += 2; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '*': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HTML_TAG | ST_IMAGE | ST_MACRO_BODY | ST_PRE - | ST_YAML)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - pline_len = strlen((char*)pline); - if (colno == 1 - && pline_len > 1 && *(pline+1) == '[') - { - process_line_start(line, link_prefix, first_line_in_doc, - previous_line_blank, read_yaml_macros_and_links, - list_para, output, &token, &ptoken); - - /* Ignore abbreviations (for now) */ - pline = NULL; - } - else if (colno == 1 - && pline_len > 2 && startswith((char*)pline, "***")) - { - skip_eol = TRUE; - if (!read_yaml_macros_and_links) - process_horizontal_rule(output); - pline = NULL; - } - else if (colno == 1 - && strlen((char*)pline) > 1 - && *(pline+1) == ' ') - { - if (state & ST_NUMLIST) - { - state &= ~ST_NUMLIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_numlist_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_LIST)) - process_list_start(output); - else - process_list_item_end(output); - process_list_item_start(output); - state |= ST_PARA_OPEN; - } - - processed_start_of_line = TRUE; - skip_eol = FALSE; - - state |= ST_LIST; - - pline += 2; - colno += 2; - } - else if (pline_len > 1 && *(pline+1) == '*') - { - /* Handle ** within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - u8* tag = state & ST_BOLD - ? (u8*)"</strong>" - : (u8*)"<strong>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; - } - } - else - { - /* Output existing text up to * */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_CODE | ST_HEADING))) - process_bold(output, state & ST_BOLD); - } - - state ^= ST_BOLD; - pline += 2; - colno += 2; - } - else - { - /* Handle * within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - - { - u8* tag = state & ST_ITALIC - ? (u8*)"</em>" - : (u8*)"<em>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; - } - } - else - { - /* Output existing text up to * */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_CODE | ST_HEADING))) - process_italic(output, state & ST_ITALIC); - } - - state ^= ST_ITALIC; - pline++; - colno++; - } - break; - - case ' ': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_IMAGE | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if ((state & ST_HEADING) && !(state & ST_HEADING_TEXT)) - { - if (!read_yaml_macros_and_links) - { - heading_count++; - process_heading_start(output, heading_level, - heading_count); - output_firstcol = FALSE; - } - state |= ST_HEADING_TEXT; - pline++; - colno++; - break; - } - - if (colno == 1 - && strlen((char*)pline) > 1 - && startswith((char*)pline, " ")) - { - list_para = TRUE; - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - while (*pline == ' ' || *pline == '\t') - { - pline++; - colno++; - } - break; - } - - if (strlen((char*)pline) == 2 - && *(pline+1) == ' ') - { - *ptoken = 0; - strncat((char*)ptoken, "<br />", - BUFSIZE-strlen((char*)token)-1); - ptoken += strlen("<br />"); - output_firstcol = FALSE; - pline++; - colno++; - } - else if (state & ST_LINK_MACRO) - { - *ptoken = 0; - strncpy((char*)link_macro, (char*)token, BUFSIZE-1); - *(link_macro + strlen((char*)token)) = 0; - RESET_TOKEN(token, ptoken, token_size); - state &= ~ST_LINK_MACRO; - } - else if (!(state & ST_HEADING - && *(pline-1) == '#')) - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - - case '{': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HTML_TAG | ST_PRE | ST_YAML | ST_YAML_VAL)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (state & ST_MACRO_BODY) - { - *ptoken = 0; - size_t token_len = strlen((char*)token); - - skip_eol = TRUE; - if (read_yaml_macros_and_links) - { - if (!macros) - { - macros_count = 1; - CALLOC(macros, KeyValue, macros_count); - pmacros = macros; - } - if (pmacros->value) - { - size_t value_len = strlen((char*)pmacros->value); - - if (pmacros->value_size < value_len + token_len + 1) - { - pmacros->value_size += token_len; - REALLOC(pmacros->value, u8, pmacros->value_size); - } - strncat((char*)pmacros->value, (char*)token, - pmacros->value_size-1); - } - else - { - pmacros->value_size = token_len+1; - CALLOC(pmacros->value, u8, pmacros->value_size); - strncpy((char*)pmacros->value, (char*)token, - pmacros->value_size-1); - } - } - } - else - { - /* Output existing text up to { */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, FALSE); - processed_start_of_line = TRUE; - } - - state |= ST_TAG; - RESET_TOKEN(token, ptoken, token_size); - - pline++; - colno++; - break; - - case '/': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_IMAGE | ST_PRE | ST_HEADING | ST_YAML_VAL)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if ((state & ST_TAG) && pline-1 && *(pline-1) == '{') - { - end_tag = TRUE; - pline++; - } - else - CHECKCOPY(token, ptoken, token_size, pline); - - colno++; - break; - - case '}': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_IMAGE | ST_PRE | ST_YAML | ST_YAML_VAL)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (state & ST_TAG) - { - state &= ~ST_TAG; - *ptoken = 0; - - process_tag(token, link_prefix, output, - read_yaml_macros_and_links, &skip_eol, end_tag); - - RESET_TOKEN(token, ptoken, token_size); - end_tag = FALSE; - } - - pline++; - colno++; - break; - - case '|': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HTML_TAG | ST_IMAGE | ST_PRE | ST_TAG - | ST_YAML)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (strlen((char*)pline) > 1 && *(pline+1) == '|') - { - /* Handle || within footnotes, headings and link text specially */ - if (ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - u8* tag = state & ST_KBD - ? (u8*)"</kbd>" - : (u8*)"<kbd>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; - - *ptoken = 0; - token_len = strlen((char*)token); - - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; - } - } - else - { - /* Output existing text up to || */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - if (!read_yaml_macros_and_links - && !(ANY(state, ST_PRE | ST_CODE | ST_HEADING))) - { - state ^= ST_KBD; - process_kbd(output, !(state & ST_KBD)); - output_firstcol = FALSE; - } - } - - pline += 2; - colno += 2; - } - /* Partial tables (for templating) */ - else if (!(state & ST_PRE) && colno == 1 - && strlen((char*)pline) > 1 - && *(pline+1) == '@') - { - skip_eol = TRUE; - - pline += 2; - colno += 2; - - switch (*pline) - { - case '\\': - if (!read_yaml_macros_and_links) - { - process_table_start(output); - output_firstcol = FALSE; - } - pline = NULL; - break; - - case '#': - state |= ST_TABLE_HEADER; - pline++; - colno++; - break; - - case '-': - state &= ~ST_TABLE_HEADER; - if (!read_yaml_macros_and_links) - { - process_table_body_start(output, FALSE); - output_firstcol = TRUE; - } - pline = NULL; - break; - - case ' ': - state |= ST_TABLE; - if (!read_yaml_macros_and_links) - { - process_table_body_row_start(output); - output_firstcol = FALSE; - } - break; - - case '/': - state &= ~ST_TABLE; - process_table_end(output); - output_firstcol = TRUE; - pline = NULL; - break; - - default: - warning(1, (u8*)"Invalid partial table line"); - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - } - else if (!(state & ST_PRE) && colno == 1) - { - skip_eol = TRUE; - - if (state & ST_TABLE_HEADER) - { - state &= ~ST_TABLE_HEADER; - state |= ST_TABLE_LINE; - pline = NULL; - break; - } - - if (state & ST_TABLE_LINE) - { - state &= ~ST_TABLE_LINE; - state |= ST_TABLE; - if (!read_yaml_macros_and_links) - { - process_table_body_start(output, TRUE); - output_firstcol = FALSE; - } - } - else if (state & ST_TABLE) - { - if (!read_yaml_macros_and_links) - { - process_table_body_row_start(output); - output_firstcol = FALSE; - } - } - else - { - state |= ST_TABLE_HEADER; - if (!read_yaml_macros_and_links) - { - process_table_start(output); - output_firstcol = FALSE; - } - } - - pline++; - colno++; - } - else if (state & ST_TABLE_HEADER) - { - *ptoken = 0; - if (!read_yaml_macros_and_links) - { - print_output(output, "%s", token); - if (*token) - output_firstcol = FALSE; - if (strlen((char*)pline) > 1) - { - process_table_header_cell(output); - output_firstcol = FALSE; - } - else - { - process_table_header_end(output); - output_firstcol = TRUE; - } - } - RESET_TOKEN(token, ptoken, token_size); - pline++; - colno++; - } - else if (state & ST_TABLE) - { - *ptoken = 0; - if (!read_yaml_macros_and_links) - { - print_output(output, "%s", token); - if (*token) - output_firstcol = FALSE; - if (strlen((char*)pline) > 1) - { - process_table_body_cell(output); - output_firstcol = FALSE; - } - else - { - process_table_body_row_end(output); - output_firstcol = TRUE; - } - } - RESET_TOKEN(token, ptoken, token_size); - pline++; - colno++; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '<': - if (read_yaml_macros_and_links - || ANY(state, ST_DISPLAY_FORMULA | ST_FORMULA - | ST_IMAGE)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (ANY(state, ST_CODE | ST_KBD | ST_PRE)) - { - size_t lt_len = strlen("&lt;"); - *ptoken = 0; - strncat((char*)token, "&lt;", - BUFSIZE-(ptoken-token)-1); - ptoken += lt_len; - pline++; - colno++; - break; - } - - state |= ST_HTML_TAG; - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - - case '>': - if (state & ST_HTML_TAG) - state &= ~ST_HTML_TAG; - - if (ANY(state, ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE - | ST_MACRO_BODY)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (!read_yaml_macros_and_links - && !ANY(state, ST_CODE | ST_HEADING | ST_PRE) - && colno == 1) - { - if (!read_yaml_macros_and_links - && !(state & ST_BLOCKQUOTE)) - { - process_blockquote(output, FALSE); - output_firstcol = FALSE; - } - - state |= ST_BLOCKQUOTE; - - pline++; - colno++; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '!': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_FOOTNOTE_TEXT | ST_HEADING | ST_IMAGE - | ST_INLINE_FOOTNOTE | ST_LINK | ST_MACRO_BODY - | ST_PRE)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (strlen((char*)pline) > 1 && *(pline+1) == '[') - { - /* Output existing text up to ! */ - *ptoken = 0; - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, FALSE); - processed_start_of_line = TRUE; - - RESET_TOKEN(token, ptoken, token_size); - - state |= ST_IMAGE; - keep_token = TRUE; - pline += 2; - colno += 2; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - - break; - - case '=': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_MACRO_BODY | ST_PRE | ST_TAG)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (colno != 1 && *(pline-1) == '[') - { - state |= ST_LINK_MACRO; - pline++; - colno++; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '[': - pline_len = strlen((char*)pline); - - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HEADING | ST_IMAGE | ST_MACRO_BODY | ST_PRE)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (state & ST_FOOTNOTE_TEXT) - { - if (!read_yaml_macros_and_links && (state & ST_PARA_OPEN)) - { - print_output(output, "</p>\n"); - output_firstcol = TRUE; - } - state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN); - } - - if (pline_len > 1 && *(pline+1) == '^') - { - footnote_at_line_start = colno == 1; - if (*token) - { - /* Output existing text up to [^ */ - *ptoken = 0; - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - } - processed_start_of_line = TRUE; - - RESET_TOKEN(token, ptoken, token_size); - state |= ST_FOOTNOTE; - pline += 2; - colno += 2; - break; - } - if (*token) - { - /* Output existing text up to [ */ - *ptoken = 0; - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - } - processed_start_of_line = TRUE; - - RESET_TOKEN(token, ptoken, token_size); - state |= ST_LINK; - keep_token = TRUE; - *link_macro = 0; - pline++; - colno++; + u8* title = NULL; + u8* title_heading_level = NULL; + u8* header_text = NULL; + u8* author = NULL; + u8* date = NULL; + u8* permalink_url = NULL; + u8* ext_in_permalink = NULL; + u8* var_add_article_header = NULL; + u8* var_add_image_links = NULL; + u8* var_add_figcaption = NULL; + u8* var_add_footnote_div = NULL; + u8* link_prefix = NULL; + u8* pbuffer = NULL; + u8* line = NULL; + u8* pline = NULL; + size_t line_len = 0; + u8* token = NULL; + u8* ptoken = NULL; + size_t token_size = 0; + u8* link_text = NULL; + size_t link_size = 0; + u8* link_macro = NULL; + UBYTE heading_level = 0; + ULONG heading_count = 0; + BOOL end_tag = FALSE; + BOOL first_line_in_doc = TRUE; + BOOL skip_change_first_line_in_doc = FALSE; + BOOL skip_eol = FALSE; + BOOL keep_token = FALSE; + BOOL previous_line_blank = FALSE; + BOOL processed_start_of_line = FALSE; + BOOL add_image_links = TRUE; + BOOL add_figcaption = TRUE; + BOOL add_footnote_div = FALSE; + BOOL list_para = FALSE; + BOOL footnote_at_line_start = FALSE; + size_t pline_len = 0; + + if (!buffer) + exit(error(1, (u8*)"Empty buffer")); + + title = get_value(vars, vars_count, (u8*)"title", NULL); + title_heading_level + = get_value(vars, vars_count, (u8*)"title-heading-level", NULL); + header_text = get_value(vars, vars_count, (u8*)"header-text", NULL); + author = get_value(vars, vars_count, (u8*)"author", NULL); + date = get_value(vars, vars_count, (u8*)"date", NULL); + permalink_url = get_value(vars, vars_count, (u8*)"permalink-url", NULL); + ext_in_permalink + = get_value(vars, vars_count, (u8*)"ext-in-permalink", NULL); + var_add_article_header + = get_value(vars, vars_count, (u8*)"add-article-header", NULL); + var_add_image_links + = get_value(vars, vars_count, (u8*)"add-image-links", NULL); + add_image_links = !(var_add_image_links && *var_add_image_links == '0'); + var_add_figcaption + = get_value(vars, vars_count, (u8*)"add-figcaption", NULL); + add_figcaption = !(var_add_figcaption && *var_add_figcaption == '0'); + var_add_footnote_div + = get_value(vars, vars_count, (u8*)"add-footnote-div", NULL); + add_footnote_div = var_add_footnote_div && *var_add_footnote_div == '1'; + link_prefix = get_value(vars, vars_count, (u8*)"link-prefix", NULL); + + CALLOC(line, u8, BUFSIZE); + token_size = BUFSIZE; + CALLOC(token, u8, BUFSIZE); + CALLOC(link_macro, u8, BUFSIZE); + + pbuffer = buffer; + pvars = vars; + pmacros = macros; + plinks = links; + pfootnotes = footnotes; + lineno = 0; + + if (!read_yaml_macros_and_links && !body_only) + { + begin_html_and_head(output); + add_css(output); + end_head_start_body(output); + } + + if (!read_yaml_macros_and_links) + begin_article(output, link_prefix, + var_add_article_header && *var_add_article_header == '1', + author, title, header_text, (char*)title_heading_level, + date, ext_in_permalink && *ext_in_permalink != '0', + (char*)permalink_url); + + RESET_TOKEN(token, ptoken, token_size); + +do_buffer:; + u8* eol = (u8*)strchr((char*)pbuffer, '\n'); + if (!eol) + goto done_buffer; + + pline = line; + while (pbuffer != eol) + *pline++ = *pbuffer++; + pbuffer++; + *pline = 0; + pline = line; + line_len = strlen((char*)line); + + lineno++; + colno = 1; + output_firstcol = TRUE; + processed_start_of_line = FALSE; + skip_eol = FALSE; + /*list_item = FALSE;*/ + list_para = FALSE; + +do_line: + if (!pline || !*pline) + goto done_line; + switch (*pline) + { + case '-': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + else if (colno == 1 && strlen((char*)pline) > 2 + && startswith((char*)pline, "---")) + { + skip_eol = TRUE; - if (*pline == '(') + if (!(state & ST_YAML) && lineno > 1 + && !read_yaml_macros_and_links) + process_horizontal_rule(output); + else + { + if (lineno == 1) + state |= ST_YAML; + else + state &= ~ST_YAML; + + skip_change_first_line_in_doc = TRUE; + } + pline = NULL; + } + else if (colno == 1 && strlen((char*)pline) > 1 + && *(pline + 1) == ' ') { - u8* tag = (u8*)"<span>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; + if (state & ST_NUMLIST) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_numlist_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(state & ST_LIST)) + process_list_start(output); + else + process_list_item_end(output); + process_list_item_start(output); + state |= ST_PARA_OPEN; + } - *ptoken = 0; - token_len = strlen((char*)token); + processed_start_of_line = TRUE; + skip_eol = FALSE; - if (token_len + tag_len < BUFSIZE) - { - strncat((char*)token, (char*)tag, BUFSIZE-token_len-1); - ptoken += tag_len; - } + state |= ST_LIST; - state |= ST_LINK_SPAN; - pline++; - colno++; + pline += 2; + colno += 2; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; } + break; + + case ':': + if (state & ST_YAML + && !(ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HEADING | ST_IMAGE | ST_MACRO_BODY + | ST_PRE | ST_TAG | ST_YAML_VAL)) + && read_yaml_macros_and_links) + { + *ptoken = 0; - break; + vars_count++; - case '(': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_FOOTNOTE_TEXT | ST_HEADING - | ST_INLINE_FOOTNOTE | ST_LINK_SECOND_ARG_END - | ST_MACRO_BODY | ST_PRE)) - CHECKCOPY(token, ptoken, token_size, pline); - else if (*link_macro && (state & ST_LINK)) + if (!vars) + { + CALLOC(vars, KeyValue, vars_count); + pvars = vars; + } + else + { + REALLOCARRAY(vars, KeyValue, vars_count); + pvars = vars + vars_count - 1; + } + CALLOC(pvars->key, u8, KEYSIZE); + strncpy((char*)pvars->key, (char*)token, KEYSIZE - 1); + pvars->value = NULL; + pvars->value_size = 0; + + state |= ST_YAML_VAL; + RESET_TOKEN(token, ptoken, token_size); + pline++; + colno++; + + while (pline && (*pline == ' ' || *pline == '\t')) + { + pline++; + colno++; + } + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '~': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HTML_TAG | ST_IMAGE | ST_LINK_SECOND_ARG + | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY + | ST_PRE | ST_TAG | ST_YAML)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '~') + { + /* Handle ~~ within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + u8* entity = (u8*)"&nbsp;"; + size_t entity_len = strlen((char*)entity); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + entity_len < BUFSIZE) + { + strncat((char*)token, (char*)entity, + BUFSIZE - token_len - 1); + ptoken += entity_len; + } + } + else + { + /* Output existing text up to ~~ */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, + ST_PRE | ST_CODE | ST_HEADING))) + print_output(output, "&nbsp;"); + } + + pline += 2; + colno += 2; + } + else + { + /* Handle ~ within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) { - u8* tag = (u8*)"<span>"; + u8* tag = state & ST_STRIKE ? (u8*)"</s>" + : (u8*)"<s>"; size_t tag_len = strlen((char*)tag); size_t token_len = 0; - *ptoken = 0; + *ptoken = 0; token_len = strlen((char*)token); if (token_len + tag_len < BUFSIZE) { strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); + BUFSIZE - token_len - 1); ptoken += tag_len; } - - state |= ST_LINK_SPAN; - pline++; } else - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - - case ')': - if (ANY(state, ST_DISPLAY_FORMULA | ST_FORMULA - | ST_LINK_SECOND_ARG_END)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } + { + /* Output existing text up to ~ */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, + ST_PRE | ST_CODE | ST_HEADING))) + process_strike( + output, state & ST_STRIKE); + } - pline_len = strlen((char*)pline); + state ^= ST_STRIKE; + pline++; + colno++; + } + break; - if (state & ST_LINK_SPAN && pline_len > 1 - && *(pline+1) == ']') + case '`': + if (ANY(state, + ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE + | ST_MACRO_BODY)) { - u8* tag = (u8*)"</span>"; - size_t tag_len = strlen((char*)tag); - size_t token_len = 0; + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } - *ptoken = 0; - token_len = strlen((char*)token); + if (colno == 1 && strlen((char*)pline) > 2 + && startswith((char*)pline, "```")) + { + state ^= ST_PRE; - if (token_len + tag_len + 1 < BUFSIZE) + if (!read_yaml_macros_and_links) { - strncat((char*)token, (char*)tag, - BUFSIZE-token_len-1); - ptoken += tag_len; + if (state & ST_PRE) + print_output(output, "<pre>"); + else + print_output(output, "</pre>"); + output_firstcol = FALSE; } - state &= ~ST_LINK_SPAN; - /*pline += 2;*/ - pline++; - colno++; + + /* Skip the rest of the line (language) */ + pline = NULL; } - else if (ANY(state, ST_LINK_SECOND_ARG | ST_IMAGE_SECOND_ARG)) - { - *ptoken = 0; - if (!read_yaml_macros_and_links) - { - if (state & ST_LINK_SECOND_ARG) - process_inline_link(link_text, link_prefix, - get_value(macros, macros_count, - link_macro, NULL), - token, output); - else - process_inline_image(link_text, link_prefix, token, - output, add_image_links, add_figcaption); - } - RESET_TOKEN(token, ptoken, token_size); - state &= ~(ST_LINK | ST_LINK_SECOND_ARG - | ST_IMAGE | ST_IMAGE_SECOND_ARG); - pline++; - } - else - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - - case ']': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HEADING | ST_MACRO_BODY | ST_PRE)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - *ptoken = 0; - if (state & ST_INLINE_FOOTNOTE) - { - process_inline_footnote(token, read_yaml_macros_and_links, - output); - - keep_token = FALSE; - RESET_TOKEN(token, ptoken, token_size); - state &= ~ST_INLINE_FOOTNOTE; - pline++; - colno++; - } - else if (state & ST_FOOTNOTE) - { - BOOL footnote_definition = (strlen((char*)pline) > 1) - && (*(pline+1) == ':') && footnote_at_line_start; - - process_footnote(token, - footnote_definition && read_yaml_macros_and_links, - !footnote_definition && !read_yaml_macros_and_links, - output); - - RESET_TOKEN(token, ptoken, token_size); - state &= ~ST_FOOTNOTE; - footnote_at_line_start = FALSE; - pline++; - colno++; - if (footnote_definition) - { - pline++; - colno++; - while (*pline == ' ' || *pline == '\t') - { - pline++; - colno++; - } - state |= ST_FOOTNOTE_TEXT; - } - } - else if (state & ST_LINK_SECOND_ARG) - { - if (!read_yaml_macros_and_links) - process_link(link_text, link_prefix, - get_value(macros, macros_count, - link_macro, NULL), - token, output); - RESET_TOKEN(token, ptoken, token_size); - state &= ~(ST_LINK | ST_LINK_SECOND_ARG); - pline++; - colno++; - } - else if (state & ST_IMAGE_SECOND_ARG) - { - if (!read_yaml_macros_and_links) - process_image(link_text, link_prefix, token, output, - add_image_links, add_figcaption); - RESET_TOKEN(token, ptoken, token_size); - state &= ~(ST_IMAGE | ST_IMAGE_SECOND_ARG); - pline++; - colno++; - } - else if (strlen((char*)pline) > 1) - { - size_t token_len = 0; - switch (*(pline+1)) - { - case ':': - links_count++; - - if (!links) - { - CALLOC(links, KeyValue, links_count); - plinks = links; - } - else - { - REALLOCARRAY(links, KeyValue, links_count); - plinks = links + links_count - 1; - } - CALLOC(plinks->key, u8, KEYSIZE); - strncpy((char*)plinks->key, (char*)token, - KEYSIZE-1); - plinks->value = NULL; - plinks->value_size = 0; - pline += 2; - colno += 2; - while (pline && (*pline == ' ' || *pline == '\t')) - { - pline++; - colno++; - } - RESET_TOKEN(token, ptoken, token_size); - keep_token = FALSE; - state |= ST_LINK_SECOND_ARG_END; - break; - - case '[': - case '(': - token_len = strlen((char*)token); - if (link_text) - { - if (token_len + 1 > link_size) - { - link_size = token_len+1; - REALLOC(link_text, u8, link_size); - } - } - else - { - link_size = token_len+1; - CALLOC(link_text, u8, link_size); - } - strncpy((char*)link_text, (char*)token, - link_size-1); - *(link_text + token_len) = 0; - pline += 2; - colno += 2; - RESET_TOKEN(token, ptoken, token_size); - keep_token = FALSE; - if (state & ST_LINK) - state |= ST_LINK_SECOND_ARG; - else - state |= ST_IMAGE_SECOND_ARG; - break; - default: - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - state &= ~(ST_LINK | ST_IMAGE); - break; - } - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '^': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_FOOTNOTE_TEXT | ST_IMAGE | ST_INLINE_FOOTNOTE - | ST_PRE | ST_YAML)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (strlen((char*)pline) > 1 && *(pline+1) == '[') - { - /* Output existing text up to ^[ */ - *ptoken = 0; - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - processed_start_of_line = TRUE; - - RESET_TOKEN(token, ptoken, token_size); - state |= ST_INLINE_FOOTNOTE; - keep_token = TRUE; - pline += 2; - colno += 2; - break; - } - - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - - case '\\': - *ptoken = 0; - if (ANY(state, ST_DISPLAY_FORMULA | ST_FORMULA - | ST_HTML_TAG | ST_IMAGE | ST_LINK - | ST_MACRO_BODY)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (strlen((char*)pline) > 1) - { - pline++; - colno++; - - CHECKCOPY(token, ptoken, token_size, pline); - } - else - { - pline = NULL; - break; - } - - colno++; - break; - - case '$': - if (ANY(state, ST_CODE | ST_CSV_BODY | ST_IMAGE | ST_PRE - | ST_YAML)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - if (strlen((char*)pline) > 1 - && *(pline+1) == '$') - { - if (state & ST_FORMULA) - exit(error(1, (u8*)"Display formula within an" - " open formula")); - - if (!(state & ST_DISPLAY_FORMULA)) - { - /* Output existing text up to $$ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - } - processed_start_of_line = TRUE; - - state ^= ST_DISPLAY_FORMULA; - - if (state & ST_DISPLAY_FORMULA) - keep_token = TRUE; - else - { - *ptoken = 0; - - if (!read_yaml_macros_and_links) - process_formula(output, token, TRUE); - - keep_token = FALSE; - RESET_TOKEN(token, ptoken, token_size); - } - - pline += 2; - colno += 2; - } - else - { - if (state & ST_DISPLAY_FORMULA) - exit(error(1, (u8*)"Formula within an open" - " display formula")); - - if (!(state & ST_FORMULA)) - { - /* Output existing text up to $ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - } - processed_start_of_line = TRUE; - - state ^= ST_FORMULA; - - if (state & ST_FORMULA) - keep_token = TRUE; - else - { - *ptoken = 0; - - if (!read_yaml_macros_and_links) - process_formula(output, token, FALSE); - - keep_token = FALSE; - RESET_TOKEN(token, ptoken, token_size); - } - - pline++; - colno++; - } - break; - - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': case '0': - if (ANY(state, ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - else if (colno == 1 - && strlen((char*)pline) > 1 - && (*(pline+1) == '.' || *(pline+1) == ')')) - { - if (state & ST_LIST) - { - state &= ~ST_LIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_list_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_NUMLIST)) - process_numlist_start(output); - else - process_list_item_end(output); - process_list_item_start(output); - state |= ST_PARA_OPEN; - } - - processed_start_of_line = TRUE; - skip_eol = FALSE; - - state |= ST_NUMLIST; - - pline += 2; - colno += 2; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - default: - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - } - - *ptoken = 0; - - if (*token && read_yaml_macros_and_links - && (state & ST_YAML_VAL)) - { - *ptoken = 0; - if (!vars) - { - vars_count = 1; - CALLOC(vars, KeyValue, vars_count); - pvars = vars; - } - pvars->value_size = strlen((char*)token)+1; - CALLOC(pvars->value, u8, pvars->value_size); - strncpy((char*)pvars->value, (char*)token, pvars->value_size-1); - } - else if (keep_token) - { - size_t token_len = strlen((char*)token); - - if (ptoken + 2 > token + token_size) - { - size_t old_size = token_size; - token_size += BUFSIZE; - REALLOC(token, u8, token_size); - ptoken = token + old_size - 1; - } - strncat((char*)token, ANY(state, ST_IMAGE | ST_LINK) - ? " " : "\n", - token_size - token_len - 1); - ptoken++; - *ptoken = 0; - } - else - { - if (*token) - { - *ptoken = 0; - size_t token_len = strlen((char*)token); - if (state & ST_MACRO_BODY) - { - skip_eol = TRUE; - if (read_yaml_macros_and_links) - { - if (!macros) - { - macros_count = 1; - CALLOC(macros, KeyValue, macros_count); - pmacros = macros; - } - if (pmacros->value) - { - size_t value_len = strlen((char*)pmacros->value); - - if (pmacros->value_size < value_len + token_len + 1) - { - pmacros->value_size += BUFSIZE; - REALLOC(pmacros->value, u8, pmacros->value_size); - } - strncat((char*)pmacros->value, (char*)token, - pmacros->value_size-value_len-1); - strncat((char*)pmacros->value, "\n", - pmacros->value_size - -strlen((char*)pmacros->value)-1); - } - else - { - pmacros->value_size = BUFSIZE; - CALLOC(pmacros->value, u8, pmacros->value_size); - strncpy((char*)pmacros->value, (char*)token, - pmacros->value_size-1); - strncat((char*)pmacros->value, "\n", - pmacros->value_size - -strlen((char*)pmacros->value)-1); - } - } - } - else if (state & ST_FOOTNOTE_TEXT) - { - skip_eol = TRUE; - if (read_yaml_macros_and_links) - { - if (!footnotes) - { - footnote_count = 1; - CALLOC(footnotes, KeyValue, footnote_count); - pfootnotes = footnotes; - } - if (pfootnotes->value) - { - size_t value_len = strlen((char*)pfootnotes->value); - - if (pfootnotes->value_size < value_len + token_len + 1) - { - pfootnotes->value_size += BUFSIZE; - REALLOC(pfootnotes->value, u8, pfootnotes->value_size); - } - strncat((char*)pfootnotes->value, (char*)token, - pfootnotes->value_size-value_len-1); - strncat((char*)pfootnotes->value, "\n", - pfootnotes->value_size - -strlen((char*)pfootnotes->value)-1); - } - else - { - pfootnotes->value_size = BUFSIZE; - CALLOC(pfootnotes->value, u8, pfootnotes->value_size); - strncpy((char*)pfootnotes->value, (char*)token, - pfootnotes->value_size-1); - strncat((char*)pfootnotes->value, "\n", - pfootnotes->value_size - -strlen((char*)pfootnotes->value)-1); - } - } - } - else if (ANY(state, ST_LINK_SECOND_ARG | ST_LINK_SECOND_ARG_END)) - { - if (read_yaml_macros_and_links) - { - if (!links) - { - links_count = 1; - CALLOC(links, KeyValue, links_count); - plinks = links; - } - plinks->value_size = strlen((char*)token)+1; - CALLOC(plinks->value, u8, plinks->value_size); - strncpy((char*)plinks->value, (char*)token, - plinks->value_size-1); - } - } - else if (state & ST_HEADING) - { - state &= ~(ST_HEADING | ST_HEADING_TEXT); - if (!read_yaml_macros_and_links) - { - process_heading(token, output, heading_level); - output_firstcol = FALSE; - } - first_line_in_doc = FALSE; - RESET_TOKEN(token, ptoken, token_size); - heading_level = 0; - } - else if (!ANY(state, ST_DISPLAY_FORMULA | ST_FORMULA - | ST_IMAGE | ST_LINK)) - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - read_yaml_macros_and_links, list_para, output, - &token, &ptoken, &token_size, TRUE); - } - - if (!ANY(state, ST_PRE | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG - | ST_LINK_SECOND_ARG_END) - && (!*pbuffer || *pbuffer == '\n')) - { - if (state & ST_PARA_OPEN) - { - if (!read_yaml_macros_and_links) - { - print_output(output, "</p>"); - output_firstcol = FALSE; - } - if (state & ST_LIST) - skip_eol = TRUE; - state &= ~ST_PARA_OPEN; - } - - if (!*pbuffer && (state & ST_LIST)) - { - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_list_end(output); - } - state &= ~ST_LIST; - } - - if (!*pbuffer && (state & ST_NUMLIST)) - { - if (!read_yaml_macros_and_links) - { - process_list_item_end(output); - process_numlist_end(output); - } - state &= ~ST_NUMLIST; - } - - if (!*pbuffer && (state & ST_FOOTNOTE_TEXT)) - { - state &= ~ST_FOOTNOTE_TEXT; - - *ptoken = 0; - size_t token_len = strlen((char*)token); - - skip_eol = TRUE; - if (read_yaml_macros_and_links) - { - if (!footnotes) - { - footnote_count = 1; - CALLOC(footnotes, KeyValue, footnote_count); - pfootnotes = footnotes; - } - if (pfootnotes->value) - { - size_t value_len = strlen((char*)pfootnotes->value); - - if (pfootnotes->value_size < value_len + token_len + 1) - { - pfootnotes->value_size - = value_len + token_len + 1; - REALLOC(pfootnotes->value, u8, pfootnotes->value_size); - } - strncat((char*)pfootnotes->value, (char*)token, - pfootnotes->value_size-1); - } - else - { - pfootnotes->value_size = token_len + 1; - CALLOC(pfootnotes->value, u8, pfootnotes->value_size); - strncpy((char*)pfootnotes->value, (char*)token, - pfootnotes->value_size-1); - } - } - } - } - - if (state & ST_BLOCKQUOTE - && (!*pbuffer || *pbuffer != '>')) - { - state &= ~ST_BLOCKQUOTE; - process_blockquote(output, TRUE); - output_firstcol = FALSE; - } - - if (ANY(state, ST_TABLE_HEADER | ST_TABLE_LINE) - && (!line_len || !*pbuffer)) - { - if (!read_yaml_macros_and_links) - { - warning(1, (u8*)"Malformed table"); - process_table_end(output); - output_firstcol = TRUE; - } - state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE); - } - - if ((state & ST_TABLE) && (!line_len || !*pbuffer)) - { - if (!read_yaml_macros_and_links) - { - process_table_end(output); - output_firstcol = TRUE; - } - state &= ~ST_TABLE; - } - - previous_line_blank = FALSE; - } - - if (!skip_eol && !keep_token && !read_yaml_macros_and_links - && !ANY(state, ST_YAML | ST_YAML_VAL | ST_LINK_SECOND_ARG - | ST_LINK_SECOND_ARG_END)) - { - print_output(output, "\n"); - output_firstcol = TRUE; - } - - if (!keep_token) - RESET_TOKEN(token, ptoken, token_size); - - if (!skip_change_first_line_in_doc - && !(state & ST_YAML)) - first_line_in_doc = FALSE; - - skip_change_first_line_in_doc = FALSE; - if (line_len == 0) - previous_line_blank = TRUE; - - /* Lasts until the end of line */ - state &= ~(ST_YAML_VAL | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG - | ST_LINK_SECOND_ARG_END); - } - while (pbuffer && *pbuffer); - - if (!read_yaml_macros_and_links - && (footnote_count > 0 || inline_footnote_count > 0)) - end_footnotes(output, add_footnote_div); - - if (!read_yaml_macros_and_links && !body_only) - end_body_and_html(output); - - if (link_text) - free(link_text); - - free(link_macro); - free(token); - free(line); - - return 0; -} + else if (!ANY(state, ST_PRE | ST_TAG | ST_YAML)) + { + /* Handle ` within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + u8* tag = state & ST_CODE ? (u8*)"</code>" + : (u8*)"<code>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + } + else + { + /* Output existing text up to ` */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, ST_PRE | ST_HEADING))) + process_code(output, state & ST_CODE); + } + + state ^= ST_CODE; + + pline++; + colno++; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '#': + if (colno == 1 + && !(ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_MACRO_BODY | ST_PRE | ST_YAML))) + { + if (state & ST_LIST) + { + state &= ~ST_LIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_list_end(output); + } + } + + if (state & ST_NUMLIST) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_numlist_end(output); + } + } + + state |= ST_HEADING; + heading_level = 1; + pline++; + colno++; + } + else if (state & ST_HEADING && *(pline - 1) == '#') + { + if (heading_level < MAX_HEADING_LEVEL) + heading_level++; + pline++; + colno++; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '_': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HTML_TAG | ST_IMAGE | ST_LINK_SECOND_ARG + | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY + | ST_PRE | ST_TAG | ST_YAML)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '_') + { + /* Handle __ within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + u8* tag = state & ST_BOLD ? (u8*)"</strong>" + : (u8*)"<strong>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + } + else + { + /* Output existing text up to __ */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, + ST_PRE | ST_CODE | ST_HEADING))) + process_bold(output, state & ST_BOLD); + } + + state ^= ST_BOLD; + pline += 2; + colno += 2; + } + else + { + /* Handle _ within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + u8* tag = state & ST_ITALIC ? (u8*)"</em>" + : (u8*)"<em>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + } + else + { + /* Output existing text up to _ */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, + ST_PRE | ST_CODE | ST_HEADING))) + process_italic( + output, state & ST_ITALIC); + } + + state ^= ST_ITALIC; + pline++; + colno++; + } + break; + + case 'o': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HTML_TAG | ST_IMAGE | ST_MACRO_BODY + | ST_PRE | ST_YAML)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (colno == 1 && strlen((char*)pline) > 1 + && *(pline + 1) == ' ') + { + if (state & ST_NUMLIST) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_numlist_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(state & ST_LIST)) + process_list_start(output); + else + process_list_item_end(output); + process_list_item_start(output); + state |= ST_PARA_OPEN; + } + + processed_start_of_line = TRUE; + skip_eol = FALSE; + + state |= ST_LIST; + + pline += 2; + colno += 2; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '*': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HTML_TAG | ST_IMAGE | ST_MACRO_BODY + | ST_PRE | ST_YAML)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + pline_len = strlen((char*)pline); + if (colno == 1 && pline_len > 1 && *(pline + 1) == '[') + { + process_line_start(line, link_prefix, first_line_in_doc, + previous_line_blank, read_yaml_macros_and_links, + list_para, output, &token, &ptoken); + + /* Ignore abbreviations (for now) */ + pline = NULL; + } + else if (colno == 1 && pline_len > 2 + && startswith((char*)pline, "***")) + { + skip_eol = TRUE; + if (!read_yaml_macros_and_links) + process_horizontal_rule(output); + pline = NULL; + } + else if (colno == 1 && strlen((char*)pline) > 1 + && *(pline + 1) == ' ') + { + if (state & ST_NUMLIST) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_numlist_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(state & ST_LIST)) + process_list_start(output); + else + process_list_item_end(output); + process_list_item_start(output); + state |= ST_PARA_OPEN; + } + + processed_start_of_line = TRUE; + skip_eol = FALSE; + + state |= ST_LIST; + + pline += 2; + colno += 2; + } + else if (pline_len > 1 && *(pline + 1) == '*') + { + /* Handle ** within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + u8* tag = state & ST_BOLD ? (u8*)"</strong>" + : (u8*)"<strong>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + } + else + { + /* Output existing text up to * */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, + ST_PRE | ST_CODE | ST_HEADING))) + process_bold(output, state & ST_BOLD); + } + + state ^= ST_BOLD; + pline += 2; + colno += 2; + } + else + { + /* Handle * within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + + { + u8* tag = state & ST_ITALIC ? (u8*)"</em>" + : (u8*)"<em>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + } + else + { + /* Output existing text up to * */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, + ST_PRE | ST_CODE | ST_HEADING))) + process_italic( + output, state & ST_ITALIC); + } + + state ^= ST_ITALIC; + pline++; + colno++; + } + break; + + case ' ': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE + | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if ((state & ST_HEADING) && !(state & ST_HEADING_TEXT)) + { + if (!read_yaml_macros_and_links) + { + heading_count++; + process_heading_start( + output, heading_level, heading_count); + output_firstcol = FALSE; + } + state |= ST_HEADING_TEXT; + pline++; + colno++; + break; + } + + if (colno == 1 && strlen((char*)pline) > 1 + && startswith((char*)pline, " ")) + { + list_para = TRUE; + process_text_token(line, link_prefix, first_line_in_doc, + previous_line_blank, processed_start_of_line, + read_yaml_macros_and_links, list_para, output, + &token, &ptoken, &token_size, TRUE); + processed_start_of_line = TRUE; + while (*pline == ' ' || *pline == '\t') + { + pline++; + colno++; + } + break; + } + + if (strlen((char*)pline) == 2 && *(pline + 1) == ' ') + { + *ptoken = 0; + strncat((char*)ptoken, "<br />", + BUFSIZE - strlen((char*)token) - 1); + ptoken += strlen("<br />"); + output_firstcol = FALSE; + pline++; + colno++; + } + else if (state & ST_LINK_MACRO) + { + *ptoken = 0; + strncpy((char*)link_macro, (char*)token, BUFSIZE - 1); + *(link_macro + strlen((char*)token)) = 0; + RESET_TOKEN(token, ptoken, token_size); + state &= ~ST_LINK_MACRO; + } + else if (!(state & ST_HEADING && *(pline - 1) == '#')) + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + + case '{': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HTML_TAG | ST_PRE | ST_YAML + | ST_YAML_VAL)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (state & ST_MACRO_BODY) + { + *ptoken = 0; + size_t token_len = strlen((char*)token); + + skip_eol = TRUE; + if (read_yaml_macros_and_links) + { + if (!macros) + { + macros_count = 1; + CALLOC(macros, KeyValue, macros_count); + pmacros = macros; + } + if (pmacros->value) + { + size_t value_len + = strlen((char*)pmacros->value); + + if (pmacros->value_size + < value_len + token_len + 1) + { + pmacros->value_size + += token_len; + REALLOC(pmacros->value, u8, + pmacros->value_size); + } + strncat((char*)pmacros->value, + (char*)token, + pmacros->value_size - 1); + } + else + { + pmacros->value_size = token_len + 1; + CALLOC(pmacros->value, u8, + pmacros->value_size); + strncpy((char*)pmacros->value, + (char*)token, + pmacros->value_size - 1); + } + } + } + else + { + /* Output existing text up to { */ + process_text_token(line, link_prefix, first_line_in_doc, + previous_line_blank, processed_start_of_line, + read_yaml_macros_and_links, list_para, output, + &token, &ptoken, &token_size, FALSE); + processed_start_of_line = TRUE; + } + + state |= ST_TAG; + RESET_TOKEN(token, ptoken, token_size); + + pline++; + colno++; + break; + + case '/': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE + | ST_PRE | ST_HEADING | ST_YAML_VAL)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if ((state & ST_TAG) && pline - 1 && *(pline - 1) == '{') + { + end_tag = TRUE; + pline++; + } + else + CHECKCOPY(token, ptoken, token_size, pline); + + colno++; + break; + + case '}': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE + | ST_PRE | ST_YAML | ST_YAML_VAL)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (state & ST_TAG) + { + state &= ~ST_TAG; + *ptoken = 0; + + process_tag(token, link_prefix, output, + read_yaml_macros_and_links, &skip_eol, end_tag); + + RESET_TOKEN(token, ptoken, token_size); + end_tag = FALSE; + } + + pline++; + colno++; + break; + + case '|': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HTML_TAG | ST_IMAGE | ST_PRE | ST_TAG + | ST_YAML)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '|') + { + /* Handle || within footnotes, headings and link text + * specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + u8* tag = state & ST_KBD ? (u8*)"</kbd>" + : (u8*)"<kbd>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + } + else + { + /* Output existing text up to || */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + processed_start_of_line = TRUE; + + if (!read_yaml_macros_and_links + && !(ANY(state, + ST_PRE | ST_CODE | ST_HEADING))) + { + state ^= ST_KBD; + process_kbd(output, !(state & ST_KBD)); + output_firstcol = FALSE; + } + } + + pline += 2; + colno += 2; + } + /* Partial tables (for templating) */ + else if (!(state & ST_PRE) && colno == 1 + && strlen((char*)pline) > 1 && *(pline + 1) == '@') + { + skip_eol = TRUE; + + pline += 2; + colno += 2; + + switch (*pline) + { + case '\\': + if (!read_yaml_macros_and_links) + { + process_table_start(output); + output_firstcol = FALSE; + } + pline = NULL; + break; + + case '#': + state |= ST_TABLE_HEADER; + pline++; + colno++; + break; + + case '-': + state &= ~ST_TABLE_HEADER; + if (!read_yaml_macros_and_links) + { + process_table_body_start(output, FALSE); + output_firstcol = TRUE; + } + pline = NULL; + break; + + case ' ': + state |= ST_TABLE; + if (!read_yaml_macros_and_links) + { + process_table_body_row_start(output); + output_firstcol = FALSE; + } + break; + + case '/': + state &= ~ST_TABLE; + process_table_end(output); + output_firstcol = TRUE; + pline = NULL; + break; + + default: + warning(1, (u8*)"Invalid partial table line"); + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + } + else if (!(state & ST_PRE) && colno == 1) + { + skip_eol = TRUE; + + if (state & ST_TABLE_HEADER) + { + state &= ~ST_TABLE_HEADER; + state |= ST_TABLE_LINE; + pline = NULL; + break; + } + + if (state & ST_TABLE_LINE) + { + state &= ~ST_TABLE_LINE; + state |= ST_TABLE; + if (!read_yaml_macros_and_links) + { + process_table_body_start(output, TRUE); + output_firstcol = FALSE; + } + } + else if (state & ST_TABLE) + { + if (!read_yaml_macros_and_links) + { + process_table_body_row_start(output); + output_firstcol = FALSE; + } + } + else + { + state |= ST_TABLE_HEADER; + if (!read_yaml_macros_and_links) + { + process_table_start(output); + output_firstcol = FALSE; + } + } + + pline++; + colno++; + } + else if (state & ST_TABLE_HEADER) + { + *ptoken = 0; + if (!read_yaml_macros_and_links) + { + print_output(output, "%s", token); + if (*token) + output_firstcol = FALSE; + if (strlen((char*)pline) > 1) + { + process_table_header_cell(output); + output_firstcol = FALSE; + } + else + { + process_table_header_end(output); + output_firstcol = TRUE; + } + } + RESET_TOKEN(token, ptoken, token_size); + pline++; + colno++; + } + else if (state & ST_TABLE) + { + *ptoken = 0; + if (!read_yaml_macros_and_links) + { + print_output(output, "%s", token); + if (*token) + output_firstcol = FALSE; + if (strlen((char*)pline) > 1) + { + process_table_body_cell(output); + output_firstcol = FALSE; + } + else + { + process_table_body_row_end(output); + output_firstcol = TRUE; + } + } + RESET_TOKEN(token, ptoken, token_size); + pline++; + colno++; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '<': + if (read_yaml_macros_and_links + || ANY(state, + ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (ANY(state, ST_CODE | ST_KBD | ST_PRE)) + { + size_t lt_len = strlen("&lt;"); + *ptoken = 0; + strncat((char*)token, "&lt;", + BUFSIZE - (ptoken - token) - 1); + ptoken += lt_len; + pline++; + colno++; + break; + } + + state |= ST_HTML_TAG; + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + + case '>': + if (state & ST_HTML_TAG) + state &= ~ST_HTML_TAG; + + if (ANY(state, + ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE + | ST_MACRO_BODY)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (!read_yaml_macros_and_links + && !ANY(state, ST_CODE | ST_HEADING | ST_PRE) + && colno == 1) + { + if (!read_yaml_macros_and_links + && !(state & ST_BLOCKQUOTE)) + { + process_blockquote(output, FALSE); + output_firstcol = FALSE; + } + + state |= ST_BLOCKQUOTE; + + pline++; + colno++; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '!': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_FOOTNOTE_TEXT | ST_HEADING | ST_IMAGE + | ST_INLINE_FOOTNOTE | ST_LINK + | ST_MACRO_BODY | ST_PRE)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '[') + { + /* Output existing text up to ! */ + *ptoken = 0; + process_text_token(line, link_prefix, first_line_in_doc, + previous_line_blank, processed_start_of_line, + read_yaml_macros_and_links, list_para, output, + &token, &ptoken, &token_size, FALSE); + processed_start_of_line = TRUE; + + RESET_TOKEN(token, ptoken, token_size); + + state |= ST_IMAGE; + keep_token = TRUE; + pline += 2; + colno += 2; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + + break; + + case '=': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_MACRO_BODY | ST_PRE | ST_TAG)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (colno != 1 && *(pline - 1) == '[') + { + state |= ST_LINK_MACRO; + pline++; + colno++; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '[': + pline_len = strlen((char*)pline); + + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HEADING | ST_IMAGE | ST_MACRO_BODY + | ST_PRE)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (state & ST_FOOTNOTE_TEXT) + { + if (!read_yaml_macros_and_links + && (state & ST_PARA_OPEN)) + { + print_output(output, "</p>\n"); + output_firstcol = TRUE; + } + state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN); + } + + if (pline_len > 1 && *(pline + 1) == '^') + { + footnote_at_line_start = colno == 1; + if (*token) + { + /* Output existing text up to [^ */ + *ptoken = 0; + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + } + processed_start_of_line = TRUE; + + RESET_TOKEN(token, ptoken, token_size); + state |= ST_FOOTNOTE; + pline += 2; + colno += 2; + break; + } + if (*token) + { + /* Output existing text up to [ */ + *ptoken = 0; + process_text_token(line, link_prefix, first_line_in_doc, + previous_line_blank, processed_start_of_line, + read_yaml_macros_and_links, list_para, output, + &token, &ptoken, &token_size, TRUE); + } + processed_start_of_line = TRUE; + + RESET_TOKEN(token, ptoken, token_size); + state |= ST_LINK; + keep_token = TRUE; + *link_macro = 0; + pline++; + colno++; + + if (*pline == '(') + { + u8* tag = (u8*)"<span>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + + state |= ST_LINK_SPAN; + pline++; + colno++; + } + + break; + + case '(': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_FOOTNOTE_TEXT | ST_HEADING + | ST_INLINE_FOOTNOTE | ST_LINK_SECOND_ARG_END + | ST_MACRO_BODY | ST_PRE)) + CHECKCOPY(token, ptoken, token_size, pline); + else if (*link_macro && (state & ST_LINK)) + { + u8* tag = (u8*)"<span>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + + state |= ST_LINK_SPAN; + pline++; + } + else + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + + case ')': + if (ANY(state, + ST_DISPLAY_FORMULA | ST_FORMULA + | ST_LINK_SECOND_ARG_END)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + pline_len = strlen((char*)pline); + + if (state & ST_LINK_SPAN && pline_len > 1 && *(pline + 1) == ']') + { + u8* tag = (u8*)"</span>"; + size_t tag_len = strlen((char*)tag); + size_t token_len = 0; + + *ptoken = 0; + token_len = strlen((char*)token); + + if (token_len + tag_len + 1 < BUFSIZE) + { + strncat((char*)token, (char*)tag, + BUFSIZE - token_len - 1); + ptoken += tag_len; + } + state &= ~ST_LINK_SPAN; + /*pline += 2;*/ + pline++; + colno++; + } + else if (ANY(state, ST_LINK_SECOND_ARG | ST_IMAGE_SECOND_ARG)) + { + *ptoken = 0; + if (!read_yaml_macros_and_links) + { + if (state & ST_LINK_SECOND_ARG) + process_inline_link(link_text, + link_prefix, + get_value(macros, macros_count, + link_macro, NULL), + token, output); + else + process_inline_image(link_text, + link_prefix, token, output, + add_image_links, + add_figcaption); + } + RESET_TOKEN(token, ptoken, token_size); + state &= ~(ST_LINK | ST_LINK_SECOND_ARG | ST_IMAGE + | ST_IMAGE_SECOND_ARG); + pline++; + } + else + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + + case ']': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_HEADING | ST_MACRO_BODY | ST_PRE)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + *ptoken = 0; + if (state & ST_INLINE_FOOTNOTE) + { + process_inline_footnote( + token, read_yaml_macros_and_links, output); + + keep_token = FALSE; + RESET_TOKEN(token, ptoken, token_size); + state &= ~ST_INLINE_FOOTNOTE; + pline++; + colno++; + } + else if (state & ST_FOOTNOTE) + { + BOOL footnote_definition = (strlen((char*)pline) > 1) + && (*(pline + 1) == ':') + && footnote_at_line_start; + + process_footnote(token, + footnote_definition + && read_yaml_macros_and_links, + !footnote_definition + && !read_yaml_macros_and_links, + output); + + RESET_TOKEN(token, ptoken, token_size); + state &= ~ST_FOOTNOTE; + footnote_at_line_start = FALSE; + pline++; + colno++; + if (footnote_definition) + { + pline++; + colno++; + while (*pline == ' ' || *pline == '\t') + { + pline++; + colno++; + } + state |= ST_FOOTNOTE_TEXT; + } + } + else if (state & ST_LINK_SECOND_ARG) + { + if (!read_yaml_macros_and_links) + process_link(link_text, link_prefix, + get_value(macros, macros_count, + link_macro, NULL), + token, output); + RESET_TOKEN(token, ptoken, token_size); + state &= ~(ST_LINK | ST_LINK_SECOND_ARG); + pline++; + colno++; + } + else if (state & ST_IMAGE_SECOND_ARG) + { + if (!read_yaml_macros_and_links) + process_image(link_text, link_prefix, token, + output, add_image_links, + add_figcaption); + RESET_TOKEN(token, ptoken, token_size); + state &= ~(ST_IMAGE | ST_IMAGE_SECOND_ARG); + pline++; + colno++; + } + else if (strlen((char*)pline) > 1) + { + size_t token_len = 0; + switch (*(pline + 1)) + { + case ':': + links_count++; + + if (!links) + { + CALLOC(links, KeyValue, links_count); + plinks = links; + } + else + { + REALLOCARRAY( + links, KeyValue, links_count); + plinks = links + links_count - 1; + } + CALLOC(plinks->key, u8, KEYSIZE); + strncpy((char*)plinks->key, (char*)token, + KEYSIZE - 1); + plinks->value = NULL; + plinks->value_size = 0; + pline += 2; + colno += 2; + while (pline + && (*pline == ' ' || *pline == '\t')) + { + pline++; + colno++; + } + RESET_TOKEN(token, ptoken, token_size); + keep_token = FALSE; + state |= ST_LINK_SECOND_ARG_END; + break; + + case '[': + case '(': + token_len = strlen((char*)token); + if (link_text && token_len + 1 > link_size) + { + link_size = token_len + 1; + REALLOC(link_text, u8, link_size); + } + else if (!link_text) + { + link_size = token_len + 1; + CALLOC(link_text, u8, link_size); + } + strncpy((char*)link_text, (char*)token, + link_size - 1); + *(link_text + token_len) = 0; + pline += 2; + colno += 2; + RESET_TOKEN(token, ptoken, token_size); + keep_token = FALSE; + if (state & ST_LINK) + state |= ST_LINK_SECOND_ARG; + else + state |= ST_IMAGE_SECOND_ARG; + break; + default: + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + state &= ~(ST_LINK | ST_IMAGE); + break; + } + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '^': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_FOOTNOTE_TEXT | ST_IMAGE + | ST_INLINE_FOOTNOTE | ST_PRE | ST_YAML)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '[') + { + /* Output existing text up to ^[ */ + *ptoken = 0; + process_text_token(line, link_prefix, first_line_in_doc, + previous_line_blank, processed_start_of_line, + read_yaml_macros_and_links, list_para, output, + &token, &ptoken, &token_size, TRUE); + processed_start_of_line = TRUE; + + RESET_TOKEN(token, ptoken, token_size); + state |= ST_INLINE_FOOTNOTE; + keep_token = TRUE; + pline += 2; + colno += 2; + break; + } + + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + + case '\\': + *ptoken = 0; + if (ANY(state, + ST_DISPLAY_FORMULA | ST_FORMULA | ST_HTML_TAG + | ST_IMAGE | ST_LINK | ST_MACRO_BODY)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (strlen((char*)pline) > 1) + { + pline++; + colno++; + + CHECKCOPY(token, ptoken, token_size, pline); + } + else + { + pline = NULL; + break; + } + + colno++; + break; + + case '$': + if (ANY(state, + ST_CODE | ST_CSV_BODY | ST_IMAGE | ST_PRE | ST_YAML)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '$') + { + if (state & ST_FORMULA) + exit(error(1, + (u8*)"Display formula within an" + " open formula")); + + if (!(state & ST_DISPLAY_FORMULA)) + { + /* Output existing text up to $$ */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + } + processed_start_of_line = TRUE; + + state ^= ST_DISPLAY_FORMULA; + + if (state & ST_DISPLAY_FORMULA) + keep_token = TRUE; + else + { + *ptoken = 0; + + if (!read_yaml_macros_and_links) + process_formula(output, token, TRUE); + + keep_token = FALSE; + RESET_TOKEN(token, ptoken, token_size); + } + + pline += 2; + colno += 2; + } + else + { + if (state & ST_DISPLAY_FORMULA) + exit(error(1, + (u8*)"Formula within an open" + " display formula")); + + if (!(state & ST_FORMULA)) + { + /* Output existing text up to $ */ + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + } + processed_start_of_line = TRUE; + + state ^= ST_FORMULA; + + if (state & ST_FORMULA) + keep_token = TRUE; + else + { + *ptoken = 0; + + if (!read_yaml_macros_and_links) + process_formula(output, token, FALSE); + + keep_token = FALSE; + RESET_TOKEN(token, ptoken, token_size); + } + + pline++; + colno++; + } + break; + + case '1': /* FALLTHROUGH */ + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + if (ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + else if (colno == 1 && strlen((char*)pline) > 1 + && (*(pline + 1) == '.' || *(pline + 1) == ')')) + { + if (state & ST_LIST) + { + state &= ~ST_LIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_list_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(state & ST_NUMLIST)) + process_numlist_start(output); + else + process_list_item_end(output); + process_list_item_start(output); + state |= ST_PARA_OPEN; + } + + processed_start_of_line = TRUE; + skip_eol = FALSE; + + state |= ST_NUMLIST; + + pline += 2; + colno += 2; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + default: + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + goto do_line; + +done_line: + *ptoken = 0; + + if (*token && read_yaml_macros_and_links && (state & ST_YAML_VAL)) + { + *ptoken = 0; + if (!vars) + { + vars_count = 1; + CALLOC(vars, KeyValue, vars_count); + pvars = vars; + } + pvars->value_size = strlen((char*)token) + 1; + CALLOC(pvars->value, u8, pvars->value_size); + strncpy((char*)pvars->value, (char*)token, + pvars->value_size - 1); + } + else if (keep_token) + { + size_t token_len = strlen((char*)token); + + if (ptoken + 2 > token + token_size) + { + size_t old_size = token_size; + token_size += BUFSIZE; + REALLOC(token, u8, token_size); + ptoken = token + old_size - 1; + } + strncat((char*)token, + ANY(state, ST_IMAGE | ST_LINK) ? " " : "\n", + token_size - token_len - 1); + ptoken++; + *ptoken = 0; + } + else + { + if (*token) + { + *ptoken = 0; + size_t token_len = strlen((char*)token); + if (state & ST_MACRO_BODY) + { + skip_eol = TRUE; + if (!read_yaml_macros_and_links) + goto skip_macro_read; + if (!macros) + { + macros_count = 1; + CALLOC(macros, KeyValue, macros_count); + pmacros = macros; + } + if (pmacros->value) + { + size_t value_len + = strlen((char*)pmacros->value); + + if (pmacros->value_size + < value_len + token_len + 1) + { + pmacros->value_size += BUFSIZE; + REALLOC(pmacros->value, u8, + pmacros->value_size); + } + strncat((char*)pmacros->value, + (char*)token, + pmacros->value_size - value_len + - 1); + strncat((char*)pmacros->value, "\n", + pmacros->value_size + - strlen((char*)pmacros + ->value) + - 1); + } + else + { + pmacros->value_size = BUFSIZE; + CALLOC(pmacros->value, u8, + pmacros->value_size); + strncpy((char*)pmacros->value, + (char*)token, + pmacros->value_size - 1); + strncat((char*)pmacros->value, "\n", + pmacros->value_size + - strlen((char*)pmacros + ->value) + - 1); + } + skip_macro_read:; + } + else if (state & ST_FOOTNOTE_TEXT) + { + skip_eol = TRUE; + if (!read_yaml_macros_and_links) + goto skip_footnote_read; + if (!footnotes) + { + footnote_count = 1; + CALLOC(footnotes, KeyValue, + footnote_count); + pfootnotes = footnotes; + } + if (pfootnotes->value) + { + size_t value_len = strlen( + (char*)pfootnotes->value); + + if (pfootnotes->value_size + < value_len + token_len + 1) + { + pfootnotes->value_size + += BUFSIZE; + REALLOC(pfootnotes->value, u8, + pfootnotes->value_size); + } + strncat((char*)pfootnotes->value, + (char*)token, + pfootnotes->value_size + - value_len - 1); + strncat((char*)pfootnotes->value, "\n", + pfootnotes->value_size + - strlen( + (char*)pfootnotes + ->value) + - 1); + } + else + { + pfootnotes->value_size = BUFSIZE; + CALLOC(pfootnotes->value, u8, + pfootnotes->value_size); + strncpy((char*)pfootnotes->value, + (char*)token, + pfootnotes->value_size - 1); + strncat((char*)pfootnotes->value, "\n", + pfootnotes->value_size + - strlen( + (char*)pfootnotes + ->value) + - 1); + } + skip_footnote_read:; + } + else if (ANY(state, + ST_LINK_SECOND_ARG + | ST_LINK_SECOND_ARG_END)) + { + if (read_yaml_macros_and_links) + { + if (!links) + { + links_count = 1; + CALLOC(links, KeyValue, + links_count); + plinks = links; + } + plinks->value_size + = strlen((char*)token) + 1; + CALLOC(plinks->value, u8, + plinks->value_size); + strncpy((char*)plinks->value, + (char*)token, + plinks->value_size - 1); + } + } + else if (state & ST_HEADING) + { + state &= ~(ST_HEADING | ST_HEADING_TEXT); + if (!read_yaml_macros_and_links) + { + process_heading( + token, output, heading_level); + output_firstcol = FALSE; + } + first_line_in_doc = FALSE; + RESET_TOKEN(token, ptoken, token_size); + heading_level = 0; + } + else if (!ANY(state, + ST_DISPLAY_FORMULA | ST_FORMULA + | ST_IMAGE | ST_LINK)) + process_text_token(line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + output, &token, &ptoken, &token_size, + TRUE); + } + + if (!ANY(state, + ST_PRE | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG + | ST_LINK_SECOND_ARG_END) + && (!*pbuffer || *pbuffer == '\n')) + { + if (state & ST_PARA_OPEN) + { + if (!read_yaml_macros_and_links) + { + print_output(output, "</p>"); + output_firstcol = FALSE; + } + if (state & ST_LIST) + skip_eol = TRUE; + state &= ~ST_PARA_OPEN; + } + + if (!*pbuffer && (state & ST_LIST)) + { + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_list_end(output); + } + state &= ~ST_LIST; + } + + if (!*pbuffer && (state & ST_NUMLIST)) + { + if (!read_yaml_macros_and_links) + { + process_list_item_end(output); + process_numlist_end(output); + } + state &= ~ST_NUMLIST; + } + + if (!*pbuffer && (state & ST_FOOTNOTE_TEXT)) + { + state &= ~ST_FOOTNOTE_TEXT; + + *ptoken = 0; + size_t token_len = strlen((char*)token); + + skip_eol = TRUE; + if (!read_yaml_macros_and_links) + goto skip_footnote_text_read; + if (!footnotes) + { + footnote_count = 1; + CALLOC(footnotes, KeyValue, + footnote_count); + pfootnotes = footnotes; + } + if (pfootnotes->value) + { + size_t value_len = strlen( + (char*)pfootnotes->value); + + if (pfootnotes->value_size + < value_len + token_len + 1) + { + pfootnotes->value_size + = value_len + token_len + + 1; + REALLOC(pfootnotes->value, u8, + pfootnotes->value_size); + } + strncat((char*)pfootnotes->value, + (char*)token, + pfootnotes->value_size - 1); + } + else + { + pfootnotes->value_size = token_len + 1; + CALLOC(pfootnotes->value, u8, + pfootnotes->value_size); + strncpy((char*)pfootnotes->value, + (char*)token, + pfootnotes->value_size - 1); + } + skip_footnote_text_read:; + } + } + + if (state & ST_BLOCKQUOTE && (!*pbuffer || *pbuffer != '>')) + { + state &= ~ST_BLOCKQUOTE; + process_blockquote(output, TRUE); + output_firstcol = FALSE; + } + + if (ANY(state, ST_TABLE_HEADER | ST_TABLE_LINE) + && (!line_len || !*pbuffer)) + { + if (!read_yaml_macros_and_links) + { + warning(1, (u8*)"Malformed table"); + process_table_end(output); + output_firstcol = TRUE; + } + state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE); + } + + if ((state & ST_TABLE) && (!line_len || !*pbuffer)) + { + if (!read_yaml_macros_and_links) + { + process_table_end(output); + output_firstcol = TRUE; + } + state &= ~ST_TABLE; + } + + previous_line_blank = FALSE; + } + + if (!skip_eol && !keep_token && !read_yaml_macros_and_links + && !ANY(state, + ST_YAML | ST_YAML_VAL | ST_LINK_SECOND_ARG + | ST_LINK_SECOND_ARG_END)) + { + print_output(output, "\n"); + output_firstcol = TRUE; + } + + if (!keep_token) + RESET_TOKEN(token, ptoken, token_size); + + if (!skip_change_first_line_in_doc && !(state & ST_YAML)) + first_line_in_doc = FALSE; + + skip_change_first_line_in_doc = FALSE; + if (line_len == 0) + previous_line_blank = TRUE; + + /* Lasts until the end of line */ + state &= ~(ST_YAML_VAL | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG + | ST_LINK_SECOND_ARG_END); + + if (pbuffer && *pbuffer) + goto do_buffer; +done_buffer: + if (!read_yaml_macros_and_links + && (footnote_count > 0 || inline_footnote_count > 0)) + end_footnotes(output, add_footnote_div); + + if (!read_yaml_macros_and_links && !body_only) + end_body_and_html(output); + + if (link_text) + free(link_text); + + free(link_macro); + free(token); + free(line); + + return 0; +} int main(int argc, char** argv) { - char* arg; - Command cmd = CMD_NONE; - BOOL body_only = FALSE; - int result = 0; - - basedir_size = 2; - CALLOC(basedir, char, basedir_size); - *basedir = '.'; - - while ((arg = *++argv)) - { - if (*arg == '-') - { - arg++; - char c = *arg++; - int result; - - if (c == '-') - { - if (!strcmp(arg, "version")) - cmd = CMD_VERSION; - else if (!strcmp(arg, "body-only")) - { - arg += strlen("body-only"); - body_only = TRUE; - } - else if (startswith(arg, "basedir")) - { - arg += strlen("basedir"); - result = set_basedir(arg, &basedir, &basedir_size); - if (result) - return result; - } - else if (startswith(arg, "global-link-prefix")) - { - arg += strlen("global-link-prefix"); - result = set_global_link_prefix(arg, &global_link_prefix, - &global_link_prefix_size); - if (result) - return result; - } - else if (!strcmp(arg, "help")) - return usage(); - else - { - error(EINVAL, (u8*)"Invalid argument: --%s", arg); - return usage(); - } - } - else - { - switch (c) - { - case 'b': - body_only = TRUE; - break; - case 'd': - cmd = CMD_BASEDIR; - break; - case 'h': - return usage(); - break; - case 'p': - cmd = CMD_LINK_PREFIX; - break; - case 'v': - cmd = CMD_VERSION; - break; - default: - error(EINVAL, (u8*)"Invalid argument: -%c", c); - return usage(); - } - } - } - else - { - int result; - - if (cmd == CMD_BASEDIR) - { - result = set_basedir(arg, &basedir, &basedir_size); - if (result) - return result; - } - else - input_filename = arg; - cmd = CMD_NONE; - } - } - - if (cmd == CMD_BASEDIR) - return error(1, (u8*)"-d: Argument required"); - - if (cmd == CMD_VERSION) - return version(); - - FILE* input = NULL; - FILE* output = stdout; - u8* buffer = NULL; - size_t buffer_size = 0; - - if (input_filename) - read_file_into_buffer(&buffer, &buffer_size, input_filename, &input_dirname, &input); - else - { - u8* bufline = NULL; - size_t buffer_len = 0; - size_t buffer_size = 0; - size_t bufline_len = 0; - - input = stdin; - - CALLOC(bufline, u8, BUFSIZE); - buffer_size = BUFSIZE; - CALLOC(buffer, u8, buffer_size); - - while (!feof(input)) - { - u8* eol = NULL; - if (!fgets((char*)bufline, BUFSIZE-1, input)) - break; - eol = (u8*)strchr((char*)bufline, '\n'); - if (eol) - *(eol+1) = 0; - bufline_len = strlen((char*)bufline); - if (buffer_len + bufline_len + 1 > buffer_size) - { - buffer_size += BUFSIZE; - REALLOC(buffer, u8, buffer_size); - } - strncat((char*)buffer, (char*)bufline, buffer_size-buffer_len-1); - buffer_len += bufline_len; - } - free(bufline); - } - - vars = NULL; - vars_count = 0; - macros = NULL; - macros_count = 0; - links = NULL; - links_count = 0; - footnotes = NULL; - footnote_count = 0; - inline_footnotes = NULL; - inline_footnote_count = 0; - - /* First pass: read YAML, macros and links */ - result = slweb_parse(buffer, output, body_only, TRUE); - - if (result) - { - slweb_cleanup(); - free(buffer); - return result; - } - - state = ST_NONE; - current_footnote = 0; - current_inline_footnote = 0; - - /* Second pass: parse and output */ - result = slweb_parse(buffer, output, body_only, FALSE); - - slweb_cleanup(); - free(buffer); - - return result; -} + char* arg; + Command cmd = CMD_NONE; + BOOL body_only = FALSE; + int result = 0; + + basedir_size = 2; + CALLOC(basedir, char, basedir_size); + *basedir = '.'; + + while ((arg = *++argv)) + { + if (*arg == '-') + { + arg++; + char c = *arg++; + int result; + + if (c == '-') + { + if (!strcmp(arg, "version")) + cmd = CMD_VERSION; + else if (!strcmp(arg, "body-only")) + { + arg += strlen("body-only"); + body_only = TRUE; + } + else if (startswith(arg, "basedir")) + { + arg += strlen("basedir"); + result = set_basedir( + arg, &basedir, &basedir_size); + if (result) + return result; + } + else if (startswith(arg, "global-link-prefix")) + { + arg += strlen("global-link-prefix"); + result = set_global_link_prefix(arg, + &global_link_prefix, + &global_link_prefix_size); + if (result) + return result; + } + else if (!strcmp(arg, "help")) + return usage(); + else + { + error(EINVAL, + (u8*)"Invalid argument: --%s", + arg); + return usage(); + } + } + else + { + switch (c) + { + case 'b': + body_only = TRUE; + break; + case 'd': + cmd = CMD_BASEDIR; + break; + case 'h': + return usage(); + break; + case 'p': + cmd = CMD_LINK_PREFIX; + break; + case 'v': + cmd = CMD_VERSION; + break; + default: + error(EINVAL, + (u8*)"Invalid argument: -%c", + c); + return usage(); + } + } + } + else + { + int result; + if (cmd == CMD_BASEDIR) + { + result = set_basedir( + arg, &basedir, &basedir_size); + if (result) + return result; + } + else + input_filename = arg; + cmd = CMD_NONE; + } + } + + if (cmd == CMD_BASEDIR) + return error(1, (u8*)"-d: Argument required"); + + if (cmd == CMD_VERSION) + return version(); + + FILE* input = NULL; + FILE* output = stdout; + u8* buffer = NULL; + size_t buffer_size = 0; + + if (input_filename) + read_file_into_buffer(&buffer, &buffer_size, input_filename, + &input_dirname, &input); + else + { + u8* bufline = NULL; + size_t buffer_len = 0; + size_t buffer_size = 0; + size_t bufline_len = 0; + + input = stdin; + + CALLOC(bufline, u8, BUFSIZE); + buffer_size = BUFSIZE; + CALLOC(buffer, u8, buffer_size); + + while (!feof(input)) + { + u8* eol = NULL; + if (!fgets((char*)bufline, BUFSIZE - 1, input)) + break; + eol = (u8*)strchr((char*)bufline, '\n'); + if (eol) + *(eol + 1) = 0; + bufline_len = strlen((char*)bufline); + if (buffer_len + bufline_len + 1 > buffer_size) + { + buffer_size += BUFSIZE; + REALLOC(buffer, u8, buffer_size); + } + strncat((char*)buffer, (char*)bufline, + buffer_size - buffer_len - 1); + buffer_len += bufline_len; + } + free(bufline); + } + + vars = NULL; + vars_count = 0; + macros = NULL; + macros_count = 0; + links = NULL; + links_count = 0; + footnotes = NULL; + footnote_count = 0; + inline_footnotes = NULL; + inline_footnote_count = 0; + + /* First pass: read YAML, macros and links */ + result = slweb_parse(buffer, output, body_only, TRUE); + + if (result) + { + slweb_cleanup(); + free(buffer); + return result; + } + + state = ST_NONE; + current_footnote = 0; + current_inline_footnote = 0; + + /* Second pass: parse and output */ + result = slweb_parse(buffer, output, body_only, FALSE); + + slweb_cleanup(); + free(buffer); + + return result; +}