summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Mendez <jmendeth@gmail.com>2014-03-23 19:58:47 +0100
committerXavier Mendez <jmendeth@gmail.com>2014-03-23 19:58:47 +0100
commit77392bf35071c0fae5ced8ec018335c8aa09f027 (patch)
tree735cbaa17ce0d754cee6ae0569f0494f9f020873
parent8bc7aadbff5b50eb603cf76c6844f9711bae45a9 (diff)
downloadrust-hoedown-77392bf35071c0fae5ced8ec018335c8aa09f027.tar.gz
Rewrite hoedown.c (option parsing and lots of features :)
-rw-r--r--examples/common.h43
-rw-r--r--examples/hoedown.c475
2 files changed, 492 insertions, 26 deletions
diff --git a/examples/common.h b/examples/common.h
new file mode 100644
index 0000000..d9ace1b
--- /dev/null
+++ b/examples/common.h
@@ -0,0 +1,43 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define str(x) __str(x)
+#define __str(x) #x
+
+#define count_of(arr) (sizeof(arr)/sizeof(0[arr]))
+
+int
+parseint(const char *string, long *result) {
+ char *end;
+ errno = 0;
+ *result = strtol(string, &end, 10);
+ return !(*end || errno);
+}
+
+const char *
+strprefix(const char *str, const char *prefix) {
+ while (*prefix) {
+ if (!(*str && *str == *prefix)) return 0;
+ prefix++; str++;
+ }
+ return str;
+}
+
+void
+print_option(char short_opt, const char *long_opt, const char *description) {
+ if (short_opt)
+ printf(" -%c, ", short_opt);
+ else
+ printf(" ");
+
+ printf("--%-13s %s\n", long_opt, description);
+}
+
+void
+print_version() {
+ int major, minor, revision;
+ hoedown_version(&major, &minor, &revision);
+ printf("Built with Hoedown v%d.%d.%d.\n", major, minor, revision);
+} \ No newline at end of file
diff --git a/examples/hoedown.c b/examples/hoedown.c
index 293ab5f..36a7c99 100644
--- a/examples/hoedown.c
+++ b/examples/hoedown.c
@@ -1,79 +1,502 @@
#include "document.h"
#include "html.h"
+#include "version.h"
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "common.h"
+//#include <time.h>
-#define READ_UNIT 1024
-#define OUTPUT_UNIT 64
+/* NULL RENDERER */
+
+enum renderer_type {
+ RENDERER_HTML,
+ RENDERER_HTML_TOC,
+ RENDERER_NULL,
+};
+
+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;
+ const char *label;
+};
+
+struct extension_info {
+ unsigned int flag;
+ const char *option_name;
+ const char *description;
+};
+
+struct html_flag_info {
+ unsigned int flag;
+ const char *option_name;
+ const char *description;
+};
+
+static struct extension_category_info categories_info[] = {
+ {HOEDOWN_EXT_BLOCK, "block", "Block extensions"},
+ {HOEDOWN_EXT_SPAN, "span", "Span extensions"},
+ {HOEDOWN_EXT_FLAGS, "flags", "Other flags"},
+ {HOEDOWN_EXT_NEGATIVE, "negative", "Negative flags"},
+};
+
+static struct extension_info extensions_info[] = {
+ {HOEDOWN_EXT_TABLES, "tables", "Parse PHP-Markdown style tables."},
+ {HOEDOWN_EXT_FENCED_CODE, "fenced-code", "Parse fenced code blocks."},
+ {HOEDOWN_EXT_FOOTNOTES, "footnotes", "Parse footnotes."},
+
+ {HOEDOWN_EXT_AUTOLINK, "autolink", "Automatically turn URLs into links."},
+ {HOEDOWN_EXT_STRIKETHROUGH, "strikethrough", "Parse ~~stikethrough~~ spans."},
+ {HOEDOWN_EXT_UNDERLINE, "underline", "Parse _underline_ instead of emphasis."},
+ {HOEDOWN_EXT_HIGHLIGHT, "highlight", "Parse ==highlight== spans."},
+ {HOEDOWN_EXT_QUOTE, "quote", "Render \"quotes\" as <q>quotes</q>."},
+ {HOEDOWN_EXT_SUPERSCRIPT, "superscript", "Parse super^script."},
+
+ {HOEDOWN_EXT_LAX_SPACING, "lax-spacing", "Allow HTML blocks on the same line as text."},
+ {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_DISABLE_INDENTED_CODE, "disable-indented-code", "Don't parse indented code blocks."},
+};
+
+static struct html_flag_info html_flags_info[] = {
+ {HOEDOWN_HTML_SKIP_HTML, "skip-html", "Strip all HTML tags."},
+ {HOEDOWN_HTML_SKIP_STYLE, "skip-style", "Strip <style> tags."},
+ {HOEDOWN_HTML_SKIP_IMAGES, "skip-images", "Don't render images."},
+ {HOEDOWN_HTML_SKIP_LINKS, "skip-links", "Don't render links."},
+ {HOEDOWN_HTML_EXPAND_TABS, "expand-tabs", "Expand tabs to spaces."},
+ {HOEDOWN_HTML_SAFELINK, "safelink", "Only allow links to safe protocols."},
+ {HOEDOWN_HTML_TOC, "toc", "Produce links to the Table of Contents."},
+ {HOEDOWN_HTML_HARD_WRAP, "hard-wrap", "Render each linebreak as <br>."},
+ {HOEDOWN_HTML_USE_XHTML, "xhtml", "Render XHTML."},
+ {HOEDOWN_HTML_ESCAPE, "escape", "Escape all HTML."},
+};
+
+static const char *category_prefix = "all-";
+static const char *negative_prefix = "no-";
+
+#define DEF_IUNIT 1024
+#define DEF_OUNIT 64
+#define DEF_MAX_NESTING 16
+
+
+/* PRINT HELP */
+
+void
+print_help(const char *basename) {
+ /* usage */
+ printf("Usage: %s [OPTION]... [FILE]\n\n", basename);
+
+ /* description */
+ printf("Process the Markdown in FILE (or standard input) and render it to standard output, using the Hoedown library. "
+ "Parsing and rendering can be customized through the options below. The default is to parse pure markdown and output HTML.\n\n");
+
+ /* main options */
+ printf("Main options:\n");
+ print_option('n', "max-nesting=N", "Maximum level of block nesting parsed. Default is " str(DEF_MAX_NESTING) ".");
+ print_option('t', "toc-level=N", "Maximum level for headers included in the TOC. Implies '--toc'.");
+ 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) ".");
+ print_option('h', "help", "Print this help text.");
+ print_option('v', "version", "Print Hoedown version.");
+ 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);
+ for (e = 0; e < count_of(extensions_info); e++) {
+ struct extension_info *extension = extensions_info+e;
+ if (extension->flag & category->flags) {
+ print_option( 0, extension->option_name, extension->description);
+ }
+ }
+ printf("\n");
+ }
+
+ /* html-specific */
+ printf("HTML-specific options:\n");
+ for (i = 0; i < count_of(html_flags_info); i++) {
+ struct html_flag_info *html_flag = html_flags_info+i;
+ print_option( 0, html_flag->option_name, html_flag->description);
+ }
+ printf("\n");
+
+ /* ending */
+ printf("Flags and extensions can be negated by prepending 'no' to them, as in '--no-tables', '--no-span' or '--no-escape'. "
+ "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");
+}
+
+
+/* MAIN LOGIC */
int
main(int argc, char **argv)
{
+ int show_time = 0;
+ //struct timespec start, end;
+
+ /* buffers */
hoedown_buffer *ib, *ob;
- FILE *in = stdin;
+ size_t iunit = DEF_IUNIT, ounit = DEF_OUNIT;
- hoedown_renderer *renderer;
+ /* files */
+ FILE *in = NULL;
+
+ /* renderer */
+ int toc_level = 0;
+ int renderer_type = RENDERER_HTML;
+
+ /* 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;
+ }
+
+ if (!arg[1]) {
+ /* arg is "-" */
+ in = stdin;
+ continue;
+ }
+
+ 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;
+ }
- /* opening the file if given from the command line */
- if (argc > 1) {
- in = fopen(argv[1], "r");
- if (!in) {
- fprintf(stderr, "Unable to open input file \"%s\": %s\n", argv[1], strerror(errno));
+ if (!arg[2]) {
+ /* arg is "--" */
+ just_args = 1;
+ continue;
+ }
+
+ /* parse long option */
+ char opt [100];
+ strncpy(opt, arg+2, 100);
+ opt[99] = 0;
+
+ char *val = strchr(opt, '=');
+
+ long int num = 0;
+ int isNum = 0;
+
+ if (val) {
+ *val = 0;
+ val++;
+
+ if (*val)
+ isNum = parseint(val, &num);
+ }
+
+ int opt_parsed = 0;
+
+ if (strcmp(opt, "help")==0) {
+ print_help(argv[0]);
+ return 1;
+ }
+
+ if (strcmp(opt, "version")==0) {
+ print_version();
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+ }
+
+ /* 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;
+ }
+ }
+
+ /* 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;
+ }
+ }
+
+ /* 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;
+ }
+ }
+ }
+
+ if (strcmp(opt, "time")==0) {
+ opt_parsed = 1;
+ show_time = 1;
+ }
+
+ if (!opt_parsed) {
+ fprintf(stderr, "Wrong option '%s' found.\n", arg);
return 1;
}
}
+ if (!in)
+ in = stdin;
+
+
/* reading everything */
- ib = hoedown_buffer_new(READ_UNIT);
+ ib = hoedown_buffer_new(iunit);
if (!ib) {
fprintf(stderr, "Couldn't allocate input buffer.\n");
- return 1;
+ return 4;
}
while (!feof(in)) {
if (ferror(in)) {
fprintf(stderr, "I/O errors found while reading input.\n");
- return 1;
+ return 5;
}
- if (hoedown_buffer_grow(ib, ib->size + READ_UNIT) != HOEDOWN_BUF_OK) {
+ if (hoedown_buffer_grow(ib, ib->size + iunit) != HOEDOWN_BUF_OK) {
fprintf(stderr, "Couldn't grow input buffer.\n");
- return 1;
+ return 4;
}
- ib->size += fread(ib->data + ib->size, 1, READ_UNIT, in);
+ ib->size += fread(ib->data + ib->size, 1, iunit, in);
}
if (in != stdin)
fclose(in);
+
+ /* creating the renderer */
+ hoedown_renderer *renderer;
+ void (*renderer_free)(hoedown_renderer*);
+
+ 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;
+ };
+
+ if (!renderer) {
+ fprintf(stderr, "Couldn't allocate renderer.\n");
+ return 4;
+ }
+
+
/* performing markdown parsing */
- ob = hoedown_buffer_new(OUTPUT_UNIT);
+ ob = hoedown_buffer_new(ounit);
if (!ob) {
fprintf(stderr, "Couldn't allocate output buffer.\n");
- return 1;
+ return 4;
}
- renderer = hoedown_html_renderer_new(0, 0);
- document = hoedown_document_new(renderer, 0, 16);
+ document = hoedown_document_new(renderer, extensions, max_nesting);
+ if (!document) {
+ fprintf(stderr, "Couldn't allocate document parser.\n");
+ return 4;
+ }
+ //clock_gettime(CLOCK_MONOTONIC, &start);
hoedown_document_render(document, ob, ib->data, ib->size);
+ //clock_gettime(CLOCK_MONOTONIC, &end);
- hoedown_document_free(document);
- hoedown_html_renderer_free(renderer);
/* writing the result to stdout */
(void)fwrite(ob->data, 1, ob->size, stdout);
+
+ /* 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);
+ }
+
+
/* cleanup */
hoedown_buffer_free(ib);
hoedown_buffer_free(ob);
+ hoedown_document_free(document);
+ renderer_free(renderer);
+
if (ferror(stdout)) {
fprintf(stderr, "I/O errors found while writing output.\n");
- return 1;
+ return 5;
}
return 0;