slw2gmi

Конвертор из slweb-а у Џемини
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 0b2aee9da644525ff828cb4c0adaf545f4a843b9
родитељ 2d92894a36850f6e07d2ec1cc532d322ce8e0ce4
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Sat,  4 Sep 2021 12:33:31 +0200

Updated to match slweb v0.3.12

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

Diffstat:
MTODO | 2+-
Mdefs.h | 6+++++-
Mslw2gmi.c | 7949+++++++++++++++++++++++++++++++++++++++++--------------------------------------
измењених датотека: 3, додавања: 4103(+), брисања: 3854(-)

diff --git a/TODO b/TODO @@ -11,7 +11,7 @@ [x] Remove libunistring as a dependency - < > Remove realpath as a dependency? + <x> Remove realpath as a dependency? [x] Fix leaks [x] Fix leaks in slw2gmi diff --git a/defs.h b/defs.h @@ -21,7 +21,7 @@ #include "utf8.h" #define PROGRAMNAME "slw2gmi" -#define VERSION "0.1.2" +#define VERSION "0.1.3" #define COPYRIGHTYEAR "2021" #define MADEBY_URL "https://strahinja.srht.site/slw2gmi/" @@ -44,6 +44,10 @@ typedef enum TRUE = 1 } BOOL; +#define ALL(var, mask) (((var) & (mask)) == (mask)) +#define ANY(var, mask) (((var) & (mask)) != 0) +#define IN(var, mask) ALL(var, mask) + typedef unsigned char UBYTE; typedef unsigned long ULONG; typedef unsigned long long ULLONG; diff --git a/slw2gmi.c b/slw2gmi.c @@ -2,4201 +2,4446 @@ #include "defs.h" -static size_t lineno = 0; -static size_t colno = 1; -static BOOL output_firstcol = TRUE; -static BOOL skip_para_end = FALSE; -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 BOOL skip_para_end = FALSE; +static const 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* paralinks = NULL; -static KeyValue* pparalinks = NULL; -static size_t paralinks_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* paralinks = NULL; +static KeyValue* pparalinks = NULL; +static size_t paralinks_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) 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>]" - " [-p|--global-link-prefix <URL>] [-h|--help]" - " [-v|--version] [filename]\n", PROGRAMNAME); - return 0; + printf("Usage: %s [-d|--basedir <dir>]" + " [-p|--global-link-prefix <URL>] [-h|--help]" + " [-v|--version] [filename]\n", + PROGRAMNAME); + return 0; } int -error(int code, u8* fmt, ...) +error(const int code, const 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, ...) +warning(const int code, const 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) +free_keyvalue(KeyValue** list, const 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(); +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(&paralinks, paralinks_count); + free_keyvalue(&links, links_count); + free_keyvalue(&macros, macros_count); + free_keyvalue(&vars, vars_count); + if (footnotes) + free(footnotes); + if (paralinks) + free(paralinks); + if (links) + free(links); + if (macros) + free(macros); + if (vars) + free(vars); + + return 0; +} -int -slweb_parse(u8* buffer, FILE* output, BOOL read_yaml_macros_and_links); +int slweb_parse(FILE* output, const char* source_filename, const u8* buffer, + const 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) +get_value(KeyValue* list, const size_t list_count, const u8* key, BOOL* seen) { - if (!list) - 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; + if (!list) + 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) +set_basedir(char** basedir, size_t* basedir_size, const char* arg) { - 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** global_link_prefix, + size_t* global_link_prefix_size, const char* arg) { - 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, ...) +print_output(FILE* output, const 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 (IN(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(FILE* output, const char* command, const u8* pass_arguments[], + const u8* pipe_arguments[], const 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; + + fflush(output); + 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(FILE** input, u8** buffer, size_t* buffer_size, + const char* input_filename, char** input_dirname) { - 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; + const 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 -process_heading_start(FILE* output, UBYTE heading_level, - BOOL first_line_in_doc, BOOL previous_line_blank) +process_heading_start(FILE* output, const UBYTE heading_level, + BOOL first_line_in_doc, const BOOL previous_line_blank) { - if (!output_firstcol) - print_output(output, "\n"); - if (!previous_line_blank && !first_line_in_doc) - print_output(output, "\n"); - for (size_t i = 0; i < heading_level; i++) - print_output(output, "#"); - print_output(output, " "); - output_firstcol = FALSE; - return 0; + if (!output_firstcol) + print_output(output, "\n"); + /*if (!previous_line_blank && !first_line_in_doc) + print_output(output, "\n");*/ + for (size_t i = 0; i < heading_level; i++) + print_output(output, "#"); + print_output(output, " "); + output_firstcol = FALSE; + return 0; } int -process_heading(const u8* token, FILE* output) +process_heading(FILE* output, const u8* token) { - 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\n\n", token ? (char*)token : ""); - output_firstcol = TRUE; + print_output(output, "%s\n\n", token ? (char*)token : ""); + output_firstcol = TRUE; - 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 = BUFSIZE; + 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, "Previous commit: "); - result = print_command(CMD_GIT_LOG, - (const u8**)CMD_GIT_LOG_ARGS, - (const u8**)pipe_args, output, FALSE); - print_output(output, "\n"); - output_firstcol = TRUE; + print_output(output, "Previous commit: "); + result = print_command(output, CMD_GIT_LOG, + (const u8**)CMD_GIT_LOG_ARGS, (const u8**)pipe_args, FALSE); + print_output(output, "\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) { \ - 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); } } +#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; + + 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; } 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) + 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); - 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; + (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(FILE* output, const u8* arg_token, + const BOOL read_yaml_macros_and_links, const 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((char*)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((char*)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 (IN(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((char*)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) +process_include(FILE* output, const u8* token, + const 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; - ULONG saved_state = ST_NONE; - BOOL saved_output_firstcol = TRUE; - BOOL saved_skip_para_end = FALSE; - - 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(&paralinks, paralinks_count); - free(paralinks); - paralinks = NULL; - paralinks_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; - saved_output_firstcol = output_firstcol; - saved_skip_para_end = skip_para_end; - state = ST_NONE; - output_firstcol = TRUE; - skip_para_end = FALSE; - - /* First pass: read YAML, macros and links */ - result = slweb_parse(buffer, output, TRUE); - - if (result) - { - slweb_cleanup(); - free(buffer); - return result; - } - state = ST_NONE; - output_firstcol = TRUE; - skip_para_end = FALSE; - current_footnote = 0; - current_inline_footnote = 0; - - /* Second pass: parse and output */ - result = slweb_parse(buffer, output, FALSE); - - state = saved_state; - output_firstcol = saved_output_firstcol; - skip_para_end = saved_skip_para_end; - - 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) + { + char* filename = NULL; + 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(&basedir, &basedir_size, input_dirname); + + CALLOC(filename, char, BUFSIZE); + snprintf(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; + BOOL saved_output_firstcol = TRUE; + BOOL saved_skip_para_end = FALSE; + + read_file_into_buffer(&input, &buffer, &buffer_size, + input_filename, &input_dirname); + + free_keyvalue(&links, links_count); + free(links); + links = NULL; + links_count = 0; + + free_keyvalue(&paralinks, paralinks_count); + free(paralinks); + paralinks = NULL; + paralinks_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; + saved_output_firstcol = output_firstcol; + saved_skip_para_end = skip_para_end; + state = ST_NONE; + output_firstcol = TRUE; + skip_para_end = FALSE; + + /* First pass: read YAML, macros and links */ + result = slweb_parse(output, input_filename, buffer, TRUE); + + if (result) + { + slweb_cleanup(); + free(filename); + free(buffer); + return result; + } + state = ST_NONE; + output_firstcol = TRUE; + skip_para_end = FALSE; + current_footnote = 0; + current_inline_footnote = 0; + + /* Second pass: parse and output */ + result = slweb_parse(output, input_filename, buffer, FALSE); + + state = saved_state; + output_firstcol = saved_output_firstcol; + skip_para_end = saved_skip_para_end; + + slweb_cleanup(); + free(filename); + free(buffer); + exit(result); + } + else + exit(error(1, (u8*)"Fork failed")); + + return 0; } int process_list_start(FILE* output) { - return 0; + return 0; } int process_list_item_start(FILE* output) { - fprintf(output, "%s* ", output_firstcol ? "" : "\n"); - state |= ST_PARA_OPEN; - return 0; + fprintf(output, "%s* ", output_firstcol ? "" : "\n"); + state |= ST_PARA_OPEN; + return 0; } -int -process_paralinks(const u8* link_prefix, FILE* output); +int process_paralinks(FILE* output, const u8* link_prefix); int -process_list_item_end(const u8* link_prefix, FILE* output) +process_list_item_end(FILE* output, const u8* link_prefix) { - if (state & ST_PARA_OPEN) - { - state &= ~ST_PARA_OPEN; - print_output(output, "\n\n"); - output_firstcol = TRUE; - process_paralinks(link_prefix, output); - } - return 0; + if (IN(state, ST_PARA_OPEN)) + { + state &= ~ST_PARA_OPEN; + print_output(output, "\n\n"); + output_firstcol = TRUE; + process_paralinks(output, link_prefix); + } + return 0; } int process_list_end(FILE* output) { - return 0; + return 0; } int process_numlist_start(FILE* output) { - return 0; + return 0; } int process_numlist_end(FILE* output) { - return 0; + 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, u8* title); +int process_timestamp(FILE* output, const u8* link_prefix, const char* link, + const u8* permalink_macro, const u8* date, const u8* title); int -process_incdir_subdir(const char* subdirname, const u8* link_prefix, - FILE* output, BOOL details_open, u8* macro_body) +process_incdir_subdir(FILE* output, const char* subdirname, + const u8* link_prefix, BOOL details_open, u8* macro_body) { - 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", basedir, 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; - ULONG saved_state = ST_NONE; - BOOL saved_output_firstcol = TRUE; - BOOL saved_skip_para_end = FALSE; - - 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(&paralinks, paralinks_count); - free(paralinks); - paralinks = 0; - paralinks_count = 0; - - free_keyvalue(&footnotes, footnote_count); - free(footnotes); - footnotes = 0; - 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; - saved_output_firstcol = output_firstcol; - saved_skip_para_end = skip_para_end; - state = ST_NONE; - output_firstcol = TRUE; - skip_para_end = FALSE; - - /* Read YAML, macros and links */ - result = slweb_parse(buffer, output, TRUE); - - if (result) - { - slweb_cleanup(); - free(buffer); - free(link); - free(basename); - free(abs_subdirname); - while (names_total--) - free(namelist[names_total]); - free(namelist); - return result; - } - - u8* date = get_value(vars, vars_count, (u8*)"date", NULL); - u8* title = get_value(vars, vars_count, (u8*)"title", - NULL); - if (title && date) - process_timestamp(output, link_prefix, link, NULL, date, title); - - state = saved_state; - output_firstcol = saved_output_firstcol; - skip_para_end = saved_skip_para_end; - - fflush(output); - slweb_cleanup(); - free(buffer); - free(link); - free(basename); - free(abs_subdirname); - while (names_total--) - free(namelist[names_total]); - free(namelist); - exit(result); - } - else - exit(error(1, (u8*)"Fork failed")); - - free(link); - free(basename); - pnamelist++; - names_output++; - } - - while (names_total--) - free(namelist[names_total]); - free(namelist); - free(abs_subdirname); - - return 0; + 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", basedir, 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) + { + char* filename = NULL; + set_basedir(&basedir, &basedir_size, abs_subdirname); + CALLOC(filename, char, BUFSIZE); + snprintf(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; + ULONG saved_state = ST_NONE; + BOOL saved_output_firstcol = TRUE; + BOOL saved_skip_para_end = FALSE; + + read_file_into_buffer(&input, &buffer, &buffer_size, + filename, &input_dirname); + + free_keyvalue(&links, links_count); + free(links); + links = NULL; + links_count = 0; + + free_keyvalue(&paralinks, paralinks_count); + free(paralinks); + paralinks = 0; + paralinks_count = 0; + + free_keyvalue(&footnotes, footnote_count); + free(footnotes); + footnotes = 0; + 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; + saved_output_firstcol = output_firstcol; + saved_skip_para_end = skip_para_end; + state = ST_NONE; + output_firstcol = TRUE; + skip_para_end = FALSE; + + /* Read YAML, macros and links */ + result = slweb_parse(output, filename, buffer, TRUE); + + if (result) + { + slweb_cleanup(); + free(filename); + free(buffer); + free(link); + free(basename); + free(abs_subdirname); + while (names_total--) + free(namelist[names_total]); + free(namelist); + return result; + } + + u8* date = get_value(vars, vars_count, (u8*)"date", + NULL); + u8* title = get_value(vars, vars_count, (u8*)"title", + NULL); + if (title && date) + process_timestamp(output, link_prefix, link, + NULL, date, title); + + state = saved_state; + output_firstcol = saved_output_firstcol; + skip_para_end = saved_skip_para_end; + + fflush(output); + slweb_cleanup(); + free(filename); + free(buffer); + free(link); + free(basename); + free(abs_subdirname); + while (names_total--) + free(namelist[names_total]); + free(namelist); + exit(result); + } + else + exit(error(1, (u8*)"Fork failed")); + + free(link); + free(basename); + pnamelist++; + names_output++; + } + + while (names_total--) + free(namelist[names_total]); + free(namelist); + free(abs_subdirname); + + return 0; } int -process_incdir(u8* token, const u8* link_prefix, FILE* output, - BOOL read_yaml_macros_and_links) +process_incdir(FILE* output, const u8* token, const u8* link_prefix, + const 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; - int current_name = 0; - struct dirent** namelist; - BOOL details_open = TRUE; - - - arg = (u8*)strtok_r((char*)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((char*)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++; - } - num = strtol((char*)arg, NULL, 10); - if (errno) - exit(error(errno, (u8*)"incdir: Invalid parameter 'num'")); - arg = (u8*)strtok_r((char*)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); - } - } - - if ((names_total = scandir(incdir, &namelist, &filter_subdirs, - &reverse_alphacompare)) < 0) - { - perror("scandir"); - exit(error(errno, (u8*)"incdir: scandir '%s' error", incdir)); - } - - current_name = 0; - print_output(output, "\n"); - if (names_total < num) - num = names_total; - while (current_name < num) - { - process_incdir_subdir(namelist[current_name]->d_name, link_prefix, - output, details_open, macro_body); - details_open = FALSE; - current_name++; - } - print_output(output, "\n"); - output_firstcol = TRUE; - - while (names_total--) - free(namelist[names_total]); - free(namelist); - free(incdir); - - 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; + int current_name = 0; + struct dirent** namelist; + BOOL details_open = TRUE; + + arg = (u8*)strtok_r((char*)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((char*)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++; + } + num = strtol((char*)arg, NULL, 10); + if (errno) + exit(error(errno, + (u8*)"incdir: Invalid parameter 'num'")); + arg = (u8*)strtok_r((char*)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); + } + } + + if ((names_total = scandir(incdir, &namelist, &filter_subdirs, + &reverse_alphacompare)) + < 0) + { + perror("scandir"); + exit(error(errno, (u8*)"incdir: scandir '%s' error", incdir)); + } + + current_name = 0; + print_output(output, "\n"); + if (names_total < num) + num = names_total; + while (current_name < num) + { + process_incdir_subdir(output, namelist[current_name]->d_name, + link_prefix, details_open, macro_body); + details_open = FALSE; + current_name++; + } + print_output(output, "\n"); + output_firstcol = TRUE; + + while (names_total--) + free(namelist[names_total]); + free(namelist); + free(incdir); + + return 0; } -BOOL -url_is_local(const char* url); +BOOL url_is_local(const char* url); int -process_timestamp(FILE* output, const u8* link_prefix, const char* link, - u8* permalink_macro, u8* date, u8* title) +process_timestamp(FILE* output, const u8* link_prefix, const char* link, + const u8* permalink_macro, const u8* date, const u8* title) { - 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((char*)NULL, "-", (char**)&ptr); - if (month) - { - day = (u8*)strtok_r((char*)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++; - } - if (title) - print_output(output, "=> %s%s%s.gmi\t%s %s\n", - url_is_local(link) && global_link_prefix - ? (const char*)global_link_prefix : "", - url_is_local(link) && link_prefix - ? (const char*)link_prefix : "", - link, formatted_date, title); - else - print_output(output, "=> %s%s%s.gmi\t%s\n", - url_is_local(link) && global_link_prefix - ? (const char*)global_link_prefix : "", - url_is_local(link) && link_prefix - ? (const char*)link_prefix : "", - link, 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) + { + month = (u8*)strtok_r((char*)NULL, "-", (char**)&ptr); + if (month) + { + day = (u8*)strtok_r((char*)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++; + } + if (title) + print_output(output, + "=> %s%s%s.gmi\t%s %s\n", + url_is_local( + link) && global_link_prefix + ? (const char*) + global_link_prefix + : "", + url_is_local(link) && link_prefix + ? (const char*)link_prefix + : "", + link, formatted_date, title); + else + print_output(output, + "=> %s%s%s.gmi\t%s\n", + url_is_local( + link) && global_link_prefix + ? (const char*) + global_link_prefix + : "", + url_is_local(link) && link_prefix + ? (const char*)link_prefix + : "", + link, formatted_date); + output_firstcol = TRUE; + } + } + } + + 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(FILE* output, const u8* token, + const BOOL read_yaml_macros_and_links, const 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 (IN(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_para_end(const u8* link_prefix, BOOL previous_line_blank, FILE* output); +int process_para_end(FILE* output, const u8* link_prefix, + const BOOL previous_line_blank); int -process_tag(u8* token, const u8* link_prefix, FILE* output, - BOOL previous_line_blank, BOOL read_yaml_macros_and_links, - BOOL* skip_eol, BOOL end_tag) +process_tag(FILE* output, const u8* token, const u8* link_prefix, + const BOOL previous_line_blank, const BOOL read_yaml_macros_and_links, + BOOL* skip_eol, const 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, "Generated by slw2gmi © %s Strahinya Radich.\n", - 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 (startswith((char*)token, "p") && !read_yaml_macros_and_links) - { - skip_para_end = TRUE; - *skip_eol = TRUE; - if (end_tag) - { - process_para_end(link_prefix, previous_line_blank, output); - output_firstcol = TRUE; - } - } - - 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, + "Generated by slw2gmi © %s Strahinya Radich.\n", + COPYRIGHTYEAR); + output_firstcol = TRUE; + } + else if (startswith((char*)token, "csv")) /* {csv} */ + { + process_csv(output, token, read_yaml_macros_and_links, end_tag); + } + else if (startswith((char*)token, "include")) /* {include} */ + { + process_include(output, token, read_yaml_macros_and_links); + *skip_eol = TRUE; + } + else if (startswith((char*)token, "incdir")) /* {incdir} */ + { + process_incdir(output, token, link_prefix, + read_yaml_macros_and_links); + *skip_eol = TRUE; + } + else if (*token == '=') /* {=macro} */ + { + process_macro(output, token, read_yaml_macros_and_links, + end_tag); + *skip_eol = TRUE; + } + else if (startswith((char*)token, "p") && !read_yaml_macros_and_links) + { + skip_para_end = TRUE; + *skip_eol = TRUE; + if (end_tag) + { + process_para_end(output, link_prefix, + previous_line_blank); + output_firstcol = TRUE; + } + } + + return 0; } int process_bold(FILE* output, BOOL end_tag) { - return 0; + return 0; } int process_italic(FILE* output, BOOL end_tag) { - return 0; + return 0; } int process_code(FILE* output, BOOL end_tag) { - return 0; + return 0; } int process_blockquote(FILE* output, BOOL end_tag) { - if (end_tag) - print_output(output, "\n\n"); - else - print_output(output, "%s>", output_firstcol ? "" : "\n"); - return 0; + if (end_tag) + print_output(output, "\n\n"); + else + print_output(output, "%s>", output_firstcol ? "" : "\n"); + return 0; } int process_kbd(FILE* output, BOOL end_tag) { - return 0; + return 0; } int process_strike(FILE* output, BOOL end_tag) { - print_output(output, "~"); - return 0; + print_output(output, "~"); + return 0; } int process_table_start(FILE* output) { - return 0; + return 0; } int process_table_header_cell(FILE* output) { - return 0; + return 0; } -int +int process_table_header_end(FILE* output) { - return 0; + return 0; } int process_table_body_start(FILE* output, BOOL start_row) { - return 0; + return 0; } int process_table_body_row_start(FILE* output) { - return 0; + return 0; } int process_table_body_cell(FILE* output) { - return 0; + return 0; } int process_table_body_row_end(FILE* output) { - return 0; + return 0; } int process_table_end(FILE* output) { - return 0; + 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) +process_inline_link(FILE* output, const u8* link_text) { - 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; + print_output(output, "%s", link_text); + return 0; } int -process_inline_link(u8* link_text, u8* link_macro_body, - u8* link_url, FILE* output) +process_link(FILE* output, const u8* link_text) { - print_output(output, "%s", link_text); - return 0; + return process_inline_link(output, link_text); } int -process_link(u8* link_text, u8* link_macro_body, u8* link_id, - FILE* output) +process_inline_image(FILE* output, const u8* image_text) { - u8* url = get_value(links, links_count, link_id, NULL); - return process_inline_link(link_text, link_macro_body, - url, output); + print_output(output, "%s ", image_text); + return 0; } int -process_inline_image(u8* image_text, u8* image_url, FILE* output, - BOOL add_link, BOOL add_figcaption) +process_image(FILE* output, const u8* image_text) { - print_output(output, "%s ", image_text); - return 0; + return process_inline_image(output, image_text); } int -process_image(u8* image_text, u8* image_id, FILE* output, - BOOL add_link, BOOL add_figcaption) +process_paralinks(FILE* output, const u8* link_prefix) { - u8* url = get_value(links, links_count, image_id, NULL); - return process_inline_image(image_text, url, output, add_link, - add_figcaption); + KeyValue* pparalink = paralinks; + if (!skip_para_end) + { + /* + *if (!output_firstcol && (paralinks_count > 0)) + * print_output(output, "\n"); + */ + + /*print_output(output, "\n");*/ + } + while (pparalink < paralinks + paralinks_count) + { + if (pparalink->key) + { + print_output(output, "=> %s%s%s\t%s\n", + url_is_local((const char*)pparalink->key) + && global_link_prefix + ? (const char*)global_link_prefix + : "", + url_is_local((const char*)pparalink->key) + && link_prefix + ? (const char*)link_prefix + : "", + pparalink->key, pparalink->value); + output_firstcol = TRUE; + } + pparalink++; + } + if (paralinks_count > 0) + print_output(output, "\n"); + + free_keyvalue(&paralinks, paralinks_count); + free(paralinks); + paralinks = NULL; + paralinks_count = 0; + pparalinks = paralinks; + + return 0; } int -process_paralinks(const u8* link_prefix, FILE* output) +process_para_end(FILE* output, const u8* link_prefix, + const BOOL previous_line_blank) { - KeyValue* pparalink = paralinks; - if (!skip_para_end) - { - /* - *if (!output_firstcol && (paralinks_count > 0)) - * print_output(output, "\n"); - */ - print_output(output, "\n"); - } - while (pparalink < paralinks + paralinks_count) - { - if (pparalink->key) - { - print_output(output, "=> %s%s%s\t%s\n", - url_is_local((const char*)pparalink->key) && global_link_prefix - ? (const char*)global_link_prefix : "", - url_is_local((const char*)pparalink->key) && link_prefix - ? (const char*)link_prefix : "", - pparalink->key, pparalink->value); - output_firstcol = TRUE; - } - pparalink++; - } - if (paralinks_count > 0) - print_output(output, "\n"); - - free_keyvalue(&paralinks, paralinks_count); - free(paralinks); - paralinks = NULL; - paralinks_count = 0; - pparalinks = paralinks; - - return 0; + if (!skip_para_end) + print_output(output, "\n"); + process_paralinks(output, link_prefix); + + return 0; } int -process_para_end(const u8* link_prefix, BOOL previous_line_blank, FILE* output) +process_line_start(FILE* output, const u8* line, const u8* link_prefix, + const BOOL first_line_in_doc, const BOOL previous_line_blank, + const BOOL read_yaml_macros_and_links, const BOOL list_para, u8** token, + u8** ptoken) { - if (!skip_para_end) - print_output(output, "\n"); - process_paralinks(link_prefix, output); + 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, + link_prefix); + process_list_end(output); + } + } - return 0; -} + if (state & ST_NUMLIST) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_numlist_end(output); + } + } -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 skip_eol, 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(link_prefix, output); - process_list_end(output); - } - } - - if (state & ST_NUMLIST) - { - state &= ~ST_NUMLIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(link_prefix, output); - process_numlist_end(output); - } - } - - if (state & ST_FOOTNOTE_TEXT) - { - if (!read_yaml_macros_and_links && (state & ST_PARA_OPEN)) - { - process_para_end(link_prefix, previous_line_blank, output); - 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, "%s", - output_firstcol && previous_line_blank ? "" : "\n"); - output_firstcol = TRUE; - skip_para_end = FALSE; - state |= ST_PARA_OPEN; - } - } - return 0; + if (state & ST_FOOTNOTE_TEXT) + { + if (!read_yaml_macros_and_links + && (state & ST_PARA_OPEN)) + { + process_para_end(output, link_prefix, + previous_line_blank); + 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, "%s", + output_firstcol && previous_line_blank + ? "" + : "\n"); + output_firstcol = TRUE; + skip_para_end = 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 skip_eol, - 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(FILE* output, const u8* line, const u8* link_prefix, + const BOOL first_line_in_doc, const BOOL previous_line_blank, + const BOOL processed_start_of_line, + const BOOL read_yaml_macros_and_links, const BOOL list_para, u8** token, + u8** ptoken, size_t* token_size, const 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, skip_eol, - 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 (!IN(state, ST_YAML)) + { + if (add_enclosing_paragraph && !processed_start_of_line) + process_line_start(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + read_yaml_macros_and_links, list_para, token, + ptoken); + **ptoken = 0; + if (**token && !read_yaml_macros_and_links + && !IN(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, "(%d)", 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, "(%d)", current_inline_footnote); + output_firstcol = FALSE; + } + + return 0; } int -process_footnote(u8* token, BOOL footnote_definition, BOOL footnote_output, - FILE* output) +process_footnote(FILE* output, const u8* token, const BOOL footnote_definition, + const BOOL footnote_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, "(%d)", 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, "(%d)", current_footnote); + output_firstcol = FALSE; + } + + return 0; } int -process_horizontal_rule(const u8* link_prefix, BOOL previous_line_blank, - FILE* output) +process_horizontal_rule(const u8* link_prefix, BOOL previous_line_blank, + FILE* output) { - /* Temporarily break paragraph as hr is para-level */ - if (state & ST_PARA_OPEN) - process_para_end(link_prefix, previous_line_blank, output); - print_output(output, "————————————————————\n"); - if (state & ST_PARA_OPEN) - print_output(output, "\n"); - return 0; + /* Temporarily break paragraph as hr is para-level */ + if (state & ST_PARA_OPEN) + process_para_end(output, link_prefix, previous_line_blank); + print_output(output, "————————————————————\n"); + if (state & ST_PARA_OPEN) + print_output(output, "\n"); + return 0; } int process_formula(FILE* output, const u8* token, BOOL display_formula) { - return 0; + return 0; } int -begin_article(FILE* output, 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, BOOL first_line_in_doc, - BOOL* previous_line_blank, const char* permalink_url) +begin_article(FILE* output, 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, BOOL first_line_in_doc, + BOOL* previous_line_blank, const char* permalink_url) { - if (title) - { - process_heading_start(output, 1, first_line_in_doc, - *previous_line_blank); - process_heading(title, output); - } + if (title) + { + process_heading_start(output, 1, first_line_in_doc, + *previous_line_blank); + process_heading(output, title); + } - if (header_text) - print_output(output, "%s\n\n", (char*)header_text); + if (header_text) + print_output(output, "%s\n\n", (char*)header_text); - if (author) - print_output(output, "%s\n\n", author); + if (author) + print_output(output, "%s\n\n", author); - if (date && input_filename) - { - char* link = strip_ext(input_filename); - char* real_link = NULL; - CALLOC(real_link, char, BUFSIZE); + if (date && input_filename) + { + char* link = strip_ext(input_filename); + char* real_link = NULL; + CALLOC(real_link, char, BUFSIZE); - if (ext_in_permalink) - strncat(link, timestamp_output_ext, BUFSIZE-strlen(link)-1); + if (ext_in_permalink) + strncat(link, timestamp_output_ext, + BUFSIZE - strlen(link) - 1); - free(real_link); - free(link); - } + free(real_link); + free(link); + } - output_firstcol = author || date || header_text || title; - *previous_line_blank = author || header_text || title ; + output_firstcol = author || date || header_text || title; + *previous_line_blank = author || header_text || title; - return 0; + return 0; } int end_footnotes(FILE* output, const u8* link_prefix, BOOL add_footnote_div, - BOOL previous_line_blank) + BOOL previous_line_blank) { - size_t footnote = 0; - - if (state & ST_PARA_OPEN) - { - output_firstcol = TRUE; - state &= ~ST_PARA_OPEN; - if (paralinks_count > 0) - process_paralinks(link_prefix, output); - } - - process_horizontal_rule(link_prefix, previous_line_blank, output); - output_firstcol = TRUE; - - for (footnote = 0; footnote < inline_footnote_count; footnote++) - { - print_output(output, "%d. %s\n\n", footnote+1, - (char*)inline_footnotes[footnote]); - output_firstcol = TRUE; - } - - KeyValue* pfootnote = footnotes; - footnote = 0; - while (pfootnote && footnote < footnote_count) - { - print_output(output, "%d. %s\n\n", footnote+1, (char*)pfootnote->value); - output_firstcol = TRUE; - pfootnote++; - footnote++; - } - - return 0; + size_t footnote = 0; + + if (state & ST_PARA_OPEN) + { + output_firstcol = TRUE; + state &= ~ST_PARA_OPEN; + if (paralinks_count > 0) + process_paralinks(output, link_prefix); + } + + process_horizontal_rule(link_prefix, previous_line_blank, output); + output_firstcol = TRUE; + + for (footnote = 0; footnote < inline_footnote_count; footnote++) + { + print_output(output, "%d. %s\n\n", footnote + 1, + (char*)inline_footnotes[footnote]); + output_firstcol = TRUE; + } + + KeyValue* pfootnote = footnotes; + footnote = 0; + while (pfootnote && footnote < footnote_count) + { + print_output(output, "%d. %s\n\n", footnote + 1, + (char*)pfootnote->value); + output_firstcol = TRUE; + pfootnote++; + footnote++; + } + + return 0; } int -slweb_parse(u8* buffer, FILE* output, BOOL read_yaml_macros_and_links) +slweb_parse(FILE* output, const char* source_filename, const u8* buffer, + const 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; - 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; - pparalinks = paralinks; - pfootnotes = footnotes; - lineno = 0; - - if (!read_yaml_macros_and_links) - begin_article(output, - 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', - first_line_in_doc, &previous_line_blank, (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 (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - 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(link_prefix, previous_line_blank, - output); - output_firstcol = TRUE; - } - 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(link_prefix, output); - process_numlist_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_LIST)) - process_list_start(output); - else - process_list_item_end(link_prefix, output); - process_list_item_start(output); - } - - 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 if (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - 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*)" "; - 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, - skip_eol, 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, " "); - } - - 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*)"~" - : (u8*)"~"; - 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, - skip_eol, 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; - } - else if (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - - if (colno == 1 - && strlen((char*)pline) > 2 - && startswith((char*)pline, "```")) - { - state ^= ST_PRE; - - if (!read_yaml_macros_and_links) - { - print_output(output, "```%s", (state & ST_PRE) ? "" : "\n\n"); - output_firstcol = TRUE; - output_firstcol = !(state & ST_PRE); - } - - /* Skip the rest of the line (language) */ - pline = NULL; - } - else if (!ANY(state, ST_PRE | ST_TAG | ST_YAML)) - { - if (!ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - /* Output existing text up to ` */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - skip_eol, read_yaml_macros_and_links, - list_para, output, &token, &ptoken, - &token_size, TRUE); - processed_start_of_line = TRUE; - } - - state ^= ST_CODE; - - pline++; - colno++; - } - else - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - } - break; - - case '#': - if (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - 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(link_prefix, output); - process_list_end(output); - } - } - - if (state & ST_NUMLIST) - { - state &= ~ST_NUMLIST; - if (!read_yaml_macros_and_links) - { - process_list_item_end(link_prefix, 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; - } - else if (state & ST_HTML_TAG) - { - 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)) - { - /* Output existing text up to __ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - skip_eol, 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)) - { - /* Output existing text up to _ */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - skip_eol, 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; - } - else if (state & ST_HTML_TAG) - { - 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(link_prefix, output); - process_numlist_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_LIST)) - process_list_start(output); - else - process_list_item_end(link_prefix, output); - process_list_item_start(output); - } - - 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; - } - else if (state & ST_HTML_TAG) - { - 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, - skip_eol, 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(link_prefix, previous_line_blank, - output); - output_firstcol = 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(link_prefix, output); - process_numlist_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_LIST)) - process_list_start(output); - else - process_list_item_end(link_prefix, output); - process_list_item_start(output); - } - - 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)) - { - /* Output existing text up to * */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - skip_eol, 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)) - - { - /* Output existing text up to * */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - skip_eol, 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) - process_heading_start(output, heading_level, - first_line_in_doc, previous_line_blank); - 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, - skip_eol, 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, "\n", - BUFSIZE-strlen((char*)token)-1); - ptoken += strlen("\n"); - output_firstcol = TRUE; - 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; - } - else if (state & ST_HTML_TAG) - { - 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, - skip_eol, read_yaml_macros_and_links, - list_para, output, &token, &ptoken, - &token_size, TRUE); - 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; - } - else if (state & ST_HTML_TAG) - { - 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, previous_line_blank, - 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; - } - else if (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - - if (strlen((char*)pline) > 1 && *(pline+1) == '|') - { - if (!ANY(state, ST_INLINE_FOOTNOTE | ST_HEADING - | ST_FOOTNOTE_TEXT | ST_LINK)) - { - /* Output existing text up to || */ - process_text_token(line, link_prefix, first_line_in_doc, - previous_line_blank, processed_start_of_line, - skip_eol, read_yaml_macros_and_links, - list_para, output, &token, &ptoken, - &token_size, TRUE); - processed_start_of_line = TRUE; - } - - state ^= ST_KBD; - - 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); - 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); - pline = NULL; - break; - - case ' ': - state |= ST_TABLE; - if (!read_yaml_macros_and_links) - process_table_body_row_start(output); - break; - - case '/': - state &= ~ST_TABLE; - process_table_end(output); - 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); - } - else if (state & ST_TABLE) - { - if (!read_yaml_macros_and_links) - process_table_body_row_start(output); - } - else - { - state |= ST_TABLE_HEADER; - if (!read_yaml_macros_and_links) - process_table_start(output); - } - - 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); - else - process_table_header_end(output); - } - 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); - else - process_table_body_row_end(output); - } - 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_CODE | ST_DISPLAY_FORMULA | ST_FORMULA - | ST_IMAGE | ST_PRE)) - { - CHECKCOPY(token, ptoken, token_size, pline); - colno++; - break; - } - - state |= ST_HTML_TAG; - pline++; - colno++; - break; - - case '>': - if (state & ST_HTML_TAG) - { - state &= ~ST_HTML_TAG; - pline++; - colno++; - break; - } - - 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; - } - else if (state & ST_HTML_TAG) - { - 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, - skip_eol, 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_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; - } - else if (state & ST_HTML_TAG) - { - 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; - } - else if (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - - if (state & ST_FOOTNOTE_TEXT) - { - if (!read_yaml_macros_and_links && (state & ST_PARA_OPEN)) - { - process_para_end(link_prefix, previous_line_blank, output); - 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, - skip_eol, 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, - skip_eol, 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_footnote_div = NULL; + u8* link_prefix = NULL; + const 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; + 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_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_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; + pparalinks = paralinks; + pfootnotes = footnotes; + lineno = 0; + + if (!read_yaml_macros_and_links) + begin_article(output, + 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', + first_line_in_doc, &previous_line_blank, + (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_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 (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + else if (colno == 1 && strlen((char*)pline) > 2 + && startswith((char*)pline, "---")) + { + skip_eol = TRUE; - if (*pline == '(') + if (!(IN(state, ST_YAML)) && lineno > 1 + && !read_yaml_macros_and_links) + { + process_horizontal_rule(link_prefix, + previous_line_blank, output); + output_firstcol = TRUE; + } + 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 (IN(state, ST_NUMLIST)) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_numlist_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(IN(state, ST_LIST))) + process_list_start(output); + else + process_list_item_end(output, + link_prefix); + process_list_item_start(output); + } + + processed_start_of_line = TRUE; + skip_eol = FALSE; + + state |= ST_LIST; + + pline += 2; + colno += 2; + } + else { - *ptoken = 0; - state |= ST_LINK_SPAN; - pline++; - colno++; + CHECKCOPY(token, ptoken, token_size, pline); + colno++; } + break; + + case ':': + if (IN(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')) { -/* - * u8* tag = (u8*)"<span>"; - * size_t tag_len = strlen(tag); - * size_t token_len = 0; - * - * *ptoken = 0; - * token_len = strlen(token); - * - * if (token_len + tag_len < BUFSIZE) - * { - * strncat((char*)token, tag, BUFSIZE-token_len-1); - * ptoken += tag_len; - * } - */ - - state |= ST_LINK_SPAN; pline++; + colno++; + } + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + 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_KBD + | 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) == '~') + { + u8* entity = (u8*)" "; + /* Handle ~~ within footnotes, headings + * and link text specially */ + if (ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + 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 - CHECKCOPY(token, ptoken, token_size, pline); + { + /* Output existing text up to ~~ */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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, " "); + } + + 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 = IN(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(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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, + (IN(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; + } + else if (state & ST_HTML_TAG) + { + 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; - } - else if (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - - if (state & ST_LINK_SPAN) - { - state &= ~ST_LINK_SPAN; - pline++; - } - else if (ANY(state, ST_LINK_SECOND_ARG | ST_IMAGE_SECOND_ARG)) - { - *ptoken = 0; - if (!read_yaml_macros_and_links) - { - paralinks_count++; - if (!paralinks) - { - CALLOC(paralinks, KeyValue, paralinks_count); - pparalinks = paralinks; - } - else - { - REALLOCARRAY(paralinks, KeyValue, paralinks_count); - pparalinks = paralinks + paralinks_count - 1; - } - pparalinks->key = (u8*)strdup((char*)token); - pparalinks->value_size = strlen((char*)link_text)+1; - pparalinks->value = (u8*)strdup((char*)link_text); - - if (output_firstcol - && ((*(line+line_len-1) == ']') - || *(line+line_len-1) == ')')) - { - skip_eol = TRUE; - skip_para_end = TRUE; - pline = NULL; - } - else - { - if (state & ST_LINK_SECOND_ARG) - process_inline_link(link_text, - get_value(macros, macros_count, - link_macro, NULL), - token, output); - else - process_inline_image(link_text, token, output, - add_image_links, add_figcaption); - output_firstcol = !*link_text; - } - } - RESET_TOKEN(token, ptoken, token_size); - state &= ~(ST_LINK | ST_LINK_SECOND_ARG - | ST_IMAGE | ST_IMAGE_SECOND_ARG); - if (pline) - 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; - } - else if (state & ST_HTML_TAG) - { - 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) - { - u8* url = get_value(links, links_count, token, NULL); - if (!url) - exit(error(EINVAL, (u8*)"get_value: Invalid argument")); - paralinks_count++; - if (!paralinks) - { - CALLOC(paralinks, KeyValue, paralinks_count); - pparalinks = paralinks; - } - else - { - REALLOCARRAY(paralinks, KeyValue, paralinks_count); - pparalinks = paralinks + paralinks_count - 1; - } - pparalinks->key = (u8*)strdup((char*)url); - pparalinks->value_size = strlen((char*)link_text)+1; - pparalinks->value = (u8*)strdup((char*)link_text); - - if (output_firstcol - && ((*(line+line_len-1) == ']') - || *(line+line_len-1) == ')')) - { - skip_eol = TRUE; - pline = NULL; - } - else - { - process_link(link_text, - get_value(macros, macros_count, - link_macro, NULL), - token, output); - output_firstcol = !*link_text; - } - } - RESET_TOKEN(token, ptoken, token_size); - state &= ~(ST_LINK | ST_LINK_SECOND_ARG); - if (pline) - { - pline++; - colno++; - } - } - else if (state & ST_IMAGE_SECOND_ARG) - { - if (!read_yaml_macros_and_links) - { - u8* url = get_value(links, links_count, token, NULL); - if (!url) - exit(error(EINVAL, (u8*)"get_value: Invalid argument")); - paralinks_count++; - if (!paralinks) - { - CALLOC(paralinks, KeyValue, paralinks_count); - pparalinks = paralinks; - } - else - { - REALLOCARRAY(paralinks, KeyValue, paralinks_count); - pparalinks = paralinks + paralinks_count - 1; - } - pparalinks->key = (u8*)strdup((char*)url); - pparalinks->value_size = strlen((char*)link_text)+1; - pparalinks->value = (u8*)strdup((char*)link_text); - - if (output_firstcol - && ((*(line+line_len-1) == ']') - || *(line+line_len-1) == ')')) - { - skip_eol = TRUE; - pline = NULL; - } - else - { - process_image(link_text, token, output, - add_image_links, add_figcaption); - output_firstcol = !*link_text; - } - } - RESET_TOKEN(token, ptoken, token_size); - state &= ~(ST_IMAGE | ST_IMAGE_SECOND_ARG); - if (pline) - { - 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; - } - else if (state & ST_HTML_TAG) - { - 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, - skip_eol, 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; - } - else if (state & ST_HTML_TAG) - { - 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; - } - else if (state & ST_HTML_TAG) - { - 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, - skip_eol, 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, - skip_eol, 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 (state & ST_HTML_TAG) - { - pline++; - colno++; - break; - } - 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(link_prefix, output); - process_list_end(output); - } - } - if (!read_yaml_macros_and_links) - { - if (!(state & ST_NUMLIST)) - process_numlist_start(output); - else - process_list_item_end(link_prefix, output); - process_list_item_start(output); - } - - 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: - if (state & ST_HTML_TAG) - { - pline++; - colno++; - } - else - { - 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, " ", - 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, " ", - 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, " ", - 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, " ", - 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, " ", - 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); - 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, - skip_eol, 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 (!*pbuffer && (state & ST_LIST)) - { - if (!read_yaml_macros_and_links) - { - process_list_item_end(link_prefix, output); - process_list_end(output); - } - state &= ~ST_LIST; - } - - if (!*pbuffer && (state & ST_NUMLIST)) - { - if (!read_yaml_macros_and_links) - { - process_list_item_end(link_prefix, output); - process_numlist_end(output); - } - state &= ~ST_NUMLIST; - } - - if (state & ST_PARA_OPEN) - { - /* - *if (skip_para_end) print_output(output, "SPE"); - *else print_output(output, "!SPE!"); - */ - if (!read_yaml_macros_and_links) - { - process_para_end(link_prefix, previous_line_blank, output); - output_firstcol = TRUE; - } - - if (state & ST_LIST) - skip_eol = TRUE; - - state &= ~ST_PARA_OPEN; - } - - 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 = TRUE; - } - - 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); - } - state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE); - } - - if ((state & ST_TABLE) && (!line_len || !*pbuffer)) - { - if (!read_yaml_macros_and_links) - process_table_end(output); - state &= ~ST_TABLE; - } - } - - 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, "%s", state & ST_PRE ? "\n" - : (output_firstcol ? "" : " ")); - output_firstcol = (state & ST_PRE) || output_firstcol; - } - - 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) - { - if (!previous_line_blank) - { - if (!read_yaml_macros_and_links - && (state & ST_PARA_OPEN)) - { - if (state & ST_LIST) - process_list_item_end(link_prefix, output); - else - { - process_para_end(link_prefix, previous_line_blank, output); - output_firstcol = TRUE; - } - } - previous_line_blank = TRUE; - } - } - else - previous_line_blank = FALSE; - - /* 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) - { - if (footnote_count > 0 || inline_footnote_count > 0) - end_footnotes(output, link_prefix, add_footnote_div, previous_line_blank); - - if (paralinks_count > 0) - process_paralinks(link_prefix, output); - - print_output(output, "\n"); - output_firstcol = TRUE; - } - - if (link_text) - free(link_text); - - free(link_macro); - free(token); - free(line); - - return 0; -} + if (colno == 1 && strlen((char*)pline) > 2 + && startswith((char*)pline, "```")) + { + state ^= ST_PRE; -int -slweb_cleanup() -{ - 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(&paralinks, paralinks_count); - free_keyvalue(&links, links_count); - free_keyvalue(&macros, macros_count); - free_keyvalue(&vars, vars_count); - if (footnotes) - free(footnotes); - if (paralinks) - free(paralinks); - if (links) - free(links); - if (macros) - free(macros); - if (vars) - free(vars); - - return 0; -} + if (!read_yaml_macros_and_links) + { + print_output(output, "```%s", + IN(state, ST_PRE) ? "" : "\n\n"); + output_firstcol = !IN(state, ST_PRE); + } -int -main(int argc, char** argv) -{ - char* arg; - Command cmd = CMD_NONE; - 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 (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 '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 if (cmd == CMD_LINK_PREFIX) - { - result = set_global_link_prefix(arg, &global_link_prefix, - &global_link_prefix_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; - paralinks = NULL; - paralinks_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, 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, FALSE); - - slweb_cleanup(); - free(buffer); - - return result; + /* Skip the rest of the line (language) */ + pline = NULL; + } + else if (!ANY(state, ST_PRE | ST_TAG | ST_YAML)) + { + if (!ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + /* Output existing text up to ` */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &token, &ptoken, &token_size, TRUE); + processed_start_of_line = TRUE; + } + + state ^= ST_CODE; + + pline++; + colno++; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + break; + + case '#': + if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + if (colno == 1 + && !(ANY(state, + ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_MACRO_BODY | ST_PRE | ST_YAML))) + { + if (IN(state, ST_LIST)) + { + state &= ~ST_LIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_list_end(output); + } + } + + if (IN(state, ST_NUMLIST)) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_numlist_end(output); + } + } + + state |= ST_HEADING; + heading_level = 1; + pline++; + colno++; + } + else if (IN(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; + } + else if (state & ST_HTML_TAG) + { + 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)) + { + /* Output existing text up to __ */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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, IN(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)) + { + /* Output existing text up to _ */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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, + IN(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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if (colno == 1 && strlen((char*)pline) > 1 + && *(pline + 1) == ' ') + { + if (IN(state, ST_NUMLIST)) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_numlist_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(IN(state, ST_LIST))) + process_list_start(output); + else + process_list_item_end(output, + link_prefix); + process_list_item_start(output); + } + + 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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + pline_len = strlen((char*)pline); + if (colno == 1 && pline_len > 1 && *(pline + 1) == '[') + { + process_line_start(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + read_yaml_macros_and_links, list_para, &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(link_prefix, + previous_line_blank, output); + output_firstcol = TRUE; + } + pline = NULL; + } + else if (colno == 1 && strlen((char*)pline) > 1 + && *(pline + 1) == ' ') + { + if (IN(state, ST_NUMLIST)) + { + state &= ~ST_NUMLIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_numlist_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(IN(state, ST_LIST))) + process_list_start(output); + else + process_list_item_end(output, + link_prefix); + process_list_item_start(output); + } + + 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)) + { + /* Output existing text up to * */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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, IN(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)) + + { + /* Output existing text up to * */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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, + IN(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 ((IN(state, ST_HEADING)) && !(IN(state, ST_HEADING_TEXT))) + { + if (!read_yaml_macros_and_links) + process_heading_start(output, heading_level, + first_line_in_doc, previous_line_blank); + state |= ST_HEADING_TEXT; + pline++; + colno++; + break; + } + + if (colno == 1 && strlen((char*)pline) > 1 + && startswith((char*)pline, " ")) + { + list_para = TRUE; + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, &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, "\n", + BUFSIZE - strlen((char*)token) - 1); + ptoken += strlen("\n"); + output_firstcol = TRUE; + pline++; + colno++; + } + else if (IN(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 (!(IN(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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if (IN(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(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, &token, + &ptoken, &token_size, TRUE); + 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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if ((IN(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 (IN(state, ST_TAG)) + { + state &= ~ST_TAG; + *ptoken = 0; + + process_tag(output, token, link_prefix, + previous_line_blank, 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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '|') + { + if (!ANY(state, + ST_INLINE_FOOTNOTE | ST_HEADING + | ST_FOOTNOTE_TEXT | ST_LINK)) + { + /* Output existing text up to || */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &token, &ptoken, &token_size, TRUE); + processed_start_of_line = TRUE; + } + + state ^= ST_KBD; + + pline += 2; + colno += 2; + } + /* Partial tables (for templating) */ + else if (!(IN(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); + 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); + pline = NULL; + break; + + case ' ': + state |= ST_TABLE; + if (!read_yaml_macros_and_links) + process_table_body_row_start(output); + break; + + case '/': + state &= ~ST_TABLE; + process_table_end(output); + pline = NULL; + break; + + default: + warning(1, (u8*)"Invalid partial table line"); + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + } + else if (!(IN(state, ST_PRE)) && colno == 1) + { + skip_eol = TRUE; + + if (IN(state, ST_TABLE_HEADER)) + { + state &= ~ST_TABLE_HEADER; + state |= ST_TABLE_LINE; + pline = NULL; + break; + } + + if (IN(state, ST_TABLE_LINE)) + { + state &= ~ST_TABLE_LINE; + state |= ST_TABLE; + if (!read_yaml_macros_and_links) + process_table_body_start(output, TRUE); + } + else if (IN(state, ST_TABLE)) + { + if (!read_yaml_macros_and_links) + process_table_body_row_start(output); + } + else + { + state |= ST_TABLE_HEADER; + if (!read_yaml_macros_and_links) + process_table_start(output); + } + + pline++; + colno++; + } + else if (IN(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); + else + process_table_header_end(output); + } + RESET_TOKEN(token, ptoken, token_size); + pline++; + colno++; + } + else if (IN(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); + else + process_table_body_row_end(output); + } + 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_CODE | ST_DISPLAY_FORMULA | ST_FORMULA + | ST_IMAGE | ST_PRE)) + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + break; + } + + state |= ST_HTML_TAG; + pline++; + colno++; + break; + + case '>': + if (IN(state, ST_HTML_TAG)) + { + state &= ~ST_HTML_TAG; + pline++; + colno++; + break; + } + + 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 + && !(IN(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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '[') + { + /* Output existing text up to ! */ + *ptoken = 0; + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, &token, + &ptoken, &token_size, TRUE); + 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; + } + else if (state & ST_HTML_TAG) + { + 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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if (IN(state, ST_FOOTNOTE_TEXT)) + { + if (!read_yaml_macros_and_links + && (IN(state, ST_PARA_OPEN))) + { + process_para_end(output, link_prefix, + previous_line_blank); + 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(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, &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 == '(') + { + *ptoken = 0; + 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 && (IN(state, ST_LINK))) + { + /* + * u8* + * tag = (u8*)"<span>"; size_t tag_len = + * strlen(tag); size_t token_len = 0; + * + * *ptoken + * = 0; token_len = strlen(token); + * + * if + * (token_len + tag_len < BUFSIZE) + * { + * strncat((char*)token, + * 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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + pline_len = strlen((char*)pline); + + if (IN(state, ST_LINK_SPAN) && pline_len > 1 + && *(pline + 1) == ']') + { + state &= ~ST_LINK_SPAN; + pline++; + } + else if (ANY(state, ST_LINK_SECOND_ARG | ST_IMAGE_SECOND_ARG)) + { + *ptoken = 0; + if (!read_yaml_macros_and_links) + { + paralinks_count++; + if (!paralinks) + { + CALLOC(paralinks, KeyValue, + paralinks_count); + pparalinks = paralinks; + } + else + { + REALLOCARRAY(paralinks, KeyValue, + paralinks_count); + pparalinks = paralinks + paralinks_count + - 1; + } + pparalinks->key = (u8*)strdup((char*)token); + pparalinks->value_size + = strlen((char*)link_text) + 1; + pparalinks->value + = (u8*)strdup((char*)link_text); + + if (output_firstcol + && ((*(line + line_len - 1) == ']') + || *(line + line_len - 1) + == ')')) + { + skip_eol = TRUE; + skip_para_end = TRUE; + pline = NULL; + } + else + { + if (IN(state, ST_LINK_SECOND_ARG)) + process_inline_link(output, + link_text); + else + process_inline_image(output, + link_text); + output_firstcol = !*link_text; + } + } + RESET_TOKEN(token, ptoken, token_size); + state &= ~(ST_LINK | ST_LINK_SECOND_ARG | ST_IMAGE + | ST_IMAGE_SECOND_ARG); + if (pline) + 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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + *ptoken = 0; + if (IN(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 (IN(state, ST_FOOTNOTE)) + { + BOOL footnote_definition = (strlen((char*)pline) > 1) + && (*(pline + 1) == ':') + && footnote_at_line_start; + + process_footnote(output, token, + footnote_definition + && read_yaml_macros_and_links, + !footnote_definition + && !read_yaml_macros_and_links); + + 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 (IN(state, ST_LINK_SECOND_ARG)) + { + if (!read_yaml_macros_and_links) + { + u8* url = get_value(links, links_count, token, + NULL); + if (!url) + exit(error(EINVAL, + (u8*)"get_" + "value: " + "Invalid " + "argumen" + "t")); + paralinks_count++; + if (!paralinks) + { + CALLOC(paralinks, KeyValue, + paralinks_count); + pparalinks = paralinks; + } + else + { + REALLOCARRAY(paralinks, KeyValue, + paralinks_count); + pparalinks = paralinks + paralinks_count + - 1; + } + pparalinks->key = (u8*)strdup((char*)url); + pparalinks->value_size + = strlen((char*)link_text) + 1; + pparalinks->value + = (u8*)strdup((char*)link_text); + + if (output_firstcol + && ((*(line + line_len - 1) == ']') + || *(line + line_len - 1) + == ')')) + { + skip_eol = TRUE; + pline = NULL; + } + else + { + process_link(output, link_text); + output_firstcol = !*link_text; + } + } + RESET_TOKEN(token, ptoken, token_size); + state &= ~(ST_LINK | ST_LINK_SECOND_ARG); + if (pline) + { + pline++; + colno++; + } + } + else if (IN(state, ST_IMAGE_SECOND_ARG)) + { + if (!read_yaml_macros_and_links) + { + u8* url = get_value(links, links_count, token, + NULL); + if (!url) + exit(error(EINVAL, + (u8*)"get_" + "value: " + "Invalid " + "argumen" + "t")); + paralinks_count++; + if (!paralinks) + { + CALLOC(paralinks, KeyValue, + paralinks_count); + pparalinks = paralinks; + } + else + { + REALLOCARRAY(paralinks, KeyValue, + paralinks_count); + pparalinks = paralinks + paralinks_count + - 1; + } + pparalinks->key = (u8*)strdup((char*)url); + pparalinks->value_size + = strlen((char*)link_text) + 1; + pparalinks->value + = (u8*)strdup((char*)link_text); + + if (output_firstcol + && ((*(line + line_len - 1) == ']') + || *(line + line_len - 1) + == ')')) + { + skip_eol = TRUE; + pline = NULL; + } + else + { + process_image(output, link_text); + output_firstcol = !*link_text; + } + } + RESET_TOKEN(token, ptoken, token_size); + state &= ~(ST_IMAGE | ST_IMAGE_SECOND_ARG); + if (pline) + { + 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 (IN(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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '[') + { + /* Output existing text up to ^[ */ + *ptoken = 0; + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, &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; + } + else if (state & ST_HTML_TAG) + { + 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; + } + else if (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + + if (strlen((char*)pline) > 1 && *(pline + 1) == '$') + { + if (IN(state, ST_FORMULA)) + exit(error(1, + (u8*)"Display formula within an" + " open formula")); + + if (!(IN(state, ST_DISPLAY_FORMULA))) + { + /* Output existing text up to $$ */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &token, &ptoken, &token_size, TRUE); + } + processed_start_of_line = TRUE; + + state ^= ST_DISPLAY_FORMULA; + + if (IN(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 (IN(state, ST_DISPLAY_FORMULA)) + exit(error(1, + (u8*)"Formula within an open" + " display formula")); + + if (!(IN(state, ST_FORMULA))) + { + /* Output existing text up to $ */ + process_text_token(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &token, &ptoken, &token_size, TRUE); + } + processed_start_of_line = TRUE; + + state ^= ST_FORMULA; + + if (IN(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 (state & ST_HTML_TAG) + { + pline++; + colno++; + break; + } + else if (colno == 1 && strlen((char*)pline) > 1 + && (*(pline + 1) == '.' || *(pline + 1) == ')')) + { + if (IN(state, ST_LIST)) + { + state &= ~ST_LIST; + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_list_end(output); + } + } + if (!read_yaml_macros_and_links) + { + if (!(IN(state, ST_NUMLIST))) + process_numlist_start(output); + else + process_list_item_end(output, + link_prefix); + process_list_item_start(output); + } + + 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: + if (state & ST_HTML_TAG) + { + pline++; + colno++; + } + else + { + CHECKCOPY(token, ptoken, token_size, pline); + colno++; + } + } + goto do_line; + +done_line: + *ptoken = 0; + + if (*token && read_yaml_macros_and_links && (IN(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, " ", token_size - token_len - 1); + ptoken++; + *ptoken = 0; + } + else + { + if (*token) + { + *ptoken = 0; + size_t token_len = strlen((char*)token); + if (IN(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, " ", + 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, " ", + pmacros->value_size + - strlen((char*)pmacros + ->value) + - 1); + } + skip_macro_read:; + } + else if (IN(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, " ", + 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, " ", + 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 (IN(state, ST_HEADING)) + { + state &= ~(ST_HEADING | ST_HEADING_TEXT); + if (!read_yaml_macros_and_links) + process_heading(output, token); + 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(output, line, link_prefix, + first_line_in_doc, previous_line_blank, + processed_start_of_line, + read_yaml_macros_and_links, list_para, + &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 (IN(state, ST_PARA_OPEN)) + { + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_list_end(output); + } + state &= ~ST_LIST; + } + + if (!*pbuffer && (state & ST_NUMLIST)) + { + if (!read_yaml_macros_and_links) + { + process_list_item_end(output, + link_prefix); + process_numlist_end(output); + } + state &= ~ST_NUMLIST; + } + + if (IN(state, ST_PARA_OPEN)) + { + /* + *if (skip_para_end) + *print_output(output, "SPE"); else + *print_output(output, "!SPE!"); + */ + if (!read_yaml_macros_and_links) + { + process_para_end(output, link_prefix, + previous_line_blank); + output_firstcol = TRUE; + } + + if (IN(state, ST_LIST)) + skip_eol = TRUE; + + state &= ~ST_PARA_OPEN; + } + + if (!*pbuffer && (IN(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 = TRUE; + } + + 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); + } + state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE); + } + + if ((state & ST_TABLE) && (!line_len || !*pbuffer)) + { + if (!read_yaml_macros_and_links) + process_table_end(output); + state &= ~ST_TABLE; + } + } + + 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, "%s", + state & ST_PRE ? "\n" : (output_firstcol ? "" : " ")); + output_firstcol = (state & ST_PRE) || output_firstcol; + } + + 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) + { + if (!previous_line_blank) + { + if (!read_yaml_macros_and_links + && (state & ST_PARA_OPEN)) + { + if (state & ST_LIST) + process_list_item_end(output, + link_prefix); + else + { + process_para_end(output, link_prefix, + previous_line_blank); + output_firstcol = TRUE; + } + } + previous_line_blank = TRUE; + } + } + else + previous_line_blank = FALSE; + + /* 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, link_prefix, add_footnote_div, + previous_line_blank); + + if (paralinks_count > 0) + process_paralinks(output, link_prefix); + + print_output(output, "\n"); + output_firstcol = TRUE; + + 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; + 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 (startswith(arg, "basedir")) + { + arg += strlen("basedir"); + result = set_basedir(&basedir, + &basedir_size, arg); + if (result) + return result; + } + else if (startswith(arg, "global-link-prefix")) + { + arg += strlen("global-link-prefix"); + result = set_global_link_prefix( + &global_link_prefix, + &global_link_prefix_size, arg); + 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 '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(&basedir, &basedir_size, + arg); + if (result) + return result; + } + else if (cmd == CMD_LINK_PREFIX) + { + result = set_global_link_prefix(&global_link_prefix, + &global_link_prefix_size, arg); + 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(&input, &buffer, &buffer_size, + input_filename, &input_dirname); + 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; + paralinks = NULL; + paralinks_count = 0; + footnotes = NULL; + footnote_count = 0; + inline_footnotes = NULL; + inline_footnote_count = 0; + + /* First pass: read YAML, macros and links */ + result = slweb_parse(output, input_filename, buffer, 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(output, input_filename, buffer, FALSE); + + slweb_cleanup(); + free(buffer); + + return result; }