чување e96e4c18ba5ebec60ca19e3d99e3ca03f2ed9d15
родитељ 9c5daccbf45b9d5aa21a474cf9b1f9d096783d1b
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Mon, 22 Jul 2024 21:01:40 +0200
slweb.c: Code cleanup; use assert; sort variables and functions
Diffstat:
| M | config.mk | | | 5 | ++--- |
| M | slweb.c | | | 4555 | ++++++++++++++++++++++++++++++++++++++----------------------------------------- |
измењених датотека: 2, додавања: 2194(+), брисања: 2366(-)
diff --git a/config.mk b/config.mk
@@ -1,10 +1,10 @@
#CC = cc
CFLAGS = -Os -Wextra -Wall -pedantic -std=c99
CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
- -D_XOPEN_SOURCE=700
+ -D_XOPEN_SOURCE=700 -DNDEBUG
# OpenBSD
#CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
-# -D_XOPEN_SOURCE=700 -D_BSD_SOURCE
+# -D_XOPEN_SOURCE=700 -D_BSD_SOURCE -DNDEBUG
HTMLS = examples/basic/index.html examples/blockquote/index.html \
examples/breakmarks/index.html \
examples/csv-tsv/index-csv.html examples/csv-tsv/index.html \
@@ -26,7 +26,6 @@ SLW_includes = examples/includes/index.slw examples/includes/bugs.slw \
examples/includes/options.slw \
examples/includes/reference.slw examples/includes/seealso.slw
-
# Derived macros
BINDIR = $(DESTDIR)$(PREFIX)/bin
DOCDIR = $(DESTDIR)$(PREFIX)/share/doc/$(PROG)
diff --git a/slweb.c b/slweb.c
@@ -24,112 +24,273 @@
#include "defs.h"
#include "version.h"
-static size_t lineno = 0;
-static size_t colno = 1;
-static int output_firstcol = 1;
-static char* input_filename = NULL;
-static char* input_dirname = NULL;
-static size_t ifn_size = 0;
-static char* basedir = NULL;
-static size_t basedir_size = 0;
-static char* incdir = NULL;
-static char* global_link_prefix = NULL;
-static size_t global_link_prefix_size = 0;
-static KeyValue* vars = NULL;
-static KeyValue* pvars = NULL;
-static size_t vars_count = 0;
-static KeyValue* macros = NULL;
-static KeyValue* pmacros = NULL;
-static size_t macros_count = 0;
-static KeyValue* links = NULL;
-static KeyValue* plinks = NULL;
-static size_t links_count = 0;
static KeyValue* footnotes = NULL;
+static KeyValue* links = NULL;
static KeyValue* pfootnotes = NULL;
-static size_t footnote_count = 0;
-static size_t current_footnote = 0;
+static KeyValue* plinks = NULL;
+static KeyValue* macros = NULL;
+static KeyValue* pmacros = NULL;
+static KeyValue* pvars = NULL;
+static KeyValue* vars = NULL;
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 u8* tsv_template = NULL;
+static char* basedir = NULL;
+static char* csv_filename = NULL;
+static char* global_link_prefix = NULL;
+static char* incdir = NULL;
+static char* input_dirname = NULL;
+static char* input_filename = NULL;
+static char* tsv_filename = NULL;
+static unsigned long long state = ST_NONE;
+static size_t basedir_size = 0;
+static size_t colno = 1;
+static size_t csv_iter = 0;
static size_t csv_template_len = 0;
static size_t csv_template_size = 0;
-static char* csv_filename = NULL;
-static long csv_iter = 0;
-static u8* tsv_template = NULL;
+static size_t current_footnote = 0;
+static size_t current_inline_footnote = 0;
+static size_t footnote_count = 0;
+static size_t global_link_prefix_size = 0;
+static size_t ifn_size = 0;
+static size_t inline_footnote_count = 0;
+static size_t lineno = 0;
+static size_t links_count = 0;
+static size_t macros_count = 0;
+static size_t tsv_iter = 0;
static size_t tsv_template_len = 0;
static size_t tsv_template_size = 0;
-static char* tsv_filename = NULL;
-static long tsv_iter = 0;
-static ULLONG state = ST_NONE;
+static size_t vars_count = 0;
static int incdir_only_summary = 0;
+static int output_firstcol = 1;
-int cleanup(void);
-int usage(void);
-int version(const int full);
+static int add_css(FILE* output, int output_yaml);
+static int error(const int code, const char* file, const char* func,
+ const int line, const char* fmt, ...);
+static u8* format_date(const u8* date_arg, const char* timestamp_format);
+static int free_keyvalue(KeyValue** list, const size_t list_count);
+static u8* get_value(KeyValue* list, const size_t list_count, const u8* key,
+ KeyValue** cursor);
+static int print_meta_var(FILE* output, u8** tsv_header, u8** tsv_register);
+static int print_output(FILE* output, const char* fmt, ...);
+static int process_horizontal_rule(FILE* output);
+static int process_incdir_subdir(FILE* output, const char* subdirname,
+ const u8* link_prefix, const int details_open, const u8* macro_body,
+ const u8* footer_permalink_text, const char* permalink_url,
+ const int ext_in_permalink, const int list_only);
+static int process_inline_image(FILE* output, const u8* image_text,
+ const u8* image_file_prefix, const u8* link_prefix, const u8* image_url,
+ const int add_link, const int add_figcaption);
+static int process_inline_stylesheet(FILE* output, const u8* css_filename,
+ int output_yaml);
+static void process_list_end(FILE* output);
+static void process_list_item_end(FILE* output);
+static void process_numlist_end(FILE* output);
+static void process_stylesheet(FILE* output, const u8* css_filename,
+ int output_yaml);
+static void process_timestamp(FILE* output, const u8* link_prefix,
+ const char* link, const u8* permalink_macro, const u8* date,
+ const int ext_in_permalink);
+static int process_tsv(FILE* output, const u8* arg_token,
+ const int read_yaml_macros_and_links, const int end_tag);
+static int process_tsv_count(FILE* output, const u8* arg_token,
+ const int read_yaml_macros_and_links, const int is_tsv);
+static void read_csv(FILE* output, const char* filename,
+ csv_callback_t callback);
+static int read_file_into_buffer(FILE** input, u8** buffer, size_t* buffer_size,
+ const char* input_filename, char** input_dirname);
+static int read_tsv(FILE* output, const char* filename, tsv_callback_t callback);
+static int reverse_alphacompare(const struct dirent** a,
+ const struct dirent** b);
+static int set_basedir(char** basedir, size_t* basedir_size, const char* arg);
+static int simple_parse_yaml_line(const u8* line, KeyValue** vars,
+ size_t* vars_count, KeyValue** pvars);
+static int slweb_parse(FILE* output, const char* source_filename,
+ const size_t sfn_size, const u8* buffer, const int body_only,
+ const int read_yaml_macros_and_links);
+static int startswith(const char* s, const char* what);
+static char* strip_ext(const char* fn, const size_t fn_size);
+static int url_is_local(const char* url);
+static int warning(const int code, const u8* fmt, ...);
-int
-version(const int full)
+static int
+add_css(FILE* output, int output_yaml)
{
- printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
- if (full)
- puts(COPYRIGHT);
+ KeyValue* pvars = vars;
+ assert(vars != NULL);
+ while (pvars < vars + vars_count)
+ {
+ assert(pvars->key != NULL);
+ if (!strcmp((char*)pvars->key, "stylesheet"))
+ process_stylesheet(output, pvars->value, output_yaml);
+ else if (!strcmp((char*)pvars->key, "inline-stylesheet"))
+ process_inline_stylesheet(output, pvars->value,
+ output_yaml);
+ pvars++;
+ }
return 0;
}
-int
-usage(void)
+static int
+all_numeric(const u8* start, const u8* end)
{
- printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n"
- "\t%s [-b | --body-only] [-d directory | --basedir directory]"
- " [-p URL | --global-link-prefix URL] [filename]\n",
- PROGRAMNAME, PROGRAMNAME);
- return 0;
+ const u8* ps = start;
+ assert((start != NULL) && (end != NULL));
+ while (ps != end)
+ {
+ if (*ps && !isdigit((const int)*ps))
+ return 0;
+ ps++;
+ }
+ return 1;
}
-int
-error(const int code, const char* file, const char* func, const int line,
- const char* fmt, ...)
+static int
+begin_article(FILE* output, const char* source_filename, const size_t sfn_size,
+ const u8* link_prefix, const int add_article_header, const u8* author,
+ const u8* title, const u8* header_text, const char* title_heading_level,
+ u8* date, const int ext_in_permalink, const char* permalink_url)
{
- 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:%s:%d: %s\n", PROGRAMNAME,
- input_filename ? input_filename : "(init/stdin)", lineno, colno,
- func, file, line, buf);
- return code;
-}
+ char* temp = NULL;
+ int save_incdir = IN(state, ST_INCDIR);
-int
-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;
+ UNUSED(add_article_header);
+ CLEAR(state, ST_INCDIR);
+
+ if (author || date || header_text || title)
+ print_output(output, "<header>\n");
+
+ if (title)
+ print_output(output, "<h%s>%s</h%s>\n",
+ title_heading_level ? title_heading_level : "2",
+ (char*)title,
+ title_heading_level ? title_heading_level : "2");
+
+ if (header_text)
+ print_output(output, "<p>%s\n", (char*)header_text);
+
+ if (author)
+ print_output(output, "<address>%s</address>\n", author);
+
+ if (date && source_filename)
+ {
+ u8* permalink_macro = NULL;
+ char* link = NULL;
+ size_t link_len;
+
+ permalink_macro = get_value(macros, macros_count,
+ (u8*)"permalink", NULL);
+ link = strip_ext((const char*)source_filename, BUFSIZE);
+ assert(sfn_size != 0);
+ link_len = strlen(link);
+ if (ext_in_permalink)
+ MEMCCPY((link + link_len), timestamp_output_ext,
+ sfn_size - link_len, temp);
+ if (permalink_url)
+ process_timestamp(output, link_prefix, permalink_url,
+ permalink_macro, date, ext_in_permalink);
+ else
+ process_timestamp(output, link_prefix, link,
+ permalink_macro, date, ext_in_permalink);
+ free(link);
+ }
+
+ if (author || date || header_text || title)
+ print_output(output, "</header>\n");
+
+ output_firstcol = author || date || header_text || title;
+
+ if (save_incdir)
+ SET(state, ST_INCDIR);
+
+ return 0;
}
-int
-free_keyvalue(KeyValue** list, const size_t list_count)
+static int
+begin_html_and_head(FILE* output)
{
- if (!list || !*list)
- return 1;
+ KeyValue* cursor = NULL;
+ KeyValue* cursor_type = NULL;
+ KeyValue* cursor_desc = NULL;
+ u8* lang = get_value(vars, vars_count, (u8*)"lang", NULL);
+ u8* site_name = get_value(vars, vars_count, (u8*)"site-name", NULL);
+ u8* site_desc = get_value(vars, vars_count, (u8*)"site-desc", NULL);
+ u8* canonical = get_value(vars, vars_count, (u8*)"canonical", NULL);
+ u8* favicon_url = get_value(vars, vars_count, (u8*)"favicon-url", NULL);
+ u8* meta = get_value(vars, vars_count, (u8*)"meta", NULL);
+ u8* feed = NULL;
+ u8* feed_type = NULL;
+ u8* feed_desc = NULL;
+ char* favicon = NULL;
+ char* filename = NULL;
- for (size_t index = 0; index < list_count; index++)
+ print_output(output,
+ "<!DOCTYPE html>\n"
+ "<html lang=\"%s\">\n"
+ "<head>\n"
+ "<title>%s</title>\n"
+ "<meta charset=\"utf-8\">\n",
+ lang ? (char*)lang : "en", site_name ? (char*)site_name : "");
+
+ CALLOC(favicon, char, BUFSIZE);
+ snprintf(favicon, BUFSIZE - 1, "%s/favicon.ico", basedir);
+ if (!access(favicon, R_OK))
+ print_output(output,
+ "<link rel=\"shortcut icon\" type=\"image/x-icon\""
+ " href=\"%s\">\n",
+ favicon_url ? (char*)favicon_url : "/favicon.ico");
+ free(favicon);
+
+ if (meta)
{
- KeyValue* current = *list + index;
- free(current->value);
- free(current->key);
+ CALLOC(filename, char, BUFSIZE);
+ snprintf(filename, BUFSIZE, "%s/%s", input_dirname, (char*)meta);
+ read_tsv(output, filename, &print_meta_var);
+ free(filename);
}
+
+ if (canonical && *canonical)
+ print_output(output, "<link rel=\"canonical\" href=\"%s\">\n",
+ (char*)canonical);
+
+ cursor = vars;
+ cursor_type = vars;
+ cursor_desc = vars;
+ do
+ {
+ feed = get_value(cursor, vars_count - (cursor - vars),
+ (u8*)"feed", &cursor);
+ feed_type = get_value(cursor_type,
+ vars_count - (cursor_type - vars), (u8*)"feed-type",
+ &cursor_type);
+ feed_desc = get_value(cursor_desc,
+ vars_count - (cursor_desc - vars), (u8*)"feed-desc",
+ &cursor_desc);
+ if (feed && *feed && feed_type && *feed_type && feed_desc
+ && *feed_desc)
+ print_output(output,
+ "<link rel=\"alternate\""
+ " type=\"%s\" href=\"%s\" "
+ "title=\"%s\">\n",
+ (char*)feed_type, (char*)feed,
+ (char*)feed_desc);
+ } while (feed && feed_type && feed_desc);
+
+ if (site_desc && *site_desc)
+ print_output(output,
+ "<meta name=\"description\" content=\"%s\">\n",
+ (char*)site_desc);
+
+ print_output(output,
+ "<meta name=\"viewport\" content=\"width=device-width,"
+ " initial-scale=1\">\n<meta name=\"generator\" "
+ "content=\"slweb\">\n");
+
+ output_firstcol = 1;
return 0;
}
-int
+static int
cleanup(void)
{
free(basedir);
@@ -149,73 +310,216 @@ cleanup(void)
return 0;
}
-int add_css(FILE* output, int output_yaml);
-u8* format_date(const u8* date_arg, const char* timestamp_format);
-int simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count,
- KeyValue** pvars);
-int slweb_parse(FILE* output, const char* source_filename,
- const size_t sfn_size, const u8* buffer, const int body_only,
- const int read_yaml_macros_and_links);
-
-int
-startswith(const char* s, const char* what)
+static int
+end_body_and_html(FILE* output)
{
- if (!s || !what)
- return 0;
+ print_output(output, "</body>\n</html>\n");
+ output_firstcol = 1;
+ return 0;
+}
- do
+static int
+end_footnotes(FILE* output, int add_footnote_div)
+{
+ size_t footnote = 0;
+
+ CLEAR(state, ST_PARA_OPEN);
+
+ if (add_footnote_div)
{
- if (*s++ != *what++)
- return 0;
- } while (*s && *what);
+ print_output(output, "<div class=\"footnotes\">\n");
+ output_firstcol = 1;
+ }
- return !*what;
+ (void)process_horizontal_rule(output);
+
+ for (footnote = 0; footnote < inline_footnote_count; footnote++)
+ {
+ print_output(output,
+ "<p id=\"inline-footnote-%d\">"
+ "<a href=\"#inline-footnote-text-%d\">%d.</a> "
+ "%s\n",
+ footnote + 1, footnote + 1, footnote + 1,
+ (char*)inline_footnotes[footnote]);
+ output_firstcol = 1;
+ }
+
+ KeyValue* pfootnote = footnotes;
+ footnote = 0;
+ while (pfootnote && footnote < footnote_count)
+ {
+ print_output(output,
+ "<p id=\"footnote-%d\">"
+ "<a href=\"#footnote-text-%d\">%d.</a> %s\n",
+ footnote + 1, footnote + 1, footnote + 1,
+ (char*)pfootnote->value);
+ output_firstcol = 1;
+ pfootnote++;
+ footnote++;
+ }
+
+ if (add_footnote_div)
+ {
+ print_output(output, "</div><!--footnotes-->\n");
+ output_firstcol = 1;
+ }
+
+ return 0;
}
-int
-endswith(const char* s, const char* what)
+static int
+end_head_start_body(FILE* output)
+{
+ print_output(output, "</head>\n<body>\n");
+ output_firstcol = 1;
+ return 0;
+}
+
+static int
+error(const int code, const char* file, const char* func, const int line,
+ const char* 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:%s:%d: %s\n", PROGRAMNAME,
+ input_filename ? input_filename : "(init/stdin)", lineno, colno,
+ func, file, line, buf);
+ return code;
+}
+
+static int
+filter_slw(const struct dirent* node)
+{
+ size_t node_len;
+ size_t slw_len;
+
+ assert(node != NULL);
+ if (((*node->d_name == '.')
+ && (!*(node->d_name + 1)
+ || ((*(node->d_name + 1) == '.')
+ && !(*(node->d_name + 2))))))
+ return 0;
+ node_len = strlen(node->d_name);
+ slw_len = strlen(".slw");
+ if (slw_len >= node_len)
+ return 0;
+ return !strcmp(node->d_name + strlen(node->d_name) - slw_len, ".slw");
+}
+
+static int
+filter_subdirs(const struct dirent* node)
{
- const char* ps = s;
- const char* pwhat = what;
+ struct stat st;
+ char* nodename = NULL;
- if (!s || !what)
+ assert(node != NULL);
+ if (((*node->d_name == '.')
+ && (!*(node->d_name + 1)
+ || ((*(node->d_name + 1) == '.')
+ && !(*(node->d_name + 2))))))
return 0;
- if (!*what)
- return 1;
+ CALLOC(nodename, char, BUFSIZE);
+ snprintf(nodename, BUFSIZE, "%s/%s", incdir, node->d_name);
- while (*ps)
- ps++;
- while (*pwhat)
- pwhat++;
+ if (lstat(nodename, &st) < 0 || !S_ISDIR(st.st_mode))
+ {
+ free(nodename);
+ return 0;
+ }
- while (ps != s && pwhat != what)
- if (*ps-- != *pwhat--)
- return 0;
+ free(nodename);
- return *ps == *pwhat && pwhat == what;
+ return 1;
}
-int
-all_numeric(const u8* start, const u8* end)
+static u8*
+format_date(const u8* date_arg, const char* timestamp_format)
{
- const u8* ps = start;
- while (ps != end)
+ u8* day = NULL;
+ u8* month = NULL;
+ u8* year = NULL;
+ u8* formatted_date = NULL;
+ u8* ptr = NULL;
+ u8* date = NULL;
+ const char* ptimestamp_format = NULL;
+ char* temp = NULL;
+ size_t formatted_date_len;
+
+ assert((date_arg != NULL) && (timestamp_format != NULL));
+ date = (u8*)strdup((const char*)date_arg);
+ if (!date)
+ goto format_date_cleanup;
+ CALLOC(formatted_date, u8, DATEBUFSIZE);
+ ptr = NULL;
+ year = (u8*)strtok_r((char*)date, "-", (char**)&ptr);
+ if (!year)
+ goto format_date_cleanup;
+ month = (u8*)strtok_r(NULL, "-", (char**)&ptr);
+ if (!month)
+ goto format_date_cleanup;
+ day = (u8*)strtok_r(NULL, "T", (char**)&ptr);
+ if (!day)
+ goto format_date_cleanup;
+ ptimestamp_format = timestamp_format;
+ formatted_date_len = 0;
+ while (*ptimestamp_format)
{
- if (*ps && !isdigit((const int)*ps))
- return 0;
- ps++;
+ if (*ptimestamp_format == 'd' || *ptimestamp_format == 'D')
+ {
+ MEMCCPY((formatted_date + formatted_date_len), day,
+ DATEBUFSIZE - formatted_date_len, temp);
+ formatted_date_len += strlen((char*)day);
+ }
+ else if (*ptimestamp_format == 'm' || *ptimestamp_format == 'M')
+ {
+ MEMCCPY((formatted_date + formatted_date_len), month,
+ DATEBUFSIZE - formatted_date_len, temp);
+ formatted_date_len += strlen((char*)month);
+ }
+ else if (*ptimestamp_format == 'y' || *ptimestamp_format == 'Y')
+ {
+ MEMCCPY((formatted_date + formatted_date_len), year,
+ DATEBUFSIZE - formatted_date_len, temp);
+ formatted_date_len += strlen((char*)year);
+ }
+ else
+ *(formatted_date + formatted_date_len++)
+ = *ptimestamp_format;
+ ptimestamp_format++;
}
- return 1;
+ return formatted_date;
+
+format_date_cleanup:
+ free(formatted_date);
+ free(date);
+ return NULL;
+}
+
+static int
+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;
+ free(current->value);
+ free(current->key);
+ }
+ return 0;
}
-u8*
+static u8*
get_value(KeyValue* list, const size_t list_count, const u8* key,
KeyValue** cursor)
{
- if (!list)
- return NULL;
KeyValue* plist = list;
+ assert((list != NULL) && (key != NULL));
while (plist < list + list_count)
{
if (!strcmp((char*)plist->key, (char*)key))
@@ -236,205 +540,53 @@ get_value(KeyValue* list, const size_t list_count, const u8* key,
return NULL;
}
-int
-set_basedir(char** basedir, size_t* basedir_size, const char* arg)
-{
- size_t arg_len;
- char* temp = NULL;
-
- if (!arg)
- exit(error(1, __FILE__, __func__, __LINE__,
- "NULL argument supplied to set_basedir"));
- if (!*arg)
- exit(error(1, __FILE__, __func__, __LINE__,
- "Argument required"));
-
- arg_len = strlen(arg);
- if (arg_len + 1 > *basedir_size)
- {
- *basedir_size = arg_len + 1;
- REALLOC(*basedir, char, *basedir_size);
- }
-
- MEMCCPY(*basedir, arg, *basedir_size, temp);
-
- return 0;
-}
+#define PIPE_READ_INDEX 0
+#define PIPE_WRITE_INDEX 1
-int
-set_global_link_prefix(char** global_link_prefix,
- size_t* global_link_prefix_size, const char* arg)
+static int
+print_command(FILE* output, const char* command, const u8* pass_arguments[],
+ const u8* pipe_arguments[], const int strip_newlines)
{
- char* temp = NULL;
- size_t arg_len;
-
- if (!arg)
- exit(error(1, __FILE__, __func__, __LINE__,
- "NULL argument supplied to set_basedir"));
- if (!*arg)
- exit(error(1, __FILE__, __func__, __LINE__,
- "Argument required"));
+ FILE* cmd_input = NULL;
+ FILE* cmd_output = NULL;
+ u8* cmd_output_line = NULL;
+ char* eol = NULL;
+ pid_t pid = 0;
+ pid_t wpid;
+ int pstatus = 0;
+ int arg_pipe_fds[2];
+ int output_pipe_fds[2];
- arg_len = strlen(arg);
- if (arg_len + 1 > *global_link_prefix_size)
+ assert((output != NULL) && (command != NULL)
+ && (pass_arguments != NULL));
+ fflush(output);
+ pipe(arg_pipe_fds);
+ pipe(output_pipe_fds);
+ pid = fork();
+ if (pid == 0)
{
- *global_link_prefix_size = arg_len + 1;
- REALLOC(*global_link_prefix, char, *global_link_prefix_size);
- }
- MEMCCPY(*global_link_prefix, arg, *global_link_prefix_size, temp);
-
- return 0;
-}
+ close(arg_pipe_fds[PIPE_WRITE_INDEX]);
+ close(output_pipe_fds[PIPE_READ_INDEX]);
-char*
-strip_ext(const char* fn, const size_t fn_size)
-{
- char* newname = NULL;
- char* pnewname = NULL;
- const char* pfn = NULL;
- char* dot = NULL;
+ dup2(arg_pipe_fds[PIPE_READ_INDEX], STDIN_FILENO);
+ dup2(output_pipe_fds[PIPE_WRITE_INDEX], STDOUT_FILENO);
- dot = strrchr(fn, '.');
+ execvp(command, (char* const*)pass_arguments);
+ exit(1);
+ }
+ else if (pid < 0)
+ exit(error(errno, __FILE__, __func__, __LINE__, "fork failed"));
- if (!dot)
- return NULL;
+ /* Parent */
+ close(arg_pipe_fds[PIPE_READ_INDEX]);
+ close(output_pipe_fds[PIPE_WRITE_INDEX]);
+ cmd_input = fdopen(arg_pipe_fds[PIPE_WRITE_INDEX], "w");
+ cmd_output = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r");
- newname = strndup(fn, fn_size);
- if (!newname)
+ if (!cmd_input || !cmd_output)
{
- perror(PROGRAMNAME ": strndup");
- exit(error(ENOMEM, __FILE__, __func__, __LINE__,
- "Allocation error"));
- }
- pnewname = newname;
- pfn = fn;
-
- while (pfn != dot && *pfn)
- *pnewname++ = *pfn++;
- *pnewname = 0;
-
- return newname;
-}
-
-int
-print_output(FILE* output, const char* fmt, ...)
-{
- u8* var_incdir_only_summary = NULL;
- char* temp = NULL;
- char buf[BUFSIZE];
- va_list args;
- int incdir_only_summary = 0;
-
- if (!output || !fmt)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "Invalid argument"));
-
- va_start(args, fmt);
-
- var_incdir_only_summary
- = get_value(vars, vars_count, (u8*)"incdir-only-summary", NULL);
- incdir_only_summary
- = var_incdir_only_summary && *var_incdir_only_summary == '1';
-
- if (IN(state, ST_INCDIR) && !IN(state, ST_SUMMARY)
- && incdir_only_summary)
- return 0;
- else 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);
- MEMCCPY(csv_template, buf, csv_template_size, temp);
- }
- else
- {
- csv_template_len = strlen((char*)csv_template);
- if (csv_template_len + buf_len > csv_template_size)
- {
- csv_template_size += BUFSIZE;
- REALLOC(csv_template, u8, csv_template_size);
- }
- MEMCCPY((csv_template + csv_template_len), buf,
- csv_template_size - csv_template_len, temp);
- }
- }
- else if (IN(state, ST_TSV_BODY))
- {
- size_t buf_len = 0;
- vsnprintf((char*)buf, BUFSIZE, fmt, args);
- buf_len = strlen(buf);
- if (!tsv_template)
- {
- tsv_template_size = BUFSIZE;
- CALLOC(tsv_template, u8, tsv_template_size);
- MEMCCPY(tsv_template, buf, tsv_template_size, temp);
- }
- else
- {
- tsv_template_len = strlen((char*)tsv_template);
- if (tsv_template_len + buf_len > tsv_template_size)
- {
- tsv_template_size += BUFSIZE;
- REALLOC(tsv_template, u8, tsv_template_size);
- }
- MEMCCPY((tsv_template + tsv_template_len), buf,
- tsv_template_size - tsv_template_len, temp);
- }
- }
- else
- vfprintf(output, fmt, args);
- va_end(args);
- return 0;
-}
-
-#define PIPE_READ_INDEX 0
-#define PIPE_WRITE_INDEX 1
-
-int
-print_command(FILE* output, const char* command, const u8* pass_arguments[],
- const u8* pipe_arguments[], const int strip_newlines)
-{
- if (!command || !pass_arguments)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "Invalid argument"));
-
- pid_t pid = 0;
- int pstatus = 0;
- int arg_pipe_fds[2];
- int output_pipe_fds[2];
-
- 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]);
-
- 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, __FILE__, __func__, __LINE__, "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)
- {
- perror("fdopen");
- exit(error(1, __FILE__, __func__, __LINE__, "fdopen failed"));
+ perror("fdopen");
+ exit(error(1, __FILE__, __func__, __LINE__, "fdopen failed"));
}
if (pipe_arguments)
@@ -448,236 +600,258 @@ print_command(FILE* output, const char* command, const u8* pass_arguments[],
}
fclose(cmd_input);
- u8* cmd_output_line = NULL;
CALLOC(cmd_output_line, u8, BUFSIZE);
while (!feof(cmd_output))
{
- char* eol = NULL;
-
if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output))
continue;
-
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 = 1;
free(cmd_output_line);
-
fclose(cmd_output);
-
kill(pid, SIGKILL);
- pid_t wpid = waitpid(pid, &pstatus, 0);
+ 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(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)
- {
- perror("fopen");
- return error(ENOENT, __FILE__, __func__, __LINE__,
- "fopen failed: %s", input_filename);
- }
-
- fstat(fileno(*input), &fs);
- if (S_ISDIR(fs.st_mode))
- return error(EISDIR, __FILE__, __func__, __LINE__,
- "Is a directory: %s", input_filename);
-
- free(*buffer);
- *buffer_size = fs.st_size + 1;
- CALLOC(*buffer, u8, *buffer_size);
- fread((void*)*buffer, 1, *buffer_size, *input);
-
- 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_tsv(FILE* output, const char* filename, tsv_callback_t callback);
+#define PRINTCSVIF(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_meta_var(FILE* output, u8** tsv_header, u8** tsv_register)
+static int
+print_csv_row(FILE* output, u8** csv_header, u8** csv_register)
{
- UNUSED(tsv_header);
- if (!*tsv_register || !*(tsv_register + 1))
- return 0;
- u8* pvalue = *(tsv_register + 1);
- size_t value_len = strlen((char*)pvalue);
- if (*pvalue == '%' && *(pvalue + value_len - 1) == '%')
+ u8* pcsv_template = csv_template;
+ unsigned char csv_state = ST_CS_NONE;
+ unsigned char num = 0;
+ unsigned char conditional_index = 0;
+ assert((output != NULL) && (csv_header != NULL)
+ && (csv_register != NULL));
+csv_template_start:
+ if (!*pcsv_template)
+ goto csv_template_done;
+ if (IN(csv_state, ST_CS_ESCAPE))
{
- u8* var_name = NULL;
- var_name = (u8*)strdup((char*)pvalue + 1);
- *(var_name + value_len - 2) = 0;
- u8* value = get_value(vars, vars_count, var_name, NULL);
-
- if (value)
- print_output(output,
- "<meta name=\"%s\" content=\"%s\">\n",
- *tsv_register, value);
-
- free(var_name);
+ PRINTCSVIF("%c", *pcsv_template);
+ CLEAR(csv_state, ST_CS_ESCAPE);
+ pcsv_template++;
+ goto csv_template_start;
}
- else
- print_output(output, "<meta name=\"%s\" content=\"%s\">\n",
- *tsv_register, *(tsv_register + 1));
- return 0;
-}
-
-int
-process_heading_start(FILE* output, const UBYTE heading_level,
- const ULONG heading_count)
-{
- print_output(output, "<h%d id=\"heading-%lu\">", heading_level,
- heading_count);
- return 0;
-}
-
-int
-process_heading(FILE* output, const u8* token, const UBYTE heading_level)
-{
- if (!token || strlen((char*)token) < 1)
- warning(1, (u8*)"Empty heading");
-
- print_output(output, "%s</h%d>", token ? (char*)token : "",
- heading_level);
-
- return 0;
-}
-
-int
-process_git_log(FILE* output)
-{
- if (!input_filename)
- return warning(1, (u8*)"Cannot use 'git-log' in stdin");
-
- u8* pipe_args[] = {NULL, NULL};
- char* basename = NULL;
- char* temp = NULL;
- size_t basename_size = BUFSIZE;
- char* slash = NULL;
- pid_t result = 0;
-
- slash = strrchr(input_filename, '/');
- CALLOC(basename, char, basename_size);
- if (slash)
- MEMCCPY(basename, slash + 1, basename_size, temp);
- else
- MEMCCPY(basename, input_filename, basename_size, temp);
- pipe_args[0] = (u8*)basename;
-
- print_output(output, "<div id=\"git-log\">\nPrevious commit:\n");
- result = print_command(output, CMD_GIT_LOG,
- (const u8**)CMD_GIT_LOG_ARGS, (const u8**)pipe_args, 0);
- print_output(output, "</div><!--git-log-->\n");
- output_firstcol = 1;
- free(basename);
-
- return result ? warning(result, (u8*)"git-log: Cannot run git") : 0;
-}
-
-int
-process_stylesheet(FILE* output, const u8* css_filename, int output_yaml)
-{
- if (output_yaml)
- print_output(output, "stylesheet: %s\n", css_filename);
- else
- print_output(output, "<link rel=\"stylesheet\" href=\"%s\">\n",
- css_filename);
- output_firstcol = 1;
+ switch (*pcsv_template)
+ {
+ case '\\':
+ SET(csv_state, ST_CS_ESCAPE);
+ pcsv_template++;
+ break;
+ case '$':
+ if (IN(csv_state, ST_CS_REGISTER))
+ {
+ PRINTCSVIF("%c", '$');
+ CLEAR(csv_state, ST_CS_REGISTER);
+ }
+ else
+ SET(csv_state, ST_CS_REGISTER);
+ pcsv_template++;
+ break;
+ case '#':
+ if (IN(csv_state, ST_CS_HEADER))
+ {
+ error(1, __FILE__, __func__, __LINE__,
+ "Invalid header register mark");
+ CLEAR(csv_state, ST_CS_REGISTER | ST_CS_HEADER);
+ }
+ else if (IN(csv_state, ST_CS_REGISTER))
+ {
+ CLEAR(csv_state, ST_CS_REGISTER);
+ SET(csv_state, ST_CS_HEADER);
+ }
+ else
+ PRINTCSVIF("%c", '#');
+ pcsv_template++;
+ break;
+ case '?':
+ if (IN(csv_state, ST_CS_COND))
+ {
+ error(1, __FILE__, __func__, __LINE__,
+ "Invalid conditional mark");
+ CLEAR(csv_state, ST_CS_COND);
+ }
+ else if (IN(csv_state, ST_CS_REGISTER))
+ {
+ CLEAR(csv_state, ST_CS_REGISTER);
+ SET(csv_state, ST_CS_COND);
+ }
+ else
+ PRINTCSVIF("%c", '?');
+ pcsv_template++;
+ break;
+ case '!':
+ if (ALL(csv_state, ST_CS_COND | ST_CS_COND_NONEMPTY))
+ {
+ CLEAR(csv_state, ST_CS_COND | ST_CS_COND_NONEMPTY);
+ SET(csv_state, ST_CS_COND_EMPTY);
+ }
+ else if (IN(csv_state, ST_CS_COND))
+ {
+ error(1, __FILE__, __func__, __LINE__,
+ "Empty conditional before/without nonempty "
+ "conditional");
+ CLEAR(csv_state, ST_CS_COND | ST_CS_COND_EMPTY);
+ }
+ else
+ PRINTCSVIF("%c", '!');
+ pcsv_template++;
+ break;
+ case '/':
+ if (ALL(csv_state, ST_CS_COND | ST_CS_COND_EMPTY))
+ CLEAR(csv_state, ST_CS_COND | ST_CS_COND_EMPTY);
+ else if (ALL(csv_state, ST_CS_COND))
+ CLEAR(csv_state, ST_CS_COND | ST_CS_COND_NONEMPTY);
+ else
+ PRINTCSVIF("%c", '/');
+ pcsv_template++;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (IN(csv_state, ST_CS_COND))
+ {
+ conditional_index = *pcsv_template - '0';
+ CLEAR(csv_state, ST_CS_COND);
+ SET(csv_state, ST_CS_COND_NONEMPTY);
+ }
+ else if (IN(csv_state, ST_CS_HEADER))
+ {
+ num = *pcsv_template - '0';
+ PRINTCSVIF("%s", csv_header[num - 1]);
+ CLEAR(csv_state, ST_CS_HEADER);
+ }
+ else if (IN(csv_state, ST_CS_REGISTER))
+ {
+ num = *pcsv_template - '0';
+ PRINTCSVIF("%s", csv_register[num - 1]);
+ CLEAR(csv_state, ST_CS_REGISTER);
+ }
+ else
+ PRINTCSVIF("%c", *pcsv_template);
+ pcsv_template++;
+ break;
+ default:
+ if (IN(csv_state, ST_CS_HEADER))
+ {
+ error(1, __FILE__, __func__, __LINE__,
+ "Invalid header register mark");
+ CLEAR(csv_state, ST_CS_HEADER);
+ }
+ else if (IN(csv_state, ST_CS_REGISTER))
+ {
+ error(1, __FILE__, __func__, __LINE__,
+ "Invalid register mark");
+ CLEAR(csv_state, ST_CS_REGISTER);
+ }
+ else
+ PRINTCSVIF("%c", *pcsv_template);
+ pcsv_template++;
+ }
+ goto csv_template_start;
+csv_template_done:
return 0;
}
-int
-process_inline_stylesheet(FILE* output, const u8* css_filename, int output_yaml)
+static int
+print_image_dimensions(FILE* output, const u8* image_file_prefix, const u8* path)
{
- FILE* css = NULL;
- u8* bufline = NULL;
- char* css_pathname = NULL;
- size_t css_filename_len = 0;
- size_t css_pathname_size = 0;
-
- if (!css_filename)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "Invalid argument"));
-
- css_filename_len = strlen((char*)css_filename);
- css_pathname_size = basedir_size + css_filename_len + 1;
+ struct stat sb;
+ FILE* cmd_output = NULL;
+ char command[BUFSIZE];
+ char cmd_output_line[BUFSIZE];
+ char image_path[BUFSIZE - strlen(CMD_IDENTIFY)];
+ char* eol = NULL;
- CALLOC(css_pathname, char, css_pathname_size);
- if ((size_t)snprintf(css_pathname, css_pathname_size, "%s/%s", basedir,
- css_filename)
- > css_pathname_size)
- warning(1, (u8*)"snprintf:%d: Overflow", __LINE__);
- if (!(css = fopen((const char*)css_pathname, "rt")))
- {
- perror("fopen");
- exit(error(ENOENT, __FILE__, __func__, __LINE__,
- "fopen failed: %s", css_pathname));
- }
+ assert((output != NULL) && (path != NULL));
+ if (!*path)
+ return 1;
+ snprintf(image_path, BUFSIZE - strlen(CMD_IDENTIFY) - 1, "%s%s%s",
+ image_file_prefix ? (char*)image_file_prefix : ".",
+ *path == '/' ? "" : "/", path);
+ errno = 0;
+ if (stat(image_path, &sb) < 0)
+ return 1;
+ snprintf(command, BUFSIZE - 1, CMD_IDENTIFY, image_path);
+ cmd_output = popen(command, "r");
+ if (!cmd_output)
+ return error(errno, __FILE__, __func__, __LINE__,
+ "popen failed");
+ if (!fgets(cmd_output_line, BUFSIZE, cmd_output))
+ goto print_dimensions_cleanup;
- if (output_yaml)
- {
- print_output(output, "inline-stylesheet: %s\n", css_filename);
- output_firstcol = 1;
- goto process_inline_stylesheet_cleanup;
- }
+ eol = strchr((char*)cmd_output_line, '\n');
+ if (eol)
+ *eol = 0;
+ print_output(output, "%s", cmd_output_line);
+ output_firstcol = 0;
- CALLOC(bufline, u8, BUFSIZE);
+print_dimensions_cleanup:
+ pclose(cmd_output);
+ return 0;
+}
- print_output(output, "<style>\n");
- while (!feof(css))
+static int
+print_meta_var(FILE* output, u8** tsv_header, u8** tsv_register)
+{
+ u8* pvalue;
+ u8* var_name = NULL;
+ size_t value_len;
+ UNUSED(tsv_header);
+ assert((output != NULL) && (tsv_header != NULL)
+ && (tsv_register != NULL));
+ if (!*tsv_register || !*(tsv_register + 1))
+ return 0;
+ pvalue = *(tsv_register + 1);
+ value_len = strlen((char*)pvalue);
+ if (*pvalue == '%' && *(pvalue + value_len - 1) == '%')
{
- if (!fgets((char*)bufline, BUFSIZE, css))
- break;
- print_output(output, "%s", bufline);
+ var_name = (u8*)strdup((char*)pvalue + 1);
+ *(var_name + value_len - 2) = 0;
+ u8* value = get_value(vars, vars_count, var_name, NULL);
+ if (value)
+ print_output(output,
+ "<meta name=\"%s\" content=\"%s\">\n",
+ *tsv_register, value);
+ free(var_name);
}
- print_output(output, "</style>\n");
- output_firstcol = 1;
-
-process_inline_stylesheet_cleanup:
- free(css_pathname);
- fclose(css);
- free(bufline);
-
+ else
+ print_output(output, "<meta name=\"%s\" content=\"%s\">\n",
+ *tsv_register, *(tsv_register + 1));
return 0;
}
@@ -690,26 +864,26 @@ process_inline_stylesheet_cleanup:
&& !*tsv_register[conditional_index - 1]) \
|| !ANY(tsv_state, \
ST_CS_COND_EMPTY | ST_CS_COND_NONEMPTY)) \
- { \
fprintf(output, format, arg); \
- } \
} while (0)
-int
+static int
print_tsv_row(FILE* output, u8** tsv_header, u8** tsv_register)
{
- u8* ptsv_template = tsv_template;
- UBYTE tsv_state = ST_CS_NONE;
- UBYTE num = 0;
- UBYTE conditional_index = 0;
+ u8* ptsv_template = tsv_template;
+ unsigned char tsv_state = ST_CS_NONE;
+ unsigned char num = 0;
+ unsigned char conditional_index = 0;
+ assert((output != NULL) && (tsv_header != NULL)
+ && (tsv_register != NULL));
tsv_template_start:
if (!*ptsv_template)
goto tsv_template_done;
if (IN(tsv_state, ST_CS_ESCAPE))
{
PRINTTSVIF("%c", *ptsv_template);
- tsv_state &= ~ST_CS_ESCAPE;
+ CLEAR(tsv_state, ST_CS_ESCAPE);
ptsv_template++;
goto tsv_template_start;
}
@@ -717,17 +891,17 @@ tsv_template_start:
switch (*ptsv_template)
{
case '\\':
- tsv_state |= ST_CS_ESCAPE;
+ SET(tsv_state, ST_CS_ESCAPE);
ptsv_template++;
break;
case '$':
if (IN(tsv_state, ST_CS_REGISTER))
{
PRINTTSVIF("%c", '$');
- tsv_state &= ~ST_CS_REGISTER;
+ CLEAR(tsv_state, ST_CS_REGISTER);
}
else
- tsv_state |= ST_CS_REGISTER;
+ SET(tsv_state, ST_CS_REGISTER);
ptsv_template++;
break;
case '#':
@@ -735,12 +909,12 @@ tsv_template_start:
{
error(1, __FILE__, __func__, __LINE__,
"Invalid header register mark");
- tsv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER);
+ CLEAR(tsv_state, ST_CS_REGISTER | ST_CS_HEADER);
}
else if (IN(tsv_state, ST_CS_REGISTER))
{
- tsv_state &= ~ST_CS_REGISTER;
- tsv_state |= ST_CS_HEADER;
+ CLEAR(tsv_state, ST_CS_REGISTER);
+ SET(tsv_state, ST_CS_HEADER);
}
else
PRINTTSVIF("%c", '#');
@@ -751,12 +925,12 @@ tsv_template_start:
{
error(1, __FILE__, __func__, __LINE__,
"Invalid conditional mark");
- tsv_state &= ~ST_CS_COND;
+ CLEAR(tsv_state, ST_CS_COND);
}
else if (IN(tsv_state, ST_CS_REGISTER))
{
- tsv_state &= ~ST_CS_REGISTER;
- tsv_state |= ST_CS_COND;
+ CLEAR(tsv_state, ST_CS_REGISTER);
+ SET(tsv_state, ST_CS_COND);
}
else
PRINTTSVIF("%c", '?');
@@ -765,15 +939,15 @@ tsv_template_start:
case '!':
if (ALL(tsv_state, ST_CS_COND | ST_CS_COND_NONEMPTY))
{
- tsv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY);
- tsv_state |= ST_CS_COND_EMPTY;
+ CLEAR(tsv_state, ST_CS_COND | ST_CS_COND_NONEMPTY);
+ SET(tsv_state, ST_CS_COND_EMPTY);
}
else if (IN(tsv_state, ST_CS_COND))
{
error(1, __FILE__, __func__, __LINE__,
"Empty conditional before/without nonempty "
"conditional");
- tsv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY);
+ CLEAR(tsv_state, ST_CS_COND | ST_CS_COND_EMPTY);
}
else
PRINTTSVIF("%c", '!');
@@ -781,9 +955,9 @@ tsv_template_start:
break;
case '/':
if (ALL(tsv_state, ST_CS_COND | ST_CS_COND_EMPTY))
- tsv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY);
+ CLEAR(tsv_state, ST_CS_COND | ST_CS_COND_EMPTY);
else if (ALL(tsv_state, ST_CS_COND))
- tsv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY);
+ CLEAR(tsv_state, ST_CS_COND | ST_CS_COND_NONEMPTY);
else
PRINTTSVIF("%c", '/');
ptsv_template++;
@@ -800,20 +974,20 @@ tsv_template_start:
if (IN(tsv_state, ST_CS_COND))
{
conditional_index = *ptsv_template - '0';
- tsv_state &= ~ST_CS_COND;
- tsv_state |= ST_CS_COND_NONEMPTY;
+ CLEAR(tsv_state, ST_CS_COND);
+ SET(tsv_state, ST_CS_COND_NONEMPTY);
}
else if (IN(tsv_state, ST_CS_HEADER))
{
num = *ptsv_template - '0';
PRINTTSVIF("%s", tsv_header[num - 1]);
- tsv_state &= ~ST_CS_HEADER;
+ CLEAR(tsv_state, ST_CS_HEADER);
}
else if (IN(tsv_state, ST_CS_REGISTER))
{
num = *ptsv_template - '0';
PRINTTSVIF("%s", tsv_register[num - 1]);
- tsv_state &= ~ST_CS_REGISTER;
+ CLEAR(tsv_state, ST_CS_REGISTER);
}
else
PRINTTSVIF("%c", *ptsv_template);
@@ -824,13 +998,13 @@ tsv_template_start:
{
error(1, __FILE__, __func__, __LINE__,
"Invalid header register mark");
- tsv_state &= ~ST_CS_HEADER;
+ CLEAR(tsv_state, ST_CS_HEADER);
}
else if (IN(tsv_state, ST_CS_REGISTER))
{
error(1, __FILE__, __func__, __LINE__,
"Invalid register mark");
- tsv_state &= ~ST_CS_REGISTER;
+ CLEAR(tsv_state, ST_CS_REGISTER);
}
else
PRINTTSVIF("%c", *ptsv_template);
@@ -841,157 +1015,128 @@ tsv_template_done:
return 0;
}
-int
-read_tsv(FILE* output, const char* filename, tsv_callback_t callback)
+static int
+print_output(FILE* output, const char* fmt, ...)
{
- FILE* tsv = NULL;
- u8* bufline = NULL;
- u8* pbufline = NULL;
- u8* token = NULL;
- u8* ptoken = NULL;
- u8* tsv_header[MAX_TSV_REGISTERS];
- u8* tsv_register[MAX_TSV_REGISTERS];
- char* ctemp = NULL;
- size_t tsv_lineno = 0;
- size_t token_size = 0;
- UBYTE current_header = 0;
- UBYTE current_register = 0;
-
- if (!callback)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "Invalid callback argument"));
+ u8* var_incdir_only_summary = NULL;
+ u8* temp = NULL;
+ char buf[BUFSIZE];
+ va_list args;
+ int incdir_only_summary = 0;
- if (!(tsv = fopen(filename, "rt")))
+ assert((output != NULL) && (fmt != NULL));
+ va_start(args, fmt);
+ var_incdir_only_summary
+ = get_value(vars, vars_count, (u8*)"incdir-only-summary", NULL);
+ incdir_only_summary
+ = var_incdir_only_summary && (*var_incdir_only_summary == '1');
+ if (IN(state, ST_INCDIR) && !IN(state, ST_SUMMARY)
+ && incdir_only_summary)
+ return 0;
+ else if (IN(state, ST_CSV_BODY))
{
- perror("fopen");
- exit(error(ENOENT, __FILE__, __func__, __LINE__,
- "fopen failed: %s", filename));
+ 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);
+ MEMCCPY(csv_template, buf, csv_template_size, temp);
+ }
+ else
+ {
+ csv_template_len = strlen((char*)csv_template);
+ ENSURE_SIZE(csv_template, temp, csv_template_size,
+ csv_template_len + buf_len + 1,
+ csv_template_len + buf_len + 1,
+ print_output_alloc_error, u8);
+ MEMCCPY((csv_template + csv_template_len), buf,
+ csv_template_size - csv_template_len, temp);
+ }
}
-
- CALLOC(bufline, u8, BUFSIZE);
- token_size = BUFSIZE;
- CALLOC(token, u8, token_size);
- for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++)
- CALLOC(tsv_header[i], u8, BUFSIZE);
- for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++)
- CALLOC(tsv_register[i], u8, BUFSIZE);
-
- while (!feof(tsv) && (!tsv_iter || tsv_lineno <= (size_t)tsv_iter))
+ else if (IN(state, ST_TSV_BODY))
{
- u8* eol = NULL;
- if (!fgets((char*)bufline, BUFSIZE, tsv))
- break;
- eol = (u8*)strchr((char*)bufline, '\n');
- if (eol)
- *eol = 0;
-
- pbufline = bufline;
- RESET_TOKEN(token, ptoken, token_size);
- current_register = 0;
- tsv_bufline_start:
- if (!*pbufline)
- goto tsv_bufline_done;
- switch (*pbufline)
- {
- case '\t':
- *ptoken = 0;
- if (tsv_lineno > 0
- && current_register < MAX_TSV_REGISTERS)
- {
- MEMCCPY(tsv_register[current_register],
- (char*)token, BUFSIZE, ctemp);
- current_register++;
- }
- else if (tsv_lineno <= 0
- && current_header < MAX_TSV_REGISTERS)
- {
- MEMCCPY(tsv_header[current_header],
- (char*)token, BUFSIZE, ctemp);
- current_header++;
- }
- RESET_TOKEN(token, ptoken, token_size);
- pbufline++;
- break;
- default:
- CHECKCOPY(token, ptoken, token_size, pbufline);
- }
- goto tsv_bufline_start;
- tsv_bufline_done:
- *ptoken = 0;
- if (tsv_lineno > 0)
+ size_t buf_len = 0;
+ vsnprintf((char*)buf, BUFSIZE, fmt, args);
+ buf_len = strlen(buf);
+ if (!tsv_template)
{
- if (current_register < MAX_TSV_REGISTERS)
- {
- MEMCCPY(tsv_register[current_register],
- (char*)token, BUFSIZE, ctemp);
- current_register++;
- }
+ tsv_template_size = BUFSIZE;
+ CALLOC(tsv_template, u8, tsv_template_size);
+ MEMCCPY(tsv_template, buf, tsv_template_size, temp);
}
else
{
- if (current_header < MAX_TSV_REGISTERS)
- {
- MEMCCPY(tsv_header[current_header],
- (char*)token, BUFSIZE, ctemp);
- current_header++;
- }
+ tsv_template_len = strlen((char*)tsv_template);
+ ENSURE_SIZE(tsv_template, temp, tsv_template_size,
+ tsv_template_len + buf_len + 1,
+ tsv_template_len + buf_len + 1,
+ print_output_alloc_error, u8);
+ MEMCCPY((tsv_template + tsv_template_len), buf,
+ tsv_template_size - tsv_template_len, temp);
}
- RESET_TOKEN(token, ptoken, token_size);
+ }
+ else
+ vfprintf(output, fmt, args);
+ va_end(args);
+ return 0;
+print_output_alloc_error:
+ exit(error(errno, __FILE__, __func__, __LINE__, "Allocation error"));
+ return 1;
+}
- if (tsv_lineno > 0 && pbufline != bufline)
- (*callback)(output, tsv_header, tsv_register);
+static int
+process_blockquote(FILE* output, const int end_tag)
+{
+ print_output(output, "<%sblockquote>", end_tag ? "/" : "");
+ return 0;
+}
- for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++)
- *tsv_register[i] = 0;
- tsv_lineno++;
- }
- fclose(tsv);
- for (UBYTE i = MAX_TSV_REGISTERS; i > 0; i--)
- free(tsv_register[i - 1]);
- for (UBYTE i = MAX_TSV_REGISTERS; i > 0; i--)
- free(tsv_header[i - 1]);
- free(token);
- free(bufline);
+static int
+process_bold(FILE* output, const int end_tag)
+{
+ print_output(output, "<%sstrong>", end_tag ? "/" : "");
+ return 0;
+}
+static int
+process_code(FILE* output, const int end_tag)
+{
+ print_output(output, "<%scode>", end_tag ? "/" : "");
return 0;
}
-int
-process_tsv(FILE* output, const u8* arg_token,
+static int
+process_csv(FILE* output, const u8* arg_token,
const int read_yaml_macros_and_links, const int end_tag)
{
- u8* saveptr = NULL;
- u8* args = NULL;
- size_t args_len;
+ u8* saveptr = NULL;
+ u8* args = NULL;
u8* args_base = NULL;
-
+ size_t args_len;
+ assert((output != NULL) && (arg_token != NULL));
if (end_tag)
{
- state &= ~ST_TSV_BODY;
-
+ CLEAR(state, ST_CSV_BODY);
if (read_yaml_macros_and_links)
return 0;
-
- read_tsv(output, tsv_filename, &print_tsv_row);
-
- free(tsv_filename);
- tsv_filename = NULL;
- free(tsv_template);
- tsv_template = NULL;
- tsv_template_size = 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 (ANY(state, ST_CSV_BODY | ST_TSV_BODY))
exit(error(1, __FILE__, __func__, __LINE__,
"Can't nest csv/tsv directives"));
-
- state |= ST_TSV_BODY;
-
+ SET(state, ST_CSV_BODY);
if (read_yaml_macros_and_links)
return 0;
-
- tsv_iter = 0;
+ csv_iter = 0;
(void)strtok_r((char*)arg_token, " ", (char**)&saveptr);
args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
if (!args)
@@ -1001,729 +1146,257 @@ process_tsv(FILE* output, const u8* arg_token,
if (*args != '"' || *(args + args_len - 1) != '"')
exit(error(EINVAL, __FILE__, __func__, __LINE__,
"First argument must be a string"));
- if (!tsv_filename)
- CALLOC(tsv_filename, char, BUFSIZE);
+ if (!csv_filename)
+ CALLOC(csv_filename, char, BUFSIZE);
args_base = (u8*)strdup((char*)args + 1);
*(args_base + strlen((char*)args_base) - 1) = 0;
- snprintf(tsv_filename, BUFSIZE, "%s/%s.tsv", input_dirname,
+ snprintf(csv_filename, BUFSIZE, "%s/%s.csv", input_dirname,
(char*)args_base);
free(args_base);
args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
if (args)
{
errno = 0;
- tsv_iter = strtol((char*)args, NULL, 10);
+ csv_iter = (size_t)strtol((char*)args, NULL, 10);
if (errno)
exit(error(errno, __FILE__, __func__, __LINE__,
"Invalid argument '%s'", args));
}
}
-
return 0;
}
-int
-process_tsv_count(FILE* output, const u8* arg_token,
- const int read_yaml_macros_and_links, const int is_tsv)
+static int
+process_footnote(FILE* output, const u8* token, const int footnote_definition,
+ const int footnote_output)
{
- FILE* tsv = NULL;
- u8* saveptr = NULL;
- u8* args = NULL;
- size_t args_len;
- u8* args_base = NULL;
- char* bufline = NULL;
- size_t tsv_lines = 0;
-
- if (read_yaml_macros_and_links)
- return 0;
-
- tsv_iter = 0;
-
- (void)strtok_r((char*)arg_token, " ", (char**)&saveptr);
- args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
- if (!args)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "Arguments required"));
- args_len = strlen((char*)args);
- if (*args != '"' || *(args + args_len - 1) != '"')
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "First argument must be a string"));
- if (!tsv_filename)
- CALLOC(tsv_filename, char, BUFSIZE);
- args_base = (u8*)strdup((char*)args + 1);
- *(args_base + strlen((char*)args_base) - 1) = 0;
- snprintf(tsv_filename, BUFSIZE, "%s/%s.%csv", input_dirname,
- (char*)args_base, is_tsv ? 't' : 'c');
- free(args_base);
-
- // output, tsv_filename, print_tsv_row
- if (!(tsv = fopen(tsv_filename, "rt")))
- {
- perror("fopen");
- exit(error(ENOENT, __FILE__, __func__, __LINE__,
- "fopen failed: %s", tsv_filename));
- }
-
- CALLOC(bufline, char, BUFSIZE);
+ char* temp = NULL;
- while (!feof(tsv))
+ assert(token != NULL);
+ current_footnote++;
+ if (footnote_definition)
{
- char* eol = NULL;
- if (!fgets(bufline, BUFSIZE, tsv))
- break;
- eol = strchr(bufline, '\n');
- if (eol)
+ footnote_count++;
+ if (footnote_count == 1 && inline_footnote_count > 0)
+ warning(1,
+ (u8*)"Both inline and regular footnotes "
+ "present");
+ else if (!footnotes)
{
- *eol = 0;
- tsv_lines++;
+ CALLOC(footnotes, KeyValue, footnote_count);
+ pfootnotes = footnotes;
+ }
+ else
+ {
+ REALLOCARRAY(footnotes, KeyValue, footnote_count);
+ pfootnotes = footnotes + footnote_count - 1;
}
+ CALLOC(pfootnotes->key, u8, KEYSIZE);
+ MEMCCPY(pfootnotes->key, (char*)token, KEYSIZE, temp);
+ pfootnotes->value = NULL;
+ pfootnotes->value_size = 0;
+ }
+ if (footnote_output)
+ {
+ print_output(output,
+ "<a href=\"#footnote-%d\" id=\"footnote-text-%d\">"
+ "<sup>%d</sup></a>",
+ current_footnote, current_footnote, current_footnote);
+ output_firstcol = 0;
}
- fclose(tsv);
-
- /* And take back one kadam for the header row */
- print_output(output, "%ld", tsv_lines - 1);
-
- free(bufline);
- free(tsv_filename);
- tsv_filename = NULL;
-
return 0;
}
-#define PRINTCSVIF(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)
+static int
+process_formula(FILE* output, const u8* token, const int display_formula)
{
- u8* pcsv_template = csv_template;
- UBYTE csv_state = ST_CS_NONE;
- UBYTE num = 0;
- UBYTE conditional_index = 0;
+ const u8* pipe_args[] = {token, NULL};
+ int result = 0;
-csv_template_start:
- if (!*pcsv_template)
- goto csv_template_done;
- if (IN(csv_state, ST_CS_ESCAPE))
+ assert(token != NULL);
+ result = print_command(output, CMD_KATEX,
+ display_formula ? (const u8**)CMD_KATEX_DISPLAY_ARGS
+ : (const u8**)CMD_KATEX_INLINE_ARGS,
+ (const u8**)pipe_args, 1);
+ if (result)
{
- PRINTCSVIF("%c", *pcsv_template);
- csv_state &= ~ST_CS_ESCAPE;
- pcsv_template++;
- goto csv_template_start;
+ print_output(output, "%s$%s$%s", display_formula ? "$" : "",
+ token, display_formula ? "$" : "");
+ output_firstcol = 0;
}
+ return result;
+}
- switch (*pcsv_template)
- {
- case '\\':
- csv_state |= ST_CS_ESCAPE;
- pcsv_template++;
- break;
- case '$':
- if (IN(csv_state, ST_CS_REGISTER))
- {
- PRINTCSVIF("%c", '$');
- csv_state &= ~ST_CS_REGISTER;
- }
- else
- csv_state |= ST_CS_REGISTER;
- pcsv_template++;
- break;
- case '#':
- if (IN(csv_state, ST_CS_HEADER))
- {
- error(1, __FILE__, __func__, __LINE__,
- "Invalid header register mark");
- csv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER);
- }
- else if (IN(csv_state, ST_CS_REGISTER))
- {
- csv_state &= ~ST_CS_REGISTER;
- csv_state |= ST_CS_HEADER;
- }
- else
- PRINTCSVIF("%c", '#');
- pcsv_template++;
- break;
- case '?':
- if (IN(csv_state, ST_CS_COND))
- {
- error(1, __FILE__, __func__, __LINE__,
- "Invalid conditional mark");
- csv_state &= ~ST_CS_COND;
- }
- else if (IN(csv_state, ST_CS_REGISTER))
- {
- csv_state &= ~ST_CS_REGISTER;
- csv_state |= ST_CS_COND;
- }
- else
- PRINTCSVIF("%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 (IN(csv_state, ST_CS_COND))
- {
- error(1, __FILE__, __func__, __LINE__,
- "Empty conditional before/without nonempty "
- "conditional");
- csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY);
- }
- else
- PRINTCSVIF("%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
- PRINTCSVIF("%c", '/');
- pcsv_template++;
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (IN(csv_state, ST_CS_COND))
- {
- conditional_index = *pcsv_template - '0';
- csv_state &= ~ST_CS_COND;
- csv_state |= ST_CS_COND_NONEMPTY;
- }
- else if (IN(csv_state, ST_CS_HEADER))
- {
- num = *pcsv_template - '0';
- PRINTCSVIF("%s", csv_header[num - 1]);
- csv_state &= ~ST_CS_HEADER;
- }
- else if (IN(csv_state, ST_CS_REGISTER))
- {
- num = *pcsv_template - '0';
- PRINTCSVIF("%s", csv_register[num - 1]);
- csv_state &= ~ST_CS_REGISTER;
- }
- else
- PRINTCSVIF("%c", *pcsv_template);
- pcsv_template++;
- break;
- default:
- if (IN(csv_state, ST_CS_HEADER))
- {
- error(1, __FILE__, __func__, __LINE__,
- "Invalid header register mark");
- csv_state &= ~ST_CS_HEADER;
- }
- else if (IN(csv_state, ST_CS_REGISTER))
- {
- error(1, __FILE__, __func__, __LINE__,
- "Invalid register mark");
- csv_state &= ~ST_CS_REGISTER;
- }
- else
- PRINTCSVIF("%c", *pcsv_template);
- pcsv_template++;
- }
- goto csv_template_start;
-csv_template_done:
- return 0;
-}
-
-int
-read_csv(FILE* output, const char* filename, csv_callback_t callback)
-{
- if (!callback)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "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 = NULL;
- char* temp = NULL;
-
- csv_delimiter = get_value(vars, vars_count, (u8*)"csv-delimiter", NULL);
-
- if (!(csv = fopen(filename, "rt")))
- {
- perror("fopen");
- exit(error(ENOENT, __FILE__, __func__, __LINE__,
- "fopen failed: %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 <= (size_t)csv_iter))
- {
- u8* eol = NULL;
- if (!fgets((char*)bufline, BUFSIZE, csv))
- break;
- eol = (u8*)strchr((char*)bufline, '\n');
- if (eol)
- *eol = 0;
-
- pbufline = bufline;
- RESET_TOKEN(token, ptoken, token_size);
- current_register = 0;
- csv_bufline_start:
- if (!*pbufline)
- goto csv_bufline_done;
- switch (*pbufline)
- {
- case '"':
- if (IN(csv_state, ST_CS_QUOTE))
- csv_state &= ~ST_CS_QUOTE;
- else
- csv_state |= ST_CS_QUOTE;
- pbufline++;
- break;
- case ';':
- case ',':
- if (IN(csv_state, ST_CS_QUOTE))
- CHECKCOPY(token, ptoken, token_size, pbufline);
- else
- {
- *ptoken = 0;
- if (csv_lineno > 0
- && current_register < MAX_CSV_REGISTERS)
- {
- MEMCCPY(csv_register[current_register],
- (char*)token, BUFSIZE, temp);
- current_register++;
- }
- else if (csv_lineno <= 0
- && current_header < MAX_CSV_REGISTERS)
- {
- MEMCCPY(csv_header[current_header],
- (char*)token, BUFSIZE, temp);
- current_header++;
- }
- RESET_TOKEN(token, ptoken, token_size);
- pbufline++;
- }
- break;
- default:
- if (IN(csv_state, ST_CS_QUOTE))
- CHECKCOPY(token, ptoken, token_size, pbufline);
- else if (csv_delimiter && *pbufline == *csv_delimiter)
- {
- *ptoken = 0;
- if (csv_lineno > 0
- && current_register < MAX_CSV_REGISTERS)
- {
- MEMCCPY(csv_register[current_register],
- (char*)token, BUFSIZE, temp);
- current_register++;
- }
- else if (csv_lineno <= 0
- && current_header < MAX_CSV_REGISTERS)
- {
- MEMCCPY(csv_header[current_header],
- (char*)token, BUFSIZE, temp);
- current_header++;
- }
- RESET_TOKEN(token, ptoken, token_size);
- pbufline++;
- }
- else
- CHECKCOPY(token, ptoken, token_size, pbufline);
- }
- goto csv_bufline_start;
- csv_bufline_done:
- *ptoken = 0;
- if (csv_lineno > 0)
- {
- if (current_register < MAX_CSV_REGISTERS)
- {
- MEMCCPY(csv_register[current_register],
- (char*)token, BUFSIZE, temp);
- current_register++;
- }
- }
- else
- {
- if (current_header < MAX_CSV_REGISTERS)
- {
- MEMCCPY(csv_header[current_header],
- (char*)token, BUFSIZE, temp);
- current_header++;
- }
- }
- 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(FILE* output, const u8* arg_token,
- const int read_yaml_macros_and_links, const int 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 (ANY(state, ST_CSV_BODY | ST_TSV_BODY))
- exit(error(1, __FILE__, __func__, __LINE__,
- "Can't nest csv/tsv directives"));
-
- state |= ST_CSV_BODY;
-
- if (read_yaml_macros_and_links)
- return 0;
-
- csv_iter = 0;
- u8* saveptr = NULL;
- u8* args = NULL;
-
- (void)strtok_r((char*)arg_token, " ", (char**)&saveptr);
- args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
- if (!args)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "Arguments required"));
- size_t args_len = strlen((char*)args);
- if (*args != '"' || *(args + args_len - 1) != '"')
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "First argument must be a string"));
- if (!csv_filename)
- CALLOC(csv_filename, char, BUFSIZE);
- u8* args_base = (u8*)strdup((char*)args + 1);
- *(args_base + strlen((char*)args_base) - 1) = 0;
- snprintf(csv_filename, BUFSIZE, "%s/%s.csv", input_dirname,
- (char*)args_base);
- free(args_base);
- args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
- if (args)
- {
- errno = 0;
- csv_iter = strtol((char*)args, NULL, 10);
- if (errno)
- exit(error(errno, __FILE__, __func__, __LINE__,
- "Invalid argument '%s'", args));
- }
- }
-
- return 0;
-}
-
-int
-process_include(FILE* output, const u8* token,
- const int read_yaml_macros_and_links)
+static int
+process_git_log(FILE* output)
{
- u8* ptoken;
- char* include_filename = NULL;
- char* pinclude_filename = NULL;
- pid_t pid = 0;
- int pstatus = 0;
- int arg_pipe_fds[2];
- int output_pipe_fds[2];
-
- ptoken = (u8*)strchr((char*)token, ' ');
+ u8* pipe_args[] = {NULL, NULL};
+ char* basename = NULL;
+ char* temp = NULL;
+ char* slash = NULL;
+ size_t basename_size = BUFSIZE;
+ pid_t result = 0;
+ assert(output != NULL);
if (!input_filename)
- return warning(1,
- (u8*)"process_include: Cannot use 'include' in stdin");
-
- if (!ptoken)
- exit(error(1, __FILE__, __func__, __LINE__,
- "Directive 'include' requires an argument"));
-
- fflush(output);
- pipe(arg_pipe_fds);
- pipe(output_pipe_fds);
- pid = fork();
-
- if (pid > 0) /* parent */
- {
- FILE* child_output = NULL;
- u8* child_output_line = NULL;
- close(arg_pipe_fds[PIPE_READ_INDEX]);
- close(output_pipe_fds[PIPE_WRITE_INDEX]);
- if (!(child_output
- = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r")))
- {
- wait(&pstatus);
- perror("fdopen");
- exit(error(1, __FILE__, __func__, __LINE__,
- "fdopen failed"));
- }
-
- CALLOC(child_output_line, u8, BUFSIZE);
- while (!feof(child_output))
- {
- char* eol = NULL;
-
- if (!fgets((char*)child_output_line, BUFSIZE,
- child_output))
- continue;
-
- eol = strchr((char*)child_output_line, '\n');
- if (eol)
- *eol = 0;
- simple_parse_yaml_line(child_output_line, &vars,
- &vars_count, &pvars);
- }
- free(child_output_line);
- fclose(child_output);
-
- wait(&pstatus);
- }
- else if (pid == 0) /* child */
- {
- char* filename = NULL;
- FILE* input = NULL;
- FILE* output = stdout;
- u8* buffer = NULL;
- size_t buffer_size = 0;
- int result = 0;
- FILE* child_output = NULL;
-
- close(arg_pipe_fds[PIPE_WRITE_INDEX]);
- close(output_pipe_fds[PIPE_READ_INDEX]);
- if (!(child_output
- = fdopen(output_pipe_fds[PIPE_WRITE_INDEX], "w")))
- {
- perror("fdopen");
- exit(error(1, __FILE__, __func__, __LINE__,
- "fdopen failed"));
- }
-
- 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);
-
- 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(&footnotes, footnote_count);
- free(footnotes);
- footnotes = NULL;
- footnote_count = 0;
- current_footnote = 0;
-
- while (inline_footnote_count--)
- free(inline_footnotes[inline_footnote_count]);
- free(inline_footnotes);
- inline_footnotes = NULL;
- inline_footnote_count = 0;
- current_inline_footnote = 0;
- state = ST_NONE;
-
- /* First pass: read YAML, macros and links */
- result = slweb_parse(output, filename, BUFSIZE, buffer, 1, 1);
-
- add_css(child_output, 1);
- if (result || read_yaml_macros_and_links)
- goto process_include_child_cleanup;
-
- state = ST_NONE;
- current_footnote = 0;
- current_inline_footnote = 0;
-
- /* Second pass: parse and output */
- result = slweb_parse(output, filename, BUFSIZE, buffer, 1, 0);
+ return warning(1, (u8*)"Cannot use 'git-log' in stdin");
- process_include_child_cleanup:
- fflush(output);
- cleanup();
- free(filename);
- free(buffer);
- fclose(child_output);
- exit(result);
- }
+ slash = strrchr(input_filename, '/');
+ CALLOC(basename, char, basename_size);
+ if (slash)
+ MEMCCPY(basename, slash + 1, basename_size, temp);
else
- exit(error(1, __FILE__, __func__, __LINE__, "fork failed"));
-
- return 0;
+ MEMCCPY(basename, input_filename, basename_size, temp);
+ pipe_args[0] = (u8*)basename;
+ print_output(output, "<div id=\"git-log\">\nPrevious commit:\n");
+ result = print_command(output, CMD_GIT_LOG,
+ (const u8**)CMD_GIT_LOG_ARGS, (const u8**)pipe_args, 0);
+ print_output(output, "</div><!--git-log-->\n");
+ output_firstcol = 1;
+ free(basename);
+ return result ? warning(result, (u8*)"git-log: Cannot run git") : 0;
}
-int
-process_list_start(FILE* output)
+static int
+process_heading(FILE* output, const u8* token, const unsigned char heading_level)
{
- print_output(output, "<ul>");
+ assert((output != NULL) && (token != NULL));
+ if (!*token)
+ warning(1, (u8*)"Empty heading");
+ print_output(output, "%s</h%hhu>", (char*)token, heading_level);
return 0;
}
-int
-process_list_item_start(FILE* output)
+static int
+process_heading_start(FILE* output, const unsigned char heading_level,
+ const size_t heading_count)
{
- print_output(output, "%s<li><p>", output_firstcol ? "" : "\n");
+ print_output(output, "<h%hhu id=\"heading-%zu\">", heading_level,
+ heading_count);
return 0;
}
-int
-process_list_item_end(FILE* output)
+static int
+process_horizontal_rule(FILE* output)
{
+ print_output(output, "<hr>\n");
if (IN(state, ST_PARA_OPEN))
- state &= ~ST_PARA_OPEN;
- print_output(output, "</li>");
- output_firstcol = 0;
- return 0;
-}
-
-int
-process_list_end(FILE* output)
-{
- print_output(output, "</ul>\n");
+ print_output(output, "<p>\n");
output_firstcol = 1;
return 0;
}
-int
-process_numlist_start(FILE* output)
+static int
+process_image(FILE* output, const u8* image_text, const u8* image_file_prefix,
+ const u8* link_prefix, const u8* image_id, const int add_link,
+ const int add_figcaption)
{
- print_output(output, "<ol>");
- output_firstcol = 0;
- return 0;
+ u8* url = get_value(links, links_count, image_id, NULL);
+ return process_inline_image(output, image_text, image_file_prefix,
+ link_prefix, url, add_link, add_figcaption);
}
-int
-process_numlist_end(FILE* output)
+static int
+process_incdir(FILE* output, const u8* token, const u8* link_prefix,
+ const u8* footer_permalink_text, const char* permalink_url,
+ const int ext_in_permalink, const int read_yaml_macros_and_links)
{
- print_output(output, "</ol>\n");
- output_firstcol = 1;
- return 0;
-}
+ struct dirent** namelist;
+ struct dirent** pnamelist;
+ u8* saveptr = NULL;
+ /* skipping the first token (incdir) */
+ u8* arg = NULL;
+ u8* macro_body = NULL;
+ u8* parg = NULL;
+ size_t arg_len = 0;
+ int names_output;
+ int num = 5;
+ int names_total = 0;
+ int details_open = 1;
+ int list_only = 0;
+ int pass;
-int
-filter_subdirs(const struct dirent* node)
-{
- if (!node
- || ((*node->d_name == '.')
- && (!*(node->d_name + 1)
- || ((*(node->d_name + 1) == '.')
- && !(*(node->d_name + 2))))))
+ assert((output != NULL) && (token != NULL));
+ if (read_yaml_macros_and_links)
return 0;
+ (void)strtok_r((char*)token, " ", (char**)&saveptr);
+ arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
+ if (!arg)
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "Arguments required"));
+ arg_len = strlen((char*)arg);
+ if (*arg != '"' || *(arg + arg_len - 1) != '"')
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "First argument not string"));
- struct stat st;
- char* nodename = NULL;
-
- CALLOC(nodename, char, BUFSIZE);
- snprintf(nodename, BUFSIZE, "%s/%s", incdir, node->d_name);
+ incdir = strdup((char*)(arg + 1));
+ *(incdir + strlen(incdir) - 1) = 0; /* trim ending " */
- if (lstat(nodename, &st) < 0 || !S_ISDIR(st.st_mode))
+ pass = 0;
+incdir_next_arg:
+ arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
+ if (!arg)
+ goto incdir_done_args;
+ parg = arg;
+ if (*parg == '=')
+ macro_body = get_value(macros, macros_count, arg + 1, NULL);
+ else if (!strcmp((const char*)arg, "listonly"))
+ list_only = 1;
+ else
{
- free(nodename);
- return 0;
+ while (parg && *parg)
+ {
+ if (*parg < '0' || *parg > '9')
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "Invalid argument"));
+ parg++;
+ }
+ errno = 0;
+ num = strtol((char*)arg, NULL, 10);
+ if (errno)
+ exit(error(errno, __FILE__, __func__, __LINE__,
+ "Invalid parameter 'num'"));
}
+ pass++;
+ if (pass < 3)
+ goto incdir_next_arg;
- 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");
-}
-
-int
-reverse_alphacompare(const struct dirent** a, const struct dirent** b)
-{
- if (!a || !b)
- return 0;
-
- return -1 * strcmp((*a)->d_name, (*b)->d_name);
+incdir_done_args:
+ print_output(output, "<ul class=\"incdir\">\n");
+ if ((names_total = scandir(incdir, &namelist, &filter_subdirs,
+ &reverse_alphacompare))
+ < 0)
+ {
+ perror(PROGRAMNAME ": scandir");
+ exit(error(errno, __FILE__, __func__, __LINE__,
+ "scandir '%s' failed", incdir));
+ }
+ pnamelist = namelist;
+ names_output = 0;
+ while (names_output < MIN(names_total, num))
+ {
+ /*if (list_only)
+ print_output(output, "<li>%s\n",
+ (*pnamelist)->d_name);*/
+ process_incdir_subdir(output, (*pnamelist)->d_name, link_prefix,
+ details_open, macro_body, footer_permalink_text,
+ permalink_url, ext_in_permalink, list_only);
+ details_open = 0;
+ pnamelist++;
+ names_output++;
+ /*if (list_only)
+ print_output(output, "</li>\n");*/
+ }
+ while (names_total--)
+ free(namelist[names_total]);
+ free(namelist);
+ free(incdir);
+ print_output(output, "</ul>\n");
+ output_firstcol = 1;
+ return 0;
}
-int process_timestamp(FILE* output, const u8* link_prefix, const char* link,
- const u8* permalink_macro, const u8* date, const int ext_in_permalink);
-
-int
+static int
process_incdir_subdir(FILE* output, const char* subdirname,
const u8* link_prefix, const int details_open, const u8* macro_body,
const u8* footer_permalink_text, const char* permalink_url,
@@ -1731,12 +1404,29 @@ process_incdir_subdir(FILE* output, const char* subdirname,
{
struct dirent** namelist;
struct dirent** pnamelist;
- char* abs_subdirname = NULL;
- char* temp = NULL;
- long names_total = 0;
- long names_output;
+ FILE* input = NULL;
+ u8* buffer = NULL;
+ u8* date = NULL;
+ u8* formatted_date = NULL;
+ u8* title = NULL;
+ u8* var_incdir_only_summary = NULL;
+ char* abs_subdirname = NULL;
+ char* temp = NULL;
+ char* filename = NULL;
+ char* link = NULL;
+ unsigned long long saved_state = ST_NONE;
+ size_t names_total = 0;
+ size_t names_output;
+ size_t buffer_size = 0;
+ size_t link_len;
+ pid_t pid;
+ int pstatus = 0;
+ int incdir_only_summary = 0;
+ int result = 0;
UNUSED(link_prefix);
+ assert((output != NULL) && (subdirname != NULL)
+ && (link_prefix != NULL));
if (list_only)
; /*print_output(output, "<ul>\n");*/
@@ -1745,7 +1435,6 @@ process_incdir_subdir(FILE* output, const char* subdirname,
details_open ? " open" : "");
if (macro_body)
print_output(output, "%s", macro_body);
-
if (!list_only)
print_output(output, "%s</summary>\n<div>\n", subdirname);
@@ -1757,7 +1446,7 @@ process_incdir_subdir(FILE* output, const char* subdirname,
&reverse_alphacompare))
< 0)
{
- perror("scandir");
+ perror(PROGRAMNAME ": scandir");
exit(error(errno, __FILE__, __func__, __LINE__,
"scandir '%s' failed", abs_subdirname));
}
@@ -1766,33 +1455,16 @@ process_incdir_subdir(FILE* output, const char* subdirname,
names_output = 0;
while (names_output < names_total)
{
- int pstatus = 0;
-
+ pstatus = 0;
if (!list_only)
print_output(output, "<article>\n");
-
fflush(output);
- pid_t pid = fork();
-
+ pid = fork();
if (pid > 0)
wait(&pstatus);
else if (pid == 0)
{
- FILE* input = NULL;
- FILE* output = stdout;
- u8* buffer = NULL;
- u8* date = NULL;
- u8* formatted_date = NULL;
- u8* title = NULL;
- u8* var_incdir_only_summary = NULL;
- char* filename = NULL;
- char* link = NULL;
- ULLONG saved_state = ST_NONE;
- size_t buffer_size = 0;
- size_t link_len;
- int incdir_only_summary = 0;
- int result = 0;
-
+ output = stdout;
if (!strcmp(basedir, "."))
set_basedir(&basedir, &basedir_size,
abs_subdirname);
@@ -1814,7 +1486,6 @@ process_incdir_subdir(FILE* output, const char* subdirname,
footnotes = NULL;
footnote_count = 0;
current_footnote = 0;
-
while (inline_footnote_count--)
free(inline_footnotes[inline_footnote_count]);
free(inline_footnotes);
@@ -1902,727 +1573,697 @@ process_incdir_subdir(FILE* output, const char* subdirname,
MEMCCPY((link + link_len),
timestamp_output_ext,
BUFSIZE - link_len, temp);
- }
-
- /* TODO: Get rid of orphan <p> just before
- * <footer>; would probably need another flag
- * and another layer of code, so maybe not
- */
- print_output(output,
- "<footer>\n"
- "<p><a href=\"%s\">%s</a>\n"
- "</footer>\n",
- permalink_url ? permalink_url : link,
- footer_permalink_text);
- }
-
- fflush(output);
- cleanup();
- free(formatted_date);
- free(link);
- free(filename);
- free(buffer);
- free(abs_subdirname);
- while (names_total--)
- free(namelist[names_total]);
- free(namelist);
- exit(result);
- }
- else
- exit(error(1, __FILE__, __func__, __LINE__,
- "fork failed"));
-
- if (!list_only)
- print_output(output, "</article>\n");
-
- pnamelist++;
- names_output++;
- }
-
- while (names_total--)
- free(namelist[names_total]);
- free(namelist);
- free(abs_subdirname);
-
- if (list_only)
- ; /*print_output(output, "</ul>\n");*/
- else
- print_output(output, "</div>\n</details>\n</li>\n");
-
- output_firstcol = 0;
- return 0;
-}
-
-int
-process_incdir(FILE* output, const u8* token, const u8* link_prefix,
- const u8* footer_permalink_text, const char* permalink_url,
- const int ext_in_permalink, const int read_yaml_macros_and_links)
-{
- u8* saveptr = NULL;
- /* skipping the first token (incdir) */
- u8* arg = NULL;
- size_t arg_len = 0;
- long num = 5;
- u8* macro_body = NULL;
- int names_total = 0;
- struct dirent** namelist;
- struct dirent** pnamelist;
- long names_output;
- int details_open = 1;
- int list_only = 0;
- int pass;
- u8* parg = NULL;
-
- if (read_yaml_macros_and_links)
- return 0;
-
- (void)strtok_r((char*)token, " ", (char**)&saveptr);
- arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
- if (!arg)
- exit(error(1, __FILE__, __func__, __LINE__,
- "Arguments required"));
-
- arg_len = strlen((char*)arg);
-
- if (*arg != '"' || *(arg + arg_len - 1) != '"')
- exit(error(1, __FILE__, __func__, __LINE__,
- "First argument not string"));
-
- incdir = strdup((char*)(arg + 1));
- *(incdir + strlen(incdir) - 1) = 0; /* trim ending " */
-
- pass = 0;
-
-incdir_next_arg:
- arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
- if (!arg)
- goto incdir_done_args;
+ }
- parg = arg;
- if (*parg == '=')
- macro_body = get_value(macros, macros_count, arg + 1, NULL);
- else if (!strcmp((const char*)arg, "listonly"))
- list_only = 1;
- else
- {
- while (parg && *parg)
- {
- if (*parg < '0' || *parg > '9')
- exit(error(1, __FILE__, __func__, __LINE__,
- "Invalid argument"));
- parg++;
- }
- errno = 0;
- num = strtol((char*)arg, NULL, 10);
- if (errno)
- exit(error(errno, __FILE__, __func__, __LINE__,
- "Invalid parameter 'num'"));
- }
- pass++;
- if (pass < 3)
- goto incdir_next_arg;
+ /* TODO: Get rid of orphan <p> just before
+ * <footer>; would probably need another flag
+ * and another layer of code, so maybe not
+ */
+ print_output(output,
+ "<footer>\n"
+ "<p><a href=\"%s\">%s</a>\n"
+ "</footer>\n",
+ permalink_url ? permalink_url : link,
+ footer_permalink_text);
+ }
-incdir_done_args:
- print_output(output, "<ul class=\"incdir\">\n");
+ fflush(output);
+ cleanup();
+ free(formatted_date);
+ free(link);
+ free(filename);
+ free(buffer);
+ free(abs_subdirname);
+ while (names_total--)
+ free(namelist[names_total]);
+ free(namelist);
+ exit(result);
+ }
+ else
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "fork failed"));
- if ((names_total = scandir(incdir, &namelist, &filter_subdirs,
- &reverse_alphacompare))
- < 0)
- {
- perror("scandir");
- exit(error(errno, __FILE__, __func__, __LINE__,
- "scandir '%s' failed", incdir));
- }
+ if (!list_only)
+ print_output(output, "</article>\n");
- pnamelist = namelist;
- names_output = 0;
- while (names_output < MIN(names_total, num))
- {
- /*if (list_only)
- print_output(output, "<li>%s\n",
- (*pnamelist)->d_name);*/
- process_incdir_subdir(output, (*pnamelist)->d_name, link_prefix,
- details_open, macro_body, footer_permalink_text,
- permalink_url, ext_in_permalink, list_only);
- details_open = 0;
pnamelist++;
names_output++;
- /*if (list_only)
- print_output(output, "</li>\n");*/
}
+
while (names_total--)
free(namelist[names_total]);
free(namelist);
- free(incdir);
+ free(abs_subdirname);
- print_output(output, "</ul>\n");
- output_firstcol = 1;
+ if (list_only)
+ ; /*print_output(output, "</ul>\n");*/
+ else
+ print_output(output, "</div>\n</details>\n</li>\n");
+ output_firstcol = 0;
return 0;
}
-int url_is_local(const char* url);
-
-u8*
-format_date(const u8* date_arg, const char* timestamp_format)
+static int
+process_include(FILE* output, const u8* token,
+ const int read_yaml_macros_and_links)
{
- const char* ptimestamp_format = NULL;
- u8* day = NULL;
- u8* month = NULL;
- u8* year = NULL;
- u8* formatted_date = NULL;
- u8* ptr = NULL;
- u8* date = NULL;
- char* temp = NULL;
- size_t formatted_date_len;
-
- date = (u8*)strdup((const char*)date_arg);
- if (!date)
- goto format_date_cleanup;
+ FILE* child_output = NULL;
+ FILE* input = NULL;
+ u8* child_output_line = NULL;
+ u8* ptoken;
+ u8* buffer = NULL;
+ char* include_filename = NULL;
+ char* pinclude_filename = NULL;
+ char* eol = NULL;
+ char* filename = NULL;
+ int arg_pipe_fds[2];
+ int output_pipe_fds[2];
+ size_t buffer_size = 0;
+ pid_t pid = 0;
+ int pstatus = 0;
+ int result = 0;
- CALLOC(formatted_date, u8, DATEBUFSIZE);
- ptr = NULL;
- year = (u8*)strtok_r((char*)date, "-", (char**)&ptr);
- if (!year)
- goto format_date_cleanup;
- month = (u8*)strtok_r(NULL, "-", (char**)&ptr);
- if (!month)
- goto format_date_cleanup;
- day = (u8*)strtok_r(NULL, "T", (char**)&ptr);
- if (!day)
- goto format_date_cleanup;
- ptimestamp_format = timestamp_format;
- formatted_date_len = 0;
- while (*ptimestamp_format)
+ assert((output != NULL) && (token != NULL));
+ ptoken = (u8*)strchr((char*)token, ' ');
+ if (!input_filename)
+ return warning(1,
+ (u8*)"process_include: Cannot use 'include' in stdin");
+ if (!ptoken)
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "Directive 'include' requires an argument"));
+ fflush(output);
+ pipe(arg_pipe_fds);
+ pipe(output_pipe_fds);
+ pid = fork();
+ if (pid > 0) /* parent */
{
- if (*ptimestamp_format == 'd' || *ptimestamp_format == 'D')
+ child_output = NULL;
+ child_output_line = NULL;
+ close(arg_pipe_fds[PIPE_READ_INDEX]);
+ close(output_pipe_fds[PIPE_WRITE_INDEX]);
+ if (!(child_output
+ = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r")))
{
- MEMCCPY((formatted_date + formatted_date_len), day,
- DATEBUFSIZE - formatted_date_len, temp);
- formatted_date_len += strlen((char*)day);
+ wait(&pstatus);
+ perror(PROGRAMNAME ": fdopen");
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "fdopen failed"));
}
- else if (*ptimestamp_format == 'm' || *ptimestamp_format == 'M')
+ CALLOC(child_output_line, u8, BUFSIZE);
+ while (!feof(child_output))
{
- MEMCCPY((formatted_date + formatted_date_len), month,
- DATEBUFSIZE - formatted_date_len, temp);
- formatted_date_len += strlen((char*)month);
+ eol = NULL;
+ if (!fgets((char*)child_output_line, BUFSIZE,
+ child_output))
+ continue;
+ eol = strchr((char*)child_output_line, '\n');
+ if (eol)
+ *eol = 0;
+ simple_parse_yaml_line(child_output_line, &vars,
+ &vars_count, &pvars);
}
- else if (*ptimestamp_format == 'y' || *ptimestamp_format == 'Y')
+ free(child_output_line);
+ fclose(child_output);
+ wait(&pstatus);
+ }
+ else if (pid == 0) /* child */
+ {
+ close(arg_pipe_fds[PIPE_WRITE_INDEX]);
+ close(output_pipe_fds[PIPE_READ_INDEX]);
+ if (!(child_output
+ = fdopen(output_pipe_fds[PIPE_WRITE_INDEX], "w")))
{
- MEMCCPY((formatted_date + formatted_date_len), year,
- DATEBUFSIZE - formatted_date_len, temp);
- formatted_date_len += strlen((char*)year);
+ perror("fdopen");
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "fdopen failed"));
}
- else
- *(formatted_date + formatted_date_len++)
- = *ptimestamp_format;
+ 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);
- ptimestamp_format++;
- }
- return formatted_date;
+ result = read_file_into_buffer(&input, &buffer, &buffer_size,
+ filename, &input_dirname);
+ if (result)
+ exit(result);
-format_date_cleanup:
- free(formatted_date);
- free(date);
+ free_keyvalue(&links, links_count);
+ free(links);
+ links = NULL;
+ links_count = 0;
- return NULL;
-}
+ 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]);
+ free(inline_footnotes);
+ inline_footnotes = NULL;
+ inline_footnote_count = 0;
+ current_inline_footnote = 0;
+ state = ST_NONE;
-int
-process_timestamp(FILE* output, const u8* link_prefix, const char* link,
- const u8* permalink_macro, const u8* date, const int ext_in_permalink)
-{
- char* in_filename = NULL;
- char* in_line = NULL;
- u8* formatted_date = NULL;
+ /* First pass: read YAML, macros and links */
+ result = slweb_parse(output, filename, BUFSIZE, buffer, 1, 1);
- formatted_date = format_date(date, article_timestamp_format);
- if (!formatted_date)
- goto process_timestamp_cleanup;
+ add_css(child_output, 1);
+ if (result || read_yaml_macros_and_links)
+ goto process_include_child_cleanup;
- print_output(output,
- "<a href=\"%s%s%s%s\""
- " class=\"timestamp\">%s%s</a>\n",
- url_is_local(link) && global_link_prefix
- ? (const char*)global_link_prefix
- : "",
- url_is_local(link) && link_prefix ? (const char*)link_prefix
- : "",
- link, ext_in_permalink ? timestamp_output_ext : "",
- permalink_macro ? (char*)permalink_macro : "", formatted_date);
- output_firstcol = 1;
+ state = ST_NONE;
+ current_footnote = 0;
+ current_inline_footnote = 0;
-process_timestamp_cleanup:
- free(formatted_date);
- free(in_line);
- free(in_filename);
+ /* Second pass: parse and output */
+ result = slweb_parse(output, filename, BUFSIZE, buffer, 1, 0);
+ process_include_child_cleanup:
+ fflush(output);
+ cleanup();
+ free(filename);
+ free(buffer);
+ fclose(child_output);
+ exit(result);
+ }
+ else
+ exit(error(1, __FILE__, __func__, __LINE__, "fork failed"));
return 0;
}
-int
-process_macro_def(FILE* output, const u8* token,
- const int read_yaml_macros_and_links, const int end_tag)
+static int
+process_inline_footnote(const u8* token, const int read_yaml_macros_and_links,
+ FILE* output)
{
char* temp = NULL;
-
- if (end_tag)
- {
- fflush(output);
- state &= ~ST_MACRO_BODY;
- return 0;
- }
-
- if (IN(state, ST_MACRO_BODY))
- exit(error(1, __FILE__, __func__, __LINE__,
- "Cannot nest definitions"));
-
+ size_t token_len;
+ assert(token != NULL);
+ current_inline_footnote++;
if (read_yaml_macros_and_links)
{
- macros_count++;
-
- if (!macros)
+ 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)
{
- CALLOC(macros, KeyValue, macros_count);
- pmacros = macros;
+ inline_footnote_count = 1;
+ CALLOC(inline_footnotes, u8*, inline_footnote_count);
}
else
- {
- REALLOC(macros, KeyValue,
- macros_count * sizeof(KeyValue));
- pmacros = macros + macros_count - 1;
- }
- CALLOC(pmacros->key, u8, KEYSIZE);
- MEMCCPY(pmacros->key, (char*)token + 2, KEYSIZE, temp);
- pmacros->value = NULL;
- pmacros->value_size = 0;
+ REALLOC(inline_footnotes, u8*,
+ sizeof(u8*) * inline_footnote_count);
+ CALLOC(inline_footnotes[inline_footnote_count - 1], u8,
+ token_len + 1);
+ MEMCCPY(inline_footnotes[inline_footnote_count - 1],
+ (char*)token, token_len + 1, temp);
+ }
+ else
+ {
+ print_output(output,
+ "<a href=\"#inline-footnote-%d\" "
+ "id=\"inline-footnote-text-%d\"><sup>%d</sup></a>",
+ current_inline_footnote, current_inline_footnote,
+ current_inline_footnote);
+ output_firstcol = 0;
}
- fflush(output);
- state |= ST_MACRO_BODY;
-
return 0;
}
-int
-process_macro(FILE* output, const u8* token, const int end_tag)
+static int
+process_inline_image(FILE* output, const u8* image_text,
+ const u8* image_file_prefix, const u8* link_prefix, const u8* image_url,
+ const int add_link, const int add_figcaption)
{
- u8* macro_body;
- u8* eol;
- size_t macro_body_len;
-
- UNUSED(end_tag);
-
- macro_body = get_value(macros, macros_count, token + 1, NULL);
- if (!macro_body)
- exit(error(1, __FILE__, __func__, __LINE__,
- "Macro '%s' undefined", token + 1));
-
- eol = (u8*)strrchr((char*)macro_body, '\n');
- macro_body_len = strlen((char*)macro_body);
- if (eol)
- output_firstcol = macro_body + macro_body_len
- - (u8*)strrchr((char*)macro_body, '\n')
- > 0;
- else
- output_firstcol = !*macro_body
- || *(macro_body + macro_body_len - 1) == '\n';
- print_output(output, "%s", macro_body);
+ assert((output != NULL) && (image_text != NULL));
+ if (add_figcaption)
+ print_output(output, "<figure>\n");
+ if (add_link)
+ print_output(output,
+ "<a href=\"%s%s%s\" title=\"%s\" class=\"image\""
+ " target=\"_blank\">",
+ global_link_prefix ? (const char*)global_link_prefix
+ : "",
+ link_prefix ? (const char*)link_prefix : "",
+ image_url ? (char*)image_url : "", image_text);
+ print_output(output, "<img src=\"%s\" alt=\"%s\"%s",
+ image_url ? (char*)image_url : "", image_text,
+ image_url ? " " : "");
+ if (image_url)
+ print_image_dimensions(output, image_file_prefix, image_url);
+ print_output(output, ">");
+ output_firstcol = 0;
+ if (add_link)
+ print_output(output, "</a>");
+ if (add_figcaption)
+ {
+ print_output(output, "<figcaption>%s</figcaption>\n</figure>\n",
+ image_text);
+ output_firstcol = 1;
+ }
+ return 0;
+}
+static int
+process_inline_link(FILE* output, const u8* link_text, const u8* link_prefix,
+ const u8* link_macro_body, const u8* link_url)
+{
+ print_output(output, "<a href=\"%s%s%s\">%s%s</a>",
+ global_link_prefix ? (const char*)global_link_prefix : "",
+ link_prefix ? (const char*)link_prefix : "",
+ link_url ? (char*)link_url : "",
+ link_macro_body ? (char*)link_macro_body : "", link_text);
+ output_firstcol = 0;
return 0;
}
-int
-process_tag(FILE* output, const u8* token, const char* source_filename,
- const u8* link_prefix, const u8* footer_permalink_text,
- const char* permalink_url, const int ext_in_permalink,
- const int read_yaml_macros_and_links, int* skip_eol, const int end_tag)
+static int
+process_inline_stylesheet(FILE* output, const u8* css_filename, int output_yaml)
{
- UNUSED(source_filename);
- if (!token || strlen((char*)token) < 1)
- return warning(1, (u8*)"%s:%ld:%ld: Empty tag name",
- input_filename, lineno, colno);
+ FILE* css = NULL;
+ u8* bufline = NULL;
+ char* css_pathname = NULL;
+ size_t css_filename_len = 0;
+ size_t css_pathname_size = 0;
- if (!strcmp((char*)token, "git-log")
- && !read_yaml_macros_and_links) /* {git-log} */
- {
- process_git_log(output);
- }
- else if (startswith((char*)token, "csv-count")) /* {csv-count} */
- process_tsv_count(output, token, read_yaml_macros_and_links, 0);
- else if (startswith((char*)token, "csv")) /* {csv} */
- process_csv(output, token, read_yaml_macros_and_links, end_tag);
- else if (startswith((char*)token, "tsv-count")) /* {tsv-count} */
- process_tsv_count(output, token, read_yaml_macros_and_links, 1);
- else if (startswith((char*)token, "tsv")) /* {tsv} */
- process_tsv(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 = 1;
- }
- else if (startswith((char*)token, "incdir")) /* {incdir} */
+ assert((output != NULL) && (css_filename != NULL));
+ css_filename_len = strlen((char*)css_filename);
+ css_pathname_size = basedir_size + css_filename_len + 1;
+
+ CALLOC(css_pathname, char, css_pathname_size);
+ if ((size_t)snprintf(css_pathname, css_pathname_size, "%s/%s", basedir,
+ css_filename)
+ > css_pathname_size)
+ warning(1, (u8*)"snprintf:%d: Overflow", __LINE__);
+ if (!(css = fopen((const char*)css_pathname, "rt")))
{
- process_incdir(output, token, link_prefix,
- footer_permalink_text, permalink_url, ext_in_permalink,
- read_yaml_macros_and_links);
- *skip_eol = 1;
+ perror(PROGRAMNAME ": fopen");
+ exit(error(errno, __FILE__, __func__, __LINE__,
+ "fopen failed: %s", css_pathname));
}
- else if (*token == '=') /* {=macro} */
+
+ if (output_yaml)
{
- if (*(token + 1) == '!')
- process_macro_def(output, token,
- read_yaml_macros_and_links, end_tag);
- else if ((*(token + 1) != '!') && !read_yaml_macros_and_links)
- process_macro(output, token, end_tag);
- *skip_eol = 1;
+ print_output(output, "inline-stylesheet: %s\n", css_filename);
+ output_firstcol = 1;
+ goto process_inline_stylesheet_cleanup;
}
- else if (!read_yaml_macros_and_links) /* general tags */
- {
- print_output(output, "<");
- if (end_tag)
- print_output(output, "/");
-
- if (*token == '.' || *token == '#')
- {
- print_output(output, "div");
- }
-
- while (*token && *token != '#' && *token != '.')
- print_output(output, "%c", *token++);
- if (!end_tag)
- {
- if (*token == '#')
- {
- token++;
- print_output(output, " id=\"");
- while (*token && *token != '.')
- print_output(output, "%c", *token++);
- print_output(output, "\"");
- if (*token == '.')
- {
- token++;
- print_output(output, " class=\"");
- while (*token)
- print_output(output, "%c",
- *token++);
- print_output(output, "\"");
- }
- }
- else if (*token == '.')
- {
- token++;
- print_output(output, " class=\"");
- while (*token && *token != '#')
- print_output(output, "%c", *token++);
- print_output(output, "\"");
- if (*token == '#')
- {
- token++;
- print_output(output, " id=\"");
- while (*token)
- print_output(output, "%c",
- *token++);
- print_output(output, "\"");
- }
- }
- }
- print_output(output, ">");
- output_firstcol = 0;
+ CALLOC(bufline, u8, BUFSIZE);
+ print_output(output, "<style>\n");
+ while (!feof(css))
+ {
+ if (!fgets((char*)bufline, BUFSIZE, css))
+ break;
+ print_output(output, "%s", bufline);
}
-
- return 0;
-}
-
-int
-process_bold(FILE* output, const int end_tag)
-{
- print_output(output, "<%sstrong>", end_tag ? "/" : "");
+ print_output(output, "</style>\n");
+ output_firstcol = 1;
+
+process_inline_stylesheet_cleanup:
+ free(css_pathname);
+ fclose(css);
+ free(bufline);
return 0;
}
-int
+static void
process_italic(FILE* output, const int end_tag)
{
print_output(output, "<%sem>", end_tag ? "/" : "");
- return 0;
-}
-
-int
-process_code(FILE* output, const int end_tag)
-{
- print_output(output, "<%scode>", end_tag ? "/" : "");
- return 0;
}
-int
-process_blockquote(FILE* output, const int end_tag)
-{
- print_output(output, "<%sblockquote>", end_tag ? "/" : "");
- return 0;
-}
-
-int
+static void
process_kbd(FILE* output, const int end_tag)
{
print_output(output, "<%skbd>", end_tag ? "/" : "");
- return 0;
}
-int
-process_madeby(FILE* output)
+static void
+process_line_start(FILE* output, const u8* line, const u8* link_prefix,
+ const int first_line_in_doc, const int previous_line_blank,
+ int* previous_line_block, const int read_yaml_macros_and_links,
+ const int list_para, const int new_para_ok)
{
- print_output(output,
- "<div id=\"made-by\"><p><small>\n"
- "Generated by <a href=\"%s\">slweb</a>\n"
- "© %s Strahinya Radich.\n"
- "</small></div><!--made-by-->\n",
- MADEBY_URL, COPYRIGHT_YEAR);
- return 0;
-}
+ UNUSED(line);
+ UNUSED(link_prefix);
+ if ((first_line_in_doc || previous_line_blank || *previous_line_block)
+ && !(ANY(state, ST_BLOCKQUOTE | ST_PRE)))
+ {
+ if (!list_para)
+ {
+ if (IN(state, ST_LIST))
+ {
+ CLEAR(state, ST_LIST);
+ if (!read_yaml_macros_and_links)
+ {
+ process_list_item_end(output);
+ process_list_end(output);
+ }
+ }
-int
-process_strike(FILE* output, const int end_tag)
-{
- print_output(output, "<%ss>", end_tag ? "/" : "");
- return 0;
+ if (IN(state, ST_NUMLIST))
+ {
+ CLEAR(state, ST_NUMLIST);
+ if (!read_yaml_macros_and_links)
+ {
+ process_list_item_end(output);
+ process_numlist_end(output);
+ }
+ }
+
+ if (IN(state, ST_FOOTNOTE_TEXT))
+ CLEAR(state, ST_FOOTNOTE_TEXT | ST_PARA_OPEN);
+ }
+ if (new_para_ok
+ && !ANY(state,
+ ST_TABLE | ST_TABLE_HEADER | ST_TABLE_LINE))
+ {
+ if (!read_yaml_macros_and_links)
+ {
+ print_output(output, "<p>");
+ *previous_line_block = 0;
+ output_firstcol = 0;
+ }
+ SET(state, ST_PARA_OPEN);
+ }
+ }
}
-int
-process_table_start(FILE* output)
+static int
+process_link(FILE* output, const u8* link_text, const u8* link_prefix,
+ const u8* link_macro_body, const u8* link_id)
{
- print_output(output,
- "<table>\n"
- "<thead>\n<tr><th>");
- return 0;
+ u8* url = get_value(links, links_count, link_id, NULL);
+ return process_inline_link(output, link_text, link_prefix,
+ link_macro_body, url);
}
-int
-process_table_header_cell(FILE* output)
+static void
+process_list_end(FILE* output)
{
- print_output(output, "</th><th>");
- return 0;
+ print_output(output, "</ul>\n");
+ output_firstcol = 1;
}
-int
-process_table_header_end(FILE* output)
+static void
+process_list_item_end(FILE* output)
{
- print_output(output, "</th></tr>\n</thead>\n");
- return 0;
+ CLEAR(state, ST_PARA_OPEN);
+ print_output(output, "</li>");
+ output_firstcol = 0;
}
-int
-process_table_body_start(FILE* output, const int start_row)
+static void
+process_list_item_start(FILE* output)
{
- print_output(output, "<tbody>\n");
- if (start_row)
- print_output(output, "<tr><td>");
- return 0;
+ print_output(output, "%s<li><p>", output_firstcol ? "" : "\n");
}
-int
-process_table_body_row_start(FILE* output)
+static void
+process_list_start(FILE* output)
{
- print_output(output, "<tr><td>");
- return 0;
+ print_output(output, "<ul>");
}
-int
-process_table_body_cell(FILE* output)
+static int
+process_macro(FILE* output, const u8* token, const int end_tag)
{
- print_output(output, "</td><td>");
+ u8* macro_body;
+ u8* eol;
+ size_t macro_body_len;
+ UNUSED(end_tag);
+ assert((output != NULL) && (token != NULL));
+ macro_body = get_value(macros, macros_count, token + 1, NULL);
+ if (!macro_body)
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "Macro '%s' undefined", token + 1));
+
+ eol = (u8*)strrchr((char*)macro_body, '\n');
+ macro_body_len = strlen((char*)macro_body);
+ if (eol)
+ output_firstcol = macro_body + macro_body_len
+ - (u8*)strrchr((char*)macro_body, '\n')
+ > 0;
+ else
+ output_firstcol = !*macro_body
+ || *(macro_body + macro_body_len - 1) == '\n';
+ print_output(output, "%s", macro_body);
return 0;
}
-int
-process_table_body_row_end(FILE* output)
+static int
+process_macro_def(FILE* output, const u8* token,
+ const int read_yaml_macros_and_links, const int end_tag)
{
- print_output(output, "</td></tr>\n");
+ char* temp = NULL;
+ assert((output != NULL) && (token != NULL));
+ if (end_tag)
+ {
+ fflush(output);
+ CLEAR(state, ST_MACRO_BODY);
+ return 0;
+ }
+ if (IN(state, ST_MACRO_BODY))
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "Cannot nest definitions"));
+
+ 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);
+ MEMCCPY(pmacros->key, (char*)token + 2, KEYSIZE, temp);
+ pmacros->value = NULL;
+ pmacros->value_size = 0;
+ }
+ fflush(output);
+ SET(state, ST_MACRO_BODY);
return 0;
}
-int
-process_table_end(FILE* output)
+static void
+process_madeby(FILE* output)
{
- print_output(output, "</tbody>\n</table>\n");
- return 0;
+ print_output(output,
+ "<div id=\"made-by\"><p><small>\n"
+ "Generated by <a href=\"%s\">slweb</a>\n"
+ "© %s Strahinya Radich.\n"
+ "</small></div><!--made-by-->\n",
+ MADEBY_URL, COPYRIGHT_YEAR);
}
-int
-url_is_local(const char* url)
+static void
+process_numlist_end(FILE* output)
{
- return !(startswith(url, "http://") || startswith(url, "https://")
- || startswith(url, "gemini://") || startswith(url, "ftp://")
- || startswith(url, "ftps://") || startswith(url, "mailto://"));
+ print_output(output, "</ol>\n");
+ output_firstcol = 1;
}
-int
-process_inline_link(FILE* output, const u8* link_text, const u8* link_prefix,
- const u8* link_macro_body, const u8* link_url)
+static void
+process_numlist_start(FILE* output)
{
- print_output(output, "<a href=\"%s%s%s\">%s%s</a>",
- global_link_prefix ? (const char*)global_link_prefix : "",
- link_prefix ? (const char*)link_prefix : "",
- link_url ? (char*)link_url : "",
- link_macro_body ? (char*)link_macro_body : "", link_text);
+ print_output(output, "<ol>");
output_firstcol = 0;
-
- return 0;
}
-int
-process_link(FILE* output, const u8* link_text, const u8* link_prefix,
- const u8* link_macro_body, const u8* link_id)
+static void
+process_strike(FILE* output, const int end_tag)
{
- u8* url = get_value(links, links_count, link_id, NULL);
- return process_inline_link(output, link_text, link_prefix,
- link_macro_body, url);
+ print_output(output, "<%ss>", end_tag ? "/" : "");
}
-int
-print_image_dimensions(FILE* output, const u8* image_file_prefix, const u8* path)
+static void
+process_stylesheet(FILE* output, const u8* css_filename, int output_yaml)
{
- struct stat sb;
- FILE* cmd_output = NULL;
- char command[BUFSIZE];
- char cmd_output_line[BUFSIZE];
- char* eol = NULL;
- char image_path[BUFSIZE - strlen(CMD_IDENTIFY)];
-
- if (!path || !*path)
- return 1;
-
- snprintf(image_path, BUFSIZE - strlen(CMD_IDENTIFY) - 1, "%s%s%s",
- image_file_prefix ? (char*)image_file_prefix : ".",
- *path == '/' ? "" : "/", path);
-
- if (stat(image_path, &sb) < 0)
- return 1;
-
- snprintf(command, BUFSIZE - 1, CMD_IDENTIFY, image_path);
- cmd_output = popen(command, "r");
- if (!cmd_output)
- return error(errno, __FILE__, __func__, __LINE__,
- "popen failed");
- if (!fgets(cmd_output_line, BUFSIZE, cmd_output))
- goto print_dimensions_cleanup;
-
- eol = strchr((char*)cmd_output_line, '\n');
- if (eol)
- *eol = 0;
-
- print_output(output, "%s", cmd_output_line);
- output_firstcol = 0;
-
-print_dimensions_cleanup:
- pclose(cmd_output);
+ if (output_yaml)
+ print_output(output, "stylesheet: %s\n", css_filename);
+ else
+ print_output(output, "<link rel=\"stylesheet\" href=\"%s\">\n",
+ css_filename);
+ output_firstcol = 1;
+}
- return 0;
+static void
+process_table_body_cell(FILE* output)
+{
+ print_output(output, "</td><td>");
}
-int
-process_inline_image(FILE* output, const u8* image_text,
- const u8* image_file_prefix, const u8* link_prefix, const u8* image_url,
- const int add_link, const int add_figcaption)
+static void
+process_table_body_row_end(FILE* output)
{
- if (add_figcaption)
- {
- print_output(output, "<figure>\n");
- }
+ print_output(output, "</td></tr>\n");
+}
- if (add_link)
- print_output(output,
- "<a href=\"%s%s%s\" title=\"%s\" class=\"image\""
- " target=\"_blank\">",
- global_link_prefix ? (const char*)global_link_prefix
- : "",
- link_prefix ? (const char*)link_prefix : "",
- image_url ? (char*)image_url : "", image_text);
+static void
+process_table_body_row_start(FILE* output)
+{
+ print_output(output, "<tr><td>");
+}
- print_output(output, "<img src=\"%s\" alt=\"%s\"%s",
- image_url ? (char*)image_url : "", image_text,
- image_url ? " " : "");
- if (image_url)
- print_image_dimensions(output, image_file_prefix, image_url);
- print_output(output, ">");
+static void
+process_table_body_start(FILE* output, const int start_row)
+{
+ print_output(output, "<tbody>\n");
+ if (start_row)
+ print_output(output, "<tr><td>");
+}
- output_firstcol = 0;
+static void
+process_table_end(FILE* output)
+{
+ print_output(output, "</tbody>\n</table>\n");
+}
- if (add_link)
- print_output(output, "</a>");
+static void
+process_table_header_cell(FILE* output)
+{
+ print_output(output, "</th><th>");
+}
- if (add_figcaption)
- {
- print_output(output, "<figcaption>%s</figcaption>\n</figure>\n",
- image_text);
- output_firstcol = 1;
- }
- return 0;
+static void
+process_table_header_end(FILE* output)
+{
+ print_output(output, "</th></tr>\n</thead>\n");
}
-int
-process_image(FILE* output, const u8* image_text, const u8* image_file_prefix,
- const u8* link_prefix, const u8* image_id, const int add_link,
- const int add_figcaption)
+static void
+process_table_start(FILE* output)
{
- u8* url = get_value(links, links_count, image_id, NULL);
- return process_inline_image(output, image_text, image_file_prefix,
- link_prefix, url, add_link, add_figcaption);
+ print_output(output,
+ "<table>\n"
+ "<thead>\n<tr><th>");
}
-int
-process_line_start(FILE* output, const u8* line, const u8* link_prefix,
- const int first_line_in_doc, const int previous_line_blank,
- int* previous_line_block, const int read_yaml_macros_and_links,
- const int list_para, const int new_para_ok)
+static int
+process_tag(FILE* output, const u8* token, const char* source_filename,
+ const u8* link_prefix, const u8* footer_permalink_text,
+ const char* permalink_url, const int ext_in_permalink,
+ const int read_yaml_macros_and_links, int* skip_eol, const int end_tag)
{
- UNUSED(line);
- UNUSED(link_prefix);
- if ((first_line_in_doc || previous_line_blank || *previous_line_block)
- && !(ANY(state, ST_BLOCKQUOTE | ST_PRE)))
+ UNUSED(source_filename);
+ assert((output != NULL) && (token != NULL) && (source_filename != NULL)
+ && (skip_eol != NULL));
+ if (!*token)
+ 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} */
{
- if (!list_para)
+ process_git_log(output);
+ }
+ else if (startswith((char*)token, "csv-count")) /* {csv-count} */
+ process_tsv_count(output, token, read_yaml_macros_and_links, 0);
+ else if (startswith((char*)token, "csv")) /* {csv} */
+ process_csv(output, token, read_yaml_macros_and_links, end_tag);
+ else if (startswith((char*)token, "tsv-count")) /* {tsv-count} */
+ process_tsv_count(output, token, read_yaml_macros_and_links, 1);
+ else if (startswith((char*)token, "tsv")) /* {tsv} */
+ process_tsv(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 = 1;
+ }
+ else if (startswith((char*)token, "incdir")) /* {incdir} */
+ {
+ process_incdir(output, token, link_prefix,
+ footer_permalink_text, permalink_url, ext_in_permalink,
+ read_yaml_macros_and_links);
+ *skip_eol = 1;
+ }
+ else if (*token == '=') /* {=macro} */
+ {
+ if (*(token + 1) == '!')
+ process_macro_def(output, token,
+ read_yaml_macros_and_links, end_tag);
+ else if ((*(token + 1) != '!') && !read_yaml_macros_and_links)
+ process_macro(output, token, end_tag);
+ *skip_eol = 1;
+ }
+ else if (!read_yaml_macros_and_links) /* general tags */
+ {
+ print_output(output, "<");
+ if (end_tag)
+ print_output(output, "/");
+
+ if (*token == '.' || *token == '#')
{
- if (IN(state, ST_LIST))
+ print_output(output, "div");
+ }
+
+ while (*token && *token != '#' && *token != '.')
+ print_output(output, "%c", *token++);
+
+ if (!end_tag)
+ {
+ if (*token == '#')
{
- state &= ~ST_LIST;
- if (!read_yaml_macros_and_links)
+ token++;
+ print_output(output, " id=\"");
+ while (*token && *token != '.')
+ print_output(output, "%c", *token++);
+ print_output(output, "\"");
+ if (*token == '.')
{
- process_list_item_end(output);
- process_list_end(output);
+ token++;
+ print_output(output, " class=\"");
+ while (*token)
+ print_output(output, "%c",
+ *token++);
+ print_output(output, "\"");
}
}
-
- if (IN(state, ST_NUMLIST))
+ else if (*token == '.')
{
- state &= ~ST_NUMLIST;
- if (!read_yaml_macros_and_links)
+ token++;
+ print_output(output, " class=\"");
+ while (*token && *token != '#')
+ print_output(output, "%c", *token++);
+ print_output(output, "\"");
+ if (*token == '#')
{
- process_list_item_end(output);
- process_numlist_end(output);
+ token++;
+ print_output(output, " id=\"");
+ while (*token)
+ print_output(output, "%c",
+ *token++);
+ print_output(output, "\"");
}
}
-
- if (IN(state, ST_FOOTNOTE_TEXT))
- state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN);
- }
- if (new_para_ok
- && !ANY(state,
- ST_TABLE | ST_TABLE_HEADER | ST_TABLE_LINE))
- {
- if (!read_yaml_macros_and_links)
- {
- print_output(output, "<p>");
- *previous_line_block = 0;
- output_firstcol = 0;
- }
- state |= ST_PARA_OPEN;
}
+ print_output(output, ">");
+ output_firstcol = 0;
}
return 0;
}
-int
+static void
process_text_token(FILE* output, const u8* line, const u8* link_prefix,
const int first_line_in_doc, const int previous_line_blank,
int* previous_line_block, const int processed_start_of_line,
@@ -2630,393 +2271,556 @@ process_text_token(FILE* output, const u8* line, const u8* link_prefix,
u8** ptoken, size_t* token_size, const int add_enclosing_paragraph,
const int new_para_ok)
{
+ u8* eol;
+ assert((token != NULL) && (ptoken != NULL));
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,
- previous_line_block, read_yaml_macros_and_links,
- list_para, new_para_ok);
- **ptoken = 0;
- if (**token && !read_yaml_macros_and_links
- && !(IN(state, ST_MACRO_BODY)))
+ if (add_enclosing_paragraph && !processed_start_of_line)
+ process_line_start(output, line, link_prefix,
+ first_line_in_doc, previous_line_blank,
+ previous_line_block, read_yaml_macros_and_links,
+ list_para, new_para_ok);
+ **ptoken = 0;
+ if (**token && !read_yaml_macros_and_links
+ && !(IN(state, ST_MACRO_BODY)))
+ {
+ print_output(output, "%s", *token);
+ eol = (u8*)strrchr((char*)*token, '\n');
+ if (eol)
+ output_firstcol
+ = *token + strlen((char*)*token) > eol;
+ else
+ output_firstcol = 0;
+ }
+ }
+ RESET_TOKEN(*token, *ptoken, *token_size);
+}
+
+static void
+process_timestamp(FILE* output, const u8* link_prefix, const char* link,
+ const u8* permalink_macro, const u8* date, const int ext_in_permalink)
+{
+ u8* formatted_date = NULL;
+ char* in_filename = NULL;
+ char* in_line = NULL;
+
+ assert((output != NULL) && (link != NULL));
+ formatted_date = format_date(date, article_timestamp_format);
+ if (!formatted_date)
+ goto process_timestamp_cleanup;
+ print_output(output,
+ "<a href=\"%s%s%s%s\""
+ " class=\"timestamp\">%s%s</a>\n",
+ url_is_local(link) && global_link_prefix
+ ? (const char*)global_link_prefix
+ : "",
+ url_is_local(link) && link_prefix ? (const char*)link_prefix
+ : "",
+ link, ext_in_permalink ? timestamp_output_ext : "",
+ permalink_macro ? (char*)permalink_macro : "", formatted_date);
+ output_firstcol = 1;
+
+process_timestamp_cleanup:
+ free(formatted_date);
+ free(in_line);
+ free(in_filename);
+}
+
+static int
+process_tsv(FILE* output, const u8* arg_token,
+ const int read_yaml_macros_and_links, const int end_tag)
+{
+ u8* saveptr = NULL;
+ u8* args = NULL;
+ u8* args_base = NULL;
+ size_t args_len;
+
+ assert((output != NULL) && (arg_token != NULL));
+ if (end_tag)
+ {
+ CLEAR(state, ST_TSV_BODY);
+ if (read_yaml_macros_and_links)
+ return 0;
+ read_tsv(output, tsv_filename, &print_tsv_row);
+ free(tsv_filename);
+ tsv_filename = NULL;
+ free(tsv_template);
+ tsv_template = NULL;
+ tsv_template_size = 0;
+ }
+ else
+ {
+ if (ANY(state, ST_CSV_BODY | ST_TSV_BODY))
+ exit(error(1, __FILE__, __func__, __LINE__,
+ "Can't nest csv/tsv directives"));
+ SET(state, ST_TSV_BODY);
+ if (read_yaml_macros_and_links)
+ return 0;
+ tsv_iter = 0;
+ (void)strtok_r((char*)arg_token, " ", (char**)&saveptr);
+ args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
+ if (!args)
+ exit(error(EINVAL, __FILE__, __func__, __LINE__,
+ "Arguments required"));
+ args_len = strlen((char*)args);
+ if (*args != '"' || *(args + args_len - 1) != '"')
+ exit(error(EINVAL, __FILE__, __func__, __LINE__,
+ "First argument must be a string"));
+ if (!tsv_filename)
+ CALLOC(tsv_filename, char, BUFSIZE);
+ args_base = (u8*)strdup((char*)args + 1);
+ *(args_base + strlen((char*)args_base) - 1) = 0;
+ snprintf(tsv_filename, BUFSIZE, "%s/%s.tsv", input_dirname,
+ (char*)args_base);
+ free(args_base);
+ args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
+ if (args)
{
- 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 = 0;
+ errno = 0;
+ tsv_iter = (size_t)strtol((char*)args, NULL, 10);
+ if (errno)
+ exit(error(errno, __FILE__, __func__, __LINE__,
+ "Invalid argument '%s'", args));
}
}
- RESET_TOKEN(*token, *ptoken, *token_size);
return 0;
}
-int
-process_inline_footnote(const u8* token, const int read_yaml_macros_and_links,
- FILE* output)
+static int
+process_tsv_count(FILE* output, const u8* arg_token,
+ const int read_yaml_macros_and_links, const int is_tsv)
{
- char* temp = NULL;
- size_t token_len;
+ FILE* tsv = NULL;
+ u8* saveptr = NULL;
+ u8* args = NULL;
+ u8* args_base = NULL;
+ char* bufline = NULL;
+ char* eol = NULL;
+ size_t args_len;
+ size_t tsv_lines = 0;
- current_inline_footnote++;
+ assert((output != NULL) && (arg_token != NULL));
if (read_yaml_macros_and_links)
+ return 0;
+ tsv_iter = 0;
+ (void)strtok_r((char*)arg_token, " ", (char**)&saveptr);
+ args = (u8*)strtok_r(NULL, " ", (char**)&saveptr);
+ if (!args)
+ exit(error(EINVAL, __FILE__, __func__, __LINE__,
+ "Arguments required"));
+ args_len = strlen((char*)args);
+ if (*args != '"' || *(args + args_len - 1) != '"')
+ exit(error(EINVAL, __FILE__, __func__, __LINE__,
+ "First argument must be a string"));
+ if (!tsv_filename)
+ CALLOC(tsv_filename, char, BUFSIZE);
+ args_base = (u8*)strdup((char*)args + 1);
+ *(args_base + strlen((char*)args_base) - 1) = 0;
+ snprintf(tsv_filename, BUFSIZE, "%s/%s.%csv", input_dirname,
+ (char*)args_base, is_tsv ? 't' : 'c');
+ free(args_base);
+ if (!(tsv = fopen(tsv_filename, "rt")))
{
- 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);
-
- MEMCCPY(inline_footnotes[inline_footnote_count - 1],
- (char*)token, token_len + 1, temp);
+ perror("fopen");
+ exit(error(ENOENT, __FILE__, __func__, __LINE__,
+ "fopen failed: %s", tsv_filename));
}
- else
+ CALLOC(bufline, char, BUFSIZE);
+ while (!feof(tsv))
{
- print_output(output,
- "<a href=\"#inline-footnote-%d\" "
- "id=\"inline-footnote-text-%d\"><sup>%d</sup></a>",
- current_inline_footnote, current_inline_footnote,
- current_inline_footnote);
- output_firstcol = 0;
+ eol = NULL;
+ if (!fgets(bufline, BUFSIZE, tsv))
+ break;
+ eol = strchr(bufline, '\n');
+ if (eol)
+ {
+ *eol = 0;
+ tsv_lines++;
+ }
}
+ fclose(tsv);
+
+ /* And take back one kadam for the header row */
+ print_output(output, "%ld", tsv_lines - 1);
+ free(bufline);
+ free(tsv_filename);
+ tsv_filename = NULL;
return 0;
}
-int
-process_footnote(FILE* output, const u8* token, const int footnote_definition,
- const int footnote_output)
+static void
+read_csv(FILE* output, const char* filename, csv_callback_t callback)
{
- char* temp = NULL;
-
- current_footnote++;
-
- if (footnote_definition)
+ FILE* csv = NULL;
+ u8* bufline = NULL;
+ u8* pbufline = NULL;
+ u8* token = NULL;
+ u8* ptoken = NULL;
+ u8* csv_header[MAX_CSV_REGISTERS];
+ u8* csv_register[MAX_CSV_REGISTERS];
+ u8* csv_delimiter = NULL;
+ u8* eol = NULL;
+ char* temp = NULL;
+ size_t csv_lineno = 0;
+ size_t token_size = 0;
+ unsigned char csv_state = ST_CS_NONE;
+ unsigned char current_header = 0;
+ unsigned char current_register = 0;
+ unsigned char i;
+
+ assert((output != NULL) && (filename != NULL) && (callback != NULL));
+ csv_delimiter = get_value(vars, vars_count, (u8*)"csv-delimiter", NULL);
+ if (!(csv = fopen(filename, "rt")))
{
- footnote_count++;
- if (footnote_count == 1 && inline_footnote_count > 0)
- warning(1,
- (u8*)"Both inline and regular footnotes "
- "present");
- else if (!footnotes)
+ perror("fopen");
+ exit(error(errno, __FILE__, __func__, __LINE__,
+ "fopen failed: %s", filename));
+ }
+ CALLOC(bufline, u8, BUFSIZE);
+ token_size = BUFSIZE;
+ CALLOC(token, u8, token_size);
+ for (i = 0; i < MAX_CSV_REGISTERS; i++)
+ CALLOC(csv_header[i], u8, BUFSIZE);
+ for (i = 0; i < MAX_CSV_REGISTERS; i++)
+ CALLOC(csv_register[i], u8, BUFSIZE);
+ while (!feof(csv) && (!csv_iter || csv_lineno <= csv_iter))
+ {
+ eol = NULL;
+ if (!fgets((char*)bufline, BUFSIZE, csv))
+ break;
+ eol = (u8*)strchr((char*)bufline, '\n');
+ if (eol)
+ *eol = 0;
+ pbufline = bufline;
+ RESET_TOKEN(token, ptoken, token_size);
+ current_register = 0;
+ csv_bufline_start:
+ if (!*pbufline)
+ goto csv_bufline_done;
+ switch (*pbufline)
{
- CALLOC(footnotes, KeyValue, footnote_count);
- pfootnotes = footnotes;
+ case '"':
+ if (IN(csv_state, ST_CS_QUOTE))
+ csv_state &= ~ST_CS_QUOTE;
+ else
+ csv_state |= ST_CS_QUOTE;
+ pbufline++;
+ break;
+ case ';':
+ case ',':
+ if (IN(csv_state, ST_CS_QUOTE))
+ CHECKCOPY(token, ptoken, token_size, pbufline);
+ else
+ {
+ *ptoken = 0;
+ if (csv_lineno > 0
+ && current_register < MAX_CSV_REGISTERS)
+ {
+ MEMCCPY(csv_register[current_register],
+ (char*)token, BUFSIZE, temp);
+ current_register++;
+ }
+ else if (csv_lineno <= 0
+ && current_header < MAX_CSV_REGISTERS)
+ {
+ MEMCCPY(csv_header[current_header],
+ (char*)token, BUFSIZE, temp);
+ current_header++;
+ }
+ RESET_TOKEN(token, ptoken, token_size);
+ pbufline++;
+ }
+ break;
+ default:
+ if (IN(csv_state, ST_CS_QUOTE))
+ CHECKCOPY(token, ptoken, token_size, pbufline);
+ else if (csv_delimiter && *pbufline == *csv_delimiter)
+ {
+ *ptoken = 0;
+ if (csv_lineno > 0
+ && current_register < MAX_CSV_REGISTERS)
+ {
+ MEMCCPY(csv_register[current_register],
+ (char*)token, BUFSIZE, temp);
+ current_register++;
+ }
+ else if (csv_lineno <= 0
+ && current_header < MAX_CSV_REGISTERS)
+ {
+ MEMCCPY(csv_header[current_header],
+ (char*)token, BUFSIZE, temp);
+ current_header++;
+ }
+ RESET_TOKEN(token, ptoken, token_size);
+ pbufline++;
+ }
+ else
+ CHECKCOPY(token, ptoken, token_size, pbufline);
}
- else
+ goto csv_bufline_start;
+ csv_bufline_done:
+ *ptoken = 0;
+ if (csv_lineno > 0)
{
- REALLOCARRAY(footnotes, KeyValue, footnote_count);
- pfootnotes = footnotes + footnote_count - 1;
+ if (current_register < MAX_CSV_REGISTERS)
+ {
+ MEMCCPY(csv_register[current_register],
+ (char*)token, BUFSIZE, temp);
+ current_register++;
+ }
}
- CALLOC(pfootnotes->key, u8, KEYSIZE);
- MEMCCPY(pfootnotes->key, (char*)token, KEYSIZE, temp);
- pfootnotes->value = NULL;
- pfootnotes->value_size = 0;
- }
-
- if (footnote_output)
- {
- print_output(output,
- "<a href=\"#footnote-%d\" id=\"footnote-text-%d\">"
- "<sup>%d</sup></a>",
- current_footnote, current_footnote, current_footnote);
- output_firstcol = 0;
- }
+ else
+ {
+ if (current_header < MAX_CSV_REGISTERS)
+ {
+ MEMCCPY(csv_header[current_header],
+ (char*)token, BUFSIZE, temp);
+ current_header++;
+ }
+ }
+ RESET_TOKEN(token, ptoken, token_size);
- return 0;
-}
+ if (csv_lineno > 0 && pbufline != bufline)
+ (*callback)(output, csv_header, csv_register);
-int
-process_horizontal_rule(FILE* output)
-{
- print_output(output, "<hr>\n");
- if (IN(state, ST_PARA_OPEN))
- print_output(output, "<p>\n");
- output_firstcol = 1;
- return 0;
+ for (i = 0; i < MAX_CSV_REGISTERS; i++)
+ *csv_register[i] = 0;
+ csv_lineno++;
+ }
+ fclose(csv);
+ for (i = MAX_CSV_REGISTERS; i > 0; i--)
+ free(csv_register[i - 1]);
+ for (i = MAX_CSV_REGISTERS; i > 0; i--)
+ free(csv_header[i - 1]);
+ free(token);
+ free(bufline);
}
-int
-process_formula(FILE* output, const u8* token, const int display_formula)
+static int
+read_file_into_buffer(FILE** input, u8** buffer, size_t* buffer_size,
+ const char* input_filename, char** input_dirname)
{
- int result = 0;
- const u8* pipe_args[] = {token, NULL};
-
- result = print_command(output, CMD_KATEX,
- display_formula ? (const u8**)CMD_KATEX_DISPLAY_ARGS
- : (const u8**)CMD_KATEX_INLINE_ARGS,
- (const u8**)pipe_args, 1);
+ struct stat fs;
+ char* slash = NULL;
+ char* pinput_dirname;
+ const char* pinput_filename;
- if (result)
+ assert((input != NULL) && (buffer != NULL) && (buffer_size !-NULL)
+ && (input_dirname != NULL));
+ errno = 0;
+ *input = fopen(input_filename, "r");
+ if (!*input)
{
- print_output(output, "%s$%s$%s", display_formula ? "$" : "",
- token, display_formula ? "$" : "");
- output_firstcol = 0;
+ perror(PROGRAMNAME ": fopen");
+ return error(errno, __FILE__, __func__, __LINE__,
+ "fopen failed: %s", input_filename);
}
- return result;
-}
-
-int
-begin_html_and_head(FILE* output)
-{
- u8* lang = get_value(vars, vars_count, (u8*)"lang", NULL);
- u8* site_name = get_value(vars, vars_count, (u8*)"site-name", NULL);
- u8* site_desc = get_value(vars, vars_count, (u8*)"site-desc", NULL);
- u8* canonical = get_value(vars, vars_count, (u8*)"canonical", NULL);
- u8* favicon_url = get_value(vars, vars_count, (u8*)"favicon-url", NULL);
- u8* meta = get_value(vars, vars_count, (u8*)"meta", NULL);
- u8* feed = NULL;
- u8* feed_type = NULL;
- u8* feed_desc = NULL;
- KeyValue* cursor = NULL;
- KeyValue* cursor_type = NULL;
- KeyValue* cursor_desc = NULL;
-
- print_output(output,
- "<!DOCTYPE html>\n"
- "<html lang=\"%s\">\n"
- "<head>\n"
- "<title>%s</title>\n"
- "<meta charset=\"utf-8\">\n",
- lang ? (char*)lang : "en", site_name ? (char*)site_name : "");
+ errno = 0;
+ fstat(fileno(*input), &fs);
+ if (S_ISDIR(fs.st_mode))
+ return error(EISDIR, __FILE__, __func__, __LINE__,
+ "Is a directory: %s", input_filename);
- char* favicon = NULL;
- CALLOC(favicon, char, BUFSIZE);
- snprintf(favicon, BUFSIZE - 1, "%s/favicon.ico", basedir);
- if (!access(favicon, R_OK))
- print_output(output,
- "<link rel=\"shortcut icon\" type=\"image/x-icon\""
- " href=\"%s\">\n",
- favicon_url ? (char*)favicon_url : "/favicon.ico");
- free(favicon);
+ free(*buffer);
+ *buffer_size = fs.st_size + 1;
+ CALLOC(*buffer, u8, *buffer_size);
+ fread((void*)*buffer, 1, *buffer_size, *input);
+ free(*input_dirname);
- if (meta)
+ slash = strrchr(input_filename, '/');
+ if (slash)
{
- char* filename = NULL;
- CALLOC(filename, char, BUFSIZE);
- snprintf(filename, BUFSIZE, "%s/%s", input_dirname, (char*)meta);
- read_tsv(output, filename, &print_meta_var);
- free(filename);
+ CALLOC(*input_dirname, char, strlen(input_filename) + 1);
+ pinput_dirname = *input_dirname;
+ pinput_filename = input_filename;
+ while (pinput_filename && *pinput_filename
+ && pinput_filename != slash)
+ *pinput_dirname++ = *pinput_filename++;
}
-
- if (canonical && *canonical)
- print_output(output, "<link rel=\"canonical\" href=\"%s\">\n",
- (char*)canonical);
-
- cursor = vars;
- cursor_type = vars;
- cursor_desc = vars;
- do
+ else
{
- feed = get_value(cursor, vars_count - (cursor - vars),
- (u8*)"feed", &cursor);
- feed_type = get_value(cursor_type,
- vars_count - (cursor_type - vars), (u8*)"feed-type",
- &cursor_type);
- feed_desc = get_value(cursor_desc,
- vars_count - (cursor_desc - vars), (u8*)"feed-desc",
- &cursor_desc);
-
- if (feed && *feed && feed_type && *feed_type && feed_desc
- && *feed_desc)
- print_output(output,
- "<link rel=\"alternate\""
- " type=\"%s\" href=\"%s\" "
- "title=\"%s\">\n",
- (char*)feed_type, (char*)feed,
- (char*)feed_desc);
- } while (feed && feed_type && feed_desc);
-
- if (site_desc && *site_desc)
- print_output(output,
- "<meta name=\"description\" content=\"%s\">\n",
- (char*)site_desc);
+ CALLOC(*input_dirname, char, 2);
+ **input_dirname = '.';
+ }
+ fclose(*input);
- print_output(output,
- "<meta name=\"viewport\" content=\"width=device-width,"
- " initial-scale=1\">\n<meta name=\"generator\" "
- "content=\"slweb\">\n");
- output_firstcol = 1;
return 0;
}
-int
-add_css(FILE* output, int output_yaml)
+static int
+read_tsv(FILE* output, const char* filename, tsv_callback_t callback)
{
- if (!vars)
- return -1;
- KeyValue* pvars = vars;
- while (pvars < vars + vars_count)
+ FILE* tsv = NULL;
+ u8* tsv_header[MAX_TSV_REGISTERS];
+ u8* tsv_register[MAX_TSV_REGISTERS];
+ u8* bufline = NULL;
+ u8* pbufline = NULL;
+ u8* token = NULL;
+ u8* ptoken = NULL;
+ u8* eol = NULL;
+ char* ctemp = NULL;
+ size_t tsv_lineno = 0;
+ size_t token_size = 0;
+ unsigned char current_header = 0;
+ unsigned char current_register = 0;
+ unsigned char i;
+
+ assert((output != NULL) && (filename != NULL) && (callback != NULL));
+ if (!(tsv = fopen(filename, "rt")))
{
- if (!strcmp((char*)pvars->key, "stylesheet"))
- process_stylesheet(output, pvars->value, output_yaml);
- else if (!strcmp((char*)pvars->key, "inline-stylesheet"))
- process_inline_stylesheet(output, pvars->value,
- output_yaml);
- pvars++;
+ perror(PROGRAMNAME ": fopen");
+ exit(error(errno, __FILE__, __func__, __LINE__,
+ "fopen failed: %s", filename));
}
- return 0;
-}
-
-int
-end_head_start_body(FILE* output)
-{
- print_output(output, "</head>\n<body>\n");
- output_firstcol = 1;
-
- return 0;
-}
-
-int
-begin_article(FILE* output, const char* source_filename, const size_t sfn_size,
- const u8* link_prefix, const int add_article_header, const u8* author,
- const u8* title, const u8* header_text, const char* title_heading_level,
- u8* date, const int ext_in_permalink, const char* permalink_url)
-{
- int save_incdir = IN(state, ST_INCDIR);
- char* temp = NULL;
-
- UNUSED(add_article_header);
- state &= ~ST_INCDIR;
-
- if (author || date || header_text || title)
- print_output(output, "<header>\n");
-
- if (title)
- print_output(output, "<h%s>%s</h%s>\n",
- title_heading_level ? title_heading_level : "2",
- (char*)title,
- title_heading_level ? title_heading_level : "2");
-
- if (header_text)
- print_output(output, "<p>%s\n", (char*)header_text);
- if (author)
- print_output(output, "<address>%s</address>\n", author);
+ CALLOC(bufline, u8, BUFSIZE);
+ token_size = BUFSIZE;
+ CALLOC(token, u8, token_size);
+ for (i = 0; i < MAX_TSV_REGISTERS; i++)
+ CALLOC(tsv_header[i], u8, BUFSIZE);
+ for (i = 0; i < MAX_TSV_REGISTERS; i++)
+ CALLOC(tsv_register[i], u8, BUFSIZE);
- if (date && source_filename)
+ while (!feof(tsv) && (!tsv_iter || tsv_lineno <= tsv_iter))
{
- u8* permalink_macro = NULL;
- char* link = NULL;
- size_t link_len;
-
- permalink_macro = get_value(macros, macros_count,
- (u8*)"permalink", NULL);
- link = strip_ext((const char*)source_filename, BUFSIZE);
- assert(sfn_size != 0);
- link_len = strlen(link);
- if (ext_in_permalink)
- MEMCCPY((link + link_len), timestamp_output_ext,
- sfn_size - link_len, temp);
- if (permalink_url)
- process_timestamp(output, link_prefix, permalink_url,
- permalink_macro, date, ext_in_permalink);
+ eol = NULL;
+ if (!fgets((char*)bufline, BUFSIZE, tsv))
+ break;
+ eol = (u8*)strchr((char*)bufline, '\n');
+ if (eol)
+ *eol = 0;
+ pbufline = bufline;
+ RESET_TOKEN(token, ptoken, token_size);
+ current_register = 0;
+ tsv_bufline_start:
+ if (!*pbufline)
+ goto tsv_bufline_done;
+ switch (*pbufline)
+ {
+ case '\t':
+ *ptoken = 0;
+ if (tsv_lineno > 0
+ && current_register < MAX_TSV_REGISTERS)
+ {
+ MEMCCPY(tsv_register[current_register],
+ (char*)token, BUFSIZE, ctemp);
+ current_register++;
+ }
+ else if (tsv_lineno <= 0
+ && current_header < MAX_TSV_REGISTERS)
+ {
+ MEMCCPY(tsv_header[current_header],
+ (char*)token, BUFSIZE, ctemp);
+ current_header++;
+ }
+ RESET_TOKEN(token, ptoken, token_size);
+ pbufline++;
+ break;
+ default:
+ CHECKCOPY(token, ptoken, token_size, pbufline);
+ }
+ goto tsv_bufline_start;
+ tsv_bufline_done:
+ *ptoken = 0;
+ if (tsv_lineno > 0)
+ {
+ if (current_register < MAX_TSV_REGISTERS)
+ {
+ MEMCCPY(tsv_register[current_register],
+ (char*)token, BUFSIZE, ctemp);
+ current_register++;
+ }
+ }
else
- process_timestamp(output, link_prefix, link,
- permalink_macro, date, ext_in_permalink);
- free(link);
- }
-
- if (author || date || header_text || title)
- print_output(output, "</header>\n");
+ {
+ if (current_header < MAX_TSV_REGISTERS)
+ {
+ MEMCCPY(tsv_header[current_header],
+ (char*)token, BUFSIZE, ctemp);
+ current_header++;
+ }
+ }
+ RESET_TOKEN(token, ptoken, token_size);
- output_firstcol = author || date || header_text || title;
+ if (tsv_lineno > 0 && pbufline != bufline)
+ (*callback)(output, tsv_header, tsv_register);
- if (save_incdir)
- state |= ST_INCDIR;
+ for (i = 0; i < MAX_TSV_REGISTERS; i++)
+ *tsv_register[i] = 0;
+ tsv_lineno++;
+ }
+ fclose(tsv);
+ for (i = MAX_TSV_REGISTERS; i > 0; i--)
+ free(tsv_register[i - 1]);
+ for (i = MAX_TSV_REGISTERS; i > 0; i--)
+ free(tsv_header[i - 1]);
+ free(token);
+ free(bufline);
return 0;
}
-int
-end_footnotes(FILE* output, int add_footnote_div)
+static int
+reverse_alphacompare(const struct dirent** a, const struct dirent** b)
{
- size_t footnote = 0;
-
- if (IN(state, ST_PARA_OPEN))
- state &= ~ST_PARA_OPEN;
-
- if (add_footnote_div)
- {
- print_output(output, "<div class=\"footnotes\">\n");
- output_firstcol = 1;
- }
-
- process_horizontal_rule(output);
-
- for (footnote = 0; footnote < inline_footnote_count; footnote++)
- {
- print_output(output,
- "<p id=\"inline-footnote-%d\">"
- "<a href=\"#inline-footnote-text-%d\">%d.</a> "
- "%s\n",
- footnote + 1, footnote + 1, footnote + 1,
- (char*)inline_footnotes[footnote]);
- output_firstcol = 1;
- }
-
- KeyValue* pfootnote = footnotes;
- footnote = 0;
- while (pfootnote && footnote < footnote_count)
- {
- print_output(output,
- "<p id=\"footnote-%d\">"
- "<a href=\"#footnote-text-%d\">%d.</a> %s\n",
- footnote + 1, footnote + 1, footnote + 1,
- (char*)pfootnote->value);
- output_firstcol = 1;
- pfootnote++;
- footnote++;
- }
-
- if (add_footnote_div)
- {
- print_output(output, "</div><!--footnotes-->\n");
- output_firstcol = 1;
- }
+ assert((a != NULL) && (b != NULL));
+ return -1 * strcmp((*a)->d_name, (*b)->d_name);
+}
+static int
+set_basedir(char** basedir, size_t* basedir_size, const char* arg)
+{
+ size_t arg_len;
+ char* temp = NULL;
+ assert((basedir != NULL) && (*basedir != NULL) && (basedir_size != NULL)
+ && (arg != NULL));
+ arg_len = strlen(arg);
+ ENSURE_SIZE(*basedir, temp, *basedir_size, (arg_len + 1), (arg_len + 1),
+ set_basedir_alloc_error, char);
+ MEMCCPY(*basedir, arg, *basedir_size, temp);
return 0;
+set_basedir_alloc_error:
+ exit(error(errno, __FILE__, __func__, __LINE__, "Allocation error"));
+ return 1;
}
-int
-end_body_and_html(FILE* output)
+static int
+set_global_link_prefix(char** global_link_prefix,
+ size_t* global_link_prefix_size, const char* arg)
{
- print_output(output, "</body>\n</html>\n");
- output_firstcol = 1;
+ char* temp = NULL;
+ size_t arg_len;
+ assert((arg != NULL) && (global_link_prefix != NULL)
+ && (*global_link_prefix != NULL)
+ && (global_link_prefix_size != NULL));
+ arg_len = strlen(arg);
+ ENSURE_SIZE(*global_link_prefix, temp, *global_link_prefix_size,
+ (arg_len + 1), (arg_len + 1),
+ set_global_link_prefix_alloc_error, char);
+ MEMCCPY(*global_link_prefix, arg, *global_link_prefix_size, temp);
return 0;
+set_global_link_prefix_alloc_error:
+ exit(error(errno, __FILE__, __func__, __LINE__, "Allocation error"));
+ return 1;
}
-int
+static int
simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count,
KeyValue** pvars)
{
+ KeyValue* cvar = NULL;
u8* token = NULL;
u8* ptoken = NULL;
u8* var_key = NULL;
const u8* pline = line;
char* temp = NULL;
int in_arg = 0;
+ int found = 0;
- if (!line || !vars || !vars_count || !pvars)
- exit(error(EINVAL, __FILE__, __func__, __LINE__,
- "Invalid argument"));
-
+ assert((line != NULL) && (vars != NULL) && (vars_count != NULL)
+ && (pvars != NULL));
CALLOC(token, u8, BUFSIZE);
ptoken = token;
*ptoken = 0;
@@ -3044,10 +2848,8 @@ simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count,
*ptoken = 0;
if (var_key && *var_key && *token)
{
- int found = 0;
- KeyValue* cvar = NULL;
-
- cvar = *vars;
+ found = 0;
+ cvar = *vars;
while (!found && cvar != *vars + *vars_count)
{
if (!strcmp((const char*)cvar->key, (const char*)var_key)
@@ -3083,6 +2885,84 @@ simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count,
return 0;
}
+static int
+startswith(const char* s, const char* what)
+{
+ assert((s != NULL) && (what != NULL));
+ do
+ {
+ if (*s++ != *what++)
+ return 0;
+ } while (*s && *what);
+ return !*what;
+}
+
+static char*
+strip_ext(const char* fn, const size_t fn_size)
+{
+ char* newname = NULL;
+ char* pnewname = NULL;
+ const char* pfn = NULL;
+ char* dot = NULL;
+
+ assert(fn != NULL);
+ dot = strrchr(fn, '.');
+ if (!dot)
+ return NULL;
+ newname = strndup(fn, fn_size);
+ if (!newname)
+ {
+ perror(PROGRAMNAME ": strndup");
+ exit(error(errno, __FILE__, __func__, __LINE__,
+ "Allocation error"));
+ }
+ pnewname = newname;
+ pfn = fn;
+ while (pfn != dot && *pfn)
+ *pnewname++ = *pfn++;
+ *pnewname = 0;
+ return newname;
+}
+
+static int
+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://"));
+}
+
+static int
+usage(void)
+{
+ printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n"
+ "\t%s [-b | --body-only] [-d directory | --basedir directory]"
+ " [-p URL | --global-link-prefix URL] [filename]\n",
+ PROGRAMNAME, PROGRAMNAME);
+ return 0;
+}
+
+static int
+version(const int full)
+{
+ printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
+ if (full)
+ puts(COPYRIGHT);
+ return 0;
+}
+
+static int
+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;
+}
+
#define APPEND_TOKEN(what, what_len) \
do \
{ \
@@ -3094,7 +2974,7 @@ simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count,
ptoken = temp ? temp - 1 : &token[token_size - 1]; \
} while (0)
-int
+static int
slweb_parse(FILE* output, const char* source_filename, const size_t sfn_size,
const u8* buffer, const int body_only,
const int read_yaml_macros_and_links)
@@ -3117,21 +2997,22 @@ slweb_parse(FILE* output, const char* source_filename, const size_t sfn_size,
const u8* pbuffer = NULL;
u8* line = NULL;
u8* pline = NULL;
+ u8* token = NULL;
+ u8* ptoken = NULL;
+ u8* link_text = NULL;
+ u8* link_macro = NULL;
+ u8* temp = NULL;
+ u8* temp2 = NULL;
+ u8* eol = NULL;
size_t line_len = 0;
size_t line_size = 0;
size_t entity_len = 0;
- u8* token = NULL;
- u8* ptoken = NULL;
size_t token_size = 0;
size_t token_len = 0;
size_t value_len = 0;
- u8* link_text = NULL;
size_t link_size = 0;
- u8* link_macro = NULL;
- u8* temp = NULL;
- u8* temp2 = NULL;
- UBYTE heading_level = 0;
- ULONG heading_count = 0;
+ size_t heading_count = 0;
+ size_t pline_len = 0;
int break_mark = 0;
int end_tag = 0;
int first_line_in_doc = 1;
@@ -3146,9 +3027,8 @@ slweb_parse(FILE* output, const char* source_filename, const size_t sfn_size,
int add_footnote_div = 0;
int list_para = 0;
int footnote_at_line_start = 0;
- size_t pline_len = 0;
int madeby_present = 0;
- u8* eol = NULL;
+ unsigned char heading_level = 0;
if (!buffer)
exit(error(1, __FILE__, __func__, __LINE__, "Empty buffer"));
@@ -3259,9 +3139,9 @@ do_line:
else
{
if (lineno == 1)
- state |= ST_YAML;
+ SET(state, ST_YAML);
else
- state &= ~ST_YAML;
+ CLEAR(state, ST_YAML);
skip_change_first_line_in_doc = 1;
}
@@ -3271,7 +3151,7 @@ do_line:
{
if (IN(state, ST_NUMLIST))
{
- state &= ~ST_NUMLIST;
+ CLEAR(state, ST_NUMLIST);
if (!read_yaml_macros_and_links)
{
process_list_item_end(output);
@@ -3285,7 +3165,7 @@ do_line:
else
process_list_item_end(output);
process_list_item_start(output);
- state |= ST_PARA_OPEN;
+ SET(state, ST_PARA_OPEN);
}
processed_start_of_line = 1;
@@ -3754,7 +3634,7 @@ do_line:
{
if (IN(state, ST_NUMLIST))
{
- state &= ~ST_NUMLIST;
+ CLEAR(state, ST_NUMLIST);
if (!read_yaml_macros_and_links)
{
process_list_item_end(output);
@@ -3768,14 +3648,12 @@ do_line:
else
process_list_item_end(output);
process_list_item_start(output);
- state |= ST_PARA_OPEN;
+ SET(state, ST_PARA_OPEN);
}
processed_start_of_line = 1;
skip_eol = 0;
-
- state |= ST_LIST;
-
+ SET(state, ST_LIST);
pline += 2;
colno += 2;
}
@@ -3807,8 +3685,7 @@ do_line:
ST_PRE | ST_CODE | ST_HEADING)))
process_bold(output, IN(state, ST_BOLD));
}
-
- state ^= ST_BOLD;
+ TOGGLE(state, ST_BOLD);
pline += 2;
colno += 2;
}
@@ -3819,7 +3696,6 @@ do_line:
if (ANY(state,
ST_INLINE_FOOTNOTE | ST_HEADING
| ST_FOOTNOTE_TEXT | ST_LINK))
-
APPEND_TOKEN(IN(state, ST_ITALIC) ? "</em>"
: "<em>",
sizeof(IN(state, ST_ITALIC) ? "</em>"
@@ -3834,15 +3710,13 @@ do_line:
read_yaml_macros_and_links, list_para,
&token, &ptoken, &token_size, 1, 1);
processed_start_of_line = 1;
-
if (!read_yaml_macros_and_links
&& !(ANY(state,
ST_PRE | ST_CODE | ST_HEADING)))
process_italic(output,
IN(state, ST_ITALIC));
}
-
- state ^= ST_ITALIC;
+ TOGGLE(state, ST_ITALIC);
pline++;
colno++;
}
@@ -3867,7 +3741,7 @@ do_line:
heading_count);
output_firstcol = 0;
}
- state |= ST_HEADING_TEXT;
+ SET(state, ST_HEADING_TEXT);
pline++;
colno++;
break;
@@ -3904,7 +3778,7 @@ do_line:
MEMCCPY(link_macro, (char*)token, BUFSIZE, temp);
*(link_macro + strlen((char*)token)) = 0;
RESET_TOKEN(token, ptoken, token_size);
- state &= ~ST_LINK_MACRO;
+ CLEAR(state, ST_LINK_MACRO);
}
else if (!(IN(state, ST_HEADING) && *(pline - 1) == '#'))
CHECKCOPY(token, ptoken, token_size, pline);
@@ -3940,7 +3814,6 @@ do_line:
{
size_t value_len
= strlen((char*)pmacros->value);
-
if (pmacros->value_size
< value_len + token_len + 1)
{
@@ -3970,9 +3843,8 @@ do_line:
processed_start_of_line = 1;
parse_lbrace_end:
- state |= ST_TAG;
+ SET(state, ST_TAG);
RESET_TOKEN(token, ptoken, token_size);
-
pline++;
colno++;
break;
@@ -4001,16 +3873,13 @@ do_line:
read_yaml_macros_and_links, list_para, &token,
&ptoken, &token_size, 0, 0);
processed_start_of_line = 1;
-
- state |= ST_SUMMARY;
-
+ SET(state, ST_SUMMARY);
if (!read_yaml_macros_and_links)
{
- state |= ST_PARA_OPEN;
+ SET(state, ST_PARA_OPEN);
print_output(output, "<p>\n");
output_firstcol = 1;
}
-
pline += 2;
colno++;
}
@@ -4031,7 +3900,6 @@ do_line:
}
*ptoken = 0;
-
if (IN(state, ST_MACRO_BODY)
&& !(end_tag && startswith((const char*)token, "=")))
{
@@ -4040,7 +3908,6 @@ do_line:
token_len = strlen((char*)token);
skip_eol = 1;
value_len = strlen((char*)pmacros->value);
-
if (pmacros->value_size
< value_len + token_len + 4)
{
@@ -4058,20 +3925,18 @@ do_line:
*token = 0;
ptoken = token;
- state &= ~ST_TAG;
+ CLEAR(state, ST_TAG);
end_tag = 0;
}
else if (IN(state, ST_TAG))
{
- state &= ~ST_TAG;
-
+ CLEAR(state, ST_TAG);
if (break_mark)
{
- if (IN(state, ST_PARA_OPEN))
- state &= ~ST_PARA_OPEN;
+ CLEAR(state, ST_PARA_OPEN);
if (IN(state, ST_LIST))
{
- state &= ~ST_LIST;
+ CLEAR(state, ST_LIST);
if (!read_yaml_macros_and_links)
{
process_list_item_end(output);
@@ -4080,7 +3945,7 @@ do_line:
}
else if (IN(state, ST_NUMLIST))
{
- state &= ~ST_NUMLIST;
+ CLEAR(state, ST_NUMLIST);
if (!read_yaml_macros_and_links)
{
process_list_item_end(output);
@@ -4104,7 +3969,6 @@ do_line:
end_tag = 0;
previous_line_block = 0;
}
-
pline++;
colno++;
break;
@@ -4146,13 +4010,12 @@ do_line:
&& !(ANY(state,
ST_PRE | ST_CODE | ST_HEADING)))
{
- state ^= ST_KBD;
+ TOGGLE(state, ST_KBD);
process_kbd(output,
!(IN(state, ST_KBD)));
output_firstcol = 0;
}
}
-
pline += 2;
colno += 2;
}
@@ -4161,10 +4024,8 @@ do_line:
&& strlen((char*)pline) > 1 && *(pline + 1) == '@')
{
skip_eol = 1;
-
pline += 2;
colno += 2;
-
switch (*pline)
{
case '\\':
@@ -4177,13 +4038,13 @@ do_line:
break;
case '#':
- state |= ST_TABLE_HEADER;
+ SET(state, ST_TABLE_HEADER);
pline++;
colno++;
break;
case '-':
- state &= ~ST_TABLE_HEADER;
+ CLEAR(state, ST_TABLE_HEADER);
if (!read_yaml_macros_and_links)
{
process_table_body_start(output, 0);
@@ -4193,7 +4054,7 @@ do_line:
break;
case ' ':
- state |= ST_TABLE;
+ SET(state, ST_TABLE);
if (!read_yaml_macros_and_links)
{
process_table_body_row_start(output);
@@ -4202,7 +4063,7 @@ do_line:
break;
case '/':
- state &= ~ST_TABLE;
+ CLEAR(state, ST_TABLE);
if (!read_yaml_macros_and_links)
{
process_table_end(output);
@@ -4220,19 +4081,18 @@ do_line:
else if (!(IN(state, ST_PRE)) && colno == 1)
{
skip_eol = 1;
-
if (IN(state, ST_TABLE_HEADER))
{
- state &= ~ST_TABLE_HEADER;
- state |= ST_TABLE_LINE;
+ CLEAR(state, ST_TABLE_HEADER);
+ SET(state, ST_TABLE_LINE);
pline = NULL;
break;
}
if (IN(state, ST_TABLE_LINE))
{
- state &= ~ST_TABLE_LINE;
- state |= ST_TABLE;
+ CLEAR(state, ST_TABLE_LINE);
+ SET(state, ST_TABLE);
if (!read_yaml_macros_and_links)
{
process_table_body_start(output, 1);
@@ -4249,7 +4109,7 @@ do_line:
}
else
{
- state |= ST_TABLE_HEADER;
+ SET(state, ST_TABLE_HEADER);
if (!read_yaml_macros_and_links)
{
process_table_start(output);
@@ -4330,16 +4190,13 @@ do_line:
colno++;
break;
}
-
- state |= ST_HTML_TAG;
+ SET(state, ST_HTML_TAG);
CHECKCOPY(token, ptoken, token_size, pline);
colno++;
break;
case '>':
- if (IN(state, ST_HTML_TAG))
- state &= ~ST_HTML_TAG;
-
+ CLEAR(state, ST_HTML_TAG);
if (ANY(state,
ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE
| ST_MACRO_BODY))
@@ -4356,14 +4213,11 @@ do_line:
if (!read_yaml_macros_and_links
&& !(IN(state, ST_BLOCKQUOTE)))
{
- if (IN(state, ST_PARA_OPEN))
- state &= ~ST_PARA_OPEN;
+ CLEAR(state, ST_PARA_OPEN);
process_blockquote(output, 0);
output_firstcol = 0;
}
-
- state |= ST_BLOCKQUOTE;
-
+ SET(state, ST_BLOCKQUOTE);
pline++;
colno++;
}
@@ -4405,10 +4259,8 @@ do_line:
read_yaml_macros_and_links, list_para, &token,
&ptoken, &token_size, 0, 1);
processed_start_of_line = 1;
-
RESET_TOKEN(token, ptoken, token_size);
-
- state |= ST_IMAGE;
+ SET(state, ST_IMAGE);
keep_token = 1;
pline += 2;
colno += 2;
@@ -4433,7 +4285,7 @@ do_line:
if (colno != 1 && *(pline - 1) == '[')
{
- state |= ST_LINK_MACRO;
+ SET(state, ST_LINK_MACRO);
pline++;
colno++;
}
@@ -4446,7 +4298,6 @@ do_line:
case '[':
pline_len = strlen((char*)pline);
-
if (ANY(state,
ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA
| ST_HEADING | ST_IMAGE | ST_MACRO_BODY
@@ -4458,7 +4309,7 @@ do_line:
}
if (IN(state, ST_FOOTNOTE_TEXT))
- state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN);
+ CLEAR(state, ST_FOOTNOTE_TEXT | ST_PARA_OPEN);
if (pline_len > 1 && *(pline + 1) == '^')
{
@@ -4477,7 +4328,7 @@ do_line:
processed_start_of_line = 1;
RESET_TOKEN(token, ptoken, token_size);
- state |= ST_FOOTNOTE;
+ SET(state, ST_FOOTNOTE);
pline += 2;
colno += 2;
break;
@@ -4496,7 +4347,7 @@ do_line:
processed_start_of_line = 1;
RESET_TOKEN(token, ptoken, token_size);
- state |= ST_LINK;
+ SET(state, ST_LINK);
keep_token = 1;
*link_macro = 0;
pline++;
@@ -4505,7 +4356,7 @@ do_line:
if (*pline == '(')
{
APPEND_TOKEN("<span>", sizeof("<span>"));
- state |= ST_LINK_SPAN;
+ SET(state, ST_LINK_SPAN);
pline++;
colno++;
}
@@ -4522,7 +4373,7 @@ do_line:
else if (*link_macro && (IN(state, ST_LINK)))
{
APPEND_TOKEN("<span>", sizeof("<span>"));
- state |= ST_LINK_SPAN;
+ SET(state, ST_LINK_SPAN);
pline++;
}
else
@@ -4546,7 +4397,7 @@ do_line:
&& *(pline + 1) == ']')
{
APPEND_TOKEN("</span>", sizeof("</span>"));
- state &= ~ST_LINK_SPAN;
+ CLEAR(state, ST_LINK_SPAN);
pline++;
colno++;
}
@@ -4565,20 +4416,19 @@ do_line:
{
if (add_figcaption
&& IN(state, ST_PARA_OPEN))
- state &= ~ST_PARA_OPEN;
-
+ CLEAR(state, ST_PARA_OPEN);
process_inline_image(output, link_text,
image_file_prefix, link_prefix,
token, add_image_links,
add_figcaption);
-
if (add_figcaption)
previous_line_block = 1;
}
}
RESET_TOKEN(token, ptoken, token_size);
- state &= ~(ST_LINK | ST_LINK_SECOND_ARG | ST_IMAGE
- | ST_IMAGE_SECOND_ARG);
+ CLEAR(state,
+ ST_LINK | ST_LINK_SECOND_ARG | ST_IMAGE
+ | ST_IMAGE_SECOND_ARG);
pline++;
}
else
@@ -4601,10 +4451,9 @@ do_line:
{
process_inline_footnote(token,
read_yaml_macros_and_links, output);
-
keep_token = 0;
RESET_TOKEN(token, ptoken, token_size);
- state &= ~ST_INLINE_FOOTNOTE;
+ CLEAR(state, ST_INLINE_FOOTNOTE);
pline++;
colno++;
}
@@ -4613,15 +4462,13 @@ do_line:
int 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;
+ CLEAR(state, ST_FOOTNOTE);
footnote_at_line_start = 0;
pline++;
colno++;
@@ -4634,7 +4481,7 @@ do_line:
pline++;
colno++;
}
- state |= ST_FOOTNOTE_TEXT;
+ SET(state, ST_FOOTNOTE_TEXT);
}
}
else if (IN(state, ST_LINK_SECOND_ARG))
@@ -4645,7 +4492,7 @@ do_line:
link_macro, NULL),
token);
RESET_TOKEN(token, ptoken, token_size);
- state &= ~(ST_LINK | ST_LINK_SECOND_ARG);
+ CLEAR(state, ST_LINK | ST_LINK_SECOND_ARG);
pline++;
colno++;
}
@@ -4654,17 +4501,15 @@ do_line:
if (!read_yaml_macros_and_links)
{
if (add_figcaption && IN(state, ST_PARA_OPEN))
- state &= ~ST_PARA_OPEN;
-
+ CLEAR(state, ST_PARA_OPEN);
process_image(output, link_text,
image_file_prefix, link_prefix, token,
add_image_links, add_figcaption);
-
if (add_figcaption)
previous_line_block = 1;
}
RESET_TOKEN(token, ptoken, token_size);
- state &= ~(ST_IMAGE | ST_IMAGE_SECOND_ARG);
+ CLEAR(state, ST_IMAGE | ST_IMAGE_SECOND_ARG);
pline++;
colno++;
}
@@ -4707,7 +4552,7 @@ do_line:
}
RESET_TOKEN(token, ptoken, token_size);
keep_token = 0;
- state |= ST_LINK_SECOND_ARG_END;
+ SET(state, ST_LINK_SECOND_ARG_END);
break;
case '[':
@@ -4730,14 +4575,14 @@ do_line:
RESET_TOKEN(token, ptoken, token_size);
keep_token = 0;
if (IN(state, ST_LINK))
- state |= ST_LINK_SECOND_ARG;
+ SET(state, ST_LINK_SECOND_ARG);
else
- state |= ST_IMAGE_SECOND_ARG;
+ SET(state, ST_IMAGE_SECOND_ARG);
break;
default:
CHECKCOPY(token, ptoken, token_size, pline);
colno++;
- state &= ~(ST_LINK | ST_IMAGE);
+ CLEAR(state, ST_LINK | ST_IMAGE);
break;
}
}
@@ -4769,9 +4614,8 @@ do_line:
read_yaml_macros_and_links, list_para, &token,
&ptoken, &token_size, 1, 1);
processed_start_of_line = 1;
-
RESET_TOKEN(token, ptoken, token_size);
- state |= ST_INLINE_FOOTNOTE;
+ SET(state, ST_INLINE_FOOTNOTE);
keep_token = 1;
pline += 2;
colno += 2;
@@ -4838,22 +4682,17 @@ do_line:
&token, &ptoken, &token_size, 1, 1);
}
processed_start_of_line = 1;
-
- state ^= ST_DISPLAY_FORMULA;
-
+ TOGGLE(state, ST_DISPLAY_FORMULA);
if (IN(state, ST_DISPLAY_FORMULA))
keep_token = 1;
else
{
*ptoken = 0;
-
if (!read_yaml_macros_and_links)
process_formula(output, token, 1);
-
keep_token = 0;
RESET_TOKEN(token, ptoken, token_size);
}
-
pline += 2;
colno += 2;
}
@@ -4863,7 +4702,6 @@ do_line:
exit(error(1, __FILE__, __func__, __LINE__,
"Formula within an open display "
"formula"));
-
if (!(IN(state, ST_FORMULA)))
{
/* Output existing text up to $ */
@@ -4875,22 +4713,17 @@ do_line:
&token, &ptoken, &token_size, 1, 1);
}
processed_start_of_line = 1;
-
- state ^= ST_FORMULA;
-
+ TOGGLE(state, ST_FORMULA);
if (IN(state, ST_FORMULA))
keep_token = 1;
else
{
*ptoken = 0;
-
if (!read_yaml_macros_and_links)
process_formula(output, token, 0);
-
keep_token = 0;
RESET_TOKEN(token, ptoken, token_size);
}
-
pline++;
colno++;
}
@@ -4919,10 +4752,9 @@ do_line:
{
ptoken = token;
*ptoken = 0;
-
if (IN(state, ST_LIST))
{
- state &= ~ST_LIST;
+ CLEAR(state, ST_LIST);
if (!read_yaml_macros_and_links)
{
process_list_item_end(output);
@@ -4936,14 +4768,11 @@ do_line:
else
process_list_item_end(output);
process_list_item_start(output);
- state |= ST_PARA_OPEN;
+ SET(state, ST_PARA_OPEN);
}
-
processed_start_of_line = 1;
skip_eol = 0;
-
- state |= ST_NUMLIST;
-
+ SET(state, ST_NUMLIST);
pline += 2;
colno += 2;
}
@@ -5145,7 +4974,7 @@ done_line:
}
else if (IN(state, ST_HEADING))
{
- state &= ~(ST_HEADING | ST_HEADING_TEXT);
+ CLEAR(state, ST_HEADING | ST_HEADING_TEXT);
if (!read_yaml_macros_and_links)
{
process_heading(output, token,
@@ -5176,7 +5005,7 @@ done_line:
{
if (IN(state, ST_LIST))
skip_eol = 1;
- state &= ~ST_PARA_OPEN;
+ CLEAR(state, ST_PARA_OPEN);
}
if (!*pbuffer && (IN(state, ST_LIST)))
@@ -5186,7 +5015,7 @@ done_line:
process_list_item_end(output);
process_list_end(output);
}
- state &= ~ST_LIST;
+ CLEAR(state, ST_LIST);
}
if (!*pbuffer && (IN(state, ST_NUMLIST)))
@@ -5196,12 +5025,12 @@ done_line:
process_list_item_end(output);
process_numlist_end(output);
}
- state &= ~ST_NUMLIST;
+ CLEAR(state, ST_NUMLIST);
}
if (!*pbuffer && (IN(state, ST_FOOTNOTE_TEXT)))
{
- state &= ~ST_FOOTNOTE_TEXT;
+ CLEAR(state, ST_FOOTNOTE_TEXT);
*ptoken = 0;
token_len = strlen((char*)token);
skip_eol = 1;
@@ -5250,7 +5079,7 @@ done_line:
if (IN(state, ST_BLOCKQUOTE) && (!*pbuffer || *pbuffer != '>'))
{
- state &= ~ST_BLOCKQUOTE;
+ CLEAR(state, ST_BLOCKQUOTE);
if (!read_yaml_macros_and_links)
{
process_blockquote(output, 1);
@@ -5268,7 +5097,7 @@ done_line:
process_table_end(output);
output_firstcol = 1;
}
- state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE);
+ CLEAR(state, ST_TABLE_HEADER | ST_TABLE_LINE);
}
if ((IN(state, ST_TABLE)) && (!line_len || !*pbuffer))
@@ -5278,7 +5107,7 @@ done_line:
process_table_end(output);
output_firstcol = 1;
}
- state &= ~ST_TABLE;
+ CLEAR(state, ST_TABLE);
}
previous_line_blank = 0;
@@ -5304,8 +5133,9 @@ done_line:
previous_line_blank = 1;
/* Lasts until the end of line */
- state &= ~(ST_YAML_VAL | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG
- | ST_LINK_SECOND_ARG_END);
+ CLEAR(state,
+ ST_YAML_VAL | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG
+ | ST_LINK_SECOND_ARG_END);
if (pbuffer && *pbuffer)
goto do_buffer;
@@ -5329,7 +5159,6 @@ done_buffer:
free(link_macro);
free(token);
free(line);
-
return 0;
parse_alloc_error:
@@ -5373,6 +5202,7 @@ main(const int argc, const char** argv)
cmd = CMD_FULL_VERSION;
else if (!strcmp(arg, "version"))
cmd = CMD_VERSION;
+
else if (!strcmp(arg, "body-only"))
body_only = 1;
else if (startswith(arg, "basedir"))
@@ -5435,7 +5265,6 @@ main(const int argc, const char** argv)
else
{
int result;
-
switch (cmd)
{
case CMD_BASEDIR: