summaryrefslogtreecommitdiff
path: root/bin/hoedown.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/hoedown.c')
-rw-r--r--bin/hoedown.c579
1 files changed, 269 insertions, 310 deletions
diff --git a/bin/hoedown.c b/bin/hoedown.c
index 79ba426..175471a 100644
--- a/bin/hoedown.c
+++ b/bin/hoedown.c
@@ -2,32 +2,16 @@
#include "html.h"
#include "common.h"
-//#include <time.h>
+#include <time.h>
-/* NULL RENDERER */
+
+/* FEATURES INFO / DEFAULTS */
enum renderer_type {
RENDERER_HTML,
- RENDERER_HTML_TOC,
- RENDERER_NULL,
+ RENDERER_HTML_TOC
};
-hoedown_renderer *
-null_renderer_new() {
- hoedown_renderer *rend = malloc(sizeof(hoedown_renderer));
- if (rend)
- memset(rend, 0x00, sizeof(hoedown_renderer));
- return rend;
-}
-
-void
-null_renderer_free(hoedown_renderer *rend) {
- free(rend);
-}
-
-
-/* FEATURES INFO / DEFAULTS */
-
struct extension_category_info {
unsigned int flags;
const char *option_name;
@@ -66,7 +50,6 @@ static struct extension_info extensions_info[] = {
{HOEDOWN_EXT_SUPERSCRIPT, "superscript", "Parse super^script."},
{HOEDOWN_EXT_MATH, "math", "Parse TeX $$math$$ syntax, Kramdown style."},
- {HOEDOWN_EXT_LAX_SPACING, "lax-spacing", "Don't require a blank line between some blocks."},
{HOEDOWN_EXT_NO_INTRA_EMPHASIS, "disable-intra-emphasis", "Disable emphasis_between_words."},
{HOEDOWN_EXT_SPACE_HEADERS, "space-headers", "Require a space after '#' in headers."},
{HOEDOWN_EXT_MATH_EXPLICIT, "math-explicit", "Instead of guessing by context, parse $inline math$ and $$always block math$$ (requires --math)."},
@@ -77,7 +60,6 @@ static struct extension_info extensions_info[] = {
static struct html_flag_info html_flags_info[] = {
{HOEDOWN_HTML_SKIP_HTML, "skip-html", "Strip all HTML tags."},
{HOEDOWN_HTML_ESCAPE, "escape", "Escape all HTML."},
- {HOEDOWN_HTML_SAFELINK, "safelink", "Only allow links to safe protocols."},
{HOEDOWN_HTML_HARD_WRAP, "hard-wrap", "Render each linebreak as <br>."},
{HOEDOWN_HTML_USE_XHTML, "xhtml", "Render XHTML."},
};
@@ -93,7 +75,11 @@ static const char *negative_prefix = "no-";
/* PRINT HELP */
void
-print_help(const char *basename) {
+print_help(const char *basename)
+{
+ size_t i;
+ size_t e;
+
/* usage */
printf("Usage: %s [OPTION]... [FILE]\n\n", basename);
@@ -107,7 +93,6 @@ print_help(const char *basename) {
print_option('t', "toc-level=N", "Maximum level for headers included in the TOC. Zero disables TOC (the default).");
print_option( 0, "html", "Render (X)HTML. The default.");
print_option( 0, "html-toc", "Render the Table of Contents in (X)HTML.");
- print_option( 0, "null", "Use a special \"null\" renderer that has no callbacks.");
print_option('T', "time", "Show time spent in rendering.");
print_option('i', "input-unit=N", "Reading block size. Default is " str(DEF_IUNIT) ".");
print_option('o', "output-unit=N", "Writing block size. Default is " str(DEF_OUNIT) ".");
@@ -116,8 +101,6 @@ print_help(const char *basename) {
printf("\n");
/* extensions */
- size_t i;
- size_t e;
for (i = 0; i < count_of(categories_info); i++) {
struct extension_category_info *category = categories_info+i;
printf("%s (--%s%s):\n", category->label, category_prefix, category->option_name);
@@ -143,358 +126,334 @@ print_help(const char *basename) {
"Options are processed in order, so in case of contradictory options the last specified stands.\n\n");
printf("When FILE is '-', read standard input. If no FILE was given, read standard input. Use '--' to signal end of option parsing. "
- "Exit status is 0 if no errors occured, 1 with option parsing errors, 4 with memory allocation errors or 5 with I/O errors.\n\n");
+ "Exit status is 0 if no errors occurred, 1 with option parsing errors, 4 with memory allocation errors or 5 with I/O errors.\n\n");
}
-/* MAIN LOGIC */
+/* OPTION PARSING */
-int
-main(int argc, char **argv)
-{
- int show_time = 0;
- //struct timespec start, end;
+struct option_data {
+ char *basename;
+ int done;
- /* buffers */
- hoedown_buffer *ib, *ob;
- size_t iunit = DEF_IUNIT, ounit = DEF_OUNIT;
+ /* time reporting */
+ int show_time;
- /* files */
- FILE *in = NULL;
+ /* I/O */
+ size_t iunit;
+ size_t ounit;
+ const char *filename;
/* renderer */
- int toc_level = 0;
- int renderer_type = RENDERER_HTML;
+ enum renderer_type renderer;
+ int toc_level;
+ hoedown_html_flags html_flags;
- /* document */
- hoedown_document *document;
- unsigned int extensions = 0;
- size_t max_nesting = DEF_MAX_NESTING;
-
- /* HTML renderer-specific */
- unsigned int html_flags = 0;
-
-
- /* option parsing */
- int just_args = 0;
- int i, j;
- for (i = 1; i < argc; i++) {
- char *arg = argv[i];
- if (!arg[0]) continue;
-
- if (just_args || arg[0] != '-') {
- /* regular argument */
- in = fopen(arg, "r");
- if (!in) {
- fprintf(stderr, "Unable to open input file \"%s\": %s\n", arg, strerror(errno));
- return 5;
- }
- continue;
- }
+ /* parsing */
+ hoedown_extensions extensions;
+ size_t max_nesting;
+};
- if (!arg[1]) {
- /* arg is "-" */
- in = stdin;
- continue;
- }
+int
+parse_short_option(char opt, char *next, void *opaque)
+{
+ struct option_data *data = opaque;
+ long int num;
+ int isNum = next ? parseint(next, &num) : 0;
+
+ if (opt == 'h') {
+ print_help(data->basename);
+ data->done = 1;
+ return 0;
+ }
- if (arg[1] != '-') {
- /* parse short options */
- char opt;
- const char *val;
- for (j = 1; (opt = arg[j]); j++) {
- if (opt == 'h') {
- print_help(argv[0]);
- return 1;
- }
-
- if (opt == 'v') {
- print_version();
- return 1;
- }
-
- if (opt == 'T') {
- show_time = 1;
- continue;
- }
-
- /* options requiring value */
- if (arg[++j]) val = arg+j;
- else if (argv[++i]) val = argv[i];
- else {
- fprintf(stderr, "Wrong option '-%c' found.\n", opt);
- return 1;
- }
-
- long int num;
- int isNum = parseint(val, &num);
-
- if (opt == 'n' && isNum) {
- max_nesting = num;
- break;
- }
-
- if (opt == 't' && isNum) {
- toc_level = num;
- break;
- }
-
- if (opt == 'i' && isNum) {
- iunit = num;
- break;
- }
-
- if (opt == 'o' && isNum) {
- ounit = num;
- break;
- }
-
- fprintf(stderr, "Wrong option '-%c' found.\n", opt);
- return 1;
- }
- continue;
- }
+ if (opt == 'v') {
+ print_version();
+ data->done = 1;
+ return 0;
+ }
- if (!arg[2]) {
- /* arg is "--" */
- just_args = 1;
- continue;
- }
+ if (opt == 'T') {
+ data->show_time = 1;
+ return 1;
+ }
- /* parse long option */
- char opt [100];
- strncpy(opt, arg+2, 100);
- opt[99] = 0;
+ /* options requiring value */
+ /* FIXME: add validation */
- char *val = strchr(opt, '=');
+ if (opt == 'n' && isNum) {
+ data->max_nesting = num;
+ return 2;
+ }
- long int num = 0;
- int isNum = 0;
+ if (opt == 't' && isNum) {
+ data->toc_level = num;
+ return 2;
+ }
- if (val) {
- *val = 0;
- val++;
+ if (opt == 'i' && isNum) {
+ data->iunit = num;
+ return 2;
+ }
- if (*val)
- isNum = parseint(val, &num);
- }
+ if (opt == 'o' && isNum) {
+ data->ounit = num;
+ return 2;
+ }
- int opt_parsed = 0;
+ fprintf(stderr, "Wrong option '-%c' found.\n", opt);
+ return 0;
+}
- if (strcmp(opt, "help")==0) {
- print_help(argv[0]);
- return 1;
- }
+int
+parse_category_option(char *opt, struct option_data *data)
+{
+ size_t i;
+ const char *name = strprefix(opt, category_prefix);
+ if (!name) return 0;
- if (strcmp(opt, "version")==0) {
- print_version();
+ for (i = 0; i < count_of(categories_info); i++) {
+ struct extension_category_info *category = &categories_info[i];
+ if (strcmp(name, category->option_name)==0) {
+ data->extensions |= category->flags;
return 1;
}
+ }
- if (strcmp(opt, "max-nesting")==0 && isNum) {
- opt_parsed = 1;
- max_nesting = num;
- }
- if (strcmp(opt, "toc-level")==0 && isNum) {
- opt_parsed = 1;
- toc_level = num;
- }
- if (strcmp(opt, "input-unit")==0 && isNum) {
- opt_parsed = 1;
- iunit = num;
- }
- if (strcmp(opt, "output-unit")==0 && isNum) {
- opt_parsed = 1;
- ounit = num;
- }
+ return 0;
+}
- if (strcmp(opt, "html")==0) {
- opt_parsed = 1;
- renderer_type = RENDERER_HTML;
- }
- if (strcmp(opt, "html-toc")==0) {
- opt_parsed = 1;
- renderer_type = RENDERER_HTML_TOC;
- }
- if (strcmp(opt, "null")==0) {
- opt_parsed = 1;
- renderer_type = RENDERER_NULL;
- }
+int
+parse_flag_option(char *opt, struct option_data *data)
+{
+ size_t i;
- const char *name;
- size_t i;
-
- /* extension categories */
- if ((name = strprefix(opt, category_prefix))) {
- for (i = 0; i < count_of(categories_info); i++) {
- struct extension_category_info *category = categories_info+i;
- if (strcmp(name, category->option_name)==0) {
- opt_parsed = 1;
- extensions |= category->flags;
- break;
- }
- }
+ for (i = 0; i < count_of(extensions_info); i++) {
+ struct extension_info *extension = &extensions_info[i];
+ if (strcmp(opt, extension->option_name)==0) {
+ data->extensions |= extension->flag;
+ return 1;
}
+ }
- /* extensions */
- for (i = 0; i < count_of(extensions_info); i++) {
- struct extension_info *extension = extensions_info+i;
- if (strcmp(opt, extension->option_name)==0) {
- opt_parsed = 1;
- extensions |= extension->flag;
- break;
- }
+ for (i = 0; i < count_of(html_flags_info); i++) {
+ struct html_flag_info *html_flag = &html_flags_info[i];
+ if (strcmp(opt, html_flag->option_name)==0) {
+ data->html_flags |= html_flag->flag;
+ return 1;
}
+ }
- /* html flags */
- for (i = 0; i < count_of(html_flags_info); i++) {
- struct html_flag_info *html_flag = html_flags_info+i;
- if (strcmp(opt, html_flag->option_name)==0) {
- opt_parsed = 1;
- html_flags |= html_flag->flag;
- break;
- }
- }
+ return 0;
+}
- /* negations */
- if ((name = strprefix(opt, negative_prefix))) {
- for (i = 0; i < count_of(categories_info); i++) {
- struct extension_category_info *category = categories_info+i;
- if (strcmp(name, category->option_name)==0) {
- opt_parsed = 1;
- extensions &= ~(category->flags);
- break;
- }
- }
- for (i = 0; i < count_of(extensions_info); i++) {
- struct extension_info *extension = extensions_info+i;
- if (strcmp(name, extension->option_name)==0) {
- opt_parsed = 1;
- extensions &= ~(extension->flag);
- break;
- }
- }
- for (i = 0; i < count_of(html_flags_info); i++) {
- struct html_flag_info *html_flag = html_flags_info+i;
- if (strcmp(name, html_flag->option_name)==0) {
- opt_parsed = 1;
- html_flags &= ~(html_flag->flag);
- break;
- }
- }
+int
+parse_negative_option(char *opt, struct option_data *data)
+{
+ size_t i;
+ const char *name = strprefix(opt, negative_prefix);
+ if (!name) return 0;
+
+ for (i = 0; i < count_of(categories_info); i++) {
+ struct extension_category_info *category = &categories_info[i];
+ if (strcmp(name, category->option_name)==0) {
+ data->extensions &= ~(category->flags);
+ return 1;
}
+ }
- if (strcmp(opt, "time")==0) {
- opt_parsed = 1;
- show_time = 1;
+ for (i = 0; i < count_of(extensions_info); i++) {
+ struct extension_info *extension = &extensions_info[i];
+ if (strcmp(name, extension->option_name)==0) {
+ data->extensions &= ~(extension->flag);
+ return 1;
}
+ }
- if (!opt_parsed) {
- fprintf(stderr, "Wrong option '%s' found.\n", arg);
+ for (i = 0; i < count_of(html_flags_info); i++) {
+ struct html_flag_info *html_flag = &html_flags_info[i];
+ if (strcmp(name, html_flag->option_name)==0) {
+ data->html_flags &= ~(html_flag->flag);
return 1;
}
}
- if (!in)
- in = stdin;
+ return 0;
+}
+int
+parse_long_option(char *opt, char *next, void *opaque)
+{
+ struct option_data *data = opaque;
+ long int num;
+ int isNum = next ? parseint(next, &num) : 0;
+
+ if (strcmp(opt, "help")==0) {
+ print_help(data->basename);
+ data->done = 1;
+ return 0;
+ }
- /* reading everything */
- ib = hoedown_buffer_new(iunit);
- if (!ib) {
- fprintf(stderr, "Couldn't allocate input buffer.\n");
- return 4;
+ if (strcmp(opt, "version")==0) {
+ print_version();
+ data->done = 1;
+ return 0;
}
- while (!feof(in)) {
- if (ferror(in)) {
- fprintf(stderr, "I/O errors found while reading input.\n");
- return 5;
- }
- if (hoedown_buffer_grow(ib, ib->size + iunit) != HOEDOWN_BUF_OK) {
- fprintf(stderr, "Couldn't grow input buffer.\n");
- return 4;
- }
- ib->size += fread(ib->data + ib->size, 1, iunit, in);
+ if (strcmp(opt, "time")==0) {
+ data->show_time = 1;
+ return 1;
}
- if (in != stdin)
- fclose(in);
+ /* FIXME: validation */
+ if (strcmp(opt, "max-nesting")==0 && isNum) {
+ data->max_nesting = num;
+ return 2;
+ }
+ if (strcmp(opt, "toc-level")==0 && isNum) {
+ data->toc_level = num;
+ return 2;
+ }
+ if (strcmp(opt, "input-unit")==0 && isNum) {
+ data->iunit = num;
+ return 2;
+ }
+ if (strcmp(opt, "output-unit")==0 && isNum) {
+ data->ounit = num;
+ return 2;
+ }
- /* creating the renderer */
- hoedown_renderer *renderer;
- void (*renderer_free)(hoedown_renderer*);
+ if (strcmp(opt, "html")==0) {
+ data->renderer = RENDERER_HTML;
+ return 1;
+ }
+ if (strcmp(opt, "html-toc")==0) {
+ data->renderer = RENDERER_HTML_TOC;
+ return 1;
+ }
- switch (renderer_type) {
- case RENDERER_HTML:
- renderer = hoedown_html_renderer_new(html_flags, toc_level);
- renderer_free = hoedown_html_renderer_free;
- break;
- case RENDERER_HTML_TOC:
- renderer = hoedown_html_toc_renderer_new(toc_level);
- renderer_free = hoedown_html_renderer_free;
- break;
- case RENDERER_NULL:
- renderer = null_renderer_new();
- renderer_free = null_renderer_free;
- break;
- default:
- renderer = NULL;
- renderer_free = NULL;
- };
+ if (parse_category_option(opt, data) || parse_flag_option(opt, data) || parse_negative_option(opt, data))
+ return 1;
- if (!renderer) {
- fprintf(stderr, "Couldn't allocate renderer.\n");
- return 4;
- }
+ fprintf(stderr, "Wrong option '--%s' found.\n", opt);
+ return 0;
+}
+int
+parse_argument(int argn, char *arg, int is_forced, void *opaque)
+{
+ struct option_data *data = opaque;
- /* performing markdown rendering */
- ob = hoedown_buffer_new(ounit);
- if (!ob) {
- fprintf(stderr, "Couldn't allocate output buffer.\n");
- return 4;
+ if (argn == 0) {
+ /* Input file */
+ if (strcmp(arg, "-")!=0 || is_forced) data->filename = arg;
+ return 1;
}
- document = hoedown_document_new(renderer, extensions, max_nesting);
- if (!document) {
- fprintf(stderr, "Couldn't allocate document parser.\n");
- return 4;
- }
+ fprintf(stderr, "Too many arguments.\n");
+ return 0;
+}
- //clock_gettime(CLOCK_MONOTONIC, &start);
- hoedown_document_render(document, ob, ib->data, ib->size);
- //clock_gettime(CLOCK_MONOTONIC, &end);
+/* MAIN LOGIC */
- /* writing the result to stdout */
- (void)fwrite(ob->data, 1, ob->size, stdout);
+int
+main(int argc, char **argv)
+{
+ struct option_data data;
+ clock_t t1, t2;
+ FILE *file = stdin;
+ hoedown_buffer *ib, *ob;
+ hoedown_renderer *renderer = NULL;
+ void (*renderer_free)(hoedown_renderer *) = NULL;
+ hoedown_document *document;
+ /* Parse options */
+ data.basename = argv[0];
+ data.done = 0;
+ data.show_time = 0;
+ data.iunit = DEF_IUNIT;
+ data.ounit = DEF_OUNIT;
+ data.filename = NULL;
+ data.renderer = RENDERER_HTML;
+ data.toc_level = 0;
+ data.html_flags = 0;
+ data.extensions = 0;
+ data.max_nesting = DEF_MAX_NESTING;
+
+ argc = parse_options(argc, argv, parse_short_option, parse_long_option, parse_argument, &data);
+ if (data.done) return 0;
+ if (!argc) return 1;
+
+ /* Open input file, if needed */
+ if (data.filename) {
+ file = fopen(data.filename, "r");
+ if (!file) {
+ fprintf(stderr, "Unable to open input file \"%s\": %s\n", data.filename, strerror(errno));
+ return 5;
+ }
+ }
- /* showing rendering time */
- if (show_time) {
- //TODO: enable this
- //long long elapsed = ( end.tv_sec*1000000000 + end.tv_nsec)
- // - (start.tv_sec*1000000000 + start.tv_nsec);
- //if (elapsed < 1000000000)
- // fprintf(stderr, "Time spent on rendering: %.2f ms.\n", ((double)elapsed)/1000000);
- //else
- // fprintf(stderr, "Time spent on rendering: %.3f s.\n", ((double)elapsed)/1000000000);
+ /* Read everything */
+ ib = hoedown_buffer_new(data.iunit);
+
+ if (hoedown_buffer_putf(ib, file)) {
+ fprintf(stderr, "I/O errors found while reading input.\n");
+ return 5;
}
+ if (file != stdin) fclose(file);
- /* cleanup */
- hoedown_buffer_free(ib);
- hoedown_buffer_free(ob);
+ /* Create the renderer */
+ switch (data.renderer) {
+ case RENDERER_HTML:
+ renderer = hoedown_html_renderer_new(data.html_flags, data.toc_level);
+ renderer_free = hoedown_html_renderer_free;
+ break;
+ case RENDERER_HTML_TOC:
+ renderer = hoedown_html_toc_renderer_new(data.toc_level);
+ renderer_free = hoedown_html_renderer_free;
+ break;
+ };
+ /* Perform Markdown rendering */
+ ob = hoedown_buffer_new(data.ounit);
+ document = hoedown_document_new(renderer, data.extensions, data.max_nesting);
+
+ t1 = clock();
+ hoedown_document_render(document, ob, ib->data, ib->size);
+ t2 = clock();
+
+ /* Cleanup */
+ hoedown_buffer_free(ib);
hoedown_document_free(document);
renderer_free(renderer);
+ /* Write the result to stdout */
+ (void)fwrite(ob->data, 1, ob->size, stdout);
+ hoedown_buffer_free(ob);
+
if (ferror(stdout)) {
fprintf(stderr, "I/O errors found while writing output.\n");
return 5;
}
+ /* Show rendering time */
+ if (data.show_time) {
+ double elapsed;
+
+ if (t1 == ((clock_t) -1) || t2 == ((clock_t) -1)) {
+ fprintf(stderr, "Failed to get the time.\n");
+ return 1;
+ }
+
+ elapsed = (double)(t2 - t1) / CLOCKS_PER_SEC;
+ if (elapsed < 1)
+ fprintf(stderr, "Time spent on rendering: %7.2f ms.\n", elapsed*1e3);
+ else
+ fprintf(stderr, "Time spent on rendering: %6.3f s.\n", elapsed);
+ }
+
return 0;
}