diff options
Diffstat (limited to 'bin/hoedown.c')
-rw-r--r-- | bin/hoedown.c | 579 |
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; } |