From a75a5ec9df0098656101feb4ea4dd2c404f966f3 Mon Sep 17 00:00:00 2001 From: Frank Smit Date: Tue, 2 Jun 2015 15:03:44 +0200 Subject: [PATCH] Use CFFI for Hoedown binding. --- .gitignore | 1 + .gitmodules | 3 - ChangeLog.md | 5 + LICENSE.txt | 2 +- README.rst | 6 +- build_ffi.py | 284 ++ misaka/__init__.py | 2 + misaka/api.py | 250 ++ misaka/constants.py | 43 + setup.py | 68 +- src/extra.c | 21 + src/extra.h | 19 + src/{sundown => hoedown}/autolink.c | 68 +- src/hoedown/autolink.h | 46 + src/hoedown/buffer.c | 308 ++ src/hoedown/buffer.h | 134 + src/hoedown/document.c | 2966 ++++++++++++++++++ src/hoedown/document.h | 172 ++ src/hoedown/escape.c | 188 ++ src/hoedown/escape.h | 28 + src/hoedown/html.c | 754 +++++ src/hoedown/html.h | 84 + src/hoedown/html_blocks.c | 240 ++ src/hoedown/html_smartypants.c | 435 +++ src/hoedown/stack.c | 79 + src/hoedown/stack.h | 52 + src/hoedown/version.c | 9 + src/hoedown/version.h | 33 + src/misaka.c | 4467 --------------------------- src/misaka.pyx | 247 -- src/sundown.pxd | 97 - src/sundown/autolink.h | 51 - src/sundown/buffer.c | 225 -- src/sundown/buffer.h | 96 - src/sundown/houdini.h | 37 - src/sundown/houdini_href_e.c | 108 - src/sundown/houdini_html_e.c | 84 - src/sundown/html.c | 635 ---- src/sundown/html.h | 77 - src/sundown/html_blocks.h | 206 -- src/sundown/html_smartypants.c | 389 --- src/sundown/markdown.c | 2556 --------------- src/sundown/markdown.h | 138 - src/sundown/stack.c | 81 - src/sundown/stack.h | 29 - src/wrapper.c | 336 -- src/wrapper.h | 15 - src/wrapper.pxd | 16 - vendor/sundown | 1 - 49 files changed, 6192 insertions(+), 9999 deletions(-) delete mode 100644 .gitmodules create mode 100644 build_ffi.py create mode 100644 misaka/__init__.py create mode 100644 misaka/api.py create mode 100644 misaka/constants.py create mode 100644 src/extra.c create mode 100644 src/extra.h rename src/{sundown => hoedown}/autolink.c (73%) create mode 100644 src/hoedown/autolink.h create mode 100644 src/hoedown/buffer.c create mode 100644 src/hoedown/buffer.h create mode 100644 src/hoedown/document.c create mode 100644 src/hoedown/document.h create mode 100644 src/hoedown/escape.c create mode 100644 src/hoedown/escape.h create mode 100644 src/hoedown/html.c create mode 100644 src/hoedown/html.h create mode 100644 src/hoedown/html_blocks.c create mode 100644 src/hoedown/html_smartypants.c create mode 100644 src/hoedown/stack.c create mode 100644 src/hoedown/stack.h create mode 100644 src/hoedown/version.c create mode 100644 src/hoedown/version.h delete mode 100644 src/misaka.c delete mode 100644 src/misaka.pyx delete mode 100644 src/sundown.pxd delete mode 100644 src/sundown/autolink.h delete mode 100644 src/sundown/buffer.c delete mode 100644 src/sundown/buffer.h delete mode 100644 src/sundown/houdini.h delete mode 100644 src/sundown/houdini_href_e.c delete mode 100644 src/sundown/houdini_html_e.c delete mode 100644 src/sundown/html.c delete mode 100644 src/sundown/html.h delete mode 100644 src/sundown/html_blocks.h delete mode 100644 src/sundown/html_smartypants.c delete mode 100644 src/sundown/markdown.c delete mode 100644 src/sundown/markdown.h delete mode 100644 src/sundown/stack.c delete mode 100644 src/sundown/stack.h delete mode 100644 src/wrapper.c delete mode 100644 src/wrapper.h delete mode 100644 src/wrapper.pxd delete mode 160000 vendor/sundown diff --git a/.gitignore b/.gitignore index 87497f7..f1ca8c6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__ _build build dist +.eggs *.egg-info *.sublime-* *.pyc diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f146a4b..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "vendor/sundown"] - path = vendor/sundown - url = git://github.com/FSX/sundown.git diff --git a/ChangeLog.md b/ChangeLog.md index 5f1839f..8f27d95 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,11 @@ News/Changelog ============== +## 2.0.0 (????-??-??) + + - Use CFFI instead of Cython. + + ## 1.0.3 (2012-11-??) - `scripts/misaka`: Read stdin when no file is specified. ([#22][]) diff --git a/LICENSE.txt b/LICENSE.txt index 263ff2d..078c6c8 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (C) 2011 by Frank Smit +Copyright (C) 2011-2015 by Frank Smit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.rst b/README.rst index a88aa32..22d5157 100644 --- a/README.rst +++ b/README.rst @@ -3,18 +3,16 @@ Misaka .. image:: https://secure.travis-ci.org/FSX/misaka.png?branch=master -The Python binding for Sundown_, a markdown parsing library. +A CFFI binding for Hoedown_, a markdown parsing library. Documentation can be found at: http://misaka.61924.nl/ -.. _Sundown: https://github.com/vmg/sundown +.. _Hoedown: https://github.com/hoedown/hoedown Installation ------------ -Cython is only needed to compile .pyx file. - With pip:: pip install misaka diff --git a/build_ffi.py b/build_ffi.py new file mode 100644 index 0000000..a76a2ca --- /dev/null +++ b/build_ffi.py @@ -0,0 +1,284 @@ +import cffi + + +# Block-level extensions +EXT_TABLES = (1 << 0) +EXT_FENCED_CODE = (1 << 1) +EXT_FOOTNOTES = (1 << 2) + +# Span-level extensions +EXT_AUTOLINK = (1 << 3) +EXT_STRIKETHROUGH = (1 << 4) +EXT_UNDERLINE = (1 << 5) +EXT_HIGHLIGHT = (1 << 6) +EXT_QUOTE = (1 << 7) +EXT_SUPERSCRIPT = (1 << 8) +EXT_MATH = (1 << 9) + +# Other flags +EXT_NO_INTRA_EMPHASIS = (1 << 11) +EXT_SPACE_HEADERS = (1 << 12) +EXT_MATH_EXPLICIT = (1 << 13) + +# Negative flags +EXT_DISABLE_INDENTED_CODE = (1 << 14) + +# List flags +LIST_ORDERED = (1 << 0) +LI_BLOCK = (1 << 1) #
  • containing block data + +# Table flags +TABLE_ALIGN_LEFT = 1 +TABLE_ALIGN_RIGHT = 2 +TABLE_ALIGN_CENTER = 3 +TABLE_ALIGNMASK = 3 +TABLE_HEADER = 4 + +# HTML flags +HTML_SKIP_HTML = (1 << 0) +HTML_ESCAPE = (1 << 1) +HTML_HARD_WRAP = (1 << 2) +HTML_USE_XHTML = (1 << 3) + +# Autolink types +AUTOLINK_NONE = 1 # Used internally when it is not an autolink +AUTOLINK_NORMAL = 2 # Normal http/http/ftp/mailto/etc link +AUTOLINK_EMAIL = 3 # E-mail link without explit mailto: + + +ffi = cffi.FFI() + +ffi.set_source( + 'misaka._hoedown', + """\ +#include "extra.h" +#include "hoedown/buffer.h" +#include "hoedown/document.h" +#include "hoedown/html.h" +""", + sources=( + 'src/extra.c', + 'src/hoedown/version.c', + 'src/hoedown/stack.c', + 'src/hoedown/html_smartypants.c', + 'src/hoedown/html_blocks.c', + 'src/hoedown/html.c', + 'src/hoedown/escape.c', + 'src/hoedown/document.c', + 'src/hoedown/buffer.c', + 'src/hoedown/autolink.c', + ), + include_dirs=('src',)) + + +# NOTE: The constants are refined here, because CFFI +# doesn't parse the bitwise left-shift (<<). +ffi.cdef("""\ +// -------------------------- +// --- hoedown/document.h --- +// -------------------------- + +typedef enum hoedown_extensions {{ + /* block-level extensions */ + HOEDOWN_EXT_TABLES = {}, + HOEDOWN_EXT_FENCED_CODE = {}, + HOEDOWN_EXT_FOOTNOTES = {}, + HOEDOWN_EXT_AUTOLINK = {}, + HOEDOWN_EXT_STRIKETHROUGH = {}, + HOEDOWN_EXT_UNDERLINE = {}, + HOEDOWN_EXT_HIGHLIGHT = {}, + HOEDOWN_EXT_QUOTE = {}, + HOEDOWN_EXT_SUPERSCRIPT = {}, + HOEDOWN_EXT_MATH = {}, + HOEDOWN_EXT_NO_INTRA_EMPHASIS = {}, + HOEDOWN_EXT_SPACE_HEADERS = {}, + HOEDOWN_EXT_MATH_EXPLICIT = {}, + HOEDOWN_EXT_DISABLE_INDENTED_CODE = {} +}} hoedown_extensions; + +typedef enum hoedown_list_flags {{ + HOEDOWN_LIST_ORDERED = {}, + HOEDOWN_LI_BLOCK = {} +}} hoedown_list_flags; + +typedef enum hoedown_table_flags {{ + HOEDOWN_TABLE_ALIGN_LEFT = {}, + HOEDOWN_TABLE_ALIGN_RIGHT = {}, + HOEDOWN_TABLE_ALIGN_CENTER = {}, + HOEDOWN_TABLE_ALIGNMASK = {}, + HOEDOWN_TABLE_HEADER = {} +}} hoedown_table_flags; + +// ---------------------- +// --- hoedown/html.h --- +// ---------------------- + +typedef enum hoedown_html_flags {{ + HOEDOWN_HTML_SKIP_HTML = {}, + HOEDOWN_HTML_ESCAPE = {}, + HOEDOWN_HTML_HARD_WRAP = {}, + HOEDOWN_HTML_USE_XHTML = {} +}} hoedown_html_flags; +""".format( + EXT_TABLES, + EXT_FENCED_CODE, + EXT_FOOTNOTES, + EXT_AUTOLINK, + EXT_STRIKETHROUGH, + EXT_UNDERLINE, + EXT_HIGHLIGHT, + EXT_QUOTE, + EXT_SUPERSCRIPT, + EXT_MATH, + EXT_NO_INTRA_EMPHASIS, + EXT_SPACE_HEADERS, + EXT_MATH_EXPLICIT, + EXT_DISABLE_INDENTED_CODE, + LIST_ORDERED, + LI_BLOCK, + TABLE_ALIGN_LEFT, + TABLE_ALIGN_RIGHT, + TABLE_ALIGN_CENTER, + TABLE_ALIGNMASK, + TABLE_HEADER, + HTML_SKIP_HTML, + HTML_ESCAPE, + HTML_HARD_WRAP, + HTML_USE_XHTML)) + + +ffi.cdef("""\ +// ------------------------ +// --- hoedown/buffer.h --- +// ------------------------ + +typedef void *(*hoedown_realloc_callback)(void *, size_t); +typedef void (*hoedown_free_callback)(void *); + +struct hoedown_buffer { + uint8_t *data; /* actual character data */ + size_t size; /* size of the string */ + size_t asize; /* allocated size (0 = volatile buffer) */ + size_t unit; /* reallocation unit size (0 = read-only buffer) */ + + hoedown_realloc_callback data_realloc; + hoedown_free_callback data_free; + hoedown_free_callback buffer_free; +}; + +typedef struct hoedown_buffer hoedown_buffer; + +void *hoedown_malloc(size_t size); +hoedown_buffer *hoedown_buffer_new(size_t unit); +void hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz); +void hoedown_buffer_puts(hoedown_buffer *buf, const char *str); +void hoedown_buffer_free(hoedown_buffer *buf); + +// -------------------------- +// --- hoedown/document.h --- +// -------------------------- + +// NOTE: See earlier ff.cdef() for document.h's constants. + +typedef enum hoedown_autolink_type { + HOEDOWN_AUTOLINK_NONE, /* used internally when it is not an autolink*/ + HOEDOWN_AUTOLINK_NORMAL, /* normal http/http/ftp/mailto/etc link */ + HOEDOWN_AUTOLINK_EMAIL /* e-mail link without explit mailto: */ +} hoedown_autolink_type; + +struct hoedown_document; +typedef struct hoedown_document hoedown_document; + +struct hoedown_renderer_data { + void *opaque; +}; +typedef struct hoedown_renderer_data hoedown_renderer_data; + +/* hoedown_renderer - functions for rendering parsed data */ +struct hoedown_renderer { + /* state object */ + void *opaque; + + /* block level callbacks - NULL skips the block */ + void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data); + void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*header)(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data); + void (*hrule)(hoedown_buffer *ob, const hoedown_renderer_data *data); + void (*list)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); + void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); + void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_header)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_body)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data); + void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data); + void (*blockhtml)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* span level callbacks - NULL or return 0 prints the span verbatim */ + int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data); + int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*underline)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*quote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data); + int (*linebreak)(hoedown_buffer *ob, const hoedown_renderer_data *data); + int (*link)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data); + int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data); + int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data); + int (*raw_html)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* low level callbacks - NULL copies input directly into the output */ + void (*entity)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* miscellaneous callbacks */ + void (*doc_header)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); + void (*doc_footer)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); +}; +typedef struct hoedown_renderer hoedown_renderer; + +hoedown_document *hoedown_document_new( + const hoedown_renderer *renderer, + hoedown_extensions extensions, + size_t max_nesting +); + +void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); +void hoedown_document_free(hoedown_document *doc); + +// ---------------------- +// --- hoedown/html.h --- +// ---------------------- + +// NOTE: See earlier ff.cdef() for html.h's constants. + +typedef enum hoedown_html_tag { + HOEDOWN_HTML_TAG_NONE = 0, + HOEDOWN_HTML_TAG_OPEN, + HOEDOWN_HTML_TAG_CLOSE +} hoedown_html_tag; + +hoedown_renderer *hoedown_html_renderer_new( + hoedown_html_flags render_flags, + int nesting_level +); +void hoedown_html_renderer_free(hoedown_renderer *renderer); + +// --------------- +// --- extra.h --- +// --------------- + +hoedown_renderer *null_renderer_new(); +void null_renderer_free(hoedown_renderer *renderer); +""") + + +if __name__ == '__main__': + ffi.compile() diff --git a/misaka/__init__.py b/misaka/__init__.py new file mode 100644 index 0000000..1471f57 --- /dev/null +++ b/misaka/__init__.py @@ -0,0 +1,2 @@ +from .api import * +from .constants import * diff --git a/misaka/api.py b/misaka/api.py new file mode 100644 index 0000000..b8dd827 --- /dev/null +++ b/misaka/api.py @@ -0,0 +1,250 @@ +from inspect import getmembers, ismethod + +from ._hoedown import lib, ffi + + +__all__ = [ + 'html', + 'Markdown', + 'BaseRenderer', + 'HtmlRenderer' +] + + +def html(text, extensions=0, render_flags=0): + ib = lib.hoedown_buffer_new(1024) + ob = lib.hoedown_buffer_new(64) + renderer = lib.hoedown_html_renderer_new(0, 0) + document = lib.hoedown_document_new(renderer, 0, 16); + + lib.hoedown_buffer_puts(ib, text.encode('utf-8')) + lib.hoedown_document_render(document, ob, ib.data, ib.size); + lib.hoedown_buffer_free(ib); + lib.hoedown_document_free(document); + lib.hoedown_html_renderer_free(renderer); + + try: + return ffi.string(ob.data, ob.size).decode('utf-8') + finally: + lib.hoedown_buffer_free(ob); + + +class Markdown: + def __init__(self, renderer): + # NOTE: Prevent the renderer from being garbage collected + self.renderer = renderer + + def render(self, text): + ib = lib.hoedown_buffer_new(1024) + lib.hoedown_buffer_puts(ib, text.encode('utf-8')) + + ob = lib.hoedown_buffer_new(64) + document = lib.hoedown_document_new(self.renderer.renderer, 0, 16); + lib.hoedown_document_render(document, ob, ib.data, ib.size); + + lib.hoedown_buffer_free(ib); + lib.hoedown_document_free(document); + + try: + return ffi.string(ob.data, ob.size).decode('utf-8') + finally: + lib.hoedown_buffer_free(ob); + + +callback_signatures = { + # block level callbacks - NULL skips the block + 'blockcode': 'void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data)', + 'blockquote': 'void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'header': 'void (*header)(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data)', + 'hrule': 'void (*hrule)(hoedown_buffer *ob, const hoedown_renderer_data *data)', + 'list': 'void (*list)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data)', + 'listitem': 'void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data)', + 'paragraph': 'void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'table': 'void (*table)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'table_header': 'void (*table_header)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'table_body': 'void (*table_body)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'table_row': 'void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'table_cell': 'void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data)', + 'footnotes': 'void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'footnote_def': 'void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data)', + 'blockhtml': 'void (*blockhtml)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)', + + # span level callbacks - NULL or return 0 prints the span verbatim + 'autolink': 'int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data)', + 'codespan': 'int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)', + 'double_emphasis': 'int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'emphasis': 'int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'underline': 'int (*underline)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'highlight': 'int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'quote': 'int (*quote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'image': 'int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data)', + 'linebreak': 'int (*linebreak)(hoedown_buffer *ob, const hoedown_renderer_data *data)', + 'link': 'int (*link)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data)', + 'triple_emphasis': 'int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'strikethrough': 'int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'superscript': 'int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)', + 'footnote_ref': 'int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data)', + 'math': 'int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data)', + 'raw_html': 'int (*raw_html)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)', + + # low level callbacks - NULL copies input directly into the output + 'entity': 'void (*entity)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);', + 'normal_text': 'void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);', + + # miscellaneous callbacks + 'doc_header': 'void (*doc_header)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data);', + 'doc_footer': 'void (*doc_footer)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data);', +} + + +# TODO: Do this in Python: +# static hoedown_renderer * +# null_renderer_new() +# { +# hoedown_renderer *renderer; +# renderer = hoedown_malloc(sizeof(hoedown_renderer)); +# memset(renderer, 0x0, sizeof(hoedown_renderer)); + +# return renderer; +# } + +# static void +# null_renderer_free(hoedown_renderer *renderer) +# { +# free(renderer); +# } +class BaseRenderer: + def __init__(self): + # TODO: Make a null renderer. + self.renderer = None + self.set_callbacks() + + def set_callbacks(self): + callbacks = [] + + for name, func in getmembers(self, predicate=ismethod): + signature = callback_signatures.get(name) + if signature is None: + continue + + wrapper = getattr(self, '_w_' + name) + callback = ffi.callback(signature, wrapper) + callbacks.append(callback) + setattr(self.renderer, name, callback) + + # Prevent callbacks from being garbage collected. + self._callbacks = callbacks + + def _w_blockcode(self, ob, text, lang, data): + text = ffi.string(text.data, text.size).decode('utf-8') + lang = ffi.string(lang.data, lang.size).decode('utf-8') + result = self.blockcode(text, lang) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_blockquote(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.blockquote(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_header(self, ob, content, level, data): + content = ffi.string(content.data, content.size).decode('utf-8') + level = int(level) + result = self.header(content, level) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_hrule(self, ob, data): + result = self.hrule() + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + # flags: LIST_ORDERED, LI_BLOCK. + def _w_list(self, ob, content, flags, data): + content = ffi.string(content.data, content.size).decode('utf-8') + flags = int(flags) + result = self.list(content, flags) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + # flags: LIST_ORDERED, LI_BLOCK. + def _w_listitem(self, ob, content, flags, data): + content = ffi.string(content.data, content.size).decode('utf-8') + flags = int(flags) + result = self.listitem(content, flags) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_paragraph(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.paragraph(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_table(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.table(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_table_header(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.table_header(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_table_body(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.table_body(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_table_row(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.table_row(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_table_cell(self, ob, content, flags, data): + content = ffi.string(content.data, content.size).decode('utf-8') + flags = int(flags) + result = self.table_row(content, flags) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_footnotes(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.footnotes(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_footnote_def(self, ob, content, num, data): + content = ffi.string(content.data, content.size).decode('utf-8') + num = int(num) + result = self.footnote_def(content, num) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_blockhtml(self, ob, text, data): + text = ffi.string(text.data, text.size).decode('utf-8') + result = self.blockhtml(text) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + + def _w_emphasis(self, ob, content, data): + content = ffi.string(content.data, content.size).decode('utf-8') + result = self.emphasis(content) + if result: + lib.hoedown_buffer_puts(ob, result.encode('utf-8')) + return 1 + return 0 + + +class HtmlRenderer(BaseRenderer): + def __init__(self): + self.renderer = lib.hoedown_html_renderer_new(0, 0) + self.set_callbacks() + + def __del__(self): + lib.hoedown_html_renderer_free(self.renderer) diff --git a/misaka/constants.py b/misaka/constants.py new file mode 100644 index 0000000..97b4e4e --- /dev/null +++ b/misaka/constants.py @@ -0,0 +1,43 @@ +# Block-level extensions +EXT_TABLES = (1 << 0) +EXT_FENCED_CODE = (1 << 1) +EXT_FOOTNOTES = (1 << 2) + +# Span-level extensions +EXT_AUTOLINK = (1 << 3) +EXT_STRIKETHROUGH = (1 << 4) +EXT_UNDERLINE = (1 << 5) +EXT_HIGHLIGHT = (1 << 6) +EXT_QUOTE = (1 << 7) +EXT_SUPERSCRIPT = (1 << 8) +EXT_MATH = (1 << 9) + +# Other flags +EXT_NO_INTRA_EMPHASIS = (1 << 11) +EXT_SPACE_HEADERS = (1 << 12) +EXT_MATH_EXPLICIT = (1 << 13) + +# Negative flags +EXT_DISABLE_INDENTED_CODE = (1 << 14) + +# List flags +LIST_ORDERED = (1 << 0) +LI_BLOCK = (1 << 1) #
  • containing block data + +# Table flags +TABLE_ALIGN_LEFT = 1 +TABLE_ALIGN_RIGHT = 2 +TABLE_ALIGN_CENTER = 3 +TABLE_ALIGNMASK = 3 +TABLE_HEADER = 4 + +# HTML flags +HTML_SKIP_HTML = (1 << 0) +HTML_ESCAPE = (1 << 1) +HTML_HARD_WRAP = (1 << 2) +HTML_USE_XHTML = (1 << 3) + +# Autolink types +AUTOLINK_NONE = 1 # Used internally when it is not an autolink +AUTOLINK_NORMAL = 2 # Normal http/http/ftp/mailto/etc link +AUTOLINK_EMAIL = 3 # E-mail link without explit mailto: diff --git a/setup.py b/setup.py index 0a10293..21019cc 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,7 @@ import os -import glob import shutil import os.path - -try: - from setuptools import setup, Extension, Command -except ImportError: - from distutils.core import setup, Extension, Command +from setuptools import setup, Command dirname = os.path.dirname(os.path.abspath(__file__)) @@ -23,43 +18,13 @@ class BaseCommand(Command): class CleanCommand(BaseCommand): description = 'cleanup directories created by packaging and build processes' def run(self): - for path in ['build', 'dist', 'misaka.egg-info', 'docs/_build']: + for path in ('build', 'dist', 'misaka.egg-info', 'docs/_build'): if os.path.exists(path): path = os.path.join(dirname, path) print('removing %s' % path) shutil.rmtree(path) -class CythonCommand(BaseCommand): - description = 'compile Cython files(s) into C file(s)' - def run(self): - try: - from Cython.Compiler.Main import compile - path = os.path.join(dirname, 'src', 'misaka.pyx') - print('compiling %s' % path) - compile(path) - except ImportError: - print('Cython is not installed. Please install Cython first.') - - -class VendorCommand(BaseCommand): - description = 'update Sundown files. Use `git submodule init`, '\ - '`git submodule update` and `git submodule foreach git pull origin master`'\ - ' to the most recent files' - - def run(self): - files = [] - dest = os.path.join(dirname, 'src/sundown') - - for path in ['vendor/sundown/src/*', 'vendor/sundown/html/*']: - files += glob.glob(os.path.join(dirname, path)) - - for path in files: - if os.path.exists(path): - print('copy %s -> %s' % (path, dest)) - shutil.copy(path, dest) - - class TestCommand(BaseCommand): description = 'run unit tests' def run(self): @@ -68,42 +33,29 @@ class TestCommand(BaseCommand): setup( name='misaka', - version='1.0.3', - description='The Python binding for Sundown, a markdown parsing library.', + version='2.0.0', + description='A CFFI binding for Hoedown, a markdown parsing library.', author='Frank Smit', author_email='frank@61924.nl', - url='http://misaka.61924.nl/', + url='https://github.com/FSX/misaka', license='MIT', long_description=open(os.path.join(dirname, 'README.rst')).read(), scripts=['scripts/misaka'], cmdclass={ 'clean': CleanCommand, - 'compile_cython': CythonCommand, - 'update_vendor': VendorCommand, 'test': TestCommand }, - ext_modules=[Extension('misaka', [ - 'src/misaka.c', - 'src/wrapper.c', - 'src/sundown/stack.c', - 'src/sundown/buffer.c', - 'src/sundown/markdown.c', - 'src/sundown/html.c', - 'src/sundown/html_smartypants.c', - 'src/sundown/houdini_href_e.c', - 'src/sundown/houdini_html_e.c', - 'src/sundown/autolink.c' - ])], classifiers = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: C', - 'Programming Language :: Cython', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.4', 'Topic :: Text Processing :: Markup', 'Topic :: Text Processing :: Markup :: HTML', 'Topic :: Utilities' - ] + ], + setup_requires=['cffi>=1.0.0'], + install_requires=['cffi>=1.0.0'], + cffi_modules=['build_ffi.py:ffi'], ) diff --git a/src/extra.c b/src/extra.c new file mode 100644 index 0000000..98b64c2 --- /dev/null +++ b/src/extra.c @@ -0,0 +1,21 @@ +#include "extra.h" + +#include + +#include "hoedown/buffer.h" +#include "hoedown/document.h" + + +hoedown_renderer *null_renderer_new() +{ + hoedown_renderer *renderer; + renderer = hoedown_malloc(sizeof(hoedown_renderer)); + memset(renderer, 0x0, sizeof(hoedown_renderer)); + + return renderer; +} + +void null_renderer_free(hoedown_renderer *renderer) +{ + free(renderer); +} diff --git a/src/extra.h b/src/extra.h new file mode 100644 index 0000000..5cbf831 --- /dev/null +++ b/src/extra.h @@ -0,0 +1,19 @@ +/* extra.h - Helper functions */ + +#ifndef MICHIKO_EXTRA_H +#define MICHIKO_EXTRA_H + +#include "hoedown/document.h" + +#ifdef __cplusplus +extern "C" { +#endif + +hoedown_renderer *null_renderer_new(void); +void null_renderer_free(hoedown_renderer *renderer); + +#ifdef __cplusplus +} +#endif + +#endif /** MICHIKO_EXTRA_H **/ diff --git a/src/sundown/autolink.c b/src/hoedown/autolink.c similarity index 73% rename from src/sundown/autolink.c rename to src/hoedown/autolink.c index 6f8d6ab..e7019fd 100644 --- a/src/sundown/autolink.c +++ b/src/hoedown/autolink.c @@ -1,20 +1,3 @@ -/* - * Copyright (c) 2011, Vicent Marti - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "buffer.h" #include "autolink.h" #include @@ -22,26 +5,28 @@ #include #include -#if defined(_WIN32) +#ifndef _MSC_VER +#include +#else #define strncasecmp _strnicmp #endif int -sd_autolink_issafe(const uint8_t *link, size_t link_len) +hoedown_autolink_is_safe(const uint8_t *data, size_t size) { - static const size_t valid_uris_count = 5; + static const size_t valid_uris_count = 6; static const char *valid_uris[] = { - "/", "http://", "https://", "ftp://", "mailto:" + "http://", "https://", "/", "#", "ftp://", "mailto:" }; - + static const size_t valid_uris_size[] = { 7, 8, 1, 1, 6, 7 }; size_t i; for (i = 0; i < valid_uris_count; ++i) { - size_t len = strlen(valid_uris[i]); + size_t len = valid_uris_size[i]; - if (link_len > len && - strncasecmp((char *)link, valid_uris[i], len) == 0 && - isalnum(link[len])) + if (size > len && + strncasecmp((char *)data, valid_uris[i], len) == 0 && + isalnum(data[len])) return 1; } @@ -61,7 +46,7 @@ autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size) } while (link_end > 0) { - if (strchr("?!.,", data[link_end - 1]) != NULL) + if (strchr("?!.,:", data[link_end - 1]) != NULL) link_end--; else if (data[link_end - 1] == ';') { @@ -141,7 +126,7 @@ check_domain(uint8_t *data, size_t size, int allow_short) return 0; for (i = 1; i < size - 1; ++i) { - if (data[i] == '.') np++; + if (strchr(".:", data[i]) != NULL) np++; else if (!isalnum(data[i]) && data[i] != '-') break; } @@ -159,9 +144,9 @@ check_domain(uint8_t *data, size_t size, int allow_short) } size_t -sd_autolink__www( +hoedown_autolink__www( size_t *rewind_p, - struct buf *link, + hoedown_buffer *link, uint8_t *data, size_t max_rewind, size_t size, @@ -188,16 +173,16 @@ sd_autolink__www( if (link_end == 0) return 0; - bufput(link, data, link_end); + hoedown_buffer_put(link, data, link_end); *rewind_p = 0; return (int)link_end; } size_t -sd_autolink__email( +hoedown_autolink__email( size_t *rewind_p, - struct buf *link, + hoedown_buffer *link, uint8_t *data, size_t max_rewind, size_t size, @@ -207,7 +192,7 @@ sd_autolink__email( int nb = 0, np = 0; for (rewind = 0; rewind < max_rewind; ++rewind) { - uint8_t c = data[-rewind - 1]; + uint8_t c = data[-1 - rewind]; if (isalnum(c)) continue; @@ -244,16 +229,16 @@ sd_autolink__email( if (link_end == 0) return 0; - bufput(link, data - rewind, link_end + rewind); + hoedown_buffer_put(link, data - rewind, link_end + rewind); *rewind_p = rewind; return link_end; } size_t -sd_autolink__url( +hoedown_autolink__url( size_t *rewind_p, - struct buf *link, + hoedown_buffer *link, uint8_t *data, size_t max_rewind, size_t size, @@ -264,10 +249,10 @@ sd_autolink__url( if (size < 4 || data[1] != '/' || data[2] != '/') return 0; - while (rewind < max_rewind && isalpha(data[-rewind - 1])) + while (rewind < max_rewind && isalpha(data[-1 - rewind])) rewind++; - if (!sd_autolink_issafe(data - rewind, size + rewind)) + if (!hoedown_autolink_is_safe(data - rewind, size + rewind)) return 0; link_end = strlen("://"); @@ -275,7 +260,7 @@ sd_autolink__url( domain_len = check_domain( data + link_end, size - link_end, - flags & SD_AUTOLINK_SHORT_DOMAINS); + flags & HOEDOWN_AUTOLINK_SHORT_DOMAINS); if (domain_len == 0) return 0; @@ -289,9 +274,8 @@ sd_autolink__url( if (link_end == 0) return 0; - bufput(link, data - rewind, link_end + rewind); + hoedown_buffer_put(link, data - rewind, link_end + rewind); *rewind_p = rewind; return link_end; } - diff --git a/src/hoedown/autolink.h b/src/hoedown/autolink.h new file mode 100644 index 0000000..528885c --- /dev/null +++ b/src/hoedown/autolink.h @@ -0,0 +1,46 @@ +/* autolink.h - versatile autolinker */ + +#ifndef HOEDOWN_AUTOLINK_H +#define HOEDOWN_AUTOLINK_H + +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +typedef enum hoedown_autolink_flags { + HOEDOWN_AUTOLINK_SHORT_DOMAINS = (1 << 0) +} hoedown_autolink_flags; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_autolink_is_safe: verify that a URL has a safe protocol */ +int hoedown_autolink_is_safe(const uint8_t *data, size_t size); + +/* hoedown_autolink__www: search for the next www link in data */ +size_t hoedown_autolink__www(size_t *rewind_p, hoedown_buffer *link, + uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); + +/* hoedown_autolink__email: search for the next email in data */ +size_t hoedown_autolink__email(size_t *rewind_p, hoedown_buffer *link, + uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); + +/* hoedown_autolink__url: search for the next URL in data */ +size_t hoedown_autolink__url(size_t *rewind_p, hoedown_buffer *link, + uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_AUTOLINK_H **/ diff --git a/src/hoedown/buffer.c b/src/hoedown/buffer.c new file mode 100644 index 0000000..af77b70 --- /dev/null +++ b/src/hoedown/buffer.c @@ -0,0 +1,308 @@ +#include "buffer.h" + +#include +#include +#include +#include + +void * +hoedown_malloc(size_t size) +{ + void *ret = malloc(size); + + if (!ret) { + fprintf(stderr, "Allocation failed.\n"); + abort(); + } + + return ret; +} + +void * +hoedown_calloc(size_t nmemb, size_t size) +{ + void *ret = calloc(nmemb, size); + + if (!ret) { + fprintf(stderr, "Allocation failed.\n"); + abort(); + } + + return ret; +} + +void * +hoedown_realloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + + if (!ret) { + fprintf(stderr, "Allocation failed.\n"); + abort(); + } + + return ret; +} + +void +hoedown_buffer_init( + hoedown_buffer *buf, + size_t unit, + hoedown_realloc_callback data_realloc, + hoedown_free_callback data_free, + hoedown_free_callback buffer_free) +{ + assert(buf); + + buf->data = NULL; + buf->size = buf->asize = 0; + buf->unit = unit; + buf->data_realloc = data_realloc; + buf->data_free = data_free; + buf->buffer_free = buffer_free; +} + +void +hoedown_buffer_uninit(hoedown_buffer *buf) +{ + assert(buf && buf->unit); + buf->data_free(buf->data); +} + +hoedown_buffer * +hoedown_buffer_new(size_t unit) +{ + hoedown_buffer *ret = hoedown_malloc(sizeof (hoedown_buffer)); + hoedown_buffer_init(ret, unit, hoedown_realloc, free, free); + return ret; +} + +void +hoedown_buffer_free(hoedown_buffer *buf) +{ + if (!buf) return; + assert(buf && buf->unit); + + buf->data_free(buf->data); + + if (buf->buffer_free) + buf->buffer_free(buf); +} + +void +hoedown_buffer_reset(hoedown_buffer *buf) +{ + assert(buf && buf->unit); + + buf->data_free(buf->data); + buf->data = NULL; + buf->size = buf->asize = 0; +} + +void +hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz) +{ + size_t neoasz; + assert(buf && buf->unit); + + if (buf->asize >= neosz) + return; + + neoasz = buf->asize + buf->unit; + while (neoasz < neosz) + neoasz += buf->unit; + + buf->data = buf->data_realloc(buf->data, neoasz); + buf->asize = neoasz; +} + +void +hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size) +{ + assert(buf && buf->unit); + + if (buf->size + size > buf->asize) + hoedown_buffer_grow(buf, buf->size + size); + + memcpy(buf->data + buf->size, data, size); + buf->size += size; +} + +void +hoedown_buffer_puts(hoedown_buffer *buf, const char *str) +{ + hoedown_buffer_put(buf, (const uint8_t *)str, strlen(str)); +} + +void +hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c) +{ + assert(buf && buf->unit); + + if (buf->size >= buf->asize) + hoedown_buffer_grow(buf, buf->size + 1); + + buf->data[buf->size] = c; + buf->size += 1; +} + +int +hoedown_buffer_putf(hoedown_buffer *buf, FILE *file) +{ + assert(buf && buf->unit); + + while (!(feof(file) || ferror(file))) { + hoedown_buffer_grow(buf, buf->size + buf->unit); + buf->size += fread(buf->data + buf->size, 1, buf->unit, file); + } + + return ferror(file); +} + +void +hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size) +{ + assert(buf && buf->unit); + + if (size > buf->asize) + hoedown_buffer_grow(buf, size); + + memcpy(buf->data, data, size); + buf->size = size; +} + +void +hoedown_buffer_sets(hoedown_buffer *buf, const char *str) +{ + hoedown_buffer_set(buf, (const uint8_t *)str, strlen(str)); +} + +int +hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size) +{ + if (buf->size != size) return 0; + return memcmp(buf->data, data, size) == 0; +} + +int +hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str) +{ + return hoedown_buffer_eq(buf, (const uint8_t *)str, strlen(str)); +} + +int +hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix) +{ + size_t i; + + for (i = 0; i < buf->size; ++i) { + if (prefix[i] == 0) + return 0; + + if (buf->data[i] != prefix[i]) + return buf->data[i] - prefix[i]; + } + + return 0; +} + +void +hoedown_buffer_slurp(hoedown_buffer *buf, size_t size) +{ + assert(buf && buf->unit); + + if (size >= buf->size) { + buf->size = 0; + return; + } + + buf->size -= size; + memmove(buf->data, buf->data + size, buf->size); +} + +const char * +hoedown_buffer_cstr(hoedown_buffer *buf) +{ + assert(buf && buf->unit); + + if (buf->size < buf->asize && buf->data[buf->size] == 0) + return (char *)buf->data; + + hoedown_buffer_grow(buf, buf->size + 1); + buf->data[buf->size] = 0; + + return (char *)buf->data; +} + +void +hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) +{ + va_list ap; + int n; + + assert(buf && buf->unit); + + if (buf->size >= buf->asize) + hoedown_buffer_grow(buf, buf->size + 1); + + va_start(ap, fmt); + n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); + va_end(ap); + + if (n < 0) { +#ifndef _MSC_VER + return; +#else + va_start(ap, fmt); + n = _vscprintf(fmt, ap); + va_end(ap); +#endif + } + + if ((size_t)n >= buf->asize - buf->size) { + hoedown_buffer_grow(buf, buf->size + n + 1); + + va_start(ap, fmt); + n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); + va_end(ap); + } + + if (n < 0) + return; + + buf->size += n; +} + +void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int c) { + unsigned char unichar[4]; + + assert(buf && buf->unit); + + if (c < 0x80) { + hoedown_buffer_putc(buf, c); + } + else if (c < 0x800) { + unichar[0] = 192 + (c / 64); + unichar[1] = 128 + (c % 64); + hoedown_buffer_put(buf, unichar, 2); + } + else if (c - 0xd800u < 0x800) { + HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd"); + } + else if (c < 0x10000) { + unichar[0] = 224 + (c / 4096); + unichar[1] = 128 + (c / 64) % 64; + unichar[2] = 128 + (c % 64); + hoedown_buffer_put(buf, unichar, 3); + } + else if (c < 0x110000) { + unichar[0] = 240 + (c / 262144); + unichar[1] = 128 + (c / 4096) % 64; + unichar[2] = 128 + (c / 64) % 64; + unichar[3] = 128 + (c % 64); + hoedown_buffer_put(buf, unichar, 4); + } + else { + HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd"); + } +} diff --git a/src/hoedown/buffer.h b/src/hoedown/buffer.h new file mode 100644 index 0000000..d7703f8 --- /dev/null +++ b/src/hoedown/buffer.h @@ -0,0 +1,134 @@ +/* buffer.h - simple, fast buffers */ + +#ifndef HOEDOWN_BUFFER_H +#define HOEDOWN_BUFFER_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +#define __attribute__(x) +#define inline __inline +#define __builtin_expect(x,n) x +#endif + + +/********* + * TYPES * + *********/ + +typedef void *(*hoedown_realloc_callback)(void *, size_t); +typedef void (*hoedown_free_callback)(void *); + +struct hoedown_buffer { + uint8_t *data; /* actual character data */ + size_t size; /* size of the string */ + size_t asize; /* allocated size (0 = volatile buffer) */ + size_t unit; /* reallocation unit size (0 = read-only buffer) */ + + hoedown_realloc_callback data_realloc; + hoedown_free_callback data_free; + hoedown_free_callback buffer_free; +}; + +typedef struct hoedown_buffer hoedown_buffer; + + +/************* + * FUNCTIONS * + *************/ + +/* allocation wrappers */ +void *hoedown_malloc(size_t size) __attribute__ ((malloc)); +void *hoedown_calloc(size_t nmemb, size_t size) __attribute__ ((malloc)); +void *hoedown_realloc(void *ptr, size_t size) __attribute__ ((malloc)); + +/* hoedown_buffer_init: initialize a buffer with custom allocators */ +void hoedown_buffer_init( + hoedown_buffer *buffer, + size_t unit, + hoedown_realloc_callback data_realloc, + hoedown_free_callback data_free, + hoedown_free_callback buffer_free +); + +/* hoedown_buffer_uninit: uninitialize an existing buffer */ +void hoedown_buffer_uninit(hoedown_buffer *buf); + +/* hoedown_buffer_new: allocate a new buffer */ +hoedown_buffer *hoedown_buffer_new(size_t unit) __attribute__ ((malloc)); + +/* hoedown_buffer_reset: free internal data of the buffer */ +void hoedown_buffer_reset(hoedown_buffer *buf); + +/* hoedown_buffer_grow: increase the allocated size to the given value */ +void hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz); + +/* hoedown_buffer_put: append raw data to a buffer */ +void hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size); + +/* hoedown_buffer_puts: append a NUL-terminated string to a buffer */ +void hoedown_buffer_puts(hoedown_buffer *buf, const char *str); + +/* hoedown_buffer_putc: append a single char to a buffer */ +void hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c); + +/* hoedown_buffer_putf: read from a file and append to a buffer, until EOF or error */ +int hoedown_buffer_putf(hoedown_buffer *buf, FILE* file); + +/* hoedown_buffer_set: replace the buffer's contents with raw data */ +void hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size); + +/* hoedown_buffer_sets: replace the buffer's contents with a NUL-terminated string */ +void hoedown_buffer_sets(hoedown_buffer *buf, const char *str); + +/* hoedown_buffer_eq: compare a buffer's data with other data for equality */ +int hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size); + +/* hoedown_buffer_eq: compare a buffer's data with NUL-terminated string for equality */ +int hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str); + +/* hoedown_buffer_prefix: compare the beginning of a buffer with a string */ +int hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix); + +/* hoedown_buffer_slurp: remove a given number of bytes from the head of the buffer */ +void hoedown_buffer_slurp(hoedown_buffer *buf, size_t size); + +/* hoedown_buffer_cstr: NUL-termination of the string array (making a C-string) */ +const char *hoedown_buffer_cstr(hoedown_buffer *buf); + +/* hoedown_buffer_printf: formatted printing to a buffer */ +void hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +/* hoedown_buffer_put_utf8: put a Unicode character encoded as UTF-8 */ +void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int codepoint); + +/* hoedown_buffer_free: free the buffer */ +void hoedown_buffer_free(hoedown_buffer *buf); + + +/* HOEDOWN_BUFPUTSL: optimized hoedown_buffer_puts of a string literal */ +#define HOEDOWN_BUFPUTSL(output, literal) \ + hoedown_buffer_put(output, (const uint8_t *)literal, sizeof(literal) - 1) + +/* HOEDOWN_BUFSETSL: optimized hoedown_buffer_sets of a string literal */ +#define HOEDOWN_BUFSETSL(output, literal) \ + hoedown_buffer_set(output, (const uint8_t *)literal, sizeof(literal) - 1) + +/* HOEDOWN_BUFEQSL: optimized hoedown_buffer_eqs of a string literal */ +#define HOEDOWN_BUFEQSL(output, literal) \ + hoedown_buffer_eq(output, (const uint8_t *)literal, sizeof(literal) - 1) + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_BUFFER_H **/ diff --git a/src/hoedown/document.c b/src/hoedown/document.c new file mode 100644 index 0000000..b057d29 --- /dev/null +++ b/src/hoedown/document.c @@ -0,0 +1,2966 @@ +#include "document.h" + +#include +#include +#include +#include + +#include "stack.h" + +#ifndef _MSC_VER +#include +#else +#define strncasecmp _strnicmp +#endif + +#define REF_TABLE_SIZE 8 + +#define BUFFER_BLOCK 0 +#define BUFFER_SPAN 1 + +#define HOEDOWN_LI_END 8 /* internal list flag */ + +const char *hoedown_find_block_tag(const char *str, unsigned int len); + +/*************** + * LOCAL TYPES * + ***************/ + +/* link_ref: reference to a link */ +struct link_ref { + unsigned int id; + + hoedown_buffer *link; + hoedown_buffer *title; + + struct link_ref *next; +}; + +/* footnote_ref: reference to a footnote */ +struct footnote_ref { + unsigned int id; + + int is_used; + unsigned int num; + + hoedown_buffer *contents; +}; + +/* footnote_item: an item in a footnote_list */ +struct footnote_item { + struct footnote_ref *ref; + struct footnote_item *next; +}; + +/* footnote_list: linked list of footnote_item */ +struct footnote_list { + unsigned int count; + struct footnote_item *head; + struct footnote_item *tail; +}; + +/* char_trigger: function pointer to render active chars */ +/* returns the number of chars taken care of */ +/* data is the pointer of the beginning of the span */ +/* offset is the number of valid chars before data */ +typedef size_t +(*char_trigger)(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); + +static size_t char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); + +enum markdown_char_t { + MD_CHAR_NONE = 0, + MD_CHAR_EMPHASIS, + MD_CHAR_CODESPAN, + MD_CHAR_LINEBREAK, + MD_CHAR_LINK, + MD_CHAR_LANGLE, + MD_CHAR_ESCAPE, + MD_CHAR_ENTITY, + MD_CHAR_AUTOLINK_URL, + MD_CHAR_AUTOLINK_EMAIL, + MD_CHAR_AUTOLINK_WWW, + MD_CHAR_SUPERSCRIPT, + MD_CHAR_QUOTE, + MD_CHAR_MATH +}; + +static char_trigger markdown_char_ptrs[] = { + NULL, + &char_emphasis, + &char_codespan, + &char_linebreak, + &char_link, + &char_langle_tag, + &char_escape, + &char_entity, + &char_autolink_url, + &char_autolink_email, + &char_autolink_www, + &char_superscript, + &char_quote, + &char_math +}; + +struct hoedown_document { + hoedown_renderer md; + hoedown_renderer_data data; + + struct link_ref *refs[REF_TABLE_SIZE]; + struct footnote_list footnotes_found; + struct footnote_list footnotes_used; + uint8_t active_char[256]; + hoedown_stack work_bufs[2]; + hoedown_extensions ext_flags; + size_t max_nesting; + int in_link_body; +}; + +/*************************** + * HELPER FUNCTIONS * + ***************************/ + +static hoedown_buffer * +newbuf(hoedown_document *doc, int type) +{ + static const size_t buf_size[2] = {256, 64}; + hoedown_buffer *work = NULL; + hoedown_stack *pool = &doc->work_bufs[type]; + + if (pool->size < pool->asize && + pool->item[pool->size] != NULL) { + work = pool->item[pool->size++]; + work->size = 0; + } else { + work = hoedown_buffer_new(buf_size[type]); + hoedown_stack_push(pool, work); + } + + return work; +} + +static void +popbuf(hoedown_document *doc, int type) +{ + doc->work_bufs[type].size--; +} + +static void +unscape_text(hoedown_buffer *ob, hoedown_buffer *src) +{ + size_t i = 0, org; + while (i < src->size) { + org = i; + while (i < src->size && src->data[i] != '\\') + i++; + + if (i > org) + hoedown_buffer_put(ob, src->data + org, i - org); + + if (i + 1 >= src->size) + break; + + hoedown_buffer_putc(ob, src->data[i + 1]); + i += 2; + } +} + +static unsigned int +hash_link_ref(const uint8_t *link_ref, size_t length) +{ + size_t i; + unsigned int hash = 0; + + for (i = 0; i < length; ++i) + hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash; + + return hash; +} + +static struct link_ref * +add_link_ref( + struct link_ref **references, + const uint8_t *name, size_t name_size) +{ + struct link_ref *ref = hoedown_calloc(1, sizeof(struct link_ref)); + + ref->id = hash_link_ref(name, name_size); + ref->next = references[ref->id % REF_TABLE_SIZE]; + + references[ref->id % REF_TABLE_SIZE] = ref; + return ref; +} + +static struct link_ref * +find_link_ref(struct link_ref **references, uint8_t *name, size_t length) +{ + unsigned int hash = hash_link_ref(name, length); + struct link_ref *ref = NULL; + + ref = references[hash % REF_TABLE_SIZE]; + + while (ref != NULL) { + if (ref->id == hash) + return ref; + + ref = ref->next; + } + + return NULL; +} + +static void +free_link_refs(struct link_ref **references) +{ + size_t i; + + for (i = 0; i < REF_TABLE_SIZE; ++i) { + struct link_ref *r = references[i]; + struct link_ref *next; + + while (r) { + next = r->next; + hoedown_buffer_free(r->link); + hoedown_buffer_free(r->title); + free(r); + r = next; + } + } +} + +static struct footnote_ref * +create_footnote_ref(struct footnote_list *list, const uint8_t *name, size_t name_size) +{ + struct footnote_ref *ref = hoedown_calloc(1, sizeof(struct footnote_ref)); + + ref->id = hash_link_ref(name, name_size); + + return ref; +} + +static int +add_footnote_ref(struct footnote_list *list, struct footnote_ref *ref) +{ + struct footnote_item *item = hoedown_calloc(1, sizeof(struct footnote_item)); + if (!item) + return 0; + item->ref = ref; + + if (list->head == NULL) { + list->head = list->tail = item; + } else { + list->tail->next = item; + list->tail = item; + } + list->count++; + + return 1; +} + +static struct footnote_ref * +find_footnote_ref(struct footnote_list *list, uint8_t *name, size_t length) +{ + unsigned int hash = hash_link_ref(name, length); + struct footnote_item *item = NULL; + + item = list->head; + + while (item != NULL) { + if (item->ref->id == hash) + return item->ref; + item = item->next; + } + + return NULL; +} + +static void +free_footnote_ref(struct footnote_ref *ref) +{ + hoedown_buffer_free(ref->contents); + free(ref); +} + +static void +free_footnote_list(struct footnote_list *list, int free_refs) +{ + struct footnote_item *item = list->head; + struct footnote_item *next; + + while (item) { + next = item->next; + if (free_refs) + free_footnote_ref(item->ref); + free(item); + item = next; + } +} + + +/* + * Check whether a char is a Markdown spacing char. + + * Right now we only consider spaces the actual + * space and a newline: tabs and carriage returns + * are filtered out during the preprocessing phase. + * + * If we wanted to actually be UTF-8 compliant, we + * should instead extract an Unicode codepoint from + * this character and check for space properties. + */ +static int +_isspace(int c) +{ + return c == ' ' || c == '\n'; +} + +/* is_empty_all: verify that all the data is spacing */ +static int +is_empty_all(const uint8_t *data, size_t size) +{ + size_t i = 0; + while (i < size && _isspace(data[i])) i++; + return i == size; +} + +/* + * Replace all spacing characters in data with spaces. As a special + * case, this collapses a newline with the previous space, if possible. + */ +static void +replace_spacing(hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + size_t i = 0, mark; + hoedown_buffer_grow(ob, size); + while (1) { + mark = i; + while (i < size && data[i] != '\n') i++; + hoedown_buffer_put(ob, data + mark, i - mark); + + if (i >= size) break; + + if (!(i > 0 && data[i-1] == ' ')) + hoedown_buffer_putc(ob, ' '); + i++; + } +} + +/**************************** + * INLINE PARSING FUNCTIONS * + ****************************/ + +/* is_mail_autolink • looks for the address part of a mail autolink and '>' */ +/* this is less strict than the original markdown e-mail address matching */ +static size_t +is_mail_autolink(uint8_t *data, size_t size) +{ + size_t i = 0, nb = 0; + + /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */ + for (i = 0; i < size; ++i) { + if (isalnum(data[i])) + continue; + + switch (data[i]) { + case '@': + nb++; + + case '-': + case '.': + case '_': + break; + + case '>': + return (nb == 1) ? i + 1 : 0; + + default: + return 0; + } + } + + return 0; +} + +/* tag_length • returns the length of the given tag, or 0 is it's not valid */ +static size_t +tag_length(uint8_t *data, size_t size, hoedown_autolink_type *autolink) +{ + size_t i, j; + + /* a valid tag can't be shorter than 3 chars */ + if (size < 3) return 0; + + /* begins with a '<' optionally followed by '/', followed by letter or number */ + if (data[0] != '<') return 0; + i = (data[1] == '/') ? 2 : 1; + + if (!isalnum(data[i])) + return 0; + + /* scheme test */ + *autolink = HOEDOWN_AUTOLINK_NONE; + + /* try to find the beginning of an URI */ + while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-')) + i++; + + if (i > 1 && data[i] == '@') { + if ((j = is_mail_autolink(data + i, size - i)) != 0) { + *autolink = HOEDOWN_AUTOLINK_EMAIL; + return i + j; + } + } + + if (i > 2 && data[i] == ':') { + *autolink = HOEDOWN_AUTOLINK_NORMAL; + i++; + } + + /* completing autolink test: no spacing or ' or " */ + if (i >= size) + *autolink = HOEDOWN_AUTOLINK_NONE; + + else if (*autolink) { + j = i; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == '>' || data[i] == '\'' || + data[i] == '"' || data[i] == ' ' || data[i] == '\n') + break; + else i++; + } + + if (i >= size) return 0; + if (i > j && data[i] == '>') return i + 1; + /* one of the forbidden chars has been found */ + *autolink = HOEDOWN_AUTOLINK_NONE; + } + + /* looking for something looking like a tag end */ + while (i < size && data[i] != '>') i++; + if (i >= size) return 0; + return i + 1; +} + +/* parse_inline • parses inline markdown elements */ +static void +parse_inline(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t i = 0, end = 0, consumed = 0; + hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; + uint8_t *active_char = doc->active_char; + + if (doc->work_bufs[BUFFER_SPAN].size + + doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting) + return; + + while (i < size) { + /* copying inactive chars into the output */ + while (end < size && active_char[data[end]] == 0) + end++; + + if (doc->md.normal_text) { + work.data = data + i; + work.size = end - i; + doc->md.normal_text(ob, &work, &doc->data); + } + else + hoedown_buffer_put(ob, data + i, end - i); + + if (end >= size) break; + i = end; + + end = markdown_char_ptrs[ (int)active_char[data[end]] ](ob, doc, data + i, i - consumed, size - i); + if (!end) /* no action from the callback */ + end = i + 1; + else { + i += end; + end = i; + consumed = i; + } + } +} + +/* is_escaped • returns whether special char at data[loc] is escaped by '\\' */ +static int +is_escaped(uint8_t *data, size_t loc) +{ + size_t i = loc; + while (i >= 1 && data[i - 1] == '\\') + i--; + + /* odd numbers of backslashes escapes data[loc] */ + return (loc - i) % 2; +} + +/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */ +static size_t +find_emph_char(uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0; + + while (i < size) { + while (i < size && data[i] != c && data[i] != '[' && data[i] != '`') + i++; + + if (i == size) + return 0; + + /* not counting escaped chars */ + if (is_escaped(data, i)) { + i++; continue; + } + + if (data[i] == c) + return i; + + /* skipping a codespan */ + if (data[i] == '`') { + size_t span_nb = 0, bt; + size_t tmp_i = 0; + + /* counting the number of opening backticks */ + while (i < size && data[i] == '`') { + i++; span_nb++; + } + + if (i >= size) return 0; + + /* finding the matching closing sequence */ + bt = 0; + while (i < size && bt < span_nb) { + if (!tmp_i && data[i] == c) tmp_i = i; + if (data[i] == '`') bt++; + else bt = 0; + i++; + } + + /* not a well-formed codespan; use found matching emph char */ + if (i >= size) return tmp_i; + } + /* skipping a link */ + else if (data[i] == '[') { + size_t tmp_i = 0; + uint8_t cc; + + i++; + while (i < size && data[i] != ']') { + if (!tmp_i && data[i] == c) tmp_i = i; + i++; + } + + i++; + while (i < size && _isspace(data[i])) + i++; + + if (i >= size) + return tmp_i; + + switch (data[i]) { + case '[': + cc = ']'; break; + + case '(': + cc = ')'; break; + + default: + if (tmp_i) + return tmp_i; + else + continue; + } + + i++; + while (i < size && data[i] != cc) { + if (!tmp_i && data[i] == c) tmp_i = i; + i++; + } + + if (i >= size) + return tmp_i; + + i++; + } + } + + return 0; +} + +/* parse_emph1 • parsing single emphase */ +/* closed by a symbol not preceded by spacing and not followed by symbol */ +static size_t +parse_emph1(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + hoedown_buffer *work = 0; + int r; + + /* skipping one symbol if coming from emph3 */ + if (size > 1 && data[0] == c && data[1] == c) i = 1; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + if (i >= size) return 0; + + if (data[i] == c && !_isspace(data[i - 1])) { + + if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) { + if (i + 1 < size && isalnum(data[i + 1])) + continue; + } + + work = newbuf(doc, BUFFER_SPAN); + parse_inline(work, doc, data, i); + + if (doc->ext_flags & HOEDOWN_EXT_UNDERLINE && c == '_') + r = doc->md.underline(ob, work, &doc->data); + else + r = doc->md.emphasis(ob, work, &doc->data); + + popbuf(doc, BUFFER_SPAN); + return r ? i + 1 : 0; + } + } + + return 0; +} + +/* parse_emph2 • parsing single emphase */ +static size_t +parse_emph2(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + hoedown_buffer *work = 0; + int r; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + + if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) { + work = newbuf(doc, BUFFER_SPAN); + parse_inline(work, doc, data, i); + + if (c == '~') + r = doc->md.strikethrough(ob, work, &doc->data); + else if (c == '=') + r = doc->md.highlight(ob, work, &doc->data); + else + r = doc->md.double_emphasis(ob, work, &doc->data); + + popbuf(doc, BUFFER_SPAN); + return r ? i + 2 : 0; + } + i++; + } + return 0; +} + +/* parse_emph3 • parsing single emphase */ +/* finds the first closing tag, and delegates to the other emph */ +static size_t +parse_emph3(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + int r; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + + /* skip spacing preceded symbols */ + if (data[i] != c || _isspace(data[i - 1])) + continue; + + if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && doc->md.triple_emphasis) { + /* triple symbol found */ + hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); + + parse_inline(work, doc, data, i); + r = doc->md.triple_emphasis(ob, work, &doc->data); + popbuf(doc, BUFFER_SPAN); + return r ? i + 3 : 0; + + } else if (i + 1 < size && data[i + 1] == c) { + /* double symbol found, handing over to emph1 */ + len = parse_emph1(ob, doc, data - 2, size + 2, c); + if (!len) return 0; + else return len - 2; + + } else { + /* single symbol found, handing over to emph2 */ + len = parse_emph2(ob, doc, data - 1, size + 1, c); + if (!len) return 0; + else return len - 1; + } + } + return 0; +} + +/* parse_math • parses a math span until the given ending delimiter */ +static size_t +parse_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size, const char *end, size_t delimsz, int displaymode) +{ + hoedown_buffer text = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t i = delimsz; + + if (!doc->md.math) + return 0; + + /* find ending delimiter */ + while (1) { + while (i < size && data[i] != (uint8_t)end[0]) + i++; + + if (i >= size) + return 0; + + if (!is_escaped(data, i) && !(i + delimsz > size) + && memcmp(data + i, end, delimsz) == 0) + break; + + i++; + } + + /* prepare buffers */ + text.data = data + delimsz; + text.size = i - delimsz; + + /* if this is a $$ and MATH_EXPLICIT is not active, + * guess whether displaymode should be enabled from the context */ + i += delimsz; + if (delimsz == 2 && !(doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT)) + displaymode = is_empty_all(data - offset, offset) && is_empty_all(data + i, size - i); + + /* call callback */ + if (doc->md.math(ob, &text, displaymode, &doc->data)) + return i; + + return 0; +} + +/* char_emphasis • single and double emphasis parsing */ +static size_t +char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + uint8_t c = data[0]; + size_t ret; + + if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) { + if (offset > 0 && !_isspace(data[-1]) && data[-1] != '>' && data[-1] != '(') + return 0; + } + + if (size > 2 && data[1] != c) { + /* spacing cannot follow an opening emphasis; + * strikethrough and highlight only takes two characters '~~' */ + if (c == '~' || c == '=' || _isspace(data[1]) || (ret = parse_emph1(ob, doc, data + 1, size - 1, c)) == 0) + return 0; + + return ret + 1; + } + + if (size > 3 && data[1] == c && data[2] != c) { + if (_isspace(data[2]) || (ret = parse_emph2(ob, doc, data + 2, size - 2, c)) == 0) + return 0; + + return ret + 2; + } + + if (size > 4 && data[1] == c && data[2] == c && data[3] != c) { + if (c == '~' || c == '=' || _isspace(data[3]) || (ret = parse_emph3(ob, doc, data + 3, size - 3, c)) == 0) + return 0; + + return ret + 3; + } + + return 0; +} + + +/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */ +static size_t +char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + if (offset < 2 || data[-1] != ' ' || data[-2] != ' ') + return 0; + + /* removing the last space from ob and rendering */ + while (ob->size && ob->data[ob->size - 1] == ' ') + ob->size--; + + return doc->md.linebreak(ob, &doc->data) ? 1 : 0; +} + + +/* char_codespan • '`' parsing a code span (assuming codespan != 0) */ +static size_t +char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t end, nb = 0, i, f_begin, f_end; + + /* counting the number of backticks in the delimiter */ + while (nb < size && data[nb] == '`') + nb++; + + /* finding the next delimiter */ + i = 0; + for (end = nb; end < size && i < nb; end++) { + if (data[end] == '`') i++; + else i = 0; + } + + if (i < nb && end >= size) + return 0; /* no matching delimiter */ + + /* trimming outside spaces */ + f_begin = nb; + while (f_begin < end && data[f_begin] == ' ') + f_begin++; + + f_end = end - nb; + while (f_end > nb && data[f_end-1] == ' ') + f_end--; + + /* real code span */ + if (f_begin < f_end) { + work.data = data + f_begin; + work.size = f_end - f_begin; + + if (!doc->md.codespan(ob, &work, &doc->data)) + end = 0; + } else { + if (!doc->md.codespan(ob, 0, &doc->data)) + end = 0; + } + + return end; +} + +/* char_quote • '"' parsing a quote */ +static size_t +char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + size_t end, nq = 0, i, f_begin, f_end; + + /* counting the number of quotes in the delimiter */ + while (nq < size && data[nq] == '"') + nq++; + + /* finding the next delimiter */ + end = nq; + while (1) { + i = end; + end += find_emph_char(data + end, size - end, '"'); + if (end == i) return 0; /* no matching delimiter */ + i = end; + while (end < size && data[end] == '"' && end - i < nq) end++; + if (end - i >= nq) break; + } + + /* trimming outside spaces */ + f_begin = nq; + while (f_begin < end && data[f_begin] == ' ') + f_begin++; + + f_end = end - nq; + while (f_end > nq && data[f_end-1] == ' ') + f_end--; + + /* real quote */ + if (f_begin < f_end) { + hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); + parse_inline(work, doc, data + f_begin, f_end - f_begin); + + if (!doc->md.quote(ob, work, &doc->data)) + end = 0; + popbuf(doc, BUFFER_SPAN); + } else { + if (!doc->md.quote(ob, 0, &doc->data)) + end = 0; + } + + return end; +} + + +/* char_escape • '\\' backslash escape */ +static size_t +char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~=\"$"; + hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; + size_t w; + + if (size > 1) { + if (data[1] == '\\' && (doc->ext_flags & HOEDOWN_EXT_MATH) && + size > 2 && (data[2] == '(' || data[2] == '[')) { + const char *end = (data[2] == '[') ? "\\\\]" : "\\\\)"; + w = parse_math(ob, doc, data, offset, size, end, 3, data[2] == '['); + if (w) return w; + } + + if (strchr(escape_chars, data[1]) == NULL) + return 0; + + if (doc->md.normal_text) { + work.data = data + 1; + work.size = 1; + doc->md.normal_text(ob, &work, &doc->data); + } + else hoedown_buffer_putc(ob, data[1]); + } else if (size == 1) { + hoedown_buffer_putc(ob, data[0]); + } + + return 2; +} + +/* char_entity • '&' escaped when it doesn't belong to an entity */ +/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */ +static size_t +char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + size_t end = 1; + hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; + + if (end < size && data[end] == '#') + end++; + + while (end < size && isalnum(data[end])) + end++; + + if (end < size && data[end] == ';') + end++; /* real entity */ + else + return 0; /* lone '&' */ + + if (doc->md.entity) { + work.data = data; + work.size = end; + doc->md.entity(ob, &work, &doc->data); + } + else hoedown_buffer_put(ob, data, end); + + return end; +} + +/* char_langle_tag • '<' when tags or autolinks are allowed */ +static size_t +char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + hoedown_autolink_type altype = HOEDOWN_AUTOLINK_NONE; + size_t end = tag_length(data, size, &altype); + int ret = 0; + + work.data = data; + work.size = end; + + if (end > 2) { + if (doc->md.autolink && altype != HOEDOWN_AUTOLINK_NONE) { + hoedown_buffer *u_link = newbuf(doc, BUFFER_SPAN); + work.data = data + 1; + work.size = end - 2; + unscape_text(u_link, &work); + ret = doc->md.autolink(ob, u_link, altype, &doc->data); + popbuf(doc, BUFFER_SPAN); + } + else if (doc->md.raw_html) + ret = doc->md.raw_html(ob, &work, &doc->data); + } + + if (!ret) return 0; + else return end; +} + +static size_t +char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer *link, *link_url, *link_text; + size_t link_len, rewind; + + if (!doc->md.link || doc->in_link_body) + return 0; + + link = newbuf(doc, BUFFER_SPAN); + + if ((link_len = hoedown_autolink__www(&rewind, link, data, offset, size, HOEDOWN_AUTOLINK_SHORT_DOMAINS)) > 0) { + link_url = newbuf(doc, BUFFER_SPAN); + HOEDOWN_BUFPUTSL(link_url, "http://"); + hoedown_buffer_put(link_url, link->data, link->size); + + ob->size -= rewind; + if (doc->md.normal_text) { + link_text = newbuf(doc, BUFFER_SPAN); + doc->md.normal_text(link_text, link, &doc->data); + doc->md.link(ob, link_text, link_url, NULL, &doc->data); + popbuf(doc, BUFFER_SPAN); + } else { + doc->md.link(ob, link, link_url, NULL, &doc->data); + } + popbuf(doc, BUFFER_SPAN); + } + + popbuf(doc, BUFFER_SPAN); + return link_len; +} + +static size_t +char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer *link; + size_t link_len, rewind; + + if (!doc->md.autolink || doc->in_link_body) + return 0; + + link = newbuf(doc, BUFFER_SPAN); + + if ((link_len = hoedown_autolink__email(&rewind, link, data, offset, size, 0)) > 0) { + ob->size -= rewind; + doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_EMAIL, &doc->data); + } + + popbuf(doc, BUFFER_SPAN); + return link_len; +} + +static size_t +char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer *link; + size_t link_len, rewind; + + if (!doc->md.autolink || doc->in_link_body) + return 0; + + link = newbuf(doc, BUFFER_SPAN); + + if ((link_len = hoedown_autolink__url(&rewind, link, data, offset, size, 0)) > 0) { + ob->size -= rewind; + doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_NORMAL, &doc->data); + } + + popbuf(doc, BUFFER_SPAN); + return link_len; +} + +/* char_link • '[': parsing a link, a footnote or an image */ +static size_t +char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + int is_img = (offset && data[-1] == '!' && !is_escaped(data - offset, offset - 1)); + int is_footnote = (doc->ext_flags & HOEDOWN_EXT_FOOTNOTES && data[1] == '^'); + size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0; + hoedown_buffer *content = NULL; + hoedown_buffer *link = NULL; + hoedown_buffer *title = NULL; + hoedown_buffer *u_link = NULL; + size_t org_work_size = doc->work_bufs[BUFFER_SPAN].size; + int ret = 0, in_title = 0, qtype = 0; + + /* checking whether the correct renderer exists */ + if ((is_footnote && !doc->md.footnote_ref) || (is_img && !doc->md.image) + || (!is_img && !is_footnote && !doc->md.link)) + goto cleanup; + + /* looking for the matching closing bracket */ + i += find_emph_char(data + i, size - i, ']'); + txt_e = i; + + if (i < size && data[i] == ']') i++; + else goto cleanup; + + /* footnote link */ + if (is_footnote) { + hoedown_buffer id = { NULL, 0, 0, 0, NULL, NULL, NULL }; + struct footnote_ref *fr; + + if (txt_e < 3) + goto cleanup; + + id.data = data + 2; + id.size = txt_e - 2; + + fr = find_footnote_ref(&doc->footnotes_found, id.data, id.size); + + /* mark footnote used */ + if (fr && !fr->is_used) { + if(!add_footnote_ref(&doc->footnotes_used, fr)) + goto cleanup; + fr->is_used = 1; + fr->num = doc->footnotes_used.count; + + /* render */ + if (doc->md.footnote_ref) + ret = doc->md.footnote_ref(ob, fr->num, &doc->data); + } + + goto cleanup; + } + + /* skip any amount of spacing */ + /* (this is much more laxist than original markdown syntax) */ + while (i < size && _isspace(data[i])) + i++; + + /* inline style link */ + if (i < size && data[i] == '(') { + size_t nb_p; + + /* skipping initial spacing */ + i++; + + while (i < size && _isspace(data[i])) + i++; + + link_b = i; + + /* looking for link end: ' " ) */ + /* Count the number of open parenthesis */ + nb_p = 0; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == '(' && i != 0) { + nb_p++; i++; + } + else if (data[i] == ')') { + if (nb_p == 0) break; + else nb_p--; i++; + } else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break; + else i++; + } + + if (i >= size) goto cleanup; + link_e = i; + + /* looking for title end if present */ + if (data[i] == '\'' || data[i] == '"') { + qtype = data[i]; + in_title = 1; + i++; + title_b = i; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == qtype) {in_title = 0; i++;} + else if ((data[i] == ')') && !in_title) break; + else i++; + } + + if (i >= size) goto cleanup; + + /* skipping spacing after title */ + title_e = i - 1; + while (title_e > title_b && _isspace(data[title_e])) + title_e--; + + /* checking for closing quote presence */ + if (data[title_e] != '\'' && data[title_e] != '"') { + title_b = title_e = 0; + link_e = i; + } + } + + /* remove spacing at the end of the link */ + while (link_e > link_b && _isspace(data[link_e - 1])) + link_e--; + + /* remove optional angle brackets around the link */ + if (data[link_b] == '<') link_b++; + if (data[link_e - 1] == '>') link_e--; + + /* building escaped link and title */ + if (link_e > link_b) { + link = newbuf(doc, BUFFER_SPAN); + hoedown_buffer_put(link, data + link_b, link_e - link_b); + } + + if (title_e > title_b) { + title = newbuf(doc, BUFFER_SPAN); + hoedown_buffer_put(title, data + title_b, title_e - title_b); + } + + i++; + } + + /* reference style link */ + else if (i < size && data[i] == '[') { + hoedown_buffer *id = newbuf(doc, BUFFER_SPAN); + struct link_ref *lr; + + /* looking for the id */ + i++; + link_b = i; + while (i < size && data[i] != ']') i++; + if (i >= size) goto cleanup; + link_e = i; + + /* finding the link_ref */ + if (link_b == link_e) + replace_spacing(id, data + 1, txt_e - 1); + else + hoedown_buffer_put(id, data + link_b, link_e - link_b); + + lr = find_link_ref(doc->refs, id->data, id->size); + if (!lr) + goto cleanup; + + /* keeping link and title from link_ref */ + link = lr->link; + title = lr->title; + i++; + } + + /* shortcut reference style link */ + else { + hoedown_buffer *id = newbuf(doc, BUFFER_SPAN); + struct link_ref *lr; + + /* crafting the id */ + replace_spacing(id, data + 1, txt_e - 1); + + /* finding the link_ref */ + lr = find_link_ref(doc->refs, id->data, id->size); + if (!lr) + goto cleanup; + + /* keeping link and title from link_ref */ + link = lr->link; + title = lr->title; + + /* rewinding the spacing */ + i = txt_e + 1; + } + + /* building content: img alt is kept, only link content is parsed */ + if (txt_e > 1) { + content = newbuf(doc, BUFFER_SPAN); + if (is_img) { + hoedown_buffer_put(content, data + 1, txt_e - 1); + } else { + /* disable autolinking when parsing inline the + * content of a link */ + doc->in_link_body = 1; + parse_inline(content, doc, data + 1, txt_e - 1); + doc->in_link_body = 0; + } + } + + if (link) { + u_link = newbuf(doc, BUFFER_SPAN); + unscape_text(u_link, link); + } + + /* calling the relevant rendering function */ + if (is_img) { + if (ob->size && ob->data[ob->size - 1] == '!') + ob->size -= 1; + + ret = doc->md.image(ob, u_link, title, content, &doc->data); + } else { + ret = doc->md.link(ob, content, u_link, title, &doc->data); + } + + /* cleanup */ +cleanup: + doc->work_bufs[BUFFER_SPAN].size = (int)org_work_size; + return ret ? i : 0; +} + +static size_t +char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + size_t sup_start, sup_len; + hoedown_buffer *sup; + + if (!doc->md.superscript) + return 0; + + if (size < 2) + return 0; + + if (data[1] == '(') { + sup_start = 2; + sup_len = find_emph_char(data + 2, size - 2, ')') + 2; + + if (sup_len == size) + return 0; + } else { + sup_start = sup_len = 1; + + while (sup_len < size && !_isspace(data[sup_len])) + sup_len++; + } + + if (sup_len - sup_start == 0) + return (sup_start == 2) ? 3 : 0; + + sup = newbuf(doc, BUFFER_SPAN); + parse_inline(sup, doc, data + sup_start, sup_len - sup_start); + doc->md.superscript(ob, sup, &doc->data); + popbuf(doc, BUFFER_SPAN); + + return (sup_start == 2) ? sup_len + 1 : sup_len; +} + +static size_t +char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + /* double dollar */ + if (size > 1 && data[1] == '$') + return parse_math(ob, doc, data, offset, size, "$$", 2, 1); + + /* single dollar allowed only with MATH_EXPLICIT flag */ + if (doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT) + return parse_math(ob, doc, data, offset, size, "$", 1, 0); + + return 0; +} + +/********************************* + * BLOCK-LEVEL PARSING FUNCTIONS * + *********************************/ + +/* is_empty • returns the line length when it is empty, 0 otherwise */ +static size_t +is_empty(const uint8_t *data, size_t size) +{ + size_t i; + + for (i = 0; i < size && data[i] != '\n'; i++) + if (data[i] != ' ') + return 0; + + return i + 1; +} + +/* is_hrule • returns whether a line is a horizontal rule */ +static int +is_hrule(uint8_t *data, size_t size) +{ + size_t i = 0, n = 0; + uint8_t c; + + /* skipping initial spaces */ + if (size < 3) return 0; + if (data[0] == ' ') { i++; + if (data[1] == ' ') { i++; + if (data[2] == ' ') { i++; } } } + + /* looking at the hrule uint8_t */ + if (i + 2 >= size + || (data[i] != '*' && data[i] != '-' && data[i] != '_')) + return 0; + c = data[i]; + + /* the whole line must be the char or space */ + while (i < size && data[i] != '\n') { + if (data[i] == c) n++; + else if (data[i] != ' ') + return 0; + + i++; + } + + return n >= 3; +} + +/* check if a line is a code fence; return the + * end of the code fence. if passed, width of + * the fence rule and character will be returned */ +static size_t +is_codefence(uint8_t *data, size_t size, size_t *width, uint8_t *chr) +{ + size_t i = 0, n = 1; + uint8_t c; + + /* skipping initial spaces */ + if (size < 3) + return 0; + + if (data[0] == ' ') { i++; + if (data[1] == ' ') { i++; + if (data[2] == ' ') { i++; } } } + + /* looking at the hrule uint8_t */ + c = data[i]; + if (i + 2 >= size || !(c=='~' || c=='`')) + return 0; + + /* the fence must be that same character */ + while (++i < size && data[i] == c) + ++n; + + if (n < 3) + return 0; + + if (width) *width = n; + if (chr) *chr = c; + return i; +} + +/* expects single line, checks if it's a codefence and extracts language */ +static size_t +parse_codefence(uint8_t *data, size_t size, hoedown_buffer *lang, size_t *width, uint8_t *chr) +{ + size_t i, w, lang_start; + + i = w = is_codefence(data, size, width, chr); + if (i == 0) + return 0; + + while (i < size && _isspace(data[i])) + i++; + + lang_start = i; + + while (i < size && !_isspace(data[i])) + i++; + + lang->data = data + lang_start; + lang->size = i - lang_start; + + /* Avoid parsing a codespan as a fence */ + i = lang_start + 2; + while (i < size && !(data[i] == *chr && data[i-1] == *chr && data[i-2] == *chr)) i++; + if (i < size) return 0; + + return w; +} + +/* is_atxheader • returns whether the line is a hash-prefixed header */ +static int +is_atxheader(hoedown_document *doc, uint8_t *data, size_t size) +{ + if (data[0] != '#') + return 0; + + if (doc->ext_flags & HOEDOWN_EXT_SPACE_HEADERS) { + size_t level = 0; + + while (level < size && level < 6 && data[level] == '#') + level++; + + if (level < size && data[level] != ' ') + return 0; + } + + return 1; +} + +/* is_headerline • returns whether the line is a setext-style hdr underline */ +static int +is_headerline(uint8_t *data, size_t size) +{ + size_t i = 0; + + /* test of level 1 header */ + if (data[i] == '=') { + for (i = 1; i < size && data[i] == '='; i++); + while (i < size && data[i] == ' ') i++; + return (i >= size || data[i] == '\n') ? 1 : 0; } + + /* test of level 2 header */ + if (data[i] == '-') { + for (i = 1; i < size && data[i] == '-'; i++); + while (i < size && data[i] == ' ') i++; + return (i >= size || data[i] == '\n') ? 2 : 0; } + + return 0; +} + +static int +is_next_headerline(uint8_t *data, size_t size) +{ + size_t i = 0; + + while (i < size && data[i] != '\n') + i++; + + if (++i >= size) + return 0; + + return is_headerline(data + i, size - i); +} + +/* prefix_quote • returns blockquote prefix length */ +static size_t +prefix_quote(uint8_t *data, size_t size) +{ + size_t i = 0; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i < size && data[i] == '>') { + if (i + 1 < size && data[i + 1] == ' ') + return i + 2; + + return i + 1; + } + + return 0; +} + +/* prefix_code • returns prefix length for block code*/ +static size_t +prefix_code(uint8_t *data, size_t size) +{ + if (size > 3 && data[0] == ' ' && data[1] == ' ' + && data[2] == ' ' && data[3] == ' ') return 4; + + return 0; +} + +/* prefix_oli • returns ordered list item prefix */ +static size_t +prefix_oli(uint8_t *data, size_t size) +{ + size_t i = 0; + + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i >= size || data[i] < '0' || data[i] > '9') + return 0; + + while (i < size && data[i] >= '0' && data[i] <= '9') + i++; + + if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ') + return 0; + + if (is_next_headerline(data + i, size - i)) + return 0; + + return i + 2; +} + +/* prefix_uli • returns ordered list item prefix */ +static size_t +prefix_uli(uint8_t *data, size_t size) +{ + size_t i = 0; + + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i + 1 >= size || + (data[i] != '*' && data[i] != '+' && data[i] != '-') || + data[i + 1] != ' ') + return 0; + + if (is_next_headerline(data + i, size - i)) + return 0; + + return i + 2; +} + + +/* parse_block • parsing of one block, returning next uint8_t to parse */ +static void parse_block(hoedown_buffer *ob, hoedown_document *doc, + uint8_t *data, size_t size); + + +/* parse_blockquote • handles parsing of a blockquote fragment */ +static size_t +parse_blockquote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t beg, end = 0, pre, work_size = 0; + uint8_t *work_data = 0; + hoedown_buffer *out = 0; + + out = newbuf(doc, BUFFER_BLOCK); + beg = 0; + while (beg < size) { + for (end = beg + 1; end < size && data[end - 1] != '\n'; end++); + + pre = prefix_quote(data + beg, end - beg); + + if (pre) + beg += pre; /* skipping prefix */ + + /* empty line followed by non-quote line */ + else if (is_empty(data + beg, end - beg) && + (end >= size || (prefix_quote(data + end, size - end) == 0 && + !is_empty(data + end, size - end)))) + break; + + if (beg < end) { /* copy into the in-place working buffer */ + /* hoedown_buffer_put(work, data + beg, end - beg); */ + if (!work_data) + work_data = data + beg; + else if (data + beg != work_data + work_size) + memmove(work_data + work_size, data + beg, end - beg); + work_size += end - beg; + } + beg = end; + } + + parse_block(out, doc, work_data, work_size); + if (doc->md.blockquote) + doc->md.blockquote(ob, out, &doc->data); + popbuf(doc, BUFFER_BLOCK); + return end; +} + +static size_t +parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render); + +/* parse_blockquote • handles parsing of a regular paragraph */ +static size_t +parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t i = 0, end = 0; + int level = 0; + + work.data = data; + + while (i < size) { + for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */; + + if (is_empty(data + i, size - i)) + break; + + if ((level = is_headerline(data + i, size - i)) != 0) + break; + + if (is_atxheader(doc, data + i, size - i) || + is_hrule(data + i, size - i) || + prefix_quote(data + i, size - i)) { + end = i; + break; + } + + i = end; + } + + work.size = i; + while (work.size && data[work.size - 1] == '\n') + work.size--; + + if (!level) { + hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK); + parse_inline(tmp, doc, work.data, work.size); + if (doc->md.paragraph) + doc->md.paragraph(ob, tmp, &doc->data); + popbuf(doc, BUFFER_BLOCK); + } else { + hoedown_buffer *header_work; + + if (work.size) { + size_t beg; + i = work.size; + work.size -= 1; + + while (work.size && data[work.size] != '\n') + work.size -= 1; + + beg = work.size + 1; + while (work.size && data[work.size - 1] == '\n') + work.size -= 1; + + if (work.size > 0) { + hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK); + parse_inline(tmp, doc, work.data, work.size); + + if (doc->md.paragraph) + doc->md.paragraph(ob, tmp, &doc->data); + + popbuf(doc, BUFFER_BLOCK); + work.data += beg; + work.size = i - beg; + } + else work.size = i; + } + + header_work = newbuf(doc, BUFFER_SPAN); + parse_inline(header_work, doc, work.data, work.size); + + if (doc->md.header) + doc->md.header(ob, header_work, (int)level, &doc->data); + + popbuf(doc, BUFFER_SPAN); + } + + return end; +} + +/* parse_fencedcode • handles parsing of a block-level code fragment */ +static size_t +parse_fencedcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + hoedown_buffer text = { 0, 0, 0, 0, NULL, NULL, NULL }; + hoedown_buffer lang = { 0, 0, 0, 0, NULL, NULL, NULL }; + size_t i = 0, text_start, line_start; + size_t w, w2; + size_t width, width2; + uint8_t chr, chr2; + + /* parse codefence line */ + while (i < size && data[i] != '\n') + i++; + + w = parse_codefence(data, i, &lang, &width, &chr); + if (!w) + return 0; + + /* search for end */ + i++; + text_start = i; + while ((line_start = i) < size) { + while (i < size && data[i] != '\n') + i++; + + w2 = is_codefence(data + line_start, i - line_start, &width2, &chr2); + if (w == w2 && width == width2 && chr == chr2 && + is_empty(data + (line_start+w), i - (line_start+w))) + break; + + i++; + } + + text.data = data + text_start; + text.size = line_start - text_start; + + if (doc->md.blockcode) + doc->md.blockcode(ob, text.size ? &text : NULL, lang.size ? &lang : NULL, &doc->data); + + return i; +} + +static size_t +parse_blockcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t beg, end, pre; + hoedown_buffer *work = 0; + + work = newbuf(doc, BUFFER_BLOCK); + + beg = 0; + while (beg < size) { + for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {}; + pre = prefix_code(data + beg, end - beg); + + if (pre) + beg += pre; /* skipping prefix */ + else if (!is_empty(data + beg, end - beg)) + /* non-empty non-prefixed line breaks the pre */ + break; + + if (beg < end) { + /* verbatim copy to the working buffer, + escaping entities */ + if (is_empty(data + beg, end - beg)) + hoedown_buffer_putc(work, '\n'); + else hoedown_buffer_put(work, data + beg, end - beg); + } + beg = end; + } + + while (work->size && work->data[work->size - 1] == '\n') + work->size -= 1; + + hoedown_buffer_putc(work, '\n'); + + if (doc->md.blockcode) + doc->md.blockcode(ob, work, NULL, &doc->data); + + popbuf(doc, BUFFER_BLOCK); + return beg; +} + +/* parse_listitem • parsing of a single list item */ +/* assuming initial prefix is already removed */ +static size_t +parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags *flags) +{ + hoedown_buffer *work = 0, *inter = 0; + size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; + int in_empty = 0, has_inside_empty = 0, in_fence = 0; + + /* keeping track of the first indentation prefix */ + while (orgpre < 3 && orgpre < size && data[orgpre] == ' ') + orgpre++; + + beg = prefix_uli(data, size); + if (!beg) + beg = prefix_oli(data, size); + + if (!beg) + return 0; + + /* skipping to the beginning of the following line */ + end = beg; + while (end < size && data[end - 1] != '\n') + end++; + + /* getting working buffers */ + work = newbuf(doc, BUFFER_SPAN); + inter = newbuf(doc, BUFFER_SPAN); + + /* putting the first line into the working buffer */ + hoedown_buffer_put(work, data + beg, end - beg); + beg = end; + + /* process the following lines */ + while (beg < size) { + size_t has_next_uli = 0, has_next_oli = 0; + + end++; + + while (end < size && data[end - 1] != '\n') + end++; + + /* process an empty line */ + if (is_empty(data + beg, end - beg)) { + in_empty = 1; + beg = end; + continue; + } + + /* calculating the indentation */ + i = 0; + while (i < 4 && beg + i < end && data[beg + i] == ' ') + i++; + + pre = i; + + if (doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) { + if (is_codefence(data + beg + i, end - beg - i, NULL, NULL)) + in_fence = !in_fence; + } + + /* Only check for new list items if we are **not** inside + * a fenced code block */ + if (!in_fence) { + has_next_uli = prefix_uli(data + beg + i, end - beg - i); + has_next_oli = prefix_oli(data + beg + i, end - beg - i); + } + + /* checking for a new item */ + if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) { + if (in_empty) + has_inside_empty = 1; + + /* the following item must have the same (or less) indentation */ + if (pre <= orgpre) { + /* if the following item has different list type, we end this list */ + if (in_empty && ( + ((*flags & HOEDOWN_LIST_ORDERED) && has_next_uli) || + (!(*flags & HOEDOWN_LIST_ORDERED) && has_next_oli))) + *flags |= HOEDOWN_LI_END; + + break; + } + + if (!sublist) + sublist = work->size; + } + /* joining only indented stuff after empty lines; + * note that now we only require 1 space of indentation + * to continue a list */ + else if (in_empty && pre == 0) { + *flags |= HOEDOWN_LI_END; + break; + } + + if (in_empty) { + hoedown_buffer_putc(work, '\n'); + has_inside_empty = 1; + in_empty = 0; + } + + /* adding the line without prefix into the working buffer */ + hoedown_buffer_put(work, data + beg + i, end - beg - i); + beg = end; + } + + /* render of li contents */ + if (has_inside_empty) + *flags |= HOEDOWN_LI_BLOCK; + + if (*flags & HOEDOWN_LI_BLOCK) { + /* intermediate render of block li */ + if (sublist && sublist < work->size) { + parse_block(inter, doc, work->data, sublist); + parse_block(inter, doc, work->data + sublist, work->size - sublist); + } + else + parse_block(inter, doc, work->data, work->size); + } else { + /* intermediate render of inline li */ + if (sublist && sublist < work->size) { + parse_inline(inter, doc, work->data, sublist); + parse_block(inter, doc, work->data + sublist, work->size - sublist); + } + else + parse_inline(inter, doc, work->data, work->size); + } + + /* render of li itself */ + if (doc->md.listitem) + doc->md.listitem(ob, inter, *flags, &doc->data); + + popbuf(doc, BUFFER_SPAN); + popbuf(doc, BUFFER_SPAN); + return beg; +} + + +/* parse_list • parsing ordered or unordered list block */ +static size_t +parse_list(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags flags) +{ + hoedown_buffer *work = 0; + size_t i = 0, j; + + work = newbuf(doc, BUFFER_BLOCK); + + while (i < size) { + j = parse_listitem(work, doc, data + i, size - i, &flags); + i += j; + + if (!j || (flags & HOEDOWN_LI_END)) + break; + } + + if (doc->md.list) + doc->md.list(ob, work, flags, &doc->data); + popbuf(doc, BUFFER_BLOCK); + return i; +} + +/* parse_atxheader • parsing of atx-style headers */ +static size_t +parse_atxheader(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t level = 0; + size_t i, end, skip; + + while (level < size && level < 6 && data[level] == '#') + level++; + + for (i = level; i < size && data[i] == ' '; i++); + + for (end = i; end < size && data[end] != '\n'; end++); + skip = end; + + while (end && data[end - 1] == '#') + end--; + + while (end && data[end - 1] == ' ') + end--; + + if (end > i) { + hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); + + parse_inline(work, doc, data + i, end - i); + + if (doc->md.header) + doc->md.header(ob, work, (int)level, &doc->data); + + popbuf(doc, BUFFER_SPAN); + } + + return skip; +} + +/* parse_footnote_def • parse a single footnote definition */ +static void +parse_footnote_def(hoedown_buffer *ob, hoedown_document *doc, unsigned int num, uint8_t *data, size_t size) +{ + hoedown_buffer *work = 0; + work = newbuf(doc, BUFFER_SPAN); + + parse_block(work, doc, data, size); + + if (doc->md.footnote_def) + doc->md.footnote_def(ob, work, num, &doc->data); + popbuf(doc, BUFFER_SPAN); +} + +/* parse_footnote_list • render the contents of the footnotes */ +static void +parse_footnote_list(hoedown_buffer *ob, hoedown_document *doc, struct footnote_list *footnotes) +{ + hoedown_buffer *work = 0; + struct footnote_item *item; + struct footnote_ref *ref; + + if (footnotes->count == 0) + return; + + work = newbuf(doc, BUFFER_BLOCK); + + item = footnotes->head; + while (item) { + ref = item->ref; + parse_footnote_def(work, doc, ref->num, ref->contents->data, ref->contents->size); + item = item->next; + } + + if (doc->md.footnotes) + doc->md.footnotes(ob, work, &doc->data); + popbuf(doc, BUFFER_BLOCK); +} + +/* htmlblock_is_end • check for end of HTML block : ( *)\n */ +/* returns tag length on match, 0 otherwise */ +/* assumes data starts with "<" */ +static size_t +htmlblock_is_end( + const char *tag, + size_t tag_len, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i = tag_len + 3, w; + + /* try to match the end tag */ + /* note: we're not considering tags like "" which are still valid */ + if (i > size || + data[1] != '/' || + strncasecmp((char *)data + 2, tag, tag_len) != 0 || + data[tag_len + 2] != '>') + return 0; + + /* rest of the line must be empty */ + if ((w = is_empty(data + i, size - i)) == 0 && i < size) + return 0; + + return i + w; +} + +/* htmlblock_find_end • try to find HTML block ending tag */ +/* returns the length on match, 0 otherwise */ +static size_t +htmlblock_find_end( + const char *tag, + size_t tag_len, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i = 0, w; + + while (1) { + while (i < size && data[i] != '<') i++; + if (i >= size) return 0; + + w = htmlblock_is_end(tag, tag_len, doc, data + i, size - i); + if (w) return i + w; + i++; + } +} + +/* htmlblock_find_end_strict • try to find end of HTML block in strict mode */ +/* (it must be an unindented line, and have a blank line afterwads) */ +/* returns the length on match, 0 otherwise */ +static size_t +htmlblock_find_end_strict( + const char *tag, + size_t tag_len, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i = 0, mark; + + while (1) { + mark = i; + while (i < size && data[i] != '\n') i++; + if (i < size) i++; + if (i == mark) return 0; + + if (data[mark] == ' ' && mark > 0) continue; + mark += htmlblock_find_end(tag, tag_len, doc, data + mark, i - mark); + if (mark == i && (is_empty(data + i, size - i) || i >= size)) break; + } + + return i; +} + +/* parse_htmlblock • parsing of inline HTML block */ +static size_t +parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t i, j = 0, tag_len, tag_end; + const char *curtag = NULL; + + work.data = data; + + /* identification of the opening tag */ + if (size < 2 || data[0] != '<') + return 0; + + i = 1; + while (i < size && data[i] != '>' && data[i] != ' ') + i++; + + if (i < size) + curtag = hoedown_find_block_tag((char *)data + 1, (int)i - 1); + + /* handling of special cases */ + if (!curtag) { + + /* HTML comment, laxist form */ + if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') { + i = 5; + + while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>')) + i++; + + i++; + + if (i < size) + j = is_empty(data + i, size - i); + + if (j) { + work.size = i + j; + if (do_render && doc->md.blockhtml) + doc->md.blockhtml(ob, &work, &doc->data); + return work.size; + } + } + + /* HR, which is the only self-closing block tag considered */ + if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) { + i = 3; + while (i < size && data[i] != '>') + i++; + + if (i + 1 < size) { + i++; + j = is_empty(data + i, size - i); + if (j) { + work.size = i + j; + if (do_render && doc->md.blockhtml) + doc->md.blockhtml(ob, &work, &doc->data); + return work.size; + } + } + } + + /* no special case recognised */ + return 0; + } + + /* looking for a matching closing tag in strict mode */ + tag_len = strlen(curtag); + tag_end = htmlblock_find_end_strict(curtag, tag_len, doc, data, size); + + /* if not found, trying a second pass looking for indented match */ + /* but not if tag is "ins" or "del" (following original Markdown.pl) */ + if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) + tag_end = htmlblock_find_end(curtag, tag_len, doc, data, size); + + if (!tag_end) + return 0; + + /* the end of the block has been found */ + work.size = tag_end; + if (do_render && doc->md.blockhtml) + doc->md.blockhtml(ob, &work, &doc->data); + + return tag_end; +} + +static void +parse_table_row( + hoedown_buffer *ob, + hoedown_document *doc, + uint8_t *data, + size_t size, + size_t columns, + hoedown_table_flags *col_data, + hoedown_table_flags header_flag) +{ + size_t i = 0, col, len; + hoedown_buffer *row_work = 0; + + if (!doc->md.table_cell || !doc->md.table_row) + return; + + row_work = newbuf(doc, BUFFER_SPAN); + + if (i < size && data[i] == '|') + i++; + + for (col = 0; col < columns && i < size; ++col) { + size_t cell_start, cell_end; + hoedown_buffer *cell_work; + + cell_work = newbuf(doc, BUFFER_SPAN); + + while (i < size && _isspace(data[i])) + i++; + + cell_start = i; + + len = find_emph_char(data + i, size - i, '|'); + + /* Two possibilities for len == 0: + 1) No more pipe char found in the current line. + 2) The next pipe is right after the current one, i.e. empty cell. + For case 1, we skip to the end of line; for case 2 we just continue. + */ + if (len == 0 && i < size && data[i] != '|') + len = size - i; + i += len; + + cell_end = i - 1; + + while (cell_end > cell_start && _isspace(data[cell_end])) + cell_end--; + + parse_inline(cell_work, doc, data + cell_start, 1 + cell_end - cell_start); + doc->md.table_cell(row_work, cell_work, col_data[col] | header_flag, &doc->data); + + popbuf(doc, BUFFER_SPAN); + i++; + } + + for (; col < columns; ++col) { + hoedown_buffer empty_cell = { 0, 0, 0, 0, NULL, NULL, NULL }; + doc->md.table_cell(row_work, &empty_cell, col_data[col] | header_flag, &doc->data); + } + + doc->md.table_row(ob, row_work, &doc->data); + + popbuf(doc, BUFFER_SPAN); +} + +static size_t +parse_table_header( + hoedown_buffer *ob, + hoedown_document *doc, + uint8_t *data, + size_t size, + size_t *columns, + hoedown_table_flags **column_data) +{ + int pipes; + size_t i = 0, col, header_end, under_end; + + pipes = 0; + while (i < size && data[i] != '\n') + if (data[i++] == '|') + pipes++; + + if (i == size || pipes == 0) + return 0; + + header_end = i; + + while (header_end > 0 && _isspace(data[header_end - 1])) + header_end--; + + if (data[0] == '|') + pipes--; + + if (header_end && data[header_end - 1] == '|') + pipes--; + + if (pipes < 0) + return 0; + + *columns = pipes + 1; + *column_data = hoedown_calloc(*columns, sizeof(hoedown_table_flags)); + + /* Parse the header underline */ + i++; + if (i < size && data[i] == '|') + i++; + + under_end = i; + while (under_end < size && data[under_end] != '\n') + under_end++; + + for (col = 0; col < *columns && i < under_end; ++col) { + size_t dashes = 0; + + while (i < under_end && data[i] == ' ') + i++; + + if (data[i] == ':') { + i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_LEFT; + dashes++; + } + + while (i < under_end && data[i] == '-') { + i++; dashes++; + } + + if (i < under_end && data[i] == ':') { + i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_RIGHT; + dashes++; + } + + while (i < under_end && data[i] == ' ') + i++; + + if (i < under_end && data[i] != '|' && data[i] != '+') + break; + + if (dashes < 3) + break; + + i++; + } + + if (col < *columns) + return 0; + + parse_table_row( + ob, doc, data, + header_end, + *columns, + *column_data, + HOEDOWN_TABLE_HEADER + ); + + return under_end + 1; +} + +static size_t +parse_table( + hoedown_buffer *ob, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i; + + hoedown_buffer *work = 0; + hoedown_buffer *header_work = 0; + hoedown_buffer *body_work = 0; + + size_t columns; + hoedown_table_flags *col_data = NULL; + + work = newbuf(doc, BUFFER_BLOCK); + header_work = newbuf(doc, BUFFER_SPAN); + body_work = newbuf(doc, BUFFER_BLOCK); + + i = parse_table_header(header_work, doc, data, size, &columns, &col_data); + if (i > 0) { + + while (i < size) { + size_t row_start; + int pipes = 0; + + row_start = i; + + while (i < size && data[i] != '\n') + if (data[i++] == '|') + pipes++; + + if (pipes == 0 || i == size) { + i = row_start; + break; + } + + parse_table_row( + body_work, + doc, + data + row_start, + i - row_start, + columns, + col_data, 0 + ); + + i++; + } + + if (doc->md.table_header) + doc->md.table_header(work, header_work, &doc->data); + + if (doc->md.table_body) + doc->md.table_body(work, body_work, &doc->data); + + if (doc->md.table) + doc->md.table(ob, work, &doc->data); + } + + free(col_data); + popbuf(doc, BUFFER_SPAN); + popbuf(doc, BUFFER_BLOCK); + popbuf(doc, BUFFER_BLOCK); + return i; +} + +/* parse_block • parsing of one block, returning next uint8_t to parse */ +static void +parse_block(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t beg, end, i; + uint8_t *txt_data; + beg = 0; + + if (doc->work_bufs[BUFFER_SPAN].size + + doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting) + return; + + while (beg < size) { + txt_data = data + beg; + end = size - beg; + + if (is_atxheader(doc, txt_data, end)) + beg += parse_atxheader(ob, doc, txt_data, end); + + else if (data[beg] == '<' && doc->md.blockhtml && + (i = parse_htmlblock(ob, doc, txt_data, end, 1)) != 0) + beg += i; + + else if ((i = is_empty(txt_data, end)) != 0) + beg += i; + + else if (is_hrule(txt_data, end)) { + if (doc->md.hrule) + doc->md.hrule(ob, &doc->data); + + while (beg < size && data[beg] != '\n') + beg++; + + beg++; + } + + else if ((doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) != 0 && + (i = parse_fencedcode(ob, doc, txt_data, end)) != 0) + beg += i; + + else if ((doc->ext_flags & HOEDOWN_EXT_TABLES) != 0 && + (i = parse_table(ob, doc, txt_data, end)) != 0) + beg += i; + + else if (prefix_quote(txt_data, end)) + beg += parse_blockquote(ob, doc, txt_data, end); + + else if (!(doc->ext_flags & HOEDOWN_EXT_DISABLE_INDENTED_CODE) && prefix_code(txt_data, end)) + beg += parse_blockcode(ob, doc, txt_data, end); + + else if (prefix_uli(txt_data, end)) + beg += parse_list(ob, doc, txt_data, end, 0); + + else if (prefix_oli(txt_data, end)) + beg += parse_list(ob, doc, txt_data, end, HOEDOWN_LIST_ORDERED); + + else + beg += parse_paragraph(ob, doc, txt_data, end); + } +} + + + +/********************* + * REFERENCE PARSING * + *********************/ + +/* is_footnote • returns whether a line is a footnote definition or not */ +static int +is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, struct footnote_list *list) +{ + size_t i = 0; + hoedown_buffer *contents = 0; + size_t ind = 0; + int in_empty = 0; + size_t start = 0; + + size_t id_offset, id_end; + + /* up to 3 optional leading spaces */ + if (beg + 3 >= end) return 0; + if (data[beg] == ' ') { i = 1; + if (data[beg + 1] == ' ') { i = 2; + if (data[beg + 2] == ' ') { i = 3; + if (data[beg + 3] == ' ') return 0; } } } + i += beg; + + /* id part: caret followed by anything between brackets */ + if (data[i] != '[') return 0; + i++; + if (i >= end || data[i] != '^') return 0; + i++; + id_offset = i; + while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') + i++; + if (i >= end || data[i] != ']') return 0; + id_end = i; + + /* spacer: colon (space | tab)* newline? (space | tab)* */ + i++; + if (i >= end || data[i] != ':') return 0; + i++; + + /* getting content buffer */ + contents = hoedown_buffer_new(64); + + start = i; + + /* process lines similar to a list item */ + while (i < end) { + while (i < end && data[i] != '\n' && data[i] != '\r') i++; + + /* process an empty line */ + if (is_empty(data + start, i - start)) { + in_empty = 1; + if (i < end && (data[i] == '\n' || data[i] == '\r')) { + i++; + if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++; + } + start = i; + continue; + } + + /* calculating the indentation */ + ind = 0; + while (ind < 4 && start + ind < end && data[start + ind] == ' ') + ind++; + + /* joining only indented stuff after empty lines; + * note that now we only require 1 space of indentation + * to continue, just like lists */ + if (ind == 0) { + if (start == id_end + 2 && data[start] == '\t') {} + else break; + } + else if (in_empty) { + hoedown_buffer_putc(contents, '\n'); + } + + in_empty = 0; + + /* adding the line into the content buffer */ + hoedown_buffer_put(contents, data + start + ind, i - start - ind); + /* add carriage return */ + if (i < end) { + hoedown_buffer_putc(contents, '\n'); + if (i < end && (data[i] == '\n' || data[i] == '\r')) { + i++; + if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++; + } + } + start = i; + } + + if (last) + *last = start; + + if (list) { + struct footnote_ref *ref; + ref = create_footnote_ref(list, data + id_offset, id_end - id_offset); + if (!ref) + return 0; + if (!add_footnote_ref(list, ref)) { + free_footnote_ref(ref); + return 0; + } + ref->contents = contents; + } + + return 1; +} + +/* is_ref • returns whether a line is a reference or not */ +static int +is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs) +{ +/* int n; */ + size_t i = 0; + size_t id_offset, id_end; + size_t link_offset, link_end; + size_t title_offset, title_end; + size_t line_end; + + /* up to 3 optional leading spaces */ + if (beg + 3 >= end) return 0; + if (data[beg] == ' ') { i = 1; + if (data[beg + 1] == ' ') { i = 2; + if (data[beg + 2] == ' ') { i = 3; + if (data[beg + 3] == ' ') return 0; } } } + i += beg; + + /* id part: anything but a newline between brackets */ + if (data[i] != '[') return 0; + i++; + id_offset = i; + while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') + i++; + if (i >= end || data[i] != ']') return 0; + id_end = i; + + /* spacer: colon (space | tab)* newline? (space | tab)* */ + i++; + if (i >= end || data[i] != ':') return 0; + i++; + while (i < end && data[i] == ' ') i++; + if (i < end && (data[i] == '\n' || data[i] == '\r')) { + i++; + if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; } + while (i < end && data[i] == ' ') i++; + if (i >= end) return 0; + + /* link: spacing-free sequence, optionally between angle brackets */ + if (data[i] == '<') + i++; + + link_offset = i; + + while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r') + i++; + + if (data[i - 1] == '>') link_end = i - 1; + else link_end = i; + + /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */ + while (i < end && data[i] == ' ') i++; + if (i < end && data[i] != '\n' && data[i] != '\r' + && data[i] != '\'' && data[i] != '"' && data[i] != '(') + return 0; + line_end = 0; + /* computing end-of-line */ + if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i; + if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') + line_end = i + 1; + + /* optional (space|tab)* spacer after a newline */ + if (line_end) { + i = line_end + 1; + while (i < end && data[i] == ' ') i++; } + + /* optional title: any non-newline sequence enclosed in '"() + alone on its line */ + title_offset = title_end = 0; + if (i + 1 < end + && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) { + i++; + title_offset = i; + /* looking for EOL */ + while (i < end && data[i] != '\n' && data[i] != '\r') i++; + if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') + title_end = i + 1; + else title_end = i; + /* stepping back */ + i -= 1; + while (i > title_offset && data[i] == ' ') + i -= 1; + if (i > title_offset + && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) { + line_end = title_end; + title_end = i; } } + + if (!line_end || link_end == link_offset) + return 0; /* garbage after the link empty link */ + + /* a valid ref has been found, filling-in return structures */ + if (last) + *last = line_end; + + if (refs) { + struct link_ref *ref; + + ref = add_link_ref(refs, data + id_offset, id_end - id_offset); + if (!ref) + return 0; + + ref->link = hoedown_buffer_new(link_end - link_offset); + hoedown_buffer_put(ref->link, data + link_offset, link_end - link_offset); + + if (title_end > title_offset) { + ref->title = hoedown_buffer_new(title_end - title_offset); + hoedown_buffer_put(ref->title, data + title_offset, title_end - title_offset); + } + } + + return 1; +} + +static void expand_tabs(hoedown_buffer *ob, const uint8_t *line, size_t size) +{ + /* This code makes two assumptions: + * - Input is valid UTF-8. (Any byte with top two bits 10 is skipped, + * whether or not it is a valid UTF-8 continuation byte.) + * - Input contains no combining characters. (Combining characters + * should be skipped but are not.) + */ + size_t i = 0, tab = 0; + + while (i < size) { + size_t org = i; + + while (i < size && line[i] != '\t') { + /* ignore UTF-8 continuation bytes */ + if ((line[i] & 0xc0) != 0x80) + tab++; + i++; + } + + if (i > org) + hoedown_buffer_put(ob, line + org, i - org); + + if (i >= size) + break; + + do { + hoedown_buffer_putc(ob, ' '); tab++; + } while (tab % 4); + + i++; + } +} + +/********************** + * EXPORTED FUNCTIONS * + **********************/ + +hoedown_document * +hoedown_document_new( + const hoedown_renderer *renderer, + hoedown_extensions extensions, + size_t max_nesting) +{ + hoedown_document *doc = NULL; + + assert(max_nesting > 0 && renderer); + + doc = hoedown_malloc(sizeof(hoedown_document)); + memcpy(&doc->md, renderer, sizeof(hoedown_renderer)); + + doc->data.opaque = renderer->opaque; + + hoedown_stack_init(&doc->work_bufs[BUFFER_BLOCK], 4); + hoedown_stack_init(&doc->work_bufs[BUFFER_SPAN], 8); + + memset(doc->active_char, 0x0, 256); + + if (extensions & HOEDOWN_EXT_UNDERLINE && doc->md.underline) { + doc->active_char['_'] = MD_CHAR_EMPHASIS; + } + + if (doc->md.emphasis || doc->md.double_emphasis || doc->md.triple_emphasis) { + doc->active_char['*'] = MD_CHAR_EMPHASIS; + doc->active_char['_'] = MD_CHAR_EMPHASIS; + if (extensions & HOEDOWN_EXT_STRIKETHROUGH) + doc->active_char['~'] = MD_CHAR_EMPHASIS; + if (extensions & HOEDOWN_EXT_HIGHLIGHT) + doc->active_char['='] = MD_CHAR_EMPHASIS; + } + + if (doc->md.codespan) + doc->active_char['`'] = MD_CHAR_CODESPAN; + + if (doc->md.linebreak) + doc->active_char['\n'] = MD_CHAR_LINEBREAK; + + if (doc->md.image || doc->md.link || doc->md.footnotes || doc->md.footnote_ref) + doc->active_char['['] = MD_CHAR_LINK; + + doc->active_char['<'] = MD_CHAR_LANGLE; + doc->active_char['\\'] = MD_CHAR_ESCAPE; + doc->active_char['&'] = MD_CHAR_ENTITY; + + if (extensions & HOEDOWN_EXT_AUTOLINK) { + doc->active_char[':'] = MD_CHAR_AUTOLINK_URL; + doc->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL; + doc->active_char['w'] = MD_CHAR_AUTOLINK_WWW; + } + + if (extensions & HOEDOWN_EXT_SUPERSCRIPT) + doc->active_char['^'] = MD_CHAR_SUPERSCRIPT; + + if (extensions & HOEDOWN_EXT_QUOTE) + doc->active_char['"'] = MD_CHAR_QUOTE; + + if (extensions & HOEDOWN_EXT_MATH) + doc->active_char['$'] = MD_CHAR_MATH; + + /* Extension data */ + doc->ext_flags = extensions; + doc->max_nesting = max_nesting; + doc->in_link_body = 0; + + return doc; +} + +void +hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + static const uint8_t UTF8_BOM[] = {0xEF, 0xBB, 0xBF}; + + hoedown_buffer *text; + size_t beg, end; + + int footnotes_enabled; + + text = hoedown_buffer_new(64); + + /* Preallocate enough space for our buffer to avoid expanding while copying */ + hoedown_buffer_grow(text, size); + + /* reset the references table */ + memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); + + footnotes_enabled = doc->ext_flags & HOEDOWN_EXT_FOOTNOTES; + + /* reset the footnotes lists */ + if (footnotes_enabled) { + memset(&doc->footnotes_found, 0x0, sizeof(doc->footnotes_found)); + memset(&doc->footnotes_used, 0x0, sizeof(doc->footnotes_used)); + } + + /* first pass: looking for references, copying everything else */ + beg = 0; + + /* Skip a possible UTF-8 BOM, even though the Unicode standard + * discourages having these in UTF-8 documents */ + if (size >= 3 && memcmp(data, UTF8_BOM, 3) == 0) + beg += 3; + + while (beg < size) /* iterating over lines */ + if (footnotes_enabled && is_footnote(data, beg, size, &end, &doc->footnotes_found)) + beg = end; + else if (is_ref(data, beg, size, &end, doc->refs)) + beg = end; + else { /* skipping to the next line */ + end = beg; + while (end < size && data[end] != '\n' && data[end] != '\r') + end++; + + /* adding the line body if present */ + if (end > beg) + expand_tabs(text, data + beg, end - beg); + + while (end < size && (data[end] == '\n' || data[end] == '\r')) { + /* add one \n per newline */ + if (data[end] == '\n' || (end + 1 < size && data[end + 1] != '\n')) + hoedown_buffer_putc(text, '\n'); + end++; + } + + beg = end; + } + + /* pre-grow the output buffer to minimize allocations */ + hoedown_buffer_grow(ob, text->size + (text->size >> 1)); + + /* second pass: actual rendering */ + if (doc->md.doc_header) + doc->md.doc_header(ob, 0, &doc->data); + + if (text->size) { + /* adding a final newline if not already present */ + if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') + hoedown_buffer_putc(text, '\n'); + + parse_block(ob, doc, text->data, text->size); + } + + /* footnotes */ + if (footnotes_enabled) + parse_footnote_list(ob, doc, &doc->footnotes_used); + + if (doc->md.doc_footer) + doc->md.doc_footer(ob, 0, &doc->data); + + /* clean-up */ + hoedown_buffer_free(text); + free_link_refs(doc->refs); + if (footnotes_enabled) { + free_footnote_list(&doc->footnotes_found, 1); + free_footnote_list(&doc->footnotes_used, 0); + } + + assert(doc->work_bufs[BUFFER_SPAN].size == 0); + assert(doc->work_bufs[BUFFER_BLOCK].size == 0); +} + +void +hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + size_t i = 0, mark; + hoedown_buffer *text = hoedown_buffer_new(64); + + /* reset the references table */ + memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); + + /* first pass: expand tabs and process newlines */ + hoedown_buffer_grow(text, size); + while (1) { + mark = i; + while (i < size && data[i] != '\n' && data[i] != '\r') + i++; + + expand_tabs(text, data + mark, i - mark); + + if (i >= size) + break; + + while (i < size && (data[i] == '\n' || data[i] == '\r')) { + /* add one \n per newline */ + if (data[i] == '\n' || (i + 1 < size && data[i + 1] != '\n')) + hoedown_buffer_putc(text, '\n'); + i++; + } + } + + /* second pass: actual rendering */ + hoedown_buffer_grow(ob, text->size + (text->size >> 1)); + + if (doc->md.doc_header) + doc->md.doc_header(ob, 1, &doc->data); + + parse_inline(ob, doc, text->data, text->size); + + if (doc->md.doc_footer) + doc->md.doc_footer(ob, 1, &doc->data); + + /* clean-up */ + hoedown_buffer_free(text); + + assert(doc->work_bufs[BUFFER_SPAN].size == 0); + assert(doc->work_bufs[BUFFER_BLOCK].size == 0); +} + +void +hoedown_document_free(hoedown_document *doc) +{ + size_t i; + + for (i = 0; i < (size_t)doc->work_bufs[BUFFER_SPAN].asize; ++i) + hoedown_buffer_free(doc->work_bufs[BUFFER_SPAN].item[i]); + + for (i = 0; i < (size_t)doc->work_bufs[BUFFER_BLOCK].asize; ++i) + hoedown_buffer_free(doc->work_bufs[BUFFER_BLOCK].item[i]); + + hoedown_stack_uninit(&doc->work_bufs[BUFFER_SPAN]); + hoedown_stack_uninit(&doc->work_bufs[BUFFER_BLOCK]); + + free(doc); +} diff --git a/src/hoedown/document.h b/src/hoedown/document.h new file mode 100644 index 0000000..a8178fe --- /dev/null +++ b/src/hoedown/document.h @@ -0,0 +1,172 @@ +/* document.h - generic markdown parser */ + +#ifndef HOEDOWN_DOCUMENT_H +#define HOEDOWN_DOCUMENT_H + +#include "buffer.h" +#include "autolink.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +typedef enum hoedown_extensions { + /* block-level extensions */ + HOEDOWN_EXT_TABLES = (1 << 0), + HOEDOWN_EXT_FENCED_CODE = (1 << 1), + HOEDOWN_EXT_FOOTNOTES = (1 << 2), + + /* span-level extensions */ + HOEDOWN_EXT_AUTOLINK = (1 << 3), + HOEDOWN_EXT_STRIKETHROUGH = (1 << 4), + HOEDOWN_EXT_UNDERLINE = (1 << 5), + HOEDOWN_EXT_HIGHLIGHT = (1 << 6), + HOEDOWN_EXT_QUOTE = (1 << 7), + HOEDOWN_EXT_SUPERSCRIPT = (1 << 8), + HOEDOWN_EXT_MATH = (1 << 9), + + /* other flags */ + HOEDOWN_EXT_NO_INTRA_EMPHASIS = (1 << 11), + HOEDOWN_EXT_SPACE_HEADERS = (1 << 12), + HOEDOWN_EXT_MATH_EXPLICIT = (1 << 13), + + /* negative flags */ + HOEDOWN_EXT_DISABLE_INDENTED_CODE = (1 << 14) +} hoedown_extensions; + +#define HOEDOWN_EXT_BLOCK (\ + HOEDOWN_EXT_TABLES |\ + HOEDOWN_EXT_FENCED_CODE |\ + HOEDOWN_EXT_FOOTNOTES ) + +#define HOEDOWN_EXT_SPAN (\ + HOEDOWN_EXT_AUTOLINK |\ + HOEDOWN_EXT_STRIKETHROUGH |\ + HOEDOWN_EXT_UNDERLINE |\ + HOEDOWN_EXT_HIGHLIGHT |\ + HOEDOWN_EXT_QUOTE |\ + HOEDOWN_EXT_SUPERSCRIPT |\ + HOEDOWN_EXT_MATH ) + +#define HOEDOWN_EXT_FLAGS (\ + HOEDOWN_EXT_NO_INTRA_EMPHASIS |\ + HOEDOWN_EXT_SPACE_HEADERS |\ + HOEDOWN_EXT_MATH_EXPLICIT ) + +#define HOEDOWN_EXT_NEGATIVE (\ + HOEDOWN_EXT_DISABLE_INDENTED_CODE ) + +typedef enum hoedown_list_flags { + HOEDOWN_LIST_ORDERED = (1 << 0), + HOEDOWN_LI_BLOCK = (1 << 1) /*
  • containing block data */ +} hoedown_list_flags; + +typedef enum hoedown_table_flags { + HOEDOWN_TABLE_ALIGN_LEFT = 1, + HOEDOWN_TABLE_ALIGN_RIGHT = 2, + HOEDOWN_TABLE_ALIGN_CENTER = 3, + HOEDOWN_TABLE_ALIGNMASK = 3, + HOEDOWN_TABLE_HEADER = 4 +} hoedown_table_flags; + +typedef enum hoedown_autolink_type { + HOEDOWN_AUTOLINK_NONE, /* used internally when it is not an autolink*/ + HOEDOWN_AUTOLINK_NORMAL, /* normal http/http/ftp/mailto/etc link */ + HOEDOWN_AUTOLINK_EMAIL /* e-mail link without explit mailto: */ +} hoedown_autolink_type; + + +/********* + * TYPES * + *********/ + +struct hoedown_document; +typedef struct hoedown_document hoedown_document; + +struct hoedown_renderer_data { + void *opaque; +}; +typedef struct hoedown_renderer_data hoedown_renderer_data; + +/* hoedown_renderer - functions for rendering parsed data */ +struct hoedown_renderer { + /* state object */ + void *opaque; + + /* block level callbacks - NULL skips the block */ + void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data); + void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*header)(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data); + void (*hrule)(hoedown_buffer *ob, const hoedown_renderer_data *data); + void (*list)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); + void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); + void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_header)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_body)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data); + void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data); + void (*blockhtml)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* span level callbacks - NULL or return 0 prints the span verbatim */ + int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data); + int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*underline)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*quote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data); + int (*linebreak)(hoedown_buffer *ob, const hoedown_renderer_data *data); + int (*link)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data); + int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data); + int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data); + int (*raw_html)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* low level callbacks - NULL copies input directly into the output */ + void (*entity)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* miscellaneous callbacks */ + void (*doc_header)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); + void (*doc_footer)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); +}; +typedef struct hoedown_renderer hoedown_renderer; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_document_new: allocate a new document processor instance */ +hoedown_document *hoedown_document_new( + const hoedown_renderer *renderer, + hoedown_extensions extensions, + size_t max_nesting +) __attribute__ ((malloc)); + +/* hoedown_document_render: render regular Markdown using the document processor */ +void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_document_render_inline: render inline Markdown using the document processor */ +void hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_document_free: deallocate a document processor instance */ +void hoedown_document_free(hoedown_document *doc); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_DOCUMENT_H **/ diff --git a/src/hoedown/escape.c b/src/hoedown/escape.c new file mode 100644 index 0000000..122c6ec --- /dev/null +++ b/src/hoedown/escape.c @@ -0,0 +1,188 @@ +#include "escape.h" + +#include +#include +#include + + +#define likely(x) __builtin_expect((x),1) +#define unlikely(x) __builtin_expect((x),0) + + +/* + * The following characters will not be escaped: + * + * -_.+!*'(),%#@?=;:/,+&$ alphanum + * + * Note that this character set is the addition of: + * + * - The characters which are safe to be in an URL + * - The characters which are *not* safe to be in + * an URL because they are RESERVED characters. + * + * We assume (lazily) that any RESERVED char that + * appears inside an URL is actually meant to + * have its native function (i.e. as an URL + * component/separator) and hence needs no escaping. + * + * There are two exceptions: the chacters & (amp) + * and ' (single quote) do not appear in the table. + * They are meant to appear in the URL as components, + * yet they require special HTML-entity escaping + * to generate valid HTML markup. + * + * All other characters will be escaped to %XX. + * + */ +static const uint8_t HREF_SAFE[UINT8_MAX+1] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void +hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + static const char hex_chars[] = "0123456789ABCDEF"; + size_t i = 0, mark; + char hex_str[3]; + + hex_str[0] = '%'; + + while (i < size) { + mark = i; + while (i < size && HREF_SAFE[data[i]]) i++; + + /* Optimization for cases where there's nothing to escape */ + if (mark == 0 && i >= size) { + hoedown_buffer_put(ob, data, size); + return; + } + + if (likely(i > mark)) { + hoedown_buffer_put(ob, data + mark, i - mark); + } + + /* escaping */ + if (i >= size) + break; + + switch (data[i]) { + /* amp appears all the time in URLs, but needs + * HTML-entity escaping to be inside an href */ + case '&': + HOEDOWN_BUFPUTSL(ob, "&"); + break; + + /* the single quote is a valid URL character + * according to the standard; it needs HTML + * entity escaping too */ + case '\'': + HOEDOWN_BUFPUTSL(ob, "'"); + break; + + /* the space can be escaped to %20 or a plus + * sign. we're going with the generic escape + * for now. the plus thing is more commonly seen + * when building GET strings */ +#if 0 + case ' ': + hoedown_buffer_putc(ob, '+'); + break; +#endif + + /* every other character goes with a %XX escaping */ + default: + hex_str[1] = hex_chars[(data[i] >> 4) & 0xF]; + hex_str[2] = hex_chars[data[i] & 0xF]; + hoedown_buffer_put(ob, (uint8_t *)hex_str, 3); + } + + i++; + } +} + + +/** + * According to the OWASP rules: + * + * & --> & + * < --> < + * > --> > + * " --> " + * ' --> ' ' is not recommended + * / --> / forward slash is included as it helps end an HTML entity + * + */ +static const uint8_t HTML_ESCAPE_TABLE[UINT8_MAX+1] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const char *HTML_ESCAPES[] = { + "", + """, + "&", + "'", + "/", + "<", + ">" +}; + +void +hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure) +{ + size_t i = 0, mark; + + while (1) { + mark = i; + while (i < size && HTML_ESCAPE_TABLE[data[i]] == 0) i++; + + /* Optimization for cases where there's nothing to escape */ + if (mark == 0 && i >= size) { + hoedown_buffer_put(ob, data, size); + return; + } + + if (likely(i > mark)) + hoedown_buffer_put(ob, data + mark, i - mark); + + if (i >= size) break; + + /* The forward slash is only escaped in secure mode */ + if (!secure && data[i] == '/') { + hoedown_buffer_putc(ob, '/'); + } else { + hoedown_buffer_puts(ob, HTML_ESCAPES[HTML_ESCAPE_TABLE[data[i]]]); + } + + i++; + } +} diff --git a/src/hoedown/escape.h b/src/hoedown/escape.h new file mode 100644 index 0000000..d7659c2 --- /dev/null +++ b/src/hoedown/escape.h @@ -0,0 +1,28 @@ +/* escape.h - escape utilities */ + +#ifndef HOEDOWN_ESCAPE_H +#define HOEDOWN_ESCAPE_H + +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_escape_href: escape (part of) a URL inside HTML */ +void hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_escape_html: escape HTML */ +void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_ESCAPE_H **/ diff --git a/src/hoedown/html.c b/src/hoedown/html.c new file mode 100644 index 0000000..b5101c1 --- /dev/null +++ b/src/hoedown/html.c @@ -0,0 +1,754 @@ +#include "html.h" + +#include +#include +#include +#include + +#include "escape.h" + +#define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML) + +hoedown_html_tag +hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname) +{ + size_t i; + int closed = 0; + + if (size < 3 || data[0] != '<') + return HOEDOWN_HTML_TAG_NONE; + + i = 1; + + if (data[i] == '/') { + closed = 1; + i++; + } + + for (; i < size; ++i, ++tagname) { + if (*tagname == 0) + break; + + if (data[i] != *tagname) + return HOEDOWN_HTML_TAG_NONE; + } + + if (i == size) + return HOEDOWN_HTML_TAG_NONE; + + if (isspace(data[i]) || data[i] == '>') + return closed ? HOEDOWN_HTML_TAG_CLOSE : HOEDOWN_HTML_TAG_OPEN; + + return HOEDOWN_HTML_TAG_NONE; +} + +static void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length) +{ + hoedown_escape_html(ob, source, length, 0); +} + +static void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length) +{ + hoedown_escape_href(ob, source, length); +} + +/******************** + * GENERIC RENDERER * + ********************/ +static int +rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (!link || !link->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, "data, link->size); + + if (state->link_attributes) { + hoedown_buffer_putc(ob, '\"'); + state->link_attributes(ob, link, data); + hoedown_buffer_putc(ob, '>'); + } else { + HOEDOWN_BUFPUTSL(ob, "\">"); + } + + /* + * Pretty printing: if we get an email address as + * an actual URI, e.g. `mailto:foo@bar.com`, we don't + * want to print the `mailto:` prefix + */ + if (hoedown_buffer_prefix(link, "mailto:") == 0) { + escape_html(ob, link->data + 7, link->size - 7); + } else { + escape_html(ob, link->data, link->size); + } + + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static void +rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + + if (lang) { + HOEDOWN_BUFPUTSL(ob, "
    data, lang->size);
    +		HOEDOWN_BUFPUTSL(ob, "\">");
    +	} else {
    +		HOEDOWN_BUFPUTSL(ob, "
    ");
    +	}
    +
    +	if (text)
    +		escape_html(ob, text->data, text->size);
    +
    +	HOEDOWN_BUFPUTSL(ob, "
    \n"); +} + +static void +rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "
    \n"); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "
    \n"); +} + +static int +rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) +{ + HOEDOWN_BUFPUTSL(ob, ""); + if (text) escape_html(ob, text->data, text->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) return 0; + HOEDOWN_BUFPUTSL(ob, ""); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_underline(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_quote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); + return 1; +} + +static void +rndr_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (ob->size) + hoedown_buffer_putc(ob, '\n'); + + if (level <= state->toc_data.nesting_level) + hoedown_buffer_printf(ob, "", level, state->toc_data.header_count++); + else + hoedown_buffer_printf(ob, "", level); + + if (content) hoedown_buffer_put(ob, content->data, content->size); + hoedown_buffer_printf(ob, "\n", level); +} + +static int +rndr_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + HOEDOWN_BUFPUTSL(ob, "size) + escape_href(ob, link->data, link->size); + + if (title && title->size) { + HOEDOWN_BUFPUTSL(ob, "\" title=\""); + escape_html(ob, title->data, title->size); + } + + if (state->link_attributes) { + hoedown_buffer_putc(ob, '\"'); + state->link_attributes(ob, link, data); + hoedown_buffer_putc(ob, '>'); + } else { + HOEDOWN_BUFPUTSL(ob, "\">"); + } + + if (content && content->size) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_list(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "
      \n" : "
        \n"), 5); + if (content) hoedown_buffer_put(ob, content->data, content->size); + hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "
    \n" : "\n"), 6); +} + +static void +rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data) +{ + HOEDOWN_BUFPUTSL(ob, "
  • "); + if (content) { + size_t size = content->size; + while (size && content->data[size - 1] == '\n') + size--; + + hoedown_buffer_put(ob, content->data, size); + } + HOEDOWN_BUFPUTSL(ob, "
  • \n"); +} + +static void +rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + size_t i = 0; + + if (ob->size) hoedown_buffer_putc(ob, '\n'); + + if (!content || !content->size) + return; + + while (i < content->size && isspace(content->data[i])) i++; + + if (i == content->size) + return; + + HOEDOWN_BUFPUTSL(ob, "

    "); + if (state->flags & HOEDOWN_HTML_HARD_WRAP) { + size_t org; + while (i < content->size) { + org = i; + while (i < content->size && content->data[i] != '\n') + i++; + + if (i > org) + hoedown_buffer_put(ob, content->data + org, i - org); + + /* + * do not insert a line break if this newline + * is the last character on the paragraph + */ + if (i >= content->size - 1) + break; + + rndr_linebreak(ob, data); + i++; + } + } else { + hoedown_buffer_put(ob, content->data + i, content->size - i); + } + HOEDOWN_BUFPUTSL(ob, "

    \n"); +} + +static void +rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) +{ + size_t org, sz; + + if (!text) + return; + + /* FIXME: Do we *really* need to trim the HTML? How does that make a difference? */ + sz = text->size; + while (sz > 0 && text->data[sz - 1] == '\n') + sz--; + + org = 0; + while (org < sz && text->data[org] == '\n') + org++; + + if (org >= sz) + return; + + if (ob->size) + hoedown_buffer_putc(ob, '\n'); + + hoedown_buffer_put(ob, text->data + org, sz - org); + hoedown_buffer_putc(ob, '\n'); +} + +static int +rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) return 0; + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + if (ob->size) hoedown_buffer_putc(ob, '\n'); + hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); +} + +static int +rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + if (!link || !link->size) return 0; + + HOEDOWN_BUFPUTSL(ob, "data, link->size); + HOEDOWN_BUFPUTSL(ob, "\" alt=\""); + + if (alt && alt->size) + escape_html(ob, alt->data, alt->size); + + if (title && title->size) { + HOEDOWN_BUFPUTSL(ob, "\" title=\""); + escape_html(ob, title->data, title->size); } + + hoedown_buffer_puts(ob, USE_XHTML(state) ? "\"/>" : "\">"); + return 1; +} + +static int +rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + /* ESCAPE overrides SKIP_HTML. It doesn't look to see if + * there are any valid tags, just escapes all of them. */ + if((state->flags & HOEDOWN_HTML_ESCAPE) != 0) { + escape_html(ob, text->data, text->size); + return 1; + } + + if ((state->flags & HOEDOWN_HTML_SKIP_HTML) != 0) + return 1; + + hoedown_buffer_put(ob, text->data, text->size); + return 1; +} + +static void +rndr_table(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "\n"); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "
    \n"); +} + +static void +rndr_table_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "\n"); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); +} + +static void +rndr_table_body(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "\n"); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); +} + +static void +rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + HOEDOWN_BUFPUTSL(ob, "\n"); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); +} + +static void +rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data) +{ + if (flags & HOEDOWN_TABLE_HEADER) { + HOEDOWN_BUFPUTSL(ob, ""); + break; + + case HOEDOWN_TABLE_ALIGN_LEFT: + HOEDOWN_BUFPUTSL(ob, " style=\"text-align: left\">"); + break; + + case HOEDOWN_TABLE_ALIGN_RIGHT: + HOEDOWN_BUFPUTSL(ob, " style=\"text-align: right\">"); + break; + + default: + HOEDOWN_BUFPUTSL(ob, ">"); + } + + if (content) + hoedown_buffer_put(ob, content->data, content->size); + + if (flags & HOEDOWN_TABLE_HEADER) { + HOEDOWN_BUFPUTSL(ob, "\n"); + } else { + HOEDOWN_BUFPUTSL(ob, "\n"); + } +} + +static int +rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) return 0; + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (content) + escape_html(ob, content->data, content->size); +} + +static void +rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "
    \n"); + hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); + HOEDOWN_BUFPUTSL(ob, "
      \n"); + + if (content) hoedown_buffer_put(ob, content->data, content->size); + + HOEDOWN_BUFPUTSL(ob, "\n
    \n
    \n"); +} + +static void +rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data) +{ + size_t i = 0; + int pfound = 0; + + /* insert anchor at the end of first paragraph block */ + if (content) { + while ((i+3) < content->size) { + if (content->data[i++] != '<') continue; + if (content->data[i++] != '/') continue; + if (content->data[i++] != 'p' && content->data[i] != 'P') continue; + if (content->data[i] != '>') continue; + i -= 3; + pfound = 1; + break; + } + } + + hoedown_buffer_printf(ob, "\n
  • \n", num); + if (pfound) { + hoedown_buffer_put(ob, content->data, i); + hoedown_buffer_printf(ob, " ", num); + hoedown_buffer_put(ob, content->data + i, content->size - i); + } else if (content) { + hoedown_buffer_put(ob, content->data, content->size); + } + HOEDOWN_BUFPUTSL(ob, "
  • \n"); +} + +static int +rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data) +{ + hoedown_buffer_printf(ob, "%d", num, num, num); + return 1; +} + +static int +rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data) +{ + hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\[" : "\\("), 2); + escape_html(ob, text->data, text->size); + hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\]" : "\\)"), 2); + return 1; +} + +static void +toc_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (level <= state->toc_data.nesting_level) { + /* set the level offset if this is the first header + * we're parsing for the document */ + if (state->toc_data.current_level == 0) + state->toc_data.level_offset = level - 1; + + level -= state->toc_data.level_offset; + + if (level > state->toc_data.current_level) { + while (level > state->toc_data.current_level) { + HOEDOWN_BUFPUTSL(ob, "
      \n
    • \n"); + state->toc_data.current_level++; + } + } else if (level < state->toc_data.current_level) { + HOEDOWN_BUFPUTSL(ob, "
    • \n"); + while (level < state->toc_data.current_level) { + HOEDOWN_BUFPUTSL(ob, "
    \n
  • \n"); + state->toc_data.current_level--; + } + HOEDOWN_BUFPUTSL(ob,"
  • \n"); + } else { + HOEDOWN_BUFPUTSL(ob,"
  • \n
  • \n"); + } + + hoedown_buffer_printf(ob, "", state->toc_data.header_count++); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); + } +} + +static int +toc_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data) +{ + if (content && content->size) hoedown_buffer_put(ob, content->data, content->size); + return 1; +} + +static void +toc_finalize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state; + + if (inline_render) + return; + + state = data->opaque; + + while (state->toc_data.current_level > 0) { + HOEDOWN_BUFPUTSL(ob, "
  • \n\n"); + state->toc_data.current_level--; + } + + state->toc_data.header_count = 0; +} + +hoedown_renderer * +hoedown_html_toc_renderer_new(int nesting_level) +{ + static const hoedown_renderer cb_default = { + NULL, + + NULL, + NULL, + toc_header, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + NULL, + rndr_codespan, + rndr_double_emphasis, + rndr_emphasis, + rndr_underline, + rndr_highlight, + rndr_quote, + NULL, + NULL, + toc_link, + rndr_triple_emphasis, + rndr_strikethrough, + rndr_superscript, + NULL, + NULL, + NULL, + + NULL, + rndr_normal_text, + + NULL, + toc_finalize + }; + + hoedown_html_renderer_state *state; + hoedown_renderer *renderer; + + /* Prepare the state pointer */ + state = hoedown_malloc(sizeof(hoedown_html_renderer_state)); + memset(state, 0x0, sizeof(hoedown_html_renderer_state)); + + state->toc_data.nesting_level = nesting_level; + + /* Prepare the renderer */ + renderer = hoedown_malloc(sizeof(hoedown_renderer)); + memcpy(renderer, &cb_default, sizeof(hoedown_renderer)); + + renderer->opaque = state; + return renderer; +} + +hoedown_renderer * +hoedown_html_renderer_new(hoedown_html_flags render_flags, int nesting_level) +{ + static const hoedown_renderer cb_default = { + NULL, + + rndr_blockcode, + rndr_blockquote, + rndr_header, + rndr_hrule, + rndr_list, + rndr_listitem, + rndr_paragraph, + rndr_table, + rndr_table_header, + rndr_table_body, + rndr_tablerow, + rndr_tablecell, + rndr_footnotes, + rndr_footnote_def, + rndr_raw_block, + + rndr_autolink, + rndr_codespan, + rndr_double_emphasis, + rndr_emphasis, + rndr_underline, + rndr_highlight, + rndr_quote, + rndr_image, + rndr_linebreak, + rndr_link, + rndr_triple_emphasis, + rndr_strikethrough, + rndr_superscript, + rndr_footnote_ref, + rndr_math, + rndr_raw_html, + + NULL, + rndr_normal_text, + + NULL, + NULL + }; + + hoedown_html_renderer_state *state; + hoedown_renderer *renderer; + + /* Prepare the state pointer */ + state = hoedown_malloc(sizeof(hoedown_html_renderer_state)); + memset(state, 0x0, sizeof(hoedown_html_renderer_state)); + + state->flags = render_flags; + state->toc_data.nesting_level = nesting_level; + + /* Prepare the renderer */ + renderer = hoedown_malloc(sizeof(hoedown_renderer)); + memcpy(renderer, &cb_default, sizeof(hoedown_renderer)); + + if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE) + renderer->blockhtml = NULL; + + renderer->opaque = state; + return renderer; +} + +void +hoedown_html_renderer_free(hoedown_renderer *renderer) +{ + free(renderer->opaque); + free(renderer); +} diff --git a/src/hoedown/html.h b/src/hoedown/html.h new file mode 100644 index 0000000..e46e7fd --- /dev/null +++ b/src/hoedown/html.h @@ -0,0 +1,84 @@ +/* html.h - HTML renderer and utilities */ + +#ifndef HOEDOWN_HTML_H +#define HOEDOWN_HTML_H + +#include "document.h" +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +typedef enum hoedown_html_flags { + HOEDOWN_HTML_SKIP_HTML = (1 << 0), + HOEDOWN_HTML_ESCAPE = (1 << 1), + HOEDOWN_HTML_HARD_WRAP = (1 << 2), + HOEDOWN_HTML_USE_XHTML = (1 << 3) +} hoedown_html_flags; + +typedef enum hoedown_html_tag { + HOEDOWN_HTML_TAG_NONE = 0, + HOEDOWN_HTML_TAG_OPEN, + HOEDOWN_HTML_TAG_CLOSE +} hoedown_html_tag; + + +/********* + * TYPES * + *********/ + +struct hoedown_html_renderer_state { + void *opaque; + + struct { + int header_count; + int current_level; + int level_offset; + int nesting_level; + } toc_data; + + hoedown_html_flags flags; + + /* extra callbacks */ + void (*link_attributes)(hoedown_buffer *ob, const hoedown_buffer *url, const hoedown_renderer_data *data); +}; +typedef struct hoedown_html_renderer_state hoedown_html_renderer_state; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_html_smartypants: process an HTML snippet using SmartyPants for smart punctuation */ +void hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_html_is_tag: checks if data starts with a specific tag, returns the tag type or NONE */ +hoedown_html_tag hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname); + + +/* hoedown_html_renderer_new: allocates a regular HTML renderer */ +hoedown_renderer *hoedown_html_renderer_new( + hoedown_html_flags render_flags, + int nesting_level +) __attribute__ ((malloc)); + +/* hoedown_html_toc_renderer_new: like hoedown_html_renderer_new, but the returned renderer produces the Table of Contents */ +hoedown_renderer *hoedown_html_toc_renderer_new( + int nesting_level +) __attribute__ ((malloc)); + +/* hoedown_html_renderer_free: deallocate an HTML renderer */ +void hoedown_html_renderer_free(hoedown_renderer *renderer); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_HTML_H **/ diff --git a/src/hoedown/html_blocks.c b/src/hoedown/html_blocks.c new file mode 100644 index 0000000..f5e9dce --- /dev/null +++ b/src/hoedown/html_blocks.c @@ -0,0 +1,240 @@ +/* ANSI-C code produced by gperf version 3.0.3 */ +/* Command-line: gperf -L ANSI-C -N hoedown_find_block_tag -c -C -E -S 1 --ignore-case -m100 html_block_names.gperf */ +/* Computed positions: -k'1-2' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +/* maximum key range = 24, duplicates = 0 */ + +#ifndef GPERF_DOWNCASE +#define GPERF_DOWNCASE 1 +static unsigned char gperf_downcase[256] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 + }; +#endif + +#ifndef GPERF_CASE_STRNCMP +#define GPERF_CASE_STRNCMP 1 +static int +gperf_case_strncmp (register const char *s1, register const char *s2, register unsigned int n) +{ + for (; n > 0;) + { + unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; + unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; + if (c1 != 0 && c1 == c2) + { + n--; + continue; + } + return (int)c1 - (int)c2; + } + return 0; +} +#endif + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 22, 21, 19, 18, 16, 0, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 1, 25, 0, 25, + 1, 0, 0, 13, 0, 25, 25, 11, 2, 1, + 0, 25, 25, 5, 0, 2, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 1, 25, + 0, 25, 1, 0, 0, 13, 0, 25, 25, 11, + 2, 1, 0, 25, 25, 5, 0, 2, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25 + }; + register int hval = (int)len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[1]+1]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +#ifdef __GNUC__ +__inline +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const char * +hoedown_find_block_tag (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 24, + MIN_WORD_LENGTH = 1, + MAX_WORD_LENGTH = 10, + MIN_HASH_VALUE = 1, + MAX_HASH_VALUE = 24 + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE) + { + register const char *resword; + + switch (key - 1) + { + case 0: + resword = "p"; + goto compare; + case 1: + resword = "h6"; + goto compare; + case 2: + resword = "div"; + goto compare; + case 3: + resword = "del"; + goto compare; + case 4: + resword = "form"; + goto compare; + case 5: + resword = "table"; + goto compare; + case 6: + resword = "figure"; + goto compare; + case 7: + resword = "pre"; + goto compare; + case 8: + resword = "fieldset"; + goto compare; + case 9: + resword = "noscript"; + goto compare; + case 10: + resword = "script"; + goto compare; + case 11: + resword = "style"; + goto compare; + case 12: + resword = "dl"; + goto compare; + case 13: + resword = "ol"; + goto compare; + case 14: + resword = "ul"; + goto compare; + case 15: + resword = "math"; + goto compare; + case 16: + resword = "ins"; + goto compare; + case 17: + resword = "h5"; + goto compare; + case 18: + resword = "iframe"; + goto compare; + case 19: + resword = "h4"; + goto compare; + case 20: + resword = "h3"; + goto compare; + case 21: + resword = "blockquote"; + goto compare; + case 22: + resword = "h2"; + goto compare; + case 23: + resword = "h1"; + goto compare; + } + return 0; + compare: + if ((((unsigned char)*str ^ (unsigned char)*resword) & ~32) == 0 && !gperf_case_strncmp (str, resword, len) && resword[len] == '\0') + return resword; + } + } + return 0; +} diff --git a/src/hoedown/html_smartypants.c b/src/hoedown/html_smartypants.c new file mode 100644 index 0000000..b0904da --- /dev/null +++ b/src/hoedown/html_smartypants.c @@ -0,0 +1,435 @@ +#include "html.h" + +#include +#include +#include +#include + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +struct smartypants_data { + int in_squote; + int in_dquote; +}; + +static size_t smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); + +static size_t (*smartypants_cb_ptrs[]) + (hoedown_buffer *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) = +{ + NULL, /* 0 */ + smartypants_cb__dash, /* 1 */ + smartypants_cb__parens, /* 2 */ + smartypants_cb__squote, /* 3 */ + smartypants_cb__dquote, /* 4 */ + smartypants_cb__amp, /* 5 */ + smartypants_cb__period, /* 6 */ + smartypants_cb__number, /* 7 */ + smartypants_cb__ltag, /* 8 */ + smartypants_cb__backtick, /* 9 */ + smartypants_cb__escape, /* 10 */ +}; + +static const uint8_t smartypants_cb_chars[UINT8_MAX+1] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0, + 0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static int +word_boundary(uint8_t c) +{ + return c == 0 || isspace(c) || ispunct(c); +} + +/* + If 'text' begins with any kind of single quote (e.g. "'" or "'" etc.), + returns the length of the sequence of characters that makes up the single- + quote. Otherwise, returns zero. +*/ +static size_t +squote_len(const uint8_t *text, size_t size) +{ + static char* single_quote_list[] = { "'", "'", "'", "'", NULL }; + char** p; + + for (p = single_quote_list; *p; ++p) { + size_t len = strlen(*p); + if (size >= len && memcmp(text, *p, len) == 0) { + return len; + } + } + + return 0; +} + +/* Converts " or ' at very beginning or end of a word to left or right quote */ +static int +smartypants_quotes(hoedown_buffer *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open) +{ + char ent[8]; + + if (*is_open && !word_boundary(next_char)) + return 0; + + if (!(*is_open) && !word_boundary(previous_char)) + return 0; + + snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote); + *is_open = !(*is_open); + hoedown_buffer_puts(ob, ent); + return 1; +} + +/* + Converts ' to left or right single quote; but the initial ' might be in + different forms, e.g. ' or ' or '. + 'squote_text' points to the original single quote, and 'squote_size' is its length. + 'text' points at the last character of the single-quote, e.g. ' or ; +*/ +static size_t +smartypants_squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size, + const uint8_t *squote_text, size_t squote_size) +{ + if (size >= 2) { + uint8_t t1 = tolower(text[1]); + size_t next_squote_len = squote_len(text+1, size-1); + + /* convert '' to “ or ” */ + if (next_squote_len > 0) { + uint8_t next_char = (size > 1+next_squote_len) ? text[1+next_squote_len] : 0; + if (smartypants_quotes(ob, previous_char, next_char, 'd', &smrt->in_dquote)) + return next_squote_len; + } + + /* Tom's, isn't, I'm, I'd */ + if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && + (size == 3 || word_boundary(text[2]))) { + HOEDOWN_BUFPUTSL(ob, "’"); + return 0; + } + + /* you're, you'll, you've */ + if (size >= 3) { + uint8_t t2 = tolower(text[2]); + + if (((t1 == 'r' && t2 == 'e') || + (t1 == 'l' && t2 == 'l') || + (t1 == 'v' && t2 == 'e')) && + (size == 4 || word_boundary(text[3]))) { + HOEDOWN_BUFPUTSL(ob, "’"); + return 0; + } + } + } + + if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote)) + return 0; + + hoedown_buffer_put(ob, squote_text, squote_size); + return 0; +} + +/* Converts ' to left or right single quote. */ +static size_t +smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + return smartypants_squote(ob, smrt, previous_char, text, size, text, 1); +} + +/* Converts (c), (r), (tm) */ +static size_t +smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3) { + uint8_t t1 = tolower(text[1]); + uint8_t t2 = tolower(text[2]); + + if (t1 == 'c' && t2 == ')') { + HOEDOWN_BUFPUTSL(ob, "©"); + return 2; + } + + if (t1 == 'r' && t2 == ')') { + HOEDOWN_BUFPUTSL(ob, "®"); + return 2; + } + + if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') { + HOEDOWN_BUFPUTSL(ob, "™"); + return 3; + } + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts "--" to em-dash, etc. */ +static size_t +smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3 && text[1] == '-' && text[2] == '-') { + HOEDOWN_BUFPUTSL(ob, "—"); + return 2; + } + + if (size >= 2 && text[1] == '-') { + HOEDOWN_BUFPUTSL(ob, "–"); + return 1; + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts " etc. */ +static size_t +smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + size_t len; + if (size >= 6 && memcmp(text, """, 6) == 0) { + if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote)) + return 5; + } + + len = squote_len(text, size); + if (len > 0) { + return (len-1) + smartypants_squote(ob, smrt, previous_char, text+(len-1), size-(len-1), text, len); + } + + if (size >= 4 && memcmp(text, "�", 4) == 0) + return 3; + + hoedown_buffer_putc(ob, '&'); + return 0; +} + +/* Converts "..." to ellipsis */ +static size_t +smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3 && text[1] == '.' && text[2] == '.') { + HOEDOWN_BUFPUTSL(ob, "…"); + return 2; + } + + if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') { + HOEDOWN_BUFPUTSL(ob, "…"); + return 4; + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts `` to opening double quote */ +static size_t +smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 2 && text[1] == '`') { + if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) + return 1; + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts 1/2, 1/4, 3/4 */ +static size_t +smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (word_boundary(previous_char) && size >= 3) { + if (text[0] == '1' && text[1] == '/' && text[2] == '2') { + if (size == 3 || word_boundary(text[3])) { + HOEDOWN_BUFPUTSL(ob, "½"); + return 2; + } + } + + if (text[0] == '1' && text[1] == '/' && text[2] == '4') { + if (size == 3 || word_boundary(text[3]) || + (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) { + HOEDOWN_BUFPUTSL(ob, "¼"); + return 2; + } + } + + if (text[0] == '3' && text[1] == '/' && text[2] == '4') { + if (size == 3 || word_boundary(text[3]) || + (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) { + HOEDOWN_BUFPUTSL(ob, "¾"); + return 2; + } + } + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts " to left or right double quote */ +static size_t +smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote)) + HOEDOWN_BUFPUTSL(ob, """); + + return 0; +} + +static size_t +smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + static const char *skip_tags[] = { + "pre", "code", "var", "samp", "kbd", "math", "script", "style" + }; + static const size_t skip_tags_count = 8; + + size_t tag, i = 0; + + /* This is a comment. Copy everything verbatim until --> or EOF is seen. */ + if (i + 4 < size && memcmp(text, "", 3) != 0) + i++; + i += 3; + hoedown_buffer_put(ob, text, i + 1); + return i; + } + + while (i < size && text[i] != '>') + i++; + + for (tag = 0; tag < skip_tags_count; ++tag) { + if (hoedown_html_is_tag(text, size, skip_tags[tag]) == HOEDOWN_HTML_TAG_OPEN) + break; + } + + if (tag < skip_tags_count) { + for (;;) { + while (i < size && text[i] != '<') + i++; + + if (i == size) + break; + + if (hoedown_html_is_tag(text + i, size - i, skip_tags[tag]) == HOEDOWN_HTML_TAG_CLOSE) + break; + + i++; + } + + while (i < size && text[i] != '>') + i++; + } + + hoedown_buffer_put(ob, text, i + 1); + return i; +} + +static size_t +smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size < 2) + return 0; + + switch (text[1]) { + case '\\': + case '"': + case '\'': + case '.': + case '-': + case '`': + hoedown_buffer_putc(ob, text[1]); + return 1; + + default: + hoedown_buffer_putc(ob, '\\'); + return 0; + } +} + +#if 0 +static struct { + uint8_t c0; + const uint8_t *pattern; + const uint8_t *entity; + int skip; +} smartypants_subs[] = { + { '\'', "'s>", "’", 0 }, + { '\'', "'t>", "’", 0 }, + { '\'', "'re>", "’", 0 }, + { '\'', "'ll>", "’", 0 }, + { '\'', "'ve>", "’", 0 }, + { '\'', "'m>", "’", 0 }, + { '\'', "'d>", "’", 0 }, + { '-', "--", "—", 1 }, + { '-', "<->", "–", 0 }, + { '.', "...", "…", 2 }, + { '.', ". . .", "…", 4 }, + { '(', "(c)", "©", 2 }, + { '(', "(r)", "®", 2 }, + { '(', "(tm)", "™", 3 }, + { '3', "<3/4>", "¾", 2 }, + { '3', "<3/4ths>", "¾", 2 }, + { '1', "<1/2>", "½", 2 }, + { '1', "<1/4>", "¼", 2 }, + { '1', "<1/4th>", "¼", 2 }, + { '&', "�", 0, 3 }, +}; +#endif + +void +hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *text, size_t size) +{ + size_t i; + struct smartypants_data smrt = {0, 0}; + + if (!text) + return; + + hoedown_buffer_grow(ob, size); + + for (i = 0; i < size; ++i) { + size_t org; + uint8_t action = 0; + + org = i; + while (i < size && (action = smartypants_cb_chars[text[i]]) == 0) + i++; + + if (i > org) + hoedown_buffer_put(ob, text + org, i - org); + + if (i < size) { + i += smartypants_cb_ptrs[(int)action] + (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i); + } + } +} diff --git a/src/hoedown/stack.c b/src/hoedown/stack.c new file mode 100644 index 0000000..5c6102c --- /dev/null +++ b/src/hoedown/stack.c @@ -0,0 +1,79 @@ +#include "stack.h" + +#include "buffer.h" + +#include +#include +#include + +void +hoedown_stack_init(hoedown_stack *st, size_t initial_size) +{ + assert(st); + + st->item = NULL; + st->size = st->asize = 0; + + if (!initial_size) + initial_size = 8; + + hoedown_stack_grow(st, initial_size); +} + +void +hoedown_stack_uninit(hoedown_stack *st) +{ + assert(st); + + free(st->item); +} + +void +hoedown_stack_grow(hoedown_stack *st, size_t neosz) +{ + assert(st); + + if (st->asize >= neosz) + return; + + st->item = hoedown_realloc(st->item, neosz * sizeof(void *)); + memset(st->item + st->asize, 0x0, (neosz - st->asize) * sizeof(void *)); + + st->asize = neosz; + + if (st->size > neosz) + st->size = neosz; +} + +void +hoedown_stack_push(hoedown_stack *st, void *item) +{ + assert(st); + + if (st->size >= st->asize) + hoedown_stack_grow(st, st->size * 2); + + st->item[st->size++] = item; +} + +void * +hoedown_stack_pop(hoedown_stack *st) +{ + assert(st); + + if (!st->size) + return NULL; + + return st->item[--st->size]; +} + +void * +hoedown_stack_top(const hoedown_stack *st) +{ + assert(st); + + if (!st->size) + return NULL; + + return st->item[st->size - 1]; +} diff --git a/src/hoedown/stack.h b/src/hoedown/stack.h new file mode 100644 index 0000000..bf9b439 --- /dev/null +++ b/src/hoedown/stack.h @@ -0,0 +1,52 @@ +/* stack.h - simple stacking */ + +#ifndef HOEDOWN_STACK_H +#define HOEDOWN_STACK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/********* + * TYPES * + *********/ + +struct hoedown_stack { + void **item; + size_t size; + size_t asize; +}; +typedef struct hoedown_stack hoedown_stack; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_stack_init: initialize a stack */ +void hoedown_stack_init(hoedown_stack *st, size_t initial_size); + +/* hoedown_stack_uninit: free internal data of the stack */ +void hoedown_stack_uninit(hoedown_stack *st); + +/* hoedown_stack_grow: increase the allocated size to the given value */ +void hoedown_stack_grow(hoedown_stack *st, size_t neosz); + +/* hoedown_stack_push: push an item to the top of the stack */ +void hoedown_stack_push(hoedown_stack *st, void *item); + +/* hoedown_stack_pop: retrieve and remove the item at the top of the stack */ +void *hoedown_stack_pop(hoedown_stack *st); + +/* hoedown_stack_top: retrieve the item at the top of the stack */ +void *hoedown_stack_top(const hoedown_stack *st); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_STACK_H **/ diff --git a/src/hoedown/version.c b/src/hoedown/version.c new file mode 100644 index 0000000..744209b --- /dev/null +++ b/src/hoedown/version.c @@ -0,0 +1,9 @@ +#include "version.h" + +void +hoedown_version(int *major, int *minor, int *revision) +{ + *major = HOEDOWN_VERSION_MAJOR; + *minor = HOEDOWN_VERSION_MINOR; + *revision = HOEDOWN_VERSION_REVISION; +} diff --git a/src/hoedown/version.h b/src/hoedown/version.h new file mode 100644 index 0000000..01ea9fc --- /dev/null +++ b/src/hoedown/version.h @@ -0,0 +1,33 @@ +/* version.h - holds Hoedown's version */ + +#ifndef HOEDOWN_VERSION_H +#define HOEDOWN_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +#define HOEDOWN_VERSION "3.0.3" +#define HOEDOWN_VERSION_MAJOR 3 +#define HOEDOWN_VERSION_MINOR 0 +#define HOEDOWN_VERSION_REVISION 3 + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_version: retrieve Hoedown's version numbers */ +void hoedown_version(int *major, int *minor, int *revision); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_VERSION_H **/ diff --git a/src/misaka.c b/src/misaka.c deleted file mode 100644 index 44c16dd..0000000 --- a/src/misaka.c +++ /dev/null @@ -1,4467 +0,0 @@ -/* Generated by Cython 0.19.1 on Fri Jul 5 11:18:30 2013 */ - -#define PY_SSIZE_T_CLEAN -#ifndef CYTHON_USE_PYLONG_INTERNALS -#ifdef PYLONG_BITS_IN_DIGIT -#define CYTHON_USE_PYLONG_INTERNALS 0 -#else -#include "pyconfig.h" -#ifdef PYLONG_BITS_IN_DIGIT -#define CYTHON_USE_PYLONG_INTERNALS 1 -#else -#define CYTHON_USE_PYLONG_INTERNALS 0 -#endif -#endif -#endif -#include "Python.h" -#ifndef Py_PYTHON_H - #error Python headers needed to compile C extensions, please install development version of Python. -#elif PY_VERSION_HEX < 0x02040000 - #error Cython requires Python 2.4+. -#else -#include /* For offsetof */ -#ifndef offsetof -#define offsetof(type, member) ( (size_t) & ((type*)0) -> member ) -#endif -#if !defined(WIN32) && !defined(MS_WINDOWS) - #ifndef __stdcall - #define __stdcall - #endif - #ifndef __cdecl - #define __cdecl - #endif - #ifndef __fastcall - #define __fastcall - #endif -#endif -#ifndef DL_IMPORT - #define DL_IMPORT(t) t -#endif -#ifndef DL_EXPORT - #define DL_EXPORT(t) t -#endif -#ifndef PY_LONG_LONG - #define PY_LONG_LONG LONG_LONG -#endif -#ifndef Py_HUGE_VAL - #define Py_HUGE_VAL HUGE_VAL -#endif -#ifdef PYPY_VERSION -#define CYTHON_COMPILING_IN_PYPY 1 -#define CYTHON_COMPILING_IN_CPYTHON 0 -#else -#define CYTHON_COMPILING_IN_PYPY 0 -#define CYTHON_COMPILING_IN_CPYTHON 1 -#endif -#if PY_VERSION_HEX < 0x02050000 - typedef int Py_ssize_t; - #define PY_SSIZE_T_MAX INT_MAX - #define PY_SSIZE_T_MIN INT_MIN - #define PY_FORMAT_SIZE_T "" - #define CYTHON_FORMAT_SSIZE_T "" - #define PyInt_FromSsize_t(z) PyInt_FromLong(z) - #define PyInt_AsSsize_t(o) __Pyx_PyInt_AsInt(o) - #define PyNumber_Index(o) ((PyNumber_Check(o) && !PyFloat_Check(o)) ? PyNumber_Int(o) : \ - (PyErr_Format(PyExc_TypeError, \ - "expected index value, got %.200s", Py_TYPE(o)->tp_name), \ - (PyObject*)0)) - #define __Pyx_PyIndex_Check(o) (PyNumber_Check(o) && !PyFloat_Check(o) && \ - !PyComplex_Check(o)) - #define PyIndex_Check __Pyx_PyIndex_Check - #define PyErr_WarnEx(category, message, stacklevel) PyErr_Warn(category, message) - #define __PYX_BUILD_PY_SSIZE_T "i" -#else - #define __PYX_BUILD_PY_SSIZE_T "n" - #define CYTHON_FORMAT_SSIZE_T "z" - #define __Pyx_PyIndex_Check PyIndex_Check -#endif -#if PY_VERSION_HEX < 0x02060000 - #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) - #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) - #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) - #define PyVarObject_HEAD_INIT(type, size) \ - PyObject_HEAD_INIT(type) size, - #define PyType_Modified(t) - typedef struct { - void *buf; - PyObject *obj; - Py_ssize_t len; - Py_ssize_t itemsize; - int readonly; - int ndim; - char *format; - Py_ssize_t *shape; - Py_ssize_t *strides; - Py_ssize_t *suboffsets; - void *internal; - } Py_buffer; - #define PyBUF_SIMPLE 0 - #define PyBUF_WRITABLE 0x0001 - #define PyBUF_FORMAT 0x0004 - #define PyBUF_ND 0x0008 - #define PyBUF_STRIDES (0x0010 | PyBUF_ND) - #define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) - #define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) - #define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) - #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) - #define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_FORMAT | PyBUF_WRITABLE) - #define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_FORMAT | PyBUF_WRITABLE) - typedef int (*getbufferproc)(PyObject *, Py_buffer *, int); - typedef void (*releasebufferproc)(PyObject *, Py_buffer *); -#endif -#if PY_MAJOR_VERSION < 3 - #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" - #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \ - PyCode_New(a, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) -#else - #define __Pyx_BUILTIN_MODULE_NAME "builtins" - #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \ - PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) -#endif -#if PY_MAJOR_VERSION < 3 && PY_MINOR_VERSION < 6 - #define PyUnicode_FromString(s) PyUnicode_Decode(s, strlen(s), "UTF-8", "strict") -#endif -#if PY_MAJOR_VERSION >= 3 - #define Py_TPFLAGS_CHECKTYPES 0 - #define Py_TPFLAGS_HAVE_INDEX 0 -#endif -#if (PY_VERSION_HEX < 0x02060000) || (PY_MAJOR_VERSION >= 3) - #define Py_TPFLAGS_HAVE_NEWBUFFER 0 -#endif -#if PY_VERSION_HEX < 0x02060000 - #define Py_TPFLAGS_HAVE_VERSION_TAG 0 -#endif -#if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND) - #define CYTHON_PEP393_ENABLED 1 - #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ? \ - 0 : _PyUnicode_Ready((PyObject *)(op))) - #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u) - #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) - #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i) -#else - #define CYTHON_PEP393_ENABLED 0 - #define __Pyx_PyUnicode_READY(op) (0) - #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_SIZE(u) - #define __Pyx_PyUnicode_READ_CHAR(u, i) ((Py_UCS4)(PyUnicode_AS_UNICODE(u)[i])) - #define __Pyx_PyUnicode_READ(k, d, i) ((k=k), (Py_UCS4)(((Py_UNICODE*)d)[i])) -#endif -#if PY_MAJOR_VERSION >= 3 - #define PyBaseString_Type PyUnicode_Type - #define PyStringObject PyUnicodeObject - #define PyString_Type PyUnicode_Type - #define PyString_Check PyUnicode_Check - #define PyString_CheckExact PyUnicode_CheckExact -#endif -#if PY_VERSION_HEX < 0x02060000 - #define PyBytesObject PyStringObject - #define PyBytes_Type PyString_Type - #define PyBytes_Check PyString_Check - #define PyBytes_CheckExact PyString_CheckExact - #define PyBytes_FromString PyString_FromString - #define PyBytes_FromStringAndSize PyString_FromStringAndSize - #define PyBytes_FromFormat PyString_FromFormat - #define PyBytes_DecodeEscape PyString_DecodeEscape - #define PyBytes_AsString PyString_AsString - #define PyBytes_AsStringAndSize PyString_AsStringAndSize - #define PyBytes_Size PyString_Size - #define PyBytes_AS_STRING PyString_AS_STRING - #define PyBytes_GET_SIZE PyString_GET_SIZE - #define PyBytes_Repr PyString_Repr - #define PyBytes_Concat PyString_Concat - #define PyBytes_ConcatAndDel PyString_ConcatAndDel -#endif -#if PY_MAJOR_VERSION >= 3 - #define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj) - #define __Pyx_PyBaseString_CheckExact(obj) PyUnicode_CheckExact(obj) -#else - #define __Pyx_PyBaseString_Check(obj) (PyString_CheckExact(obj) || PyUnicode_CheckExact(obj) || \ - PyString_Check(obj) || PyUnicode_Check(obj)) - #define __Pyx_PyBaseString_CheckExact(obj) (Py_TYPE(obj) == &PyBaseString_Type) -#endif -#if PY_VERSION_HEX < 0x02060000 - #define PySet_Check(obj) PyObject_TypeCheck(obj, &PySet_Type) - #define PyFrozenSet_Check(obj) PyObject_TypeCheck(obj, &PyFrozenSet_Type) -#endif -#ifndef PySet_CheckExact - #define PySet_CheckExact(obj) (Py_TYPE(obj) == &PySet_Type) -#endif -#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type) -#if PY_MAJOR_VERSION >= 3 - #define PyIntObject PyLongObject - #define PyInt_Type PyLong_Type - #define PyInt_Check(op) PyLong_Check(op) - #define PyInt_CheckExact(op) PyLong_CheckExact(op) - #define PyInt_FromString PyLong_FromString - #define PyInt_FromUnicode PyLong_FromUnicode - #define PyInt_FromLong PyLong_FromLong - #define PyInt_FromSize_t PyLong_FromSize_t - #define PyInt_FromSsize_t PyLong_FromSsize_t - #define PyInt_AsLong PyLong_AsLong - #define PyInt_AS_LONG PyLong_AS_LONG - #define PyInt_AsSsize_t PyLong_AsSsize_t - #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask - #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask -#endif -#if PY_MAJOR_VERSION >= 3 - #define PyBoolObject PyLongObject -#endif -#if PY_VERSION_HEX < 0x03020000 - typedef long Py_hash_t; - #define __Pyx_PyInt_FromHash_t PyInt_FromLong - #define __Pyx_PyInt_AsHash_t PyInt_AsLong -#else - #define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t - #define __Pyx_PyInt_AsHash_t PyInt_AsSsize_t -#endif -#if (PY_MAJOR_VERSION < 3) || (PY_VERSION_HEX >= 0x03010300) - #define __Pyx_PySequence_GetSlice(obj, a, b) PySequence_GetSlice(obj, a, b) - #define __Pyx_PySequence_SetSlice(obj, a, b, value) PySequence_SetSlice(obj, a, b, value) - #define __Pyx_PySequence_DelSlice(obj, a, b) PySequence_DelSlice(obj, a, b) -#else - #define __Pyx_PySequence_GetSlice(obj, a, b) (unlikely(!(obj)) ? \ - (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), (PyObject*)0) : \ - (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_GetSlice(obj, a, b)) : \ - (PyErr_Format(PyExc_TypeError, "'%.200s' object is unsliceable", (obj)->ob_type->tp_name), (PyObject*)0))) - #define __Pyx_PySequence_SetSlice(obj, a, b, value) (unlikely(!(obj)) ? \ - (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \ - (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_SetSlice(obj, a, b, value)) : \ - (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice assignment", (obj)->ob_type->tp_name), -1))) - #define __Pyx_PySequence_DelSlice(obj, a, b) (unlikely(!(obj)) ? \ - (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \ - (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_DelSlice(obj, a, b)) : \ - (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice deletion", (obj)->ob_type->tp_name), -1))) -#endif -#if PY_MAJOR_VERSION >= 3 - #define PyMethod_New(func, self, klass) ((self) ? PyMethod_New(func, self) : PyInstanceMethod_New(func)) -#endif -#if PY_VERSION_HEX < 0x02050000 - #define __Pyx_GetAttrString(o,n) PyObject_GetAttrString((o),((char *)(n))) - #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),((char *)(n)),(a)) - #define __Pyx_DelAttrString(o,n) PyObject_DelAttrString((o),((char *)(n))) -#else - #define __Pyx_GetAttrString(o,n) PyObject_GetAttrString((o),(n)) - #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),(n),(a)) - #define __Pyx_DelAttrString(o,n) PyObject_DelAttrString((o),(n)) -#endif -#if PY_VERSION_HEX < 0x02050000 - #define __Pyx_NAMESTR(n) ((char *)(n)) - #define __Pyx_DOCSTR(n) ((char *)(n)) -#else - #define __Pyx_NAMESTR(n) (n) - #define __Pyx_DOCSTR(n) (n) -#endif -#ifndef CYTHON_INLINE - #if defined(__GNUC__) - #define CYTHON_INLINE __inline__ - #elif defined(_MSC_VER) - #define CYTHON_INLINE __inline - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define CYTHON_INLINE inline - #else - #define CYTHON_INLINE - #endif -#endif -#ifndef CYTHON_RESTRICT - #if defined(__GNUC__) - #define CYTHON_RESTRICT __restrict__ - #elif defined(_MSC_VER) - #define CYTHON_RESTRICT __restrict - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define CYTHON_RESTRICT restrict - #else - #define CYTHON_RESTRICT - #endif -#endif -#ifdef NAN -#define __PYX_NAN() ((float) NAN) -#else -static CYTHON_INLINE float __PYX_NAN() { - /* Initialize NaN. The sign is irrelevant, an exponent with all bits 1 and - a nonzero mantissa means NaN. If the first bit in the mantissa is 1, it is - a quiet NaN. */ - float value; - memset(&value, 0xFF, sizeof(value)); - return value; -} -#endif - - -#if PY_MAJOR_VERSION >= 3 - #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) - #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y) -#else - #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) - #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y) -#endif - -#ifndef __PYX_EXTERN_C - #ifdef __cplusplus - #define __PYX_EXTERN_C extern "C" - #else - #define __PYX_EXTERN_C extern - #endif -#endif - -#if defined(WIN32) || defined(MS_WINDOWS) -#define _USE_MATH_DEFINES -#endif -#include -#define __PYX_HAVE__misaka -#define __PYX_HAVE_API__misaka -#include "stdint.h" -#include "sundown/buffer.h" -#include "sundown/html.h" -#include "sundown/markdown.h" -#include "wrapper.h" -#ifdef _OPENMP -#include -#endif /* _OPENMP */ - -#ifdef PYREX_WITHOUT_ASSERTIONS -#define CYTHON_WITHOUT_ASSERTIONS -#endif - -#ifndef CYTHON_UNUSED -# if defined(__GNUC__) -# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -# define CYTHON_UNUSED __attribute__ ((__unused__)) -# else -# define CYTHON_UNUSED -# endif -# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER)) -# define CYTHON_UNUSED __attribute__ ((__unused__)) -# else -# define CYTHON_UNUSED -# endif -#endif -typedef struct {PyObject **p; char *s; const Py_ssize_t n; const char* encoding; - const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/ - -#define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII 0 -#define __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT 0 -#define __PYX_DEFAULT_STRING_ENCODING "" -#define __Pyx_PyObject_FromString __Pyx_PyBytes_FromString -#define __Pyx_PyObject_FromStringAndSize __Pyx_PyBytes_FromStringAndSize -static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject*); -static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length); -#define __Pyx_PyBytes_FromString PyBytes_FromString -#define __Pyx_PyBytes_FromStringAndSize PyBytes_FromStringAndSize -static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(char*); -#if PY_MAJOR_VERSION < 3 - #define __Pyx_PyStr_FromString __Pyx_PyBytes_FromString - #define __Pyx_PyStr_FromStringAndSize __Pyx_PyBytes_FromStringAndSize -#else - #define __Pyx_PyStr_FromString __Pyx_PyUnicode_FromString - #define __Pyx_PyStr_FromStringAndSize __Pyx_PyUnicode_FromStringAndSize -#endif -#define __Pyx_PyObject_AsUString(s) ((unsigned char*) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_FromUString(s) __Pyx_PyObject_FromString((char*)s) -#define __Pyx_PyBytes_FromUString(s) __Pyx_PyBytes_FromString((char*)s) -#define __Pyx_PyStr_FromUString(s) __Pyx_PyStr_FromString((char*)s) -#define __Pyx_PyUnicode_FromUString(s) __Pyx_PyUnicode_FromString((char*)s) -#if PY_MAJOR_VERSION < 3 -static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) -{ - const Py_UNICODE *u_end = u; - while (*u_end++) ; - return u_end - u - 1; -} -#else -#define __Pyx_Py_UNICODE_strlen Py_UNICODE_strlen -#endif -#define __Pyx_PyUnicode_FromUnicode(u) PyUnicode_FromUnicode(u, __Pyx_Py_UNICODE_strlen(u)) -#define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode -#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -#define __Pyx_Owned_Py_None(b) (Py_INCREF(Py_None), Py_None) -#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); -static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x); -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); -static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); -static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject*); -#if CYTHON_COMPILING_IN_CPYTHON -#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) -#else -#define __pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x) -#endif -#define __pyx_PyFloat_AsFloat(x) ((float) __pyx_PyFloat_AsDouble(x)) -#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII -static int __Pyx_sys_getdefaultencoding_not_ascii; -static int __Pyx_init_sys_getdefaultencoding_params() { - PyObject* sys = NULL; - PyObject* default_encoding = NULL; - PyObject* ascii_chars_u = NULL; - PyObject* ascii_chars_b = NULL; - sys = PyImport_ImportModule("sys"); - if (sys == NULL) goto bad; - default_encoding = PyObject_CallMethod(sys, (char*) (const char*) "getdefaultencoding", NULL); - if (default_encoding == NULL) goto bad; - if (strcmp(PyBytes_AsString(default_encoding), "ascii") == 0) { - __Pyx_sys_getdefaultencoding_not_ascii = 0; - } else { - const char* default_encoding_c = PyBytes_AS_STRING(default_encoding); - char ascii_chars[128]; - int c; - for (c = 0; c < 128; c++) { - ascii_chars[c] = c; - } - __Pyx_sys_getdefaultencoding_not_ascii = 1; - ascii_chars_u = PyUnicode_DecodeASCII(ascii_chars, 128, NULL); - if (ascii_chars_u == NULL) goto bad; - ascii_chars_b = PyUnicode_AsEncodedString(ascii_chars_u, default_encoding_c, NULL); - if (ascii_chars_b == NULL || strncmp(ascii_chars, PyBytes_AS_STRING(ascii_chars_b), 128) != 0) { - PyErr_Format( - PyExc_ValueError, - "This module compiled with c_string_encoding=ascii, but default encoding '%s' is not a superset of ascii.", - default_encoding_c); - goto bad; - } - } - Py_XDECREF(sys); - Py_XDECREF(default_encoding); - Py_XDECREF(ascii_chars_u); - Py_XDECREF(ascii_chars_b); - return 0; -bad: - Py_XDECREF(sys); - Py_XDECREF(default_encoding); - Py_XDECREF(ascii_chars_u); - Py_XDECREF(ascii_chars_b); - return -1; -} -#endif -#if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT && PY_MAJOR_VERSION >= 3 -#define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeUTF8(c_str, size, NULL) -#else -#define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_Decode(c_str, size, __PYX_DEFAULT_STRING_ENCODING, NULL) -#if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT -static char* __PYX_DEFAULT_STRING_ENCODING; -static int __Pyx_init_sys_getdefaultencoding_params() { - PyObject* sys = NULL; - PyObject* default_encoding = NULL; - char* default_encoding_c; - sys = PyImport_ImportModule("sys"); - if (sys == NULL) goto bad; - default_encoding = PyObject_CallMethod(sys, (char*) (const char*) "getdefaultencoding", NULL); - if (default_encoding == NULL) goto bad; - default_encoding_c = PyBytes_AS_STRING(default_encoding); - __PYX_DEFAULT_STRING_ENCODING = (char*) malloc(strlen(default_encoding_c)); - strcpy(__PYX_DEFAULT_STRING_ENCODING, default_encoding_c); - Py_DECREF(sys); - Py_DECREF(default_encoding); - return 0; -bad: - Py_XDECREF(sys); - Py_XDECREF(default_encoding); - return -1; -} -#endif -#endif - - -#ifdef __GNUC__ - /* Test for GCC > 2.95 */ - #if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) - #define likely(x) __builtin_expect(!!(x), 1) - #define unlikely(x) __builtin_expect(!!(x), 0) - #else /* __GNUC__ > 2 ... */ - #define likely(x) (x) - #define unlikely(x) (x) - #endif /* __GNUC__ > 2 ... */ -#else /* __GNUC__ */ - #define likely(x) (x) - #define unlikely(x) (x) -#endif /* __GNUC__ */ - -static PyObject *__pyx_m; -static PyObject *__pyx_d; -static PyObject *__pyx_b; -static PyObject *__pyx_empty_tuple; -static PyObject *__pyx_empty_bytes; -static int __pyx_lineno; -static int __pyx_clineno = 0; -static const char * __pyx_cfilenm= __FILE__; -static const char *__pyx_filename; - - -static const char *__pyx_f[] = { - "misaka.pyx", -}; - -/*--- Type declarations ---*/ -struct __pyx_obj_6misaka_BaseRenderer; -struct __pyx_obj_6misaka_HtmlRenderer; -struct __pyx_obj_6misaka_Markdown; -struct __pyx_obj_6misaka_SmartyPants; -struct __pyx_obj_6misaka_HtmlTocRenderer; - -/* "misaka.pyx":117 - * - * - * cdef class BaseRenderer: # <<<<<<<<<<<<<< - * """The ``BaseRenderer`` is boilerplate code for creating your own renderers by - * sublassing `BaseRenderer`. It takes care of setting the callbacks and flags. - */ -struct __pyx_obj_6misaka_BaseRenderer { - PyObject_HEAD - struct sd_callbacks callbacks; - struct renderopt options; - int flags; -}; - - -/* "misaka.pyx":155 - * - * - * cdef class HtmlRenderer(BaseRenderer): # <<<<<<<<<<<<<< - * """The HTML renderer that's included in Sundown. - * - */ -struct __pyx_obj_6misaka_HtmlRenderer { - struct __pyx_obj_6misaka_BaseRenderer __pyx_base; -}; - - -/* "misaka.pyx":185 - * - * - * cdef class Markdown: # <<<<<<<<<<<<<< - * """The Markdown parser. - * - */ -struct __pyx_obj_6misaka_Markdown { - PyObject_HEAD - struct sd_markdown *markdown; - struct __pyx_obj_6misaka_BaseRenderer *renderer; -}; - - -/* "misaka.pyx":68 - * - * - * cdef class SmartyPants: # <<<<<<<<<<<<<< - * """Smartypants is a post-processor for (X)HTML renderers and can be used - * standalone or as a mixin. It adds a methode named ``postprocess`` to the - */ -struct __pyx_obj_6misaka_SmartyPants { - PyObject_HEAD -}; - - -/* "misaka.pyx":171 - * - * - * cdef class HtmlTocRenderer(BaseRenderer): # <<<<<<<<<<<<<< - * """The HTML table of contents renderer that's included in Sundown. - * - */ -struct __pyx_obj_6misaka_HtmlTocRenderer { - struct __pyx_obj_6misaka_BaseRenderer __pyx_base; -}; - -#ifndef CYTHON_REFNANNY - #define CYTHON_REFNANNY 0 -#endif -#if CYTHON_REFNANNY - typedef struct { - void (*INCREF)(void*, PyObject*, int); - void (*DECREF)(void*, PyObject*, int); - void (*GOTREF)(void*, PyObject*, int); - void (*GIVEREF)(void*, PyObject*, int); - void* (*SetupContext)(const char*, int, const char*); - void (*FinishContext)(void**); - } __Pyx_RefNannyAPIStruct; - static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL; - static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname); /*proto*/ - #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL; -#ifdef WITH_THREAD - #define __Pyx_RefNannySetupContext(name, acquire_gil) \ - if (acquire_gil) { \ - PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure(); \ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__); \ - PyGILState_Release(__pyx_gilstate_save); \ - } else { \ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__); \ - } -#else - #define __Pyx_RefNannySetupContext(name, acquire_gil) \ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__) -#endif - #define __Pyx_RefNannyFinishContext() \ - __Pyx_RefNanny->FinishContext(&__pyx_refnanny) - #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__) - #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), __LINE__) - #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), __LINE__) - #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__) - #define __Pyx_XINCREF(r) do { if((r) != NULL) {__Pyx_INCREF(r); }} while(0) - #define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r); }} while(0) - #define __Pyx_XGOTREF(r) do { if((r) != NULL) {__Pyx_GOTREF(r); }} while(0) - #define __Pyx_XGIVEREF(r) do { if((r) != NULL) {__Pyx_GIVEREF(r);}} while(0) -#else - #define __Pyx_RefNannyDeclarations - #define __Pyx_RefNannySetupContext(name, acquire_gil) - #define __Pyx_RefNannyFinishContext() - #define __Pyx_INCREF(r) Py_INCREF(r) - #define __Pyx_DECREF(r) Py_DECREF(r) - #define __Pyx_GOTREF(r) - #define __Pyx_GIVEREF(r) - #define __Pyx_XINCREF(r) Py_XINCREF(r) - #define __Pyx_XDECREF(r) Py_XDECREF(r) - #define __Pyx_XGOTREF(r) - #define __Pyx_XGIVEREF(r) -#endif /* CYTHON_REFNANNY */ -#define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0) -#define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0) - -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) { - PyTypeObject* tp = Py_TYPE(obj); - if (likely(tp->tp_getattro)) - return tp->tp_getattro(obj, attr_name); -#if PY_MAJOR_VERSION < 3 - if (likely(tp->tp_getattr)) - return tp->tp_getattr(obj, PyString_AS_STRING(attr_name)); -#endif - return PyObject_GetAttr(obj, attr_name); -} -#else -#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n) -#endif - -static PyObject *__Pyx_GetBuiltinName(PyObject *name); /*proto*/ - -static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name); /*proto*/ - -static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[], \ - PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, \ - const char* function_name); /*proto*/ - -static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact, - Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/ - -static CYTHON_INLINE PyObject *__Pyx_GetModuleGlobalName(PyObject *name); /*proto*/ - -#include - -static CYTHON_INLINE PyObject* __Pyx_decode_c_string( - const char* cstring, Py_ssize_t start, Py_ssize_t stop, - const char* encoding, const char* errors, - PyObject* (*decode_func)(const char *s, Py_ssize_t size, const char *errors)); - -static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb); /*proto*/ -static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb); /*proto*/ - -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause); /*proto*/ - -static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject *); - -static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject *); - -static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject *); - -static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject *); - -static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject *); - -static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject *); - -static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject *); - -static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject *); - -static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject *); - -static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject *); - -static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject *); - -static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject *); - -static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject *); - -static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject *); - -static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject *); - -static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject *); - -static int __Pyx_check_binary_version(void); - -typedef struct { - int code_line; - PyCodeObject* code_object; -} __Pyx_CodeObjectCacheEntry; -struct __Pyx_CodeObjectCache { - int count; - int max_count; - __Pyx_CodeObjectCacheEntry* entries; -}; -static struct __Pyx_CodeObjectCache __pyx_code_cache = {0,0,NULL}; -static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line); -static PyCodeObject *__pyx_find_code_object(int code_line); -static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object); - -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename); /*proto*/ - -static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ - - -/* Module declarations from 'libc.stdint' */ - -/* Module declarations from 'sundown' */ - -/* Module declarations from 'wrapper' */ - -/* Module declarations from 'misaka' */ -static PyTypeObject *__pyx_ptype_6misaka_BaseRenderer = 0; -static PyTypeObject *__pyx_ptype_6misaka_HtmlRenderer = 0; -static PyTypeObject *__pyx_ptype_6misaka_Markdown = 0; -static PyTypeObject *__pyx_ptype_6misaka_SmartyPants = 0; -static PyTypeObject *__pyx_ptype_6misaka_HtmlTocRenderer = 0; -#define __Pyx_MODULE_NAME "misaka" -int __pyx_module_is_main_misaka = 0; - -/* Implementation of 'misaka' */ -static PyObject *__pyx_builtin_ValueError; -static PyObject *__pyx_pf_6misaka_html(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_text, unsigned int __pyx_v_extensions, unsigned int __pyx_v_render_flags); /* proto */ -static PyObject *__pyx_pf_6misaka_11SmartyPants_postprocess(CYTHON_UNUSED struct __pyx_obj_6misaka_SmartyPants *__pyx_v_self, PyObject *__pyx_v_text); /* proto */ -static int __pyx_pf_6misaka_12BaseRenderer___init__(struct __pyx_obj_6misaka_BaseRenderer *__pyx_v_self, int __pyx_v_flags); /* proto */ -static PyObject *__pyx_pf_6misaka_12BaseRenderer_2setup(CYTHON_UNUSED struct __pyx_obj_6misaka_BaseRenderer *__pyx_v_self); /* proto */ -static PyObject *__pyx_pf_6misaka_12BaseRenderer_5flags___get__(struct __pyx_obj_6misaka_BaseRenderer *__pyx_v_self); /* proto */ -static PyObject *__pyx_pf_6misaka_12HtmlRenderer_setup(struct __pyx_obj_6misaka_HtmlRenderer *__pyx_v_self); /* proto */ -static PyObject *__pyx_pf_6misaka_15HtmlTocRenderer_setup(struct __pyx_obj_6misaka_HtmlTocRenderer *__pyx_v_self, CYTHON_UNUSED int __pyx_v_flags); /* proto */ -static int __pyx_pf_6misaka_8Markdown___cinit__(struct __pyx_obj_6misaka_Markdown *__pyx_v_self, PyObject *__pyx_v_renderer, int __pyx_v_extensions); /* proto */ -static PyObject *__pyx_pf_6misaka_8Markdown_2render(struct __pyx_obj_6misaka_Markdown *__pyx_v_self, PyObject *__pyx_v_text); /* proto */ -static void __pyx_pf_6misaka_8Markdown_4__dealloc__(struct __pyx_obj_6misaka_Markdown *__pyx_v_self); /* proto */ -static PyObject *__pyx_tp_new_6misaka_BaseRenderer(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ -static PyObject *__pyx_tp_new_6misaka_HtmlRenderer(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ -static PyObject *__pyx_tp_new_6misaka_Markdown(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ -static PyObject *__pyx_tp_new_6misaka_SmartyPants(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ -static PyObject *__pyx_tp_new_6misaka_HtmlTocRenderer(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ -static char __pyx_k_1[] = "UTF-8"; -static char __pyx_k_3[] = "expected instance of BaseRenderer, %s found"; -static char __pyx_k_5[] = "1.0.3"; -static char __pyx_k_6[] = "EXT_NO_INTRA_EMPHASIS"; -static char __pyx_k_9[] = "/home/frank/Code/misaka/src/misaka.pyx"; -static char __pyx_k__html[] = "html"; -static char __pyx_k__text[] = "text"; -static char __pyx_k__flags[] = "flags"; -static char __pyx_k__setup[] = "setup"; -static char __pyx_k__encode[] = "encode"; -static char __pyx_k__misaka[] = "misaka"; -static char __pyx_k__render[] = "render"; -static char __pyx_k__result[] = "result"; -static char __pyx_k__strict[] = "strict"; -static char __pyx_k__HTML_TOC[] = "HTML_TOC"; -static char __pyx_k____main__[] = "__main__"; -static char __pyx_k____name__[] = "__name__"; -static char __pyx_k____test__[] = "__test__"; -static char __pyx_k__markdown[] = "markdown"; -static char __pyx_k__renderer[] = "renderer"; -static char __pyx_k____class__[] = "__class__"; -static char __pyx_k__EXT_TABLES[] = "EXT_TABLES"; -static char __pyx_k__ValueError[] = "ValueError"; -static char __pyx_k__extensions[] = "extensions"; -static char __pyx_k__preprocess[] = "preprocess"; -static char __pyx_k__HTML_ESCAPE[] = "HTML_ESCAPE"; -static char __pyx_k____version__[] = "__version__"; -static char __pyx_k__postprocess[] = "postprocess"; -static char __pyx_k__EXT_AUTOLINK[] = "EXT_AUTOLINK"; -static char __pyx_k__TABLE_HEADER[] = "TABLE_HEADER"; -static char __pyx_k__render_flags[] = "render_flags"; -static char __pyx_k__HTML_SAFELINK[] = "HTML_SAFELINK"; -static char __pyx_k__HTML_TOC_TREE[] = "HTML_TOC_TREE"; -static char __pyx_k__TABLE_ALIGN_C[] = "TABLE_ALIGN_C"; -static char __pyx_k__TABLE_ALIGN_L[] = "TABLE_ALIGN_L"; -static char __pyx_k__TABLE_ALIGN_R[] = "TABLE_ALIGN_R"; -static char __pyx_k__HTML_HARD_WRAP[] = "HTML_HARD_WRAP"; -static char __pyx_k__HTML_SKIP_HTML[] = "HTML_SKIP_HTML"; -static char __pyx_k__HTML_USE_XHTML[] = "HTML_USE_XHTML"; -static char __pyx_k__EXT_FENCED_CODE[] = "EXT_FENCED_CODE"; -static char __pyx_k__EXT_LAX_SPACING[] = "EXT_LAX_SPACING"; -static char __pyx_k__EXT_SUPERSCRIPT[] = "EXT_SUPERSCRIPT"; -static char __pyx_k__HTML_SKIP_LINKS[] = "HTML_SKIP_LINKS"; -static char __pyx_k__HTML_SKIP_STYLE[] = "HTML_SKIP_STYLE"; -static char __pyx_k__TABLE_ALIGNMASK[] = "TABLE_ALIGNMASK"; -static char __pyx_k__HTML_EXPAND_TABS[] = "HTML_EXPAND_TABS"; -static char __pyx_k__HTML_SKIP_IMAGES[] = "HTML_SKIP_IMAGES"; -static char __pyx_k__HTML_SMARTYPANTS[] = "HTML_SMARTYPANTS"; -static char __pyx_k__EXT_SPACE_HEADERS[] = "EXT_SPACE_HEADERS"; -static char __pyx_k__EXT_STRIKETHROUGH[] = "EXT_STRIKETHROUGH"; -static PyObject *__pyx_kp_s_1; -static PyObject *__pyx_kp_s_3; -static PyObject *__pyx_kp_s_5; -static PyObject *__pyx_n_s_6; -static PyObject *__pyx_kp_s_9; -static PyObject *__pyx_n_s__EXT_AUTOLINK; -static PyObject *__pyx_n_s__EXT_FENCED_CODE; -static PyObject *__pyx_n_s__EXT_LAX_SPACING; -static PyObject *__pyx_n_s__EXT_SPACE_HEADERS; -static PyObject *__pyx_n_s__EXT_STRIKETHROUGH; -static PyObject *__pyx_n_s__EXT_SUPERSCRIPT; -static PyObject *__pyx_n_s__EXT_TABLES; -static PyObject *__pyx_n_s__HTML_ESCAPE; -static PyObject *__pyx_n_s__HTML_EXPAND_TABS; -static PyObject *__pyx_n_s__HTML_HARD_WRAP; -static PyObject *__pyx_n_s__HTML_SAFELINK; -static PyObject *__pyx_n_s__HTML_SKIP_HTML; -static PyObject *__pyx_n_s__HTML_SKIP_IMAGES; -static PyObject *__pyx_n_s__HTML_SKIP_LINKS; -static PyObject *__pyx_n_s__HTML_SKIP_STYLE; -static PyObject *__pyx_n_s__HTML_SMARTYPANTS; -static PyObject *__pyx_n_s__HTML_TOC; -static PyObject *__pyx_n_s__HTML_TOC_TREE; -static PyObject *__pyx_n_s__HTML_USE_XHTML; -static PyObject *__pyx_n_s__TABLE_ALIGNMASK; -static PyObject *__pyx_n_s__TABLE_ALIGN_C; -static PyObject *__pyx_n_s__TABLE_ALIGN_L; -static PyObject *__pyx_n_s__TABLE_ALIGN_R; -static PyObject *__pyx_n_s__TABLE_HEADER; -static PyObject *__pyx_n_s__ValueError; -static PyObject *__pyx_n_s____class__; -static PyObject *__pyx_n_s____main__; -static PyObject *__pyx_n_s____name__; -static PyObject *__pyx_n_s____test__; -static PyObject *__pyx_n_s____version__; -static PyObject *__pyx_n_s__encode; -static PyObject *__pyx_n_s__extensions; -static PyObject *__pyx_n_s__flags; -static PyObject *__pyx_n_s__html; -static PyObject *__pyx_n_s__markdown; -static PyObject *__pyx_n_s__misaka; -static PyObject *__pyx_n_s__postprocess; -static PyObject *__pyx_n_s__preprocess; -static PyObject *__pyx_n_s__render; -static PyObject *__pyx_n_s__render_flags; -static PyObject *__pyx_n_s__renderer; -static PyObject *__pyx_n_s__result; -static PyObject *__pyx_n_s__setup; -static PyObject *__pyx_n_s__strict; -static PyObject *__pyx_n_s__text; -static PyObject *__pyx_int_1; -static PyObject *__pyx_int_2; -static PyObject *__pyx_int_3; -static PyObject *__pyx_int_4; -static PyObject *__pyx_int_8; -static PyObject *__pyx_int_16; -static PyObject *__pyx_int_32; -static PyObject *__pyx_int_64; -static PyObject *__pyx_int_128; -static PyObject *__pyx_int_256; -static PyObject *__pyx_int_512; -static PyObject *__pyx_int_1024; -static PyObject *__pyx_int_2048; -static PyObject *__pyx_k_tuple_2; -static PyObject *__pyx_k_tuple_4; -static PyObject *__pyx_k_tuple_7; -static PyObject *__pyx_k_codeobj_8; - -/* Python wrapper */ -static PyObject *__pyx_pw_6misaka_1html(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ -static char __pyx_doc_6misaka_html[] = "Convert markdown text to (X)HTML.\n\n Returns a unicode string.\n\n :param text: A byte or unicode string.\n :param extensions: Enable additional Markdown extensions with the ``EXT_*`` constants.\n :param render_flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants.\n "; -static PyMethodDef __pyx_mdef_6misaka_1html = {__Pyx_NAMESTR("html"), (PyCFunction)__pyx_pw_6misaka_1html, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(__pyx_doc_6misaka_html)}; -static PyObject *__pyx_pw_6misaka_1html(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { - PyObject *__pyx_v_text = 0; - unsigned int __pyx_v_extensions; - unsigned int __pyx_v_render_flags; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("html (wrapper)", 0); - { - static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__text,&__pyx_n_s__extensions,&__pyx_n_s__render_flags,0}; - PyObject* values[3] = {0,0,0}; - if (unlikely(__pyx_kwds)) { - Py_ssize_t kw_args; - const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); - switch (pos_args) { - case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); - case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - kw_args = PyDict_Size(__pyx_kwds); - switch (pos_args) { - case 0: - if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__text)) != 0)) kw_args--; - else goto __pyx_L5_argtuple_error; - case 1: - if (kw_args > 0) { - PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__extensions); - if (value) { values[1] = value; kw_args--; } - } - case 2: - if (kw_args > 0) { - PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__render_flags); - if (value) { values[2] = value; kw_args--; } - } - } - if (unlikely(kw_args > 0)) { - if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "html") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } - } else { - switch (PyTuple_GET_SIZE(__pyx_args)) { - case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); - case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - break; - default: goto __pyx_L5_argtuple_error; - } - } - __pyx_v_text = values[0]; - if (values[1]) { - __pyx_v_extensions = __Pyx_PyInt_AsUnsignedInt(values[1]); if (unlikely((__pyx_v_extensions == (unsigned int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } else { - __pyx_v_extensions = ((unsigned int)0); - } - if (values[2]) { - __pyx_v_render_flags = __Pyx_PyInt_AsUnsignedInt(values[2]); if (unlikely((__pyx_v_render_flags == (unsigned int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } else { - __pyx_v_render_flags = ((unsigned int)0); - } - } - goto __pyx_L4_argument_unpacking_done; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("html", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - __pyx_L3_error:; - __Pyx_AddTraceback("misaka.html", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_6misaka_html(__pyx_self, __pyx_v_text, __pyx_v_extensions, __pyx_v_render_flags); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":44 - * - * - * def html(object text, unsigned int extensions=0, unsigned int render_flags=0): # <<<<<<<<<<<<<< - * """Convert markdown text to (X)HTML. - * - */ - -static PyObject *__pyx_pf_6misaka_html(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_text, unsigned int __pyx_v_extensions, unsigned int __pyx_v_render_flags) { - struct __pyx_obj_6misaka_BaseRenderer *__pyx_v_renderer = NULL; - struct __pyx_obj_6misaka_Markdown *__pyx_v_markdown = NULL; - PyObject *__pyx_v_result = NULL; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - int __pyx_t_4; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("html", 0); - - /* "misaka.pyx":54 - * """ - * - * if render_flags & HTML_TOC_TREE: # <<<<<<<<<<<<<< - * renderer = HtmlTocRenderer(render_flags) - * else: - */ - __pyx_t_1 = PyLong_FromUnsignedLong(__pyx_v_render_flags); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__HTML_TOC_TREE); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = PyNumber_And(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_4 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - if (__pyx_t_4) { - - /* "misaka.pyx":55 - * - * if render_flags & HTML_TOC_TREE: - * renderer = HtmlTocRenderer(render_flags) # <<<<<<<<<<<<<< - * else: - * renderer = HtmlRenderer(render_flags) - */ - __pyx_t_3 = PyLong_FromUnsignedLong(__pyx_v_render_flags); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3); - __Pyx_GIVEREF(__pyx_t_3); - __pyx_t_3 = 0; - __pyx_t_3 = PyObject_Call(((PyObject *)((PyObject*)__pyx_ptype_6misaka_HtmlTocRenderer)), ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; - __pyx_v_renderer = ((struct __pyx_obj_6misaka_BaseRenderer *)__pyx_t_3); - __pyx_t_3 = 0; - goto __pyx_L3; - } - /*else*/ { - - /* "misaka.pyx":57 - * renderer = HtmlTocRenderer(render_flags) - * else: - * renderer = HtmlRenderer(render_flags) # <<<<<<<<<<<<<< - * - * markdown = Markdown(renderer, extensions) - */ - __pyx_t_3 = PyLong_FromUnsignedLong(__pyx_v_render_flags); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 57; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 57; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3); - __Pyx_GIVEREF(__pyx_t_3); - __pyx_t_3 = 0; - __pyx_t_3 = PyObject_Call(((PyObject *)((PyObject*)__pyx_ptype_6misaka_HtmlRenderer)), ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 57; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; - __pyx_v_renderer = ((struct __pyx_obj_6misaka_BaseRenderer *)__pyx_t_3); - __pyx_t_3 = 0; - } - __pyx_L3:; - - /* "misaka.pyx":59 - * renderer = HtmlRenderer(render_flags) - * - * markdown = Markdown(renderer, extensions) # <<<<<<<<<<<<<< - * result = markdown.render(text) - * - */ - __pyx_t_3 = PyLong_FromUnsignedLong(__pyx_v_extensions); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 59; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 59; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - __Pyx_INCREF(((PyObject *)__pyx_v_renderer)); - PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject *)__pyx_v_renderer)); - __Pyx_GIVEREF(((PyObject *)__pyx_v_renderer)); - PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_3); - __Pyx_GIVEREF(__pyx_t_3); - __pyx_t_3 = 0; - __pyx_t_3 = PyObject_Call(((PyObject *)((PyObject*)__pyx_ptype_6misaka_Markdown)), ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 59; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; - __pyx_v_markdown = ((struct __pyx_obj_6misaka_Markdown *)__pyx_t_3); - __pyx_t_3 = 0; - - /* "misaka.pyx":60 - * - * markdown = Markdown(renderer, extensions) - * result = markdown.render(text) # <<<<<<<<<<<<<< - * - * if render_flags & HTML_SMARTYPANTS: - */ - __pyx_t_3 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_markdown), __pyx_n_s__render); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - __Pyx_INCREF(__pyx_v_text); - PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_text); - __Pyx_GIVEREF(__pyx_v_text); - __pyx_t_1 = PyObject_Call(__pyx_t_3, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; - __pyx_v_result = __pyx_t_1; - __pyx_t_1 = 0; - - /* "misaka.pyx":62 - * result = markdown.render(text) - * - * if render_flags & HTML_SMARTYPANTS: # <<<<<<<<<<<<<< - * result = SmartyPants().postprocess(result) - * - */ - __pyx_t_1 = PyLong_FromUnsignedLong(__pyx_v_render_flags); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__HTML_SMARTYPANTS); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = PyNumber_And(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_4 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - if (__pyx_t_4) { - - /* "misaka.pyx":63 - * - * if render_flags & HTML_SMARTYPANTS: - * result = SmartyPants().postprocess(result) # <<<<<<<<<<<<<< - * - * return result - */ - __pyx_t_3 = PyObject_Call(((PyObject *)((PyObject*)__pyx_ptype_6misaka_SmartyPants)), ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s__postprocess); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __Pyx_INCREF(__pyx_v_result); - PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_result); - __Pyx_GIVEREF(__pyx_v_result); - __pyx_t_1 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_3), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0; - __Pyx_DECREF(__pyx_v_result); - __pyx_v_result = __pyx_t_1; - __pyx_t_1 = 0; - goto __pyx_L4; - } - __pyx_L4:; - - /* "misaka.pyx":65 - * result = SmartyPants().postprocess(result) - * - * return result # <<<<<<<<<<<<<< - * - * - */ - __Pyx_XDECREF(__pyx_r); - __Pyx_INCREF(__pyx_v_result); - __pyx_r = __pyx_v_result; - goto __pyx_L0; - - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_AddTraceback("misaka.html", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XDECREF((PyObject *)__pyx_v_renderer); - __Pyx_XDECREF((PyObject *)__pyx_v_markdown); - __Pyx_XDECREF(__pyx_v_result); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static PyObject *__pyx_pw_6misaka_11SmartyPants_1postprocess(PyObject *__pyx_v_self, PyObject *__pyx_v_text); /*proto*/ -static char __pyx_doc_6misaka_11SmartyPants_postprocess[] = "Process the input text.\n\n Returns a unicode string.\n\n :param text: A byte or unicode string.\n "; -static PyObject *__pyx_pw_6misaka_11SmartyPants_1postprocess(PyObject *__pyx_v_self, PyObject *__pyx_v_text) { - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("postprocess (wrapper)", 0); - __pyx_r = __pyx_pf_6misaka_11SmartyPants_postprocess(((struct __pyx_obj_6misaka_SmartyPants *)__pyx_v_self), ((PyObject *)__pyx_v_text)); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":92 - * ``ve`` will be turned into ``’s``, ``’t``, and so on. - * """ - * def postprocess(self, object text): # <<<<<<<<<<<<<< - * """Process the input text. - * - */ - -static PyObject *__pyx_pf_6misaka_11SmartyPants_postprocess(CYTHON_UNUSED struct __pyx_obj_6misaka_SmartyPants *__pyx_v_self, PyObject *__pyx_v_text) { - PyObject *__pyx_v_py_string = 0; - char *__pyx_v_c_string; - struct buf *__pyx_v_ob; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_t_1; - int __pyx_t_2; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - char *__pyx_t_5; - size_t __pyx_t_6; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("postprocess", 0); - - /* "misaka.pyx":101 - * # Convert string - * cdef bytes py_string - * if hasattr(text, 'encode'): # <<<<<<<<<<<<<< - * py_string = text.encode('UTF-8', 'strict') - * else: - */ - __pyx_t_1 = PyObject_HasAttr(__pyx_v_text, ((PyObject *)__pyx_n_s__encode)); if (unlikely(__pyx_t_1 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_t_2 = (__pyx_t_1 != 0); - if (__pyx_t_2) { - - /* "misaka.pyx":102 - * cdef bytes py_string - * if hasattr(text, 'encode'): - * py_string = text.encode('UTF-8', 'strict') # <<<<<<<<<<<<<< - * else: - * py_string = text - */ - __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_text, __pyx_n_s__encode); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 102; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = PyObject_Call(__pyx_t_3, ((PyObject *)__pyx_k_tuple_2), NULL); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 102; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_4); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - if (!(likely(PyBytes_CheckExact(__pyx_t_4))||((__pyx_t_4) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected bytes, got %.200s", Py_TYPE(__pyx_t_4)->tp_name), 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 102; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_v_py_string = ((PyObject*)__pyx_t_4); - __pyx_t_4 = 0; - goto __pyx_L3; - } - /*else*/ { - - /* "misaka.pyx":104 - * py_string = text.encode('UTF-8', 'strict') - * else: - * py_string = text # <<<<<<<<<<<<<< - * cdef char *c_string = py_string - * - */ - if (!(likely(PyBytes_CheckExact(__pyx_v_text))||((__pyx_v_text) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected bytes, got %.200s", Py_TYPE(__pyx_v_text)->tp_name), 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 104; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_INCREF(__pyx_v_text); - __pyx_v_py_string = ((PyObject*)__pyx_v_text); - } - __pyx_L3:; - - /* "misaka.pyx":105 - * else: - * py_string = text - * cdef char *c_string = py_string # <<<<<<<<<<<<<< - * - * cdef sundown.buf *ob = sundown.bufnew(128) - */ - __pyx_t_5 = __Pyx_PyObject_AsString(((PyObject *)__pyx_v_py_string)); if (unlikely((!__pyx_t_5) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_v_c_string = __pyx_t_5; - - /* "misaka.pyx":107 - * cdef char *c_string = py_string - * - * cdef sundown.buf *ob = sundown.bufnew(128) # <<<<<<<<<<<<<< - * sundown.sdhtml_smartypants(ob, - * c_string, len(c_string)) - */ - __pyx_v_ob = bufnew(128); - - /* "misaka.pyx":109 - * cdef sundown.buf *ob = sundown.bufnew(128) - * sundown.sdhtml_smartypants(ob, - * c_string, len(c_string)) # <<<<<<<<<<<<<< - * - * try: - */ - __pyx_t_6 = strlen(__pyx_v_c_string); - sdhtml_smartypants(__pyx_v_ob, ((uint8_t *)__pyx_v_c_string), __pyx_t_6); - - /* "misaka.pyx":111 - * c_string, len(c_string)) - * - * try: # <<<<<<<<<<<<<< - * return ( ob.data)[:ob.size].decode('UTF-8', 'strict') - * finally: - */ - /*try:*/ { - - /* "misaka.pyx":112 - * - * try: - * return ( ob.data)[:ob.size].decode('UTF-8', 'strict') # <<<<<<<<<<<<<< - * finally: - * sundown.bufrelease(ob) - */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_4 = ((PyObject *)__Pyx_decode_c_string(((char *)__pyx_v_ob->data), 0, __pyx_v_ob->size, NULL, NULL, PyUnicode_DecodeUTF8)); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 112; __pyx_clineno = __LINE__; goto __pyx_L5;} - __Pyx_GOTREF(((PyObject *)__pyx_t_4)); - __pyx_r = ((PyObject *)__pyx_t_4); - __pyx_t_4 = 0; - goto __pyx_L4; - } - - /* "misaka.pyx":114 - * return ( ob.data)[:ob.size].decode('UTF-8', 'strict') - * finally: - * sundown.bufrelease(ob) # <<<<<<<<<<<<<< - * - * - */ - /*finally:*/ { - int __pyx_why; - PyObject *__pyx_exc_type, *__pyx_exc_value, *__pyx_exc_tb; - int __pyx_exc_lineno; - __pyx_exc_type = 0; __pyx_exc_value = 0; __pyx_exc_tb = 0; __pyx_exc_lineno = 0; - __pyx_why = 0; goto __pyx_L6; - __pyx_L4: __pyx_exc_type = 0; __pyx_exc_value = 0; __pyx_exc_tb = 0; __pyx_exc_lineno = 0; - __pyx_why = 3; goto __pyx_L6; - __pyx_L5: { - __pyx_why = 4; - __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; - __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0; - __Pyx_ErrFetch(&__pyx_exc_type, &__pyx_exc_value, &__pyx_exc_tb); - __pyx_exc_lineno = __pyx_lineno; - goto __pyx_L6; - } - __pyx_L6:; - bufrelease(__pyx_v_ob); - switch (__pyx_why) { - case 3: goto __pyx_L0; - case 4: { - __Pyx_ErrRestore(__pyx_exc_type, __pyx_exc_value, __pyx_exc_tb); - __pyx_lineno = __pyx_exc_lineno; - __pyx_exc_type = 0; - __pyx_exc_value = 0; - __pyx_exc_tb = 0; - goto __pyx_L1_error; - } - } - } - - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_AddTraceback("misaka.SmartyPants.postprocess", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_py_string); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static int __pyx_pw_6misaka_12BaseRenderer_1__init__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ -static int __pyx_pw_6misaka_12BaseRenderer_1__init__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { - int __pyx_v_flags; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - int __pyx_r; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__init__ (wrapper)", 0); - { - static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__flags,0}; - PyObject* values[1] = {0}; - if (unlikely(__pyx_kwds)) { - Py_ssize_t kw_args; - const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); - switch (pos_args) { - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - kw_args = PyDict_Size(__pyx_kwds); - switch (pos_args) { - case 0: - if (kw_args > 0) { - PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__flags); - if (value) { values[0] = value; kw_args--; } - } - } - if (unlikely(kw_args > 0)) { - if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "__init__") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 130; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } - } else { - switch (PyTuple_GET_SIZE(__pyx_args)) { - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - } - if (values[0]) { - __pyx_v_flags = __Pyx_PyInt_AsInt(values[0]); if (unlikely((__pyx_v_flags == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 130; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } else { - __pyx_v_flags = ((int)0); - } - } - goto __pyx_L4_argument_unpacking_done; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("__init__", 0, 0, 1, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 130; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - __pyx_L3_error:; - __Pyx_AddTraceback("misaka.BaseRenderer.__init__", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return -1; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_6misaka_12BaseRenderer___init__(((struct __pyx_obj_6misaka_BaseRenderer *)__pyx_v_self), __pyx_v_flags); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":130 - * cdef readonly int flags - * - * def __init__(self, int flags=0): # <<<<<<<<<<<<<< - * self.options.self = self - * self.flags = flags - */ - -static int __pyx_pf_6misaka_12BaseRenderer___init__(struct __pyx_obj_6misaka_BaseRenderer *__pyx_v_self, int __pyx_v_flags) { - void **__pyx_v_source; - void **__pyx_v_dest; - PyObject *__pyx_v_method_name = 0; - long __pyx_v_i; - int __pyx_r; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - int __pyx_t_3; - const char * __pyx_t_4; - int __pyx_t_5; - int __pyx_t_6; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("__init__", 0); - - /* "misaka.pyx":131 - * - * def __init__(self, int flags=0): - * self.options.self = self # <<<<<<<<<<<<<< - * self.flags = flags - * self.setup() - */ - __pyx_v_self->options.self = ((void *)__pyx_v_self); - - /* "misaka.pyx":132 - * def __init__(self, int flags=0): - * self.options.self = self - * self.flags = flags # <<<<<<<<<<<<<< - * self.setup() - * - */ - __pyx_v_self->flags = __pyx_v_flags; - - /* "misaka.pyx":133 - * self.options.self = self - * self.flags = flags - * self.setup() # <<<<<<<<<<<<<< - * - * # Set callbacks - */ - __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s__setup); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 133; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 133; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - - /* "misaka.pyx":136 - * - * # Set callbacks - * cdef void **source = &wrapper.callback_funcs # <<<<<<<<<<<<<< - * cdef void **dest = &self.callbacks - * - */ - __pyx_v_source = ((void **)(&callback_funcs)); - - /* "misaka.pyx":137 - * # Set callbacks - * cdef void **source = &wrapper.callback_funcs - * cdef void **dest = &self.callbacks # <<<<<<<<<<<<<< - * - * cdef unicode method_name - */ - __pyx_v_dest = ((void **)(&__pyx_v_self->callbacks)); - - /* "misaka.pyx":140 - * - * cdef unicode method_name - * for i from 0 <= i < wrapper.method_count by 1: # <<<<<<<<<<<<<< - * # In Python 3 ``wrapper.method_names[i]`` is a byte string. - * # This means hasattr can't find any method in the renderer, so - */ - __pyx_t_3 = ((int)method_count); - for (__pyx_v_i = 0; __pyx_v_i < __pyx_t_3; __pyx_v_i+=1) { - - /* "misaka.pyx":144 - * # This means hasattr can't find any method in the renderer, so - * # ``wrapper.method_names[i]`` is converted to a normal string first. - * method_name = wrapper.method_names[i].decode('utf-8') # <<<<<<<<<<<<<< - * if hasattr(self, method_name): - * dest[i] = source[i] - */ - __pyx_t_4 = (method_names[__pyx_v_i]); - __pyx_t_2 = ((PyObject *)__Pyx_decode_c_string(__pyx_t_4, 0, strlen(__pyx_t_4), NULL, NULL, PyUnicode_DecodeUTF8)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 144; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(((PyObject *)__pyx_t_2)); - if (!(likely(PyUnicode_CheckExact(((PyObject *)__pyx_t_2)))||((((PyObject *)__pyx_t_2)) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected unicode, got %.200s", Py_TYPE(((PyObject *)__pyx_t_2))->tp_name), 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 144; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_INCREF(__pyx_t_2); - __Pyx_XDECREF(((PyObject *)__pyx_v_method_name)); - __pyx_v_method_name = ((PyObject*)__pyx_t_2); - __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; - - /* "misaka.pyx":145 - * # ``wrapper.method_names[i]`` is converted to a normal string first. - * method_name = wrapper.method_names[i].decode('utf-8') - * if hasattr(self, method_name): # <<<<<<<<<<<<<< - * dest[i] = source[i] - * - */ - __pyx_t_5 = PyObject_HasAttr(((PyObject *)__pyx_v_self), ((PyObject *)__pyx_v_method_name)); if (unlikely(__pyx_t_5 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 145; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_t_6 = (__pyx_t_5 != 0); - if (__pyx_t_6) { - - /* "misaka.pyx":146 - * method_name = wrapper.method_names[i].decode('utf-8') - * if hasattr(self, method_name): - * dest[i] = source[i] # <<<<<<<<<<<<<< - * - * def setup(self): - */ - (__pyx_v_dest[__pyx_v_i]) = (__pyx_v_source[__pyx_v_i]); - goto __pyx_L5; - } - __pyx_L5:; - } - - __pyx_r = 0; - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_AddTraceback("misaka.BaseRenderer.__init__", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = -1; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_method_name); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static PyObject *__pyx_pw_6misaka_12BaseRenderer_3setup(PyObject *__pyx_v_self, CYTHON_UNUSED PyObject *unused); /*proto*/ -static char __pyx_doc_6misaka_12BaseRenderer_2setup[] = "A method that can be overridden by the renderer that sublasses ``BaseRenderer``.\n It's called everytime an instance of a renderer is created.\n "; -static PyObject *__pyx_pw_6misaka_12BaseRenderer_3setup(PyObject *__pyx_v_self, CYTHON_UNUSED PyObject *unused) { - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("setup (wrapper)", 0); - __pyx_r = __pyx_pf_6misaka_12BaseRenderer_2setup(((struct __pyx_obj_6misaka_BaseRenderer *)__pyx_v_self)); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":148 - * dest[i] = source[i] - * - * def setup(self): # <<<<<<<<<<<<<< - * """A method that can be overridden by the renderer that sublasses ``BaseRenderer``. - * It's called everytime an instance of a renderer is created. - */ - -static PyObject *__pyx_pf_6misaka_12BaseRenderer_2setup(CYTHON_UNUSED struct __pyx_obj_6misaka_BaseRenderer *__pyx_v_self) { - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("setup", 0); - - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static PyObject *__pyx_pw_6misaka_12BaseRenderer_5flags_1__get__(PyObject *__pyx_v_self); /*proto*/ -static PyObject *__pyx_pw_6misaka_12BaseRenderer_5flags_1__get__(PyObject *__pyx_v_self) { - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__get__ (wrapper)", 0); - __pyx_r = __pyx_pf_6misaka_12BaseRenderer_5flags___get__(((struct __pyx_obj_6misaka_BaseRenderer *)__pyx_v_self)); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":128 - * - * #: Read-only render flags - * cdef readonly int flags # <<<<<<<<<<<<<< - * - * def __init__(self, int flags=0): - */ - -static PyObject *__pyx_pf_6misaka_12BaseRenderer_5flags___get__(struct __pyx_obj_6misaka_BaseRenderer *__pyx_v_self) { - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("__get__", 0); - __Pyx_XDECREF(__pyx_r); - __pyx_t_1 = PyInt_FromLong(__pyx_v_self->flags); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 128; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __pyx_r = __pyx_t_1; - __pyx_t_1 = 0; - goto __pyx_L0; - - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_AddTraceback("misaka.BaseRenderer.flags.__get__", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static PyObject *__pyx_pw_6misaka_12HtmlRenderer_1setup(PyObject *__pyx_v_self, CYTHON_UNUSED PyObject *unused); /*proto*/ -static PyObject *__pyx_pw_6misaka_12HtmlRenderer_1setup(PyObject *__pyx_v_self, CYTHON_UNUSED PyObject *unused) { - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("setup (wrapper)", 0); - __pyx_r = __pyx_pf_6misaka_12HtmlRenderer_setup(((struct __pyx_obj_6misaka_HtmlRenderer *)__pyx_v_self)); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":163 - * :param flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants. - * """ - * def setup(self): # <<<<<<<<<<<<<< - * self.options.html.flags = self.flags - * sundown.sdhtml_renderer( - */ - -static PyObject *__pyx_pf_6misaka_12HtmlRenderer_setup(struct __pyx_obj_6misaka_HtmlRenderer *__pyx_v_self) { - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_t_1; - __Pyx_RefNannySetupContext("setup", 0); - - /* "misaka.pyx":164 - * """ - * def setup(self): - * self.options.html.flags = self.flags # <<<<<<<<<<<<<< - * sundown.sdhtml_renderer( - * &self.callbacks, - */ - __pyx_t_1 = __pyx_v_self->__pyx_base.flags; - __pyx_v_self->__pyx_base.options.html.flags = __pyx_t_1; - - /* "misaka.pyx":168 - * &self.callbacks, - * &self.options.html, - * self.options.html.flags) # <<<<<<<<<<<<<< - * - * - */ - sdhtml_renderer((&__pyx_v_self->__pyx_base.callbacks), (&__pyx_v_self->__pyx_base.options.html), __pyx_v_self->__pyx_base.options.html.flags); - - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static PyObject *__pyx_pw_6misaka_15HtmlTocRenderer_1setup(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ -static PyObject *__pyx_pw_6misaka_15HtmlTocRenderer_1setup(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { - CYTHON_UNUSED int __pyx_v_flags; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("setup (wrapper)", 0); - { - static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__flags,0}; - PyObject* values[1] = {0}; - if (unlikely(__pyx_kwds)) { - Py_ssize_t kw_args; - const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); - switch (pos_args) { - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - kw_args = PyDict_Size(__pyx_kwds); - switch (pos_args) { - case 0: - if (kw_args > 0) { - PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__flags); - if (value) { values[0] = value; kw_args--; } - } - } - if (unlikely(kw_args > 0)) { - if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "setup") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 179; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } - } else { - switch (PyTuple_GET_SIZE(__pyx_args)) { - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - } - if (values[0]) { - __pyx_v_flags = __Pyx_PyInt_AsInt(values[0]); if (unlikely((__pyx_v_flags == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 179; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } else { - __pyx_v_flags = ((int)0); - } - } - goto __pyx_L4_argument_unpacking_done; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("setup", 0, 0, 1, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 179; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - __pyx_L3_error:; - __Pyx_AddTraceback("misaka.HtmlTocRenderer.setup", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_6misaka_15HtmlTocRenderer_setup(((struct __pyx_obj_6misaka_HtmlTocRenderer *)__pyx_v_self), __pyx_v_flags); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":179 - * :param flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants. - * """ - * def setup(self, int flags=0): # <<<<<<<<<<<<<< - * sundown.sdhtml_toc_renderer( - * &self.callbacks, - */ - -static PyObject *__pyx_pf_6misaka_15HtmlTocRenderer_setup(struct __pyx_obj_6misaka_HtmlTocRenderer *__pyx_v_self, CYTHON_UNUSED int __pyx_v_flags) { - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("setup", 0); - - /* "misaka.pyx":182 - * sundown.sdhtml_toc_renderer( - * &self.callbacks, - * &self.options.html) # <<<<<<<<<<<<<< - * - * - */ - sdhtml_toc_renderer((&__pyx_v_self->__pyx_base.callbacks), (&__pyx_v_self->__pyx_base.options.html)); - - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static int __pyx_pw_6misaka_8Markdown_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ -static int __pyx_pw_6misaka_8Markdown_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { - PyObject *__pyx_v_renderer = 0; - int __pyx_v_extensions; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - int __pyx_r; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__cinit__ (wrapper)", 0); - { - static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__renderer,&__pyx_n_s__extensions,0}; - PyObject* values[2] = {0,0}; - if (unlikely(__pyx_kwds)) { - Py_ssize_t kw_args; - const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); - switch (pos_args) { - case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - kw_args = PyDict_Size(__pyx_kwds); - switch (pos_args) { - case 0: - if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__renderer)) != 0)) kw_args--; - else goto __pyx_L5_argtuple_error; - case 1: - if (kw_args > 0) { - PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__extensions); - if (value) { values[1] = value; kw_args--; } - } - } - if (unlikely(kw_args > 0)) { - if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "__cinit__") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 195; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } - } else { - switch (PyTuple_GET_SIZE(__pyx_args)) { - case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); - case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); - break; - default: goto __pyx_L5_argtuple_error; - } - } - __pyx_v_renderer = values[0]; - if (values[1]) { - __pyx_v_extensions = __Pyx_PyInt_AsInt(values[1]); if (unlikely((__pyx_v_extensions == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 195; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - } else { - __pyx_v_extensions = ((int)0); - } - } - goto __pyx_L4_argument_unpacking_done; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("__cinit__", 0, 1, 2, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 195; __pyx_clineno = __LINE__; goto __pyx_L3_error;} - __pyx_L3_error:; - __Pyx_AddTraceback("misaka.Markdown.__cinit__", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return -1; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_6misaka_8Markdown___cinit__(((struct __pyx_obj_6misaka_Markdown *)__pyx_v_self), __pyx_v_renderer, __pyx_v_extensions); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":195 - * cdef BaseRenderer renderer - * - * def __cinit__(self, object renderer, int extensions=0): # <<<<<<<<<<<<<< - * if not isinstance(renderer, BaseRenderer): - * raise ValueError('expected instance of BaseRenderer, %s found' % \ - */ - -static int __pyx_pf_6misaka_8Markdown___cinit__(struct __pyx_obj_6misaka_Markdown *__pyx_v_self, PyObject *__pyx_v_renderer, int __pyx_v_extensions) { - int __pyx_r; - __Pyx_RefNannyDeclarations - int __pyx_t_1; - int __pyx_t_2; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("__cinit__", 0); - - /* "misaka.pyx":196 - * - * def __cinit__(self, object renderer, int extensions=0): - * if not isinstance(renderer, BaseRenderer): # <<<<<<<<<<<<<< - * raise ValueError('expected instance of BaseRenderer, %s found' % \ - * renderer.__class__.__name__) - */ - __pyx_t_1 = __Pyx_TypeCheck(__pyx_v_renderer, ((PyObject*)__pyx_ptype_6misaka_BaseRenderer)); - __pyx_t_2 = ((!(__pyx_t_1 != 0)) != 0); - if (__pyx_t_2) { - - /* "misaka.pyx":198 - * if not isinstance(renderer, BaseRenderer): - * raise ValueError('expected instance of BaseRenderer, %s found' % \ - * renderer.__class__.__name__) # <<<<<<<<<<<<<< - * - * self.renderer = renderer - */ - __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_renderer, __pyx_n_s____class__); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 198; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s____name__); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 198; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_4); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_3 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_3), __pyx_t_4); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 197; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(((PyObject *)__pyx_t_3)); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 197; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_4); - PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)__pyx_t_3)); - __Pyx_GIVEREF(((PyObject *)__pyx_t_3)); - __pyx_t_3 = 0; - __pyx_t_3 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 197; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0; - __Pyx_Raise(__pyx_t_3, 0, 0, 0); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - {__pyx_filename = __pyx_f[0]; __pyx_lineno = 197; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - goto __pyx_L3; - } - __pyx_L3:; - - /* "misaka.pyx":200 - * renderer.__class__.__name__) - * - * self.renderer = renderer # <<<<<<<<<<<<<< - * self.markdown = sundown.sd_markdown_new( - * extensions, 16, - */ - __Pyx_INCREF(((PyObject *)((struct __pyx_obj_6misaka_BaseRenderer *)__pyx_v_renderer))); - __Pyx_GIVEREF(__pyx_v_renderer); - __Pyx_GOTREF(__pyx_v_self->renderer); - __Pyx_DECREF(((PyObject *)__pyx_v_self->renderer)); - __pyx_v_self->renderer = ((struct __pyx_obj_6misaka_BaseRenderer *)__pyx_v_renderer); - - /* "misaka.pyx":201 - * - * self.renderer = renderer - * self.markdown = sundown.sd_markdown_new( # <<<<<<<<<<<<<< - * extensions, 16, - * &self.renderer.callbacks, - */ - __pyx_v_self->markdown = sd_markdown_new(__pyx_v_extensions, 16, (&__pyx_v_self->renderer->callbacks), ((struct html_renderopt *)(&__pyx_v_self->renderer->options))); - - __pyx_r = 0; - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_AddTraceback("misaka.Markdown.__cinit__", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = -1; - __pyx_L0:; - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static PyObject *__pyx_pw_6misaka_8Markdown_3render(PyObject *__pyx_v_self, PyObject *__pyx_v_text); /*proto*/ -static char __pyx_doc_6misaka_8Markdown_2render[] = "Render the Markdon text.\n\n Returns a unicode string.\n\n :param text: A byte or unicode string.\n "; -static PyObject *__pyx_pw_6misaka_8Markdown_3render(PyObject *__pyx_v_self, PyObject *__pyx_v_text) { - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("render (wrapper)", 0); - __pyx_r = __pyx_pf_6misaka_8Markdown_2render(((struct __pyx_obj_6misaka_Markdown *)__pyx_v_self), ((PyObject *)__pyx_v_text)); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "misaka.pyx":206 - * &self.renderer.options) - * - * def render(self, object text): # <<<<<<<<<<<<<< - * """Render the Markdon text. - * - */ - -static PyObject *__pyx_pf_6misaka_8Markdown_2render(struct __pyx_obj_6misaka_Markdown *__pyx_v_self, PyObject *__pyx_v_text) { - PyObject *__pyx_v_py_string = 0; - char *__pyx_v_c_string; - struct buf *__pyx_v_ib; - struct buf *__pyx_v_ob; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - int __pyx_t_2; - int __pyx_t_3; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - char *__pyx_t_6; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("render", 0); - __Pyx_INCREF(__pyx_v_text); - - /* "misaka.pyx":213 - * :param text: A byte or unicode string. - * """ - * if hasattr(self.renderer, 'preprocess'): # <<<<<<<<<<<<<< - * text = self.renderer.preprocess(text) - * - */ - __pyx_t_1 = ((PyObject *)__pyx_v_self->renderer); - __Pyx_INCREF(__pyx_t_1); - __pyx_t_2 = PyObject_HasAttr(__pyx_t_1, ((PyObject *)__pyx_n_s__preprocess)); if (unlikely(__pyx_t_2 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 213; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_3 = (__pyx_t_2 != 0); - if (__pyx_t_3) { - - /* "misaka.pyx":214 - * """ - * if hasattr(self.renderer, 'preprocess'): - * text = self.renderer.preprocess(text) # <<<<<<<<<<<<<< - * - * # Convert string - */ - __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self->renderer), __pyx_n_s__preprocess); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 214; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 214; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_4); - __Pyx_INCREF(__pyx_v_text); - PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_v_text); - __Pyx_GIVEREF(__pyx_v_text); - __pyx_t_5 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 214; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_5); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0; - __Pyx_DECREF(__pyx_v_text); - __pyx_v_text = __pyx_t_5; - __pyx_t_5 = 0; - goto __pyx_L3; - } - __pyx_L3:; - - /* "misaka.pyx":218 - * # Convert string - * cdef bytes py_string - * if hasattr(text, 'encode'): # <<<<<<<<<<<<<< - * py_string = text.encode('UTF-8', 'strict') - * else: - */ - __pyx_t_3 = PyObject_HasAttr(__pyx_v_text, ((PyObject *)__pyx_n_s__encode)); if (unlikely(__pyx_t_3 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_t_2 = (__pyx_t_3 != 0); - if (__pyx_t_2) { - - /* "misaka.pyx":219 - * cdef bytes py_string - * if hasattr(text, 'encode'): - * py_string = text.encode('UTF-8', 'strict') # <<<<<<<<<<<<<< - * else: - * py_string = text # If it's a byte string it's assumed it's UTF-8 - */ - __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_v_text, __pyx_n_s__encode); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 219; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_4 = PyObject_Call(__pyx_t_5, ((PyObject *)__pyx_k_tuple_4), NULL); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 219; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_4); - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - if (!(likely(PyBytes_CheckExact(__pyx_t_4))||((__pyx_t_4) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected bytes, got %.200s", Py_TYPE(__pyx_t_4)->tp_name), 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 219; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_v_py_string = ((PyObject*)__pyx_t_4); - __pyx_t_4 = 0; - goto __pyx_L4; - } - /*else*/ { - - /* "misaka.pyx":221 - * py_string = text.encode('UTF-8', 'strict') - * else: - * py_string = text # If it's a byte string it's assumed it's UTF-8 # <<<<<<<<<<<<<< - * cdef char *c_string = py_string - * - */ - if (!(likely(PyBytes_CheckExact(__pyx_v_text))||((__pyx_v_text) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected bytes, got %.200s", Py_TYPE(__pyx_v_text)->tp_name), 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 221; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_INCREF(__pyx_v_text); - __pyx_v_py_string = ((PyObject*)__pyx_v_text); - } - __pyx_L4:; - - /* "misaka.pyx":222 - * else: - * py_string = text # If it's a byte string it's assumed it's UTF-8 - * cdef char *c_string = py_string # <<<<<<<<<<<<<< - * - * # Buffers - */ - __pyx_t_6 = __Pyx_PyObject_AsString(((PyObject *)__pyx_v_py_string)); if (unlikely((!__pyx_t_6) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 222; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_v_c_string = __pyx_t_6; - - /* "misaka.pyx":225 - * - * # Buffers - * cdef sundown.buf *ib = sundown.bufnew(128) # <<<<<<<<<<<<<< - * sundown.bufputs(ib, c_string) - * - */ - __pyx_v_ib = bufnew(128); - - /* "misaka.pyx":226 - * # Buffers - * cdef sundown.buf *ib = sundown.bufnew(128) - * sundown.bufputs(ib, c_string) # <<<<<<<<<<<<<< - * - * cdef sundown.buf *ob = sundown.bufnew(128) - */ - bufputs(__pyx_v_ib, __pyx_v_c_string); - - /* "misaka.pyx":228 - * sundown.bufputs(ib, c_string) - * - * cdef sundown.buf *ob = sundown.bufnew(128) # <<<<<<<<<<<<<< - * sundown.bufgrow(ob, (ib.size * 1.4)) - * - */ - __pyx_v_ob = bufnew(128); - - /* "misaka.pyx":229 - * - * cdef sundown.buf *ob = sundown.bufnew(128) - * sundown.bufgrow(ob, (ib.size * 1.4)) # <<<<<<<<<<<<<< - * - * # Parse! And make a unicode string - */ - bufgrow(__pyx_v_ob, ((size_t)(__pyx_v_ib->size * 1.4))); - - /* "misaka.pyx":232 - * - * # Parse! And make a unicode string - * sundown.sd_markdown_render(ob, ib.data, ib.size, self.markdown) # <<<<<<<<<<<<<< - * text = ( ob.data)[:ob.size].decode('UTF-8', 'strict') - * - */ - sd_markdown_render(__pyx_v_ob, __pyx_v_ib->data, __pyx_v_ib->size, __pyx_v_self->markdown); - - /* "misaka.pyx":233 - * # Parse! And make a unicode string - * sundown.sd_markdown_render(ob, ib.data, ib.size, self.markdown) - * text = ( ob.data)[:ob.size].decode('UTF-8', 'strict') # <<<<<<<<<<<<<< - * - * if hasattr(self.renderer, 'postprocess'): - */ - __pyx_t_4 = ((PyObject *)__Pyx_decode_c_string(((char *)__pyx_v_ob->data), 0, __pyx_v_ob->size, NULL, NULL, PyUnicode_DecodeUTF8)); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 233; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(((PyObject *)__pyx_t_4)); - __Pyx_DECREF(__pyx_v_text); - __pyx_v_text = ((PyObject *)__pyx_t_4); - __pyx_t_4 = 0; - - /* "misaka.pyx":235 - * text = ( ob.data)[:ob.size].decode('UTF-8', 'strict') - * - * if hasattr(self.renderer, 'postprocess'): # <<<<<<<<<<<<<< - * text = self.renderer.postprocess(text) - * - */ - __pyx_t_4 = ((PyObject *)__pyx_v_self->renderer); - __Pyx_INCREF(__pyx_t_4); - __pyx_t_2 = PyObject_HasAttr(__pyx_t_4, ((PyObject *)__pyx_n_s__postprocess)); if (unlikely(__pyx_t_2 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 235; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_3 = (__pyx_t_2 != 0); - if (__pyx_t_3) { - - /* "misaka.pyx":236 - * - * if hasattr(self.renderer, 'postprocess'): - * text = self.renderer.postprocess(text) # <<<<<<<<<<<<<< - * - * # Return a string and release buffers - */ - __pyx_t_4 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self->renderer), __pyx_n_s__postprocess); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 236; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 236; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_5); - __Pyx_INCREF(__pyx_v_text); - PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_text); - __Pyx_GIVEREF(__pyx_v_text); - __pyx_t_1 = PyObject_Call(__pyx_t_4, ((PyObject *)__pyx_t_5), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 236; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0; - __Pyx_DECREF(__pyx_v_text); - __pyx_v_text = __pyx_t_1; - __pyx_t_1 = 0; - goto __pyx_L5; - } - __pyx_L5:; - - /* "misaka.pyx":239 - * - * # Return a string and release buffers - * try: # <<<<<<<<<<<<<< - * return text - * finally: - */ - /*try:*/ { - - /* "misaka.pyx":240 - * # Return a string and release buffers - * try: - * return text # <<<<<<<<<<<<<< - * finally: - * sundown.bufrelease(ob) - */ - __Pyx_XDECREF(__pyx_r); - __Pyx_INCREF(__pyx_v_text); - __pyx_r = __pyx_v_text; - goto __pyx_L6; - } - - /* "misaka.pyx":242 - * return text - * finally: - * sundown.bufrelease(ob) # <<<<<<<<<<<<<< - * sundown.bufrelease(ib) - * - */ - /*finally:*/ { - int __pyx_why; - __pyx_why = 0; goto __pyx_L8; - __pyx_L6: __pyx_why = 3; goto __pyx_L8; - __pyx_L8:; - bufrelease(__pyx_v_ob); - - /* "misaka.pyx":243 - * finally: - * sundown.bufrelease(ob) - * sundown.bufrelease(ib) # <<<<<<<<<<<<<< - * - * def __dealloc__(self): - */ - bufrelease(__pyx_v_ib); - switch (__pyx_why) { - case 3: goto __pyx_L0; - } - } - - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_AddTraceback("misaka.Markdown.render", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_py_string); - __Pyx_XDECREF(__pyx_v_text); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* Python wrapper */ -static void __pyx_pw_6misaka_8Markdown_5__dealloc__(PyObject *__pyx_v_self); /*proto*/ -static void __pyx_pw_6misaka_8Markdown_5__dealloc__(PyObject *__pyx_v_self) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__dealloc__ (wrapper)", 0); - __pyx_pf_6misaka_8Markdown_4__dealloc__(((struct __pyx_obj_6misaka_Markdown *)__pyx_v_self)); - __Pyx_RefNannyFinishContext(); -} - -/* "misaka.pyx":245 - * sundown.bufrelease(ib) - * - * def __dealloc__(self): # <<<<<<<<<<<<<< - * if self.markdown is not NULL: - * sundown.sd_markdown_free(self.markdown) - */ - -static void __pyx_pf_6misaka_8Markdown_4__dealloc__(struct __pyx_obj_6misaka_Markdown *__pyx_v_self) { - __Pyx_RefNannyDeclarations - int __pyx_t_1; - __Pyx_RefNannySetupContext("__dealloc__", 0); - - /* "misaka.pyx":246 - * - * def __dealloc__(self): - * if self.markdown is not NULL: # <<<<<<<<<<<<<< - * sundown.sd_markdown_free(self.markdown) - */ - __pyx_t_1 = ((__pyx_v_self->markdown != NULL) != 0); - if (__pyx_t_1) { - - /* "misaka.pyx":247 - * def __dealloc__(self): - * if self.markdown is not NULL: - * sundown.sd_markdown_free(self.markdown) # <<<<<<<<<<<<<< - */ - sd_markdown_free(__pyx_v_self->markdown); - goto __pyx_L3; - } - __pyx_L3:; - - __Pyx_RefNannyFinishContext(); -} - -static PyObject *__pyx_tp_new_6misaka_BaseRenderer(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) { - PyObject *o; - o = (*t->tp_alloc)(t, 0); - if (unlikely(!o)) return 0; - return o; -} - -static void __pyx_tp_dealloc_6misaka_BaseRenderer(PyObject *o) { - (*Py_TYPE(o)->tp_free)(o); -} - -static PyObject *__pyx_getprop_6misaka_12BaseRenderer_flags(PyObject *o, CYTHON_UNUSED void *x) { - return __pyx_pw_6misaka_12BaseRenderer_5flags_1__get__(o); -} - -static PyMethodDef __pyx_methods_6misaka_BaseRenderer[] = { - {__Pyx_NAMESTR("setup"), (PyCFunction)__pyx_pw_6misaka_12BaseRenderer_3setup, METH_NOARGS, __Pyx_DOCSTR(__pyx_doc_6misaka_12BaseRenderer_2setup)}, - {0, 0, 0, 0} -}; - -static struct PyGetSetDef __pyx_getsets_6misaka_BaseRenderer[] = { - {(char *)"flags", __pyx_getprop_6misaka_12BaseRenderer_flags, 0, 0, 0}, - {0, 0, 0, 0, 0} -}; - -static PyTypeObject __pyx_type_6misaka_BaseRenderer = { - PyVarObject_HEAD_INIT(0, 0) - __Pyx_NAMESTR("misaka.BaseRenderer"), /*tp_name*/ - sizeof(struct __pyx_obj_6misaka_BaseRenderer), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - __pyx_tp_dealloc_6misaka_BaseRenderer, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - #if PY_MAJOR_VERSION < 3 - 0, /*tp_compare*/ - #else - 0, /*reserved*/ - #endif - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE, /*tp_flags*/ - __Pyx_DOCSTR("The ``BaseRenderer`` is boilerplate code for creating your own renderers by\n sublassing `BaseRenderer`. It takes care of setting the callbacks and flags.\n\n :param flags: Available as a read-only, integer type attribute named ``self.flags``.\n "), /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - __pyx_methods_6misaka_BaseRenderer, /*tp_methods*/ - 0, /*tp_members*/ - __pyx_getsets_6misaka_BaseRenderer, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - __pyx_pw_6misaka_12BaseRenderer_1__init__, /*tp_init*/ - 0, /*tp_alloc*/ - __pyx_tp_new_6misaka_BaseRenderer, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ - 0, /*tp_bases*/ - 0, /*tp_mro*/ - 0, /*tp_cache*/ - 0, /*tp_subclasses*/ - 0, /*tp_weaklist*/ - 0, /*tp_del*/ - #if PY_VERSION_HEX >= 0x02060000 - 0, /*tp_version_tag*/ - #endif -}; - -static PyObject *__pyx_tp_new_6misaka_HtmlRenderer(PyTypeObject *t, PyObject *a, PyObject *k) { - PyObject *o = __pyx_tp_new_6misaka_BaseRenderer(t, a, k); - if (unlikely(!o)) return 0; - return o; -} - -static PyMethodDef __pyx_methods_6misaka_HtmlRenderer[] = { - {__Pyx_NAMESTR("setup"), (PyCFunction)__pyx_pw_6misaka_12HtmlRenderer_1setup, METH_NOARGS, __Pyx_DOCSTR(0)}, - {0, 0, 0, 0} -}; - -static PyTypeObject __pyx_type_6misaka_HtmlRenderer = { - PyVarObject_HEAD_INIT(0, 0) - __Pyx_NAMESTR("misaka.HtmlRenderer"), /*tp_name*/ - sizeof(struct __pyx_obj_6misaka_HtmlRenderer), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - __pyx_tp_dealloc_6misaka_BaseRenderer, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - #if PY_MAJOR_VERSION < 3 - 0, /*tp_compare*/ - #else - 0, /*reserved*/ - #endif - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE, /*tp_flags*/ - __Pyx_DOCSTR("The HTML renderer that's included in Sundown.\n\n Do you override the ``setup`` method when subclassing ``HtmlRenderer``. If\n you do make sure to call parent class' ``setup`` method first.\n\n :param flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants.\n "), /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - __pyx_methods_6misaka_HtmlRenderer, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - #if CYTHON_COMPILING_IN_PYPY - __pyx_pw_6misaka_12BaseRenderer_1__init__, /*tp_init*/ - #else - 0, /*tp_init*/ - #endif - 0, /*tp_alloc*/ - __pyx_tp_new_6misaka_HtmlRenderer, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ - 0, /*tp_bases*/ - 0, /*tp_mro*/ - 0, /*tp_cache*/ - 0, /*tp_subclasses*/ - 0, /*tp_weaklist*/ - 0, /*tp_del*/ - #if PY_VERSION_HEX >= 0x02060000 - 0, /*tp_version_tag*/ - #endif -}; - -static PyObject *__pyx_tp_new_6misaka_Markdown(PyTypeObject *t, PyObject *a, PyObject *k) { - struct __pyx_obj_6misaka_Markdown *p; - PyObject *o; - o = (*t->tp_alloc)(t, 0); - if (unlikely(!o)) return 0; - p = ((struct __pyx_obj_6misaka_Markdown *)o); - p->renderer = ((struct __pyx_obj_6misaka_BaseRenderer *)Py_None); Py_INCREF(Py_None); - if (unlikely(__pyx_pw_6misaka_8Markdown_1__cinit__(o, a, k) < 0)) { - Py_DECREF(o); o = 0; - } - return o; -} - -static void __pyx_tp_dealloc_6misaka_Markdown(PyObject *o) { - struct __pyx_obj_6misaka_Markdown *p = (struct __pyx_obj_6misaka_Markdown *)o; - PyObject_GC_UnTrack(o); - { - PyObject *etype, *eval, *etb; - PyErr_Fetch(&etype, &eval, &etb); - ++Py_REFCNT(o); - __pyx_pw_6misaka_8Markdown_5__dealloc__(o); - if (PyErr_Occurred()) PyErr_WriteUnraisable(o); - --Py_REFCNT(o); - PyErr_Restore(etype, eval, etb); - } - Py_CLEAR(p->renderer); - (*Py_TYPE(o)->tp_free)(o); -} - -static int __pyx_tp_traverse_6misaka_Markdown(PyObject *o, visitproc v, void *a) { - int e; - struct __pyx_obj_6misaka_Markdown *p = (struct __pyx_obj_6misaka_Markdown *)o; - if (p->renderer) { - e = (*v)(((PyObject*)p->renderer), a); if (e) return e; - } - return 0; -} - -static int __pyx_tp_clear_6misaka_Markdown(PyObject *o) { - struct __pyx_obj_6misaka_Markdown *p = (struct __pyx_obj_6misaka_Markdown *)o; - PyObject* tmp; - tmp = ((PyObject*)p->renderer); - p->renderer = ((struct __pyx_obj_6misaka_BaseRenderer *)Py_None); Py_INCREF(Py_None); - Py_XDECREF(tmp); - return 0; -} - -static PyMethodDef __pyx_methods_6misaka_Markdown[] = { - {__Pyx_NAMESTR("render"), (PyCFunction)__pyx_pw_6misaka_8Markdown_3render, METH_O, __Pyx_DOCSTR(__pyx_doc_6misaka_8Markdown_2render)}, - {0, 0, 0, 0} -}; - -static PyTypeObject __pyx_type_6misaka_Markdown = { - PyVarObject_HEAD_INIT(0, 0) - __Pyx_NAMESTR("misaka.Markdown"), /*tp_name*/ - sizeof(struct __pyx_obj_6misaka_Markdown), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - __pyx_tp_dealloc_6misaka_Markdown, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - #if PY_MAJOR_VERSION < 3 - 0, /*tp_compare*/ - #else - 0, /*reserved*/ - #endif - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - __Pyx_DOCSTR("The Markdown parser.\n\n :param renderer: An instance of ``BaseRenderer``.\n :param extensions: Enable additional Markdown extensions with the ``EXT_*`` constants.\n "), /*tp_doc*/ - __pyx_tp_traverse_6misaka_Markdown, /*tp_traverse*/ - __pyx_tp_clear_6misaka_Markdown, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - __pyx_methods_6misaka_Markdown, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - __pyx_tp_new_6misaka_Markdown, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ - 0, /*tp_bases*/ - 0, /*tp_mro*/ - 0, /*tp_cache*/ - 0, /*tp_subclasses*/ - 0, /*tp_weaklist*/ - 0, /*tp_del*/ - #if PY_VERSION_HEX >= 0x02060000 - 0, /*tp_version_tag*/ - #endif -}; - -static PyObject *__pyx_tp_new_6misaka_SmartyPants(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) { - PyObject *o; - o = (*t->tp_alloc)(t, 0); - if (unlikely(!o)) return 0; - return o; -} - -static void __pyx_tp_dealloc_6misaka_SmartyPants(PyObject *o) { - (*Py_TYPE(o)->tp_free)(o); -} - -static PyMethodDef __pyx_methods_6misaka_SmartyPants[] = { - {__Pyx_NAMESTR("postprocess"), (PyCFunction)__pyx_pw_6misaka_11SmartyPants_1postprocess, METH_O, __Pyx_DOCSTR(__pyx_doc_6misaka_11SmartyPants_postprocess)}, - {0, 0, 0, 0} -}; - -static PyTypeObject __pyx_type_6misaka_SmartyPants = { - PyVarObject_HEAD_INIT(0, 0) - __Pyx_NAMESTR("misaka.SmartyPants"), /*tp_name*/ - sizeof(struct __pyx_obj_6misaka_SmartyPants), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - __pyx_tp_dealloc_6misaka_SmartyPants, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - #if PY_MAJOR_VERSION < 3 - 0, /*tp_compare*/ - #else - 0, /*reserved*/ - #endif - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE, /*tp_flags*/ - __Pyx_DOCSTR("Smartypants is a post-processor for (X)HTML renderers and can be used\n standalone or as a mixin. It adds a methode named ``postprocess`` to the\n renderer.\n\n ================================== ========\n Source Result\n ================================== ========\n `'s` (s, t, m, d, re, ll, ve) [1]_ ’s\n `--` —\n `-` –\n `...` …\n `. . .` …\n `(c)` ©\n `(r)` ®\n `(tm)` ™\n `3/4` ¾\n `1/2` ½\n `1/4` ¼\n ================================== ========\n\n .. [1] A ``'`` followed by a ``s``, ``t``, ``m``, ``d``, ``re``, ``ll`` or\n ``ve`` will be turned into ``’s``, ``’t``, and so on.\n "), /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - __pyx_methods_6misaka_SmartyPants, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - __pyx_tp_new_6misaka_SmartyPants, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ - 0, /*tp_bases*/ - 0, /*tp_mro*/ - 0, /*tp_cache*/ - 0, /*tp_subclasses*/ - 0, /*tp_weaklist*/ - 0, /*tp_del*/ - #if PY_VERSION_HEX >= 0x02060000 - 0, /*tp_version_tag*/ - #endif -}; - -static PyObject *__pyx_tp_new_6misaka_HtmlTocRenderer(PyTypeObject *t, PyObject *a, PyObject *k) { - PyObject *o = __pyx_tp_new_6misaka_BaseRenderer(t, a, k); - if (unlikely(!o)) return 0; - return o; -} - -static PyMethodDef __pyx_methods_6misaka_HtmlTocRenderer[] = { - {__Pyx_NAMESTR("setup"), (PyCFunction)__pyx_pw_6misaka_15HtmlTocRenderer_1setup, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(0)}, - {0, 0, 0, 0} -}; - -static PyTypeObject __pyx_type_6misaka_HtmlTocRenderer = { - PyVarObject_HEAD_INIT(0, 0) - __Pyx_NAMESTR("misaka.HtmlTocRenderer"), /*tp_name*/ - sizeof(struct __pyx_obj_6misaka_HtmlTocRenderer), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - __pyx_tp_dealloc_6misaka_BaseRenderer, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - #if PY_MAJOR_VERSION < 3 - 0, /*tp_compare*/ - #else - 0, /*reserved*/ - #endif - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE, /*tp_flags*/ - __Pyx_DOCSTR("The HTML table of contents renderer that's included in Sundown.\n\n Do you override the ``setup`` method when subclassing ``HtmlTocRenderer``.\n If you do make sure to call parent class' ``setup`` method first.\n\n :param flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants.\n "), /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - __pyx_methods_6misaka_HtmlTocRenderer, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - #if CYTHON_COMPILING_IN_PYPY - __pyx_pw_6misaka_12BaseRenderer_1__init__, /*tp_init*/ - #else - 0, /*tp_init*/ - #endif - 0, /*tp_alloc*/ - __pyx_tp_new_6misaka_HtmlTocRenderer, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ - 0, /*tp_bases*/ - 0, /*tp_mro*/ - 0, /*tp_cache*/ - 0, /*tp_subclasses*/ - 0, /*tp_weaklist*/ - 0, /*tp_del*/ - #if PY_VERSION_HEX >= 0x02060000 - 0, /*tp_version_tag*/ - #endif -}; - -static PyMethodDef __pyx_methods[] = { - {0, 0, 0, 0} -}; - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef __pyx_moduledef = { - #if PY_VERSION_HEX < 0x03020000 - { PyObject_HEAD_INIT(NULL) NULL, 0, NULL }, - #else - PyModuleDef_HEAD_INIT, - #endif - __Pyx_NAMESTR("misaka"), - 0, /* m_doc */ - -1, /* m_size */ - __pyx_methods /* m_methods */, - NULL, /* m_reload */ - NULL, /* m_traverse */ - NULL, /* m_clear */ - NULL /* m_free */ -}; -#endif - -static __Pyx_StringTabEntry __pyx_string_tab[] = { - {&__pyx_kp_s_1, __pyx_k_1, sizeof(__pyx_k_1), 0, 0, 1, 0}, - {&__pyx_kp_s_3, __pyx_k_3, sizeof(__pyx_k_3), 0, 0, 1, 0}, - {&__pyx_kp_s_5, __pyx_k_5, sizeof(__pyx_k_5), 0, 0, 1, 0}, - {&__pyx_n_s_6, __pyx_k_6, sizeof(__pyx_k_6), 0, 0, 1, 1}, - {&__pyx_kp_s_9, __pyx_k_9, sizeof(__pyx_k_9), 0, 0, 1, 0}, - {&__pyx_n_s__EXT_AUTOLINK, __pyx_k__EXT_AUTOLINK, sizeof(__pyx_k__EXT_AUTOLINK), 0, 0, 1, 1}, - {&__pyx_n_s__EXT_FENCED_CODE, __pyx_k__EXT_FENCED_CODE, sizeof(__pyx_k__EXT_FENCED_CODE), 0, 0, 1, 1}, - {&__pyx_n_s__EXT_LAX_SPACING, __pyx_k__EXT_LAX_SPACING, sizeof(__pyx_k__EXT_LAX_SPACING), 0, 0, 1, 1}, - {&__pyx_n_s__EXT_SPACE_HEADERS, __pyx_k__EXT_SPACE_HEADERS, sizeof(__pyx_k__EXT_SPACE_HEADERS), 0, 0, 1, 1}, - {&__pyx_n_s__EXT_STRIKETHROUGH, __pyx_k__EXT_STRIKETHROUGH, sizeof(__pyx_k__EXT_STRIKETHROUGH), 0, 0, 1, 1}, - {&__pyx_n_s__EXT_SUPERSCRIPT, __pyx_k__EXT_SUPERSCRIPT, sizeof(__pyx_k__EXT_SUPERSCRIPT), 0, 0, 1, 1}, - {&__pyx_n_s__EXT_TABLES, __pyx_k__EXT_TABLES, sizeof(__pyx_k__EXT_TABLES), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_ESCAPE, __pyx_k__HTML_ESCAPE, sizeof(__pyx_k__HTML_ESCAPE), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_EXPAND_TABS, __pyx_k__HTML_EXPAND_TABS, sizeof(__pyx_k__HTML_EXPAND_TABS), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_HARD_WRAP, __pyx_k__HTML_HARD_WRAP, sizeof(__pyx_k__HTML_HARD_WRAP), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_SAFELINK, __pyx_k__HTML_SAFELINK, sizeof(__pyx_k__HTML_SAFELINK), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_SKIP_HTML, __pyx_k__HTML_SKIP_HTML, sizeof(__pyx_k__HTML_SKIP_HTML), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_SKIP_IMAGES, __pyx_k__HTML_SKIP_IMAGES, sizeof(__pyx_k__HTML_SKIP_IMAGES), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_SKIP_LINKS, __pyx_k__HTML_SKIP_LINKS, sizeof(__pyx_k__HTML_SKIP_LINKS), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_SKIP_STYLE, __pyx_k__HTML_SKIP_STYLE, sizeof(__pyx_k__HTML_SKIP_STYLE), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_SMARTYPANTS, __pyx_k__HTML_SMARTYPANTS, sizeof(__pyx_k__HTML_SMARTYPANTS), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_TOC, __pyx_k__HTML_TOC, sizeof(__pyx_k__HTML_TOC), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_TOC_TREE, __pyx_k__HTML_TOC_TREE, sizeof(__pyx_k__HTML_TOC_TREE), 0, 0, 1, 1}, - {&__pyx_n_s__HTML_USE_XHTML, __pyx_k__HTML_USE_XHTML, sizeof(__pyx_k__HTML_USE_XHTML), 0, 0, 1, 1}, - {&__pyx_n_s__TABLE_ALIGNMASK, __pyx_k__TABLE_ALIGNMASK, sizeof(__pyx_k__TABLE_ALIGNMASK), 0, 0, 1, 1}, - {&__pyx_n_s__TABLE_ALIGN_C, __pyx_k__TABLE_ALIGN_C, sizeof(__pyx_k__TABLE_ALIGN_C), 0, 0, 1, 1}, - {&__pyx_n_s__TABLE_ALIGN_L, __pyx_k__TABLE_ALIGN_L, sizeof(__pyx_k__TABLE_ALIGN_L), 0, 0, 1, 1}, - {&__pyx_n_s__TABLE_ALIGN_R, __pyx_k__TABLE_ALIGN_R, sizeof(__pyx_k__TABLE_ALIGN_R), 0, 0, 1, 1}, - {&__pyx_n_s__TABLE_HEADER, __pyx_k__TABLE_HEADER, sizeof(__pyx_k__TABLE_HEADER), 0, 0, 1, 1}, - {&__pyx_n_s__ValueError, __pyx_k__ValueError, sizeof(__pyx_k__ValueError), 0, 0, 1, 1}, - {&__pyx_n_s____class__, __pyx_k____class__, sizeof(__pyx_k____class__), 0, 0, 1, 1}, - {&__pyx_n_s____main__, __pyx_k____main__, sizeof(__pyx_k____main__), 0, 0, 1, 1}, - {&__pyx_n_s____name__, __pyx_k____name__, sizeof(__pyx_k____name__), 0, 0, 1, 1}, - {&__pyx_n_s____test__, __pyx_k____test__, sizeof(__pyx_k____test__), 0, 0, 1, 1}, - {&__pyx_n_s____version__, __pyx_k____version__, sizeof(__pyx_k____version__), 0, 0, 1, 1}, - {&__pyx_n_s__encode, __pyx_k__encode, sizeof(__pyx_k__encode), 0, 0, 1, 1}, - {&__pyx_n_s__extensions, __pyx_k__extensions, sizeof(__pyx_k__extensions), 0, 0, 1, 1}, - {&__pyx_n_s__flags, __pyx_k__flags, sizeof(__pyx_k__flags), 0, 0, 1, 1}, - {&__pyx_n_s__html, __pyx_k__html, sizeof(__pyx_k__html), 0, 0, 1, 1}, - {&__pyx_n_s__markdown, __pyx_k__markdown, sizeof(__pyx_k__markdown), 0, 0, 1, 1}, - {&__pyx_n_s__misaka, __pyx_k__misaka, sizeof(__pyx_k__misaka), 0, 0, 1, 1}, - {&__pyx_n_s__postprocess, __pyx_k__postprocess, sizeof(__pyx_k__postprocess), 0, 0, 1, 1}, - {&__pyx_n_s__preprocess, __pyx_k__preprocess, sizeof(__pyx_k__preprocess), 0, 0, 1, 1}, - {&__pyx_n_s__render, __pyx_k__render, sizeof(__pyx_k__render), 0, 0, 1, 1}, - {&__pyx_n_s__render_flags, __pyx_k__render_flags, sizeof(__pyx_k__render_flags), 0, 0, 1, 1}, - {&__pyx_n_s__renderer, __pyx_k__renderer, sizeof(__pyx_k__renderer), 0, 0, 1, 1}, - {&__pyx_n_s__result, __pyx_k__result, sizeof(__pyx_k__result), 0, 0, 1, 1}, - {&__pyx_n_s__setup, __pyx_k__setup, sizeof(__pyx_k__setup), 0, 0, 1, 1}, - {&__pyx_n_s__strict, __pyx_k__strict, sizeof(__pyx_k__strict), 0, 0, 1, 1}, - {&__pyx_n_s__text, __pyx_k__text, sizeof(__pyx_k__text), 0, 0, 1, 1}, - {0, 0, 0, 0, 0, 0, 0} -}; -static int __Pyx_InitCachedBuiltins(void) { - __pyx_builtin_ValueError = __Pyx_GetBuiltinName(__pyx_n_s__ValueError); if (!__pyx_builtin_ValueError) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 197; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - return 0; - __pyx_L1_error:; - return -1; -} - -static int __Pyx_InitCachedConstants(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0); - - /* "misaka.pyx":102 - * cdef bytes py_string - * if hasattr(text, 'encode'): - * py_string = text.encode('UTF-8', 'strict') # <<<<<<<<<<<<<< - * else: - * py_string = text - */ - __pyx_k_tuple_2 = PyTuple_Pack(2, ((PyObject *)__pyx_kp_s_1), ((PyObject *)__pyx_n_s__strict)); if (unlikely(!__pyx_k_tuple_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 102; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_k_tuple_2); - __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_2)); - - /* "misaka.pyx":219 - * cdef bytes py_string - * if hasattr(text, 'encode'): - * py_string = text.encode('UTF-8', 'strict') # <<<<<<<<<<<<<< - * else: - * py_string = text # If it's a byte string it's assumed it's UTF-8 - */ - __pyx_k_tuple_4 = PyTuple_Pack(2, ((PyObject *)__pyx_kp_s_1), ((PyObject *)__pyx_n_s__strict)); if (unlikely(!__pyx_k_tuple_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 219; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_k_tuple_4); - __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_4)); - - /* "misaka.pyx":44 - * - * - * def html(object text, unsigned int extensions=0, unsigned int render_flags=0): # <<<<<<<<<<<<<< - * """Convert markdown text to (X)HTML. - * - */ - __pyx_k_tuple_7 = PyTuple_Pack(6, ((PyObject *)__pyx_n_s__text), ((PyObject *)__pyx_n_s__extensions), ((PyObject *)__pyx_n_s__render_flags), ((PyObject *)__pyx_n_s__renderer), ((PyObject *)__pyx_n_s__markdown), ((PyObject *)__pyx_n_s__result)); if (unlikely(!__pyx_k_tuple_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_k_tuple_7); - __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_7)); - __pyx_k_codeobj_8 = (PyObject*)__Pyx_PyCode_New(3, 0, 6, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_k_tuple_7, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_9, __pyx_n_s__html, 44, __pyx_empty_bytes); if (unlikely(!__pyx_k_codeobj_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_RefNannyFinishContext(); - return 0; - __pyx_L1_error:; - __Pyx_RefNannyFinishContext(); - return -1; -} - -static int __Pyx_InitGlobals(void) { - if (__Pyx_InitStrings(__pyx_string_tab) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_2 = PyInt_FromLong(2); if (unlikely(!__pyx_int_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_3 = PyInt_FromLong(3); if (unlikely(!__pyx_int_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_8 = PyInt_FromLong(8); if (unlikely(!__pyx_int_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_16 = PyInt_FromLong(16); if (unlikely(!__pyx_int_16)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_32 = PyInt_FromLong(32); if (unlikely(!__pyx_int_32)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_64 = PyInt_FromLong(64); if (unlikely(!__pyx_int_64)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_128 = PyInt_FromLong(128); if (unlikely(!__pyx_int_128)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_256 = PyInt_FromLong(256); if (unlikely(!__pyx_int_256)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_512 = PyInt_FromLong(512); if (unlikely(!__pyx_int_512)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_1024 = PyInt_FromLong(1024); if (unlikely(!__pyx_int_1024)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - __pyx_int_2048 = PyInt_FromLong(2048); if (unlikely(!__pyx_int_2048)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - return 0; - __pyx_L1_error:; - return -1; -} - -#if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC initmisaka(void); /*proto*/ -PyMODINIT_FUNC initmisaka(void) -#else -PyMODINIT_FUNC PyInit_misaka(void); /*proto*/ -PyMODINIT_FUNC PyInit_misaka(void) -#endif -{ - PyObject *__pyx_t_1 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannyDeclarations - #if CYTHON_REFNANNY - __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny"); - if (!__Pyx_RefNanny) { - PyErr_Clear(); - __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny"); - if (!__Pyx_RefNanny) - Py_FatalError("failed to import 'refnanny' module"); - } - #endif - __Pyx_RefNannySetupContext("PyMODINIT_FUNC PyInit_misaka(void)", 0); - if ( __Pyx_check_binary_version() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_bytes)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - #ifdef __Pyx_CyFunction_USED - if (__Pyx_CyFunction_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - #endif - #ifdef __Pyx_FusedFunction_USED - if (__pyx_FusedFunction_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - #endif - #ifdef __Pyx_Generator_USED - if (__pyx_Generator_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - #endif - /*--- Library function declarations ---*/ - /*--- Threads initialization code ---*/ - #if defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS - #ifdef WITH_THREAD /* Python build with threading support? */ - PyEval_InitThreads(); - #endif - #endif - /*--- Module creation code ---*/ - #if PY_MAJOR_VERSION < 3 - __pyx_m = Py_InitModule4(__Pyx_NAMESTR("misaka"), __pyx_methods, 0, 0, PYTHON_API_VERSION); Py_XINCREF(__pyx_m); - #else - __pyx_m = PyModule_Create(&__pyx_moduledef); - #endif - if (unlikely(!__pyx_m)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_d = PyModule_GetDict(__pyx_m); if (unlikely(!__pyx_d)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - Py_INCREF(__pyx_d); - #if PY_MAJOR_VERSION >= 3 - { - PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - if (!PyDict_GetItemString(modules, "misaka")) { - if (unlikely(PyDict_SetItemString(modules, "misaka", __pyx_m) < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - } - } - #endif - __pyx_b = PyImport_AddModule(__Pyx_NAMESTR(__Pyx_BUILTIN_MODULE_NAME)); if (unlikely(!__pyx_b)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - #if CYTHON_COMPILING_IN_PYPY - Py_INCREF(__pyx_b); - #endif - if (__Pyx_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - /*--- Initialize various global constants etc. ---*/ - if (unlikely(__Pyx_InitGlobals() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - #if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT) - if (__Pyx_init_sys_getdefaultencoding_params() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - #endif - if (__pyx_module_is_main_misaka) { - if (__Pyx_SetAttrString(__pyx_m, "__name__", __pyx_n_s____main__) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; - } - /*--- Builtin init code ---*/ - if (unlikely(__Pyx_InitCachedBuiltins() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - /*--- Constants init code ---*/ - if (unlikely(__Pyx_InitCachedConstants() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - /*--- Global init code ---*/ - /*--- Variable export code ---*/ - /*--- Function export code ---*/ - /*--- Type init code ---*/ - if (PyType_Ready(&__pyx_type_6misaka_BaseRenderer) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 117; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - if (__Pyx_SetAttrString(__pyx_m, "BaseRenderer", (PyObject *)&__pyx_type_6misaka_BaseRenderer) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 117; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_ptype_6misaka_BaseRenderer = &__pyx_type_6misaka_BaseRenderer; - __pyx_type_6misaka_HtmlRenderer.tp_base = __pyx_ptype_6misaka_BaseRenderer; - if (PyType_Ready(&__pyx_type_6misaka_HtmlRenderer) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 155; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - if (__Pyx_SetAttrString(__pyx_m, "HtmlRenderer", (PyObject *)&__pyx_type_6misaka_HtmlRenderer) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 155; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_ptype_6misaka_HtmlRenderer = &__pyx_type_6misaka_HtmlRenderer; - if (PyType_Ready(&__pyx_type_6misaka_Markdown) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 185; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - if (__Pyx_SetAttrString(__pyx_m, "Markdown", (PyObject *)&__pyx_type_6misaka_Markdown) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 185; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_ptype_6misaka_Markdown = &__pyx_type_6misaka_Markdown; - if (PyType_Ready(&__pyx_type_6misaka_SmartyPants) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - if (__Pyx_SetAttrString(__pyx_m, "SmartyPants", (PyObject *)&__pyx_type_6misaka_SmartyPants) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_ptype_6misaka_SmartyPants = &__pyx_type_6misaka_SmartyPants; - __pyx_type_6misaka_HtmlTocRenderer.tp_base = __pyx_ptype_6misaka_BaseRenderer; - if (PyType_Ready(&__pyx_type_6misaka_HtmlTocRenderer) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 171; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - if (__Pyx_SetAttrString(__pyx_m, "HtmlTocRenderer", (PyObject *)&__pyx_type_6misaka_HtmlTocRenderer) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 171; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __pyx_ptype_6misaka_HtmlTocRenderer = &__pyx_type_6misaka_HtmlTocRenderer; - /*--- Type import code ---*/ - /*--- Variable import code ---*/ - /*--- Function import code ---*/ - /*--- Execution code ---*/ - - /* "misaka.pyx":7 - * - * - * __version__ = '1.0.3' # <<<<<<<<<<<<<< - * - * - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s____version__, ((PyObject *)__pyx_kp_s_5)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":11 - * - * # Markdown extensions - * EXT_NO_INTRA_EMPHASIS = (1 << 0) # <<<<<<<<<<<<<< - * EXT_TABLES = (1 << 1) - * EXT_FENCED_CODE = (1 << 2) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s_6, __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":12 - * # Markdown extensions - * EXT_NO_INTRA_EMPHASIS = (1 << 0) - * EXT_TABLES = (1 << 1) # <<<<<<<<<<<<<< - * EXT_FENCED_CODE = (1 << 2) - * EXT_AUTOLINK = (1 << 3) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__EXT_TABLES, __pyx_int_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 12; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":13 - * EXT_NO_INTRA_EMPHASIS = (1 << 0) - * EXT_TABLES = (1 << 1) - * EXT_FENCED_CODE = (1 << 2) # <<<<<<<<<<<<<< - * EXT_AUTOLINK = (1 << 3) - * EXT_STRIKETHROUGH = (1 << 4) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__EXT_FENCED_CODE, __pyx_int_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":14 - * EXT_TABLES = (1 << 1) - * EXT_FENCED_CODE = (1 << 2) - * EXT_AUTOLINK = (1 << 3) # <<<<<<<<<<<<<< - * EXT_STRIKETHROUGH = (1 << 4) - * EXT_SPACE_HEADERS = (1 << 6) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__EXT_AUTOLINK, __pyx_int_8) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":15 - * EXT_FENCED_CODE = (1 << 2) - * EXT_AUTOLINK = (1 << 3) - * EXT_STRIKETHROUGH = (1 << 4) # <<<<<<<<<<<<<< - * EXT_SPACE_HEADERS = (1 << 6) - * EXT_SUPERSCRIPT = (1 << 7) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__EXT_STRIKETHROUGH, __pyx_int_16) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":16 - * EXT_AUTOLINK = (1 << 3) - * EXT_STRIKETHROUGH = (1 << 4) - * EXT_SPACE_HEADERS = (1 << 6) # <<<<<<<<<<<<<< - * EXT_SUPERSCRIPT = (1 << 7) - * EXT_LAX_SPACING = (1 << 8) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__EXT_SPACE_HEADERS, __pyx_int_64) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":17 - * EXT_STRIKETHROUGH = (1 << 4) - * EXT_SPACE_HEADERS = (1 << 6) - * EXT_SUPERSCRIPT = (1 << 7) # <<<<<<<<<<<<<< - * EXT_LAX_SPACING = (1 << 8) - * - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__EXT_SUPERSCRIPT, __pyx_int_128) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":18 - * EXT_SPACE_HEADERS = (1 << 6) - * EXT_SUPERSCRIPT = (1 << 7) - * EXT_LAX_SPACING = (1 << 8) # <<<<<<<<<<<<<< - * - * # HTML Render flags - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__EXT_LAX_SPACING, __pyx_int_256) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":21 - * - * # HTML Render flags - * HTML_SKIP_HTML = (1 << 0) # <<<<<<<<<<<<<< - * HTML_SKIP_STYLE = (1 << 1) - * HTML_SKIP_IMAGES = (1 << 2) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_SKIP_HTML, __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":22 - * # HTML Render flags - * HTML_SKIP_HTML = (1 << 0) - * HTML_SKIP_STYLE = (1 << 1) # <<<<<<<<<<<<<< - * HTML_SKIP_IMAGES = (1 << 2) - * HTML_SKIP_LINKS = (1 << 3) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_SKIP_STYLE, __pyx_int_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":23 - * HTML_SKIP_HTML = (1 << 0) - * HTML_SKIP_STYLE = (1 << 1) - * HTML_SKIP_IMAGES = (1 << 2) # <<<<<<<<<<<<<< - * HTML_SKIP_LINKS = (1 << 3) - * HTML_EXPAND_TABS = (1 << 4) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_SKIP_IMAGES, __pyx_int_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":24 - * HTML_SKIP_STYLE = (1 << 1) - * HTML_SKIP_IMAGES = (1 << 2) - * HTML_SKIP_LINKS = (1 << 3) # <<<<<<<<<<<<<< - * HTML_EXPAND_TABS = (1 << 4) - * HTML_SAFELINK = (1 << 5) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_SKIP_LINKS, __pyx_int_8) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":25 - * HTML_SKIP_IMAGES = (1 << 2) - * HTML_SKIP_LINKS = (1 << 3) - * HTML_EXPAND_TABS = (1 << 4) # <<<<<<<<<<<<<< - * HTML_SAFELINK = (1 << 5) - * HTML_TOC = (1 << 6) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_EXPAND_TABS, __pyx_int_16) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":26 - * HTML_SKIP_LINKS = (1 << 3) - * HTML_EXPAND_TABS = (1 << 4) - * HTML_SAFELINK = (1 << 5) # <<<<<<<<<<<<<< - * HTML_TOC = (1 << 6) - * HTML_HARD_WRAP = (1 << 7) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_SAFELINK, __pyx_int_32) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":27 - * HTML_EXPAND_TABS = (1 << 4) - * HTML_SAFELINK = (1 << 5) - * HTML_TOC = (1 << 6) # <<<<<<<<<<<<<< - * HTML_HARD_WRAP = (1 << 7) - * HTML_USE_XHTML = (1 << 8) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_TOC, __pyx_int_64) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":28 - * HTML_SAFELINK = (1 << 5) - * HTML_TOC = (1 << 6) - * HTML_HARD_WRAP = (1 << 7) # <<<<<<<<<<<<<< - * HTML_USE_XHTML = (1 << 8) - * HTML_ESCAPE = (1 << 9) - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_HARD_WRAP, __pyx_int_128) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":29 - * HTML_TOC = (1 << 6) - * HTML_HARD_WRAP = (1 << 7) - * HTML_USE_XHTML = (1 << 8) # <<<<<<<<<<<<<< - * HTML_ESCAPE = (1 << 9) - * - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_USE_XHTML, __pyx_int_256) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":30 - * HTML_HARD_WRAP = (1 << 7) - * HTML_USE_XHTML = (1 << 8) - * HTML_ESCAPE = (1 << 9) # <<<<<<<<<<<<<< - * - * # Extra HTML render flags - these are not from Sundown - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_ESCAPE, __pyx_int_512) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":33 - * - * # Extra HTML render flags - these are not from Sundown - * HTML_SMARTYPANTS = (1 << 10) # An extra flag to enable Smartypants # <<<<<<<<<<<<<< - * HTML_TOC_TREE = (1 << 11) # Only render a table of contents tree - * - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_SMARTYPANTS, __pyx_int_1024) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 33; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":34 - * # Extra HTML render flags - these are not from Sundown - * HTML_SMARTYPANTS = (1 << 10) # An extra flag to enable Smartypants - * HTML_TOC_TREE = (1 << 11) # Only render a table of contents tree # <<<<<<<<<<<<<< - * - * # Other flags - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__HTML_TOC_TREE, __pyx_int_2048) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 34; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":37 - * - * # Other flags - * TABLE_ALIGN_L = 1 # MKD_TABLE_ALIGN_L # <<<<<<<<<<<<<< - * TABLE_ALIGN_R = 2 # MKD_TABLE_ALIGN_R - * TABLE_ALIGN_C = 3 # MKD_TABLE_ALIGN_CENTER - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__TABLE_ALIGN_L, __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":38 - * # Other flags - * TABLE_ALIGN_L = 1 # MKD_TABLE_ALIGN_L - * TABLE_ALIGN_R = 2 # MKD_TABLE_ALIGN_R # <<<<<<<<<<<<<< - * TABLE_ALIGN_C = 3 # MKD_TABLE_ALIGN_CENTER - * TABLE_ALIGNMASK = 3 # MKD_TABLE_ALIGNMASK - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__TABLE_ALIGN_R, __pyx_int_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 38; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":39 - * TABLE_ALIGN_L = 1 # MKD_TABLE_ALIGN_L - * TABLE_ALIGN_R = 2 # MKD_TABLE_ALIGN_R - * TABLE_ALIGN_C = 3 # MKD_TABLE_ALIGN_CENTER # <<<<<<<<<<<<<< - * TABLE_ALIGNMASK = 3 # MKD_TABLE_ALIGNMASK - * TABLE_HEADER = 4 # MKD_TABLE_HEADER - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__TABLE_ALIGN_C, __pyx_int_3) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":40 - * TABLE_ALIGN_R = 2 # MKD_TABLE_ALIGN_R - * TABLE_ALIGN_C = 3 # MKD_TABLE_ALIGN_CENTER - * TABLE_ALIGNMASK = 3 # MKD_TABLE_ALIGNMASK # <<<<<<<<<<<<<< - * TABLE_HEADER = 4 # MKD_TABLE_HEADER - * - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__TABLE_ALIGNMASK, __pyx_int_3) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":41 - * TABLE_ALIGN_C = 3 # MKD_TABLE_ALIGN_CENTER - * TABLE_ALIGNMASK = 3 # MKD_TABLE_ALIGNMASK - * TABLE_HEADER = 4 # MKD_TABLE_HEADER # <<<<<<<<<<<<<< - * - * - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s__TABLE_HEADER, __pyx_int_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - - /* "misaka.pyx":44 - * - * - * def html(object text, unsigned int extensions=0, unsigned int render_flags=0): # <<<<<<<<<<<<<< - * """Convert markdown text to (X)HTML. - * - */ - __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_6misaka_1html, NULL, __pyx_n_s__misaka); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(__pyx_t_1); - if (PyDict_SetItem(__pyx_d, __pyx_n_s__html, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - - /* "misaka.pyx":1 - * cimport sundown # <<<<<<<<<<<<<< - * cimport wrapper - * - */ - __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_GOTREF(((PyObject *)__pyx_t_1)); - if (PyDict_SetItem(__pyx_d, __pyx_n_s____test__, ((PyObject *)__pyx_t_1)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} - __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - if (__pyx_m) { - __Pyx_AddTraceback("init misaka", __pyx_clineno, __pyx_lineno, __pyx_filename); - Py_DECREF(__pyx_m); __pyx_m = 0; - } else if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ImportError, "init misaka"); - } - __pyx_L0:; - __Pyx_RefNannyFinishContext(); - #if PY_MAJOR_VERSION < 3 - return; - #else - return __pyx_m; - #endif -} - -/* Runtime support code */ -#if CYTHON_REFNANNY -static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) { - PyObject *m = NULL, *p = NULL; - void *r = NULL; - m = PyImport_ImportModule((char *)modname); - if (!m) goto end; - p = PyObject_GetAttrString(m, (char *)"RefNannyAPI"); - if (!p) goto end; - r = PyLong_AsVoidPtr(p); -end: - Py_XDECREF(p); - Py_XDECREF(m); - return (__Pyx_RefNannyAPIStruct *)r; -} -#endif /* CYTHON_REFNANNY */ - -static PyObject *__Pyx_GetBuiltinName(PyObject *name) { - PyObject* result = __Pyx_PyObject_GetAttrStr(__pyx_b, name); - if (unlikely(!result)) { - PyErr_Format(PyExc_NameError, -#if PY_MAJOR_VERSION >= 3 - "name '%U' is not defined", name); -#else - "name '%s' is not defined", PyString_AS_STRING(name)); -#endif - } - return result; -} - -static void __Pyx_RaiseDoubleKeywordsError( - const char* func_name, - PyObject* kw_name) -{ - PyErr_Format(PyExc_TypeError, - #if PY_MAJOR_VERSION >= 3 - "%s() got multiple values for keyword argument '%U'", func_name, kw_name); - #else - "%s() got multiple values for keyword argument '%s'", func_name, - PyString_AsString(kw_name)); - #endif -} - -static int __Pyx_ParseOptionalKeywords( - PyObject *kwds, - PyObject **argnames[], - PyObject *kwds2, - PyObject *values[], - Py_ssize_t num_pos_args, - const char* function_name) -{ - PyObject *key = 0, *value = 0; - Py_ssize_t pos = 0; - PyObject*** name; - PyObject*** first_kw_arg = argnames + num_pos_args; - while (PyDict_Next(kwds, &pos, &key, &value)) { - name = first_kw_arg; - while (*name && (**name != key)) name++; - if (*name) { - values[name-argnames] = value; - continue; - } - name = first_kw_arg; - #if PY_MAJOR_VERSION < 3 - if (likely(PyString_CheckExact(key)) || likely(PyString_Check(key))) { - while (*name) { - if ((CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**name) == PyString_GET_SIZE(key)) - && _PyString_Eq(**name, key)) { - values[name-argnames] = value; - break; - } - name++; - } - if (*name) continue; - else { - PyObject*** argname = argnames; - while (argname != first_kw_arg) { - if ((**argname == key) || ( - (CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**argname) == PyString_GET_SIZE(key)) - && _PyString_Eq(**argname, key))) { - goto arg_passed_twice; - } - argname++; - } - } - } else - #endif - if (likely(PyUnicode_Check(key))) { - while (*name) { - int cmp = (**name == key) ? 0 : - #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3 - (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 : - #endif - PyUnicode_Compare(**name, key); - if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; - if (cmp == 0) { - values[name-argnames] = value; - break; - } - name++; - } - if (*name) continue; - else { - PyObject*** argname = argnames; - while (argname != first_kw_arg) { - int cmp = (**argname == key) ? 0 : - #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3 - (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 : - #endif - PyUnicode_Compare(**argname, key); - if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; - if (cmp == 0) goto arg_passed_twice; - argname++; - } - } - } else - goto invalid_keyword_type; - if (kwds2) { - if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad; - } else { - goto invalid_keyword; - } - } - return 0; -arg_passed_twice: - __Pyx_RaiseDoubleKeywordsError(function_name, key); - goto bad; -invalid_keyword_type: - PyErr_Format(PyExc_TypeError, - "%s() keywords must be strings", function_name); - goto bad; -invalid_keyword: - PyErr_Format(PyExc_TypeError, - #if PY_MAJOR_VERSION < 3 - "%s() got an unexpected keyword argument '%s'", - function_name, PyString_AsString(key)); - #else - "%s() got an unexpected keyword argument '%U'", - function_name, key); - #endif -bad: - return -1; -} - -static void __Pyx_RaiseArgtupleInvalid( - const char* func_name, - int exact, - Py_ssize_t num_min, - Py_ssize_t num_max, - Py_ssize_t num_found) -{ - Py_ssize_t num_expected; - const char *more_or_less; - if (num_found < num_min) { - num_expected = num_min; - more_or_less = "at least"; - } else { - num_expected = num_max; - more_or_less = "at most"; - } - if (exact) { - more_or_less = "exactly"; - } - PyErr_Format(PyExc_TypeError, - "%s() takes %s %" CYTHON_FORMAT_SSIZE_T "d positional argument%s (%" CYTHON_FORMAT_SSIZE_T "d given)", - func_name, more_or_less, num_expected, - (num_expected == 1) ? "" : "s", num_found); -} - -static CYTHON_INLINE PyObject *__Pyx_GetModuleGlobalName(PyObject *name) { - PyObject *result; -#if CYTHON_COMPILING_IN_CPYTHON - result = PyDict_GetItem(__pyx_d, name); - if (result) { - Py_INCREF(result); - } else { -#else - result = PyObject_GetItem(__pyx_d, name); - if (!result) { - PyErr_Clear(); -#endif - result = __Pyx_GetBuiltinName(name); - } - return result; -} - -static CYTHON_INLINE PyObject* __Pyx_decode_c_string( - const char* cstring, Py_ssize_t start, Py_ssize_t stop, - const char* encoding, const char* errors, - PyObject* (*decode_func)(const char *s, Py_ssize_t size, const char *errors)) { - Py_ssize_t length; - if (unlikely((start < 0) | (stop < 0))) { - length = strlen(cstring); - if (start < 0) { - start += length; - if (start < 0) - start = 0; - } - if (stop < 0) - stop += length; - } - length = stop - start; - if (unlikely(length <= 0)) - return PyUnicode_FromUnicode(NULL, 0); - cstring += start; - if (decode_func) { - return decode_func(cstring, length, errors); - } else { - return PyUnicode_Decode(cstring, length, encoding, errors); - } -} - -static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb) { -#if CYTHON_COMPILING_IN_CPYTHON - PyObject *tmp_type, *tmp_value, *tmp_tb; - PyThreadState *tstate = PyThreadState_GET(); - tmp_type = tstate->curexc_type; - tmp_value = tstate->curexc_value; - tmp_tb = tstate->curexc_traceback; - tstate->curexc_type = type; - tstate->curexc_value = value; - tstate->curexc_traceback = tb; - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); -#else - PyErr_Restore(type, value, tb); -#endif -} -static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb) { -#if CYTHON_COMPILING_IN_CPYTHON - PyThreadState *tstate = PyThreadState_GET(); - *type = tstate->curexc_type; - *value = tstate->curexc_value; - *tb = tstate->curexc_traceback; - tstate->curexc_type = 0; - tstate->curexc_value = 0; - tstate->curexc_traceback = 0; -#else - PyErr_Fetch(type, value, tb); -#endif -} - -#if PY_MAJOR_VERSION < 3 -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, - CYTHON_UNUSED PyObject *cause) { - Py_XINCREF(type); - if (!value || value == Py_None) - value = NULL; - else - Py_INCREF(value); - if (!tb || tb == Py_None) - tb = NULL; - else { - Py_INCREF(tb); - if (!PyTraceBack_Check(tb)) { - PyErr_SetString(PyExc_TypeError, - "raise: arg 3 must be a traceback or None"); - goto raise_error; - } - } - #if PY_VERSION_HEX < 0x02050000 - if (PyClass_Check(type)) { - #else - if (PyType_Check(type)) { - #endif -#if CYTHON_COMPILING_IN_PYPY - if (!value) { - Py_INCREF(Py_None); - value = Py_None; - } -#endif - PyErr_NormalizeException(&type, &value, &tb); - } else { - if (value) { - PyErr_SetString(PyExc_TypeError, - "instance exception may not have a separate value"); - goto raise_error; - } - value = type; - #if PY_VERSION_HEX < 0x02050000 - if (PyInstance_Check(type)) { - type = (PyObject*) ((PyInstanceObject*)type)->in_class; - Py_INCREF(type); - } else { - type = 0; - PyErr_SetString(PyExc_TypeError, - "raise: exception must be an old-style class or instance"); - goto raise_error; - } - #else - type = (PyObject*) Py_TYPE(type); - Py_INCREF(type); - if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) { - PyErr_SetString(PyExc_TypeError, - "raise: exception class must be a subclass of BaseException"); - goto raise_error; - } - #endif - } - __Pyx_ErrRestore(type, value, tb); - return; -raise_error: - Py_XDECREF(value); - Py_XDECREF(type); - Py_XDECREF(tb); - return; -} -#else /* Python 3+ */ -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) { - PyObject* owned_instance = NULL; - if (tb == Py_None) { - tb = 0; - } else if (tb && !PyTraceBack_Check(tb)) { - PyErr_SetString(PyExc_TypeError, - "raise: arg 3 must be a traceback or None"); - goto bad; - } - if (value == Py_None) - value = 0; - if (PyExceptionInstance_Check(type)) { - if (value) { - PyErr_SetString(PyExc_TypeError, - "instance exception may not have a separate value"); - goto bad; - } - value = type; - type = (PyObject*) Py_TYPE(value); - } else if (PyExceptionClass_Check(type)) { - PyObject *args; - if (!value) - args = PyTuple_New(0); - else if (PyTuple_Check(value)) { - Py_INCREF(value); - args = value; - } else - args = PyTuple_Pack(1, value); - if (!args) - goto bad; - owned_instance = PyEval_CallObject(type, args); - Py_DECREF(args); - if (!owned_instance) - goto bad; - value = owned_instance; - if (!PyExceptionInstance_Check(value)) { - PyErr_Format(PyExc_TypeError, - "calling %R should have returned an instance of " - "BaseException, not %R", - type, Py_TYPE(value)); - goto bad; - } - } else { - PyErr_SetString(PyExc_TypeError, - "raise: exception class must be a subclass of BaseException"); - goto bad; - } -#if PY_VERSION_HEX >= 0x03030000 - if (cause) { -#else - if (cause && cause != Py_None) { -#endif - PyObject *fixed_cause; - if (cause == Py_None) { - fixed_cause = NULL; - } else if (PyExceptionClass_Check(cause)) { - fixed_cause = PyObject_CallObject(cause, NULL); - if (fixed_cause == NULL) - goto bad; - } else if (PyExceptionInstance_Check(cause)) { - fixed_cause = cause; - Py_INCREF(fixed_cause); - } else { - PyErr_SetString(PyExc_TypeError, - "exception causes must derive from " - "BaseException"); - goto bad; - } - PyException_SetCause(value, fixed_cause); - } - PyErr_SetObject(type, value); - if (tb) { - PyThreadState *tstate = PyThreadState_GET(); - PyObject* tmp_tb = tstate->curexc_traceback; - if (tb != tmp_tb) { - Py_INCREF(tb); - tstate->curexc_traceback = tb; - Py_XDECREF(tmp_tb); - } - } -bad: - Py_XDECREF(owned_instance); - return; -} -#endif - -static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject* x) { - const unsigned char neg_one = (unsigned char)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(unsigned char) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(unsigned char)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to unsigned char" : - "value too large to convert to unsigned char"); - } - return (unsigned char)-1; - } - return (unsigned char)val; - } - return (unsigned char)__Pyx_PyInt_AsUnsignedLong(x); -} - -static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject* x) { - const unsigned short neg_one = (unsigned short)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(unsigned short) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(unsigned short)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to unsigned short" : - "value too large to convert to unsigned short"); - } - return (unsigned short)-1; - } - return (unsigned short)val; - } - return (unsigned short)__Pyx_PyInt_AsUnsignedLong(x); -} - -static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject* x) { - const unsigned int neg_one = (unsigned int)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(unsigned int) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(unsigned int)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to unsigned int" : - "value too large to convert to unsigned int"); - } - return (unsigned int)-1; - } - return (unsigned int)val; - } - return (unsigned int)__Pyx_PyInt_AsUnsignedLong(x); -} - -static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject* x) { - const char neg_one = (char)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(char) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(char)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to char" : - "value too large to convert to char"); - } - return (char)-1; - } - return (char)val; - } - return (char)__Pyx_PyInt_AsLong(x); -} - -static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject* x) { - const short neg_one = (short)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(short) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(short)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to short" : - "value too large to convert to short"); - } - return (short)-1; - } - return (short)val; - } - return (short)__Pyx_PyInt_AsLong(x); -} - -static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject* x) { - const int neg_one = (int)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(int) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(int)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to int" : - "value too large to convert to int"); - } - return (int)-1; - } - return (int)val; - } - return (int)__Pyx_PyInt_AsLong(x); -} - -static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject* x) { - const signed char neg_one = (signed char)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(signed char) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(signed char)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to signed char" : - "value too large to convert to signed char"); - } - return (signed char)-1; - } - return (signed char)val; - } - return (signed char)__Pyx_PyInt_AsSignedLong(x); -} - -static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject* x) { - const signed short neg_one = (signed short)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(signed short) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(signed short)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to signed short" : - "value too large to convert to signed short"); - } - return (signed short)-1; - } - return (signed short)val; - } - return (signed short)__Pyx_PyInt_AsSignedLong(x); -} - -static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject* x) { - const signed int neg_one = (signed int)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(signed int) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(signed int)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to signed int" : - "value too large to convert to signed int"); - } - return (signed int)-1; - } - return (signed int)val; - } - return (signed int)__Pyx_PyInt_AsSignedLong(x); -} - -static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject* x) { - const int neg_one = (int)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; - if (sizeof(int) < sizeof(long)) { - long val = __Pyx_PyInt_AsLong(x); - if (unlikely(val != (long)(int)val)) { - if (!unlikely(val == -1 && PyErr_Occurred())) { - PyErr_SetString(PyExc_OverflowError, - (is_unsigned && unlikely(val < 0)) ? - "can't convert negative value to int" : - "value too large to convert to int"); - } - return (int)-1; - } - return (int)val; - } - return (int)__Pyx_PyInt_AsLong(x); -} - -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS -#include "longintrepr.h" -#endif -#endif -static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject* x) { - const unsigned long neg_one = (unsigned long)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to unsigned long"); - return (unsigned long)-1; - } - return (unsigned long)val; - } else -#endif - if (likely(PyLong_Check(x))) { - if (is_unsigned) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(unsigned long)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return (unsigned long) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - if (unlikely(Py_SIZE(x) < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to unsigned long"); - return (unsigned long)-1; - } - return (unsigned long)PyLong_AsUnsignedLong(x); - } else { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(unsigned long)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return +(unsigned long) ((PyLongObject*)x)->ob_digit[0]; - case -1: return -(unsigned long) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - return (unsigned long)PyLong_AsLong(x); - } - } else { - unsigned long val; - PyObject *tmp = __Pyx_PyNumber_Int(x); - if (!tmp) return (unsigned long)-1; - val = __Pyx_PyInt_AsUnsignedLong(tmp); - Py_DECREF(tmp); - return val; - } -} - -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS -#include "longintrepr.h" -#endif -#endif -static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject* x) { - const unsigned PY_LONG_LONG neg_one = (unsigned PY_LONG_LONG)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to unsigned PY_LONG_LONG"); - return (unsigned PY_LONG_LONG)-1; - } - return (unsigned PY_LONG_LONG)val; - } else -#endif - if (likely(PyLong_Check(x))) { - if (is_unsigned) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(unsigned PY_LONG_LONG)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return (unsigned PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - if (unlikely(Py_SIZE(x) < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to unsigned PY_LONG_LONG"); - return (unsigned PY_LONG_LONG)-1; - } - return (unsigned PY_LONG_LONG)PyLong_AsUnsignedLongLong(x); - } else { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(unsigned PY_LONG_LONG)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return +(unsigned PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - case -1: return -(unsigned PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - return (unsigned PY_LONG_LONG)PyLong_AsLongLong(x); - } - } else { - unsigned PY_LONG_LONG val; - PyObject *tmp = __Pyx_PyNumber_Int(x); - if (!tmp) return (unsigned PY_LONG_LONG)-1; - val = __Pyx_PyInt_AsUnsignedLongLong(tmp); - Py_DECREF(tmp); - return val; - } -} - -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS -#include "longintrepr.h" -#endif -#endif -static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject* x) { - const long neg_one = (long)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to long"); - return (long)-1; - } - return (long)val; - } else -#endif - if (likely(PyLong_Check(x))) { - if (is_unsigned) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(long)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return (long) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - if (unlikely(Py_SIZE(x) < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to long"); - return (long)-1; - } - return (long)PyLong_AsUnsignedLong(x); - } else { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(long)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return +(long) ((PyLongObject*)x)->ob_digit[0]; - case -1: return -(long) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - return (long)PyLong_AsLong(x); - } - } else { - long val; - PyObject *tmp = __Pyx_PyNumber_Int(x); - if (!tmp) return (long)-1; - val = __Pyx_PyInt_AsLong(tmp); - Py_DECREF(tmp); - return val; - } -} - -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS -#include "longintrepr.h" -#endif -#endif -static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject* x) { - const PY_LONG_LONG neg_one = (PY_LONG_LONG)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to PY_LONG_LONG"); - return (PY_LONG_LONG)-1; - } - return (PY_LONG_LONG)val; - } else -#endif - if (likely(PyLong_Check(x))) { - if (is_unsigned) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(PY_LONG_LONG)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return (PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - if (unlikely(Py_SIZE(x) < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to PY_LONG_LONG"); - return (PY_LONG_LONG)-1; - } - return (PY_LONG_LONG)PyLong_AsUnsignedLongLong(x); - } else { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(PY_LONG_LONG)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return +(PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - case -1: return -(PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - return (PY_LONG_LONG)PyLong_AsLongLong(x); - } - } else { - PY_LONG_LONG val; - PyObject *tmp = __Pyx_PyNumber_Int(x); - if (!tmp) return (PY_LONG_LONG)-1; - val = __Pyx_PyInt_AsLongLong(tmp); - Py_DECREF(tmp); - return val; - } -} - -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS -#include "longintrepr.h" -#endif -#endif -static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject* x) { - const signed long neg_one = (signed long)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to signed long"); - return (signed long)-1; - } - return (signed long)val; - } else -#endif - if (likely(PyLong_Check(x))) { - if (is_unsigned) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(signed long)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return (signed long) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - if (unlikely(Py_SIZE(x) < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to signed long"); - return (signed long)-1; - } - return (signed long)PyLong_AsUnsignedLong(x); - } else { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(signed long)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return +(signed long) ((PyLongObject*)x)->ob_digit[0]; - case -1: return -(signed long) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - return (signed long)PyLong_AsLong(x); - } - } else { - signed long val; - PyObject *tmp = __Pyx_PyNumber_Int(x); - if (!tmp) return (signed long)-1; - val = __Pyx_PyInt_AsSignedLong(tmp); - Py_DECREF(tmp); - return val; - } -} - -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS -#include "longintrepr.h" -#endif -#endif -static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject* x) { - const signed PY_LONG_LONG neg_one = (signed PY_LONG_LONG)-1, const_zero = 0; - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to signed PY_LONG_LONG"); - return (signed PY_LONG_LONG)-1; - } - return (signed PY_LONG_LONG)val; - } else -#endif - if (likely(PyLong_Check(x))) { - if (is_unsigned) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(signed PY_LONG_LONG)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return (signed PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - if (unlikely(Py_SIZE(x) < 0)) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to signed PY_LONG_LONG"); - return (signed PY_LONG_LONG)-1; - } - return (signed PY_LONG_LONG)PyLong_AsUnsignedLongLong(x); - } else { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 -#if CYTHON_USE_PYLONG_INTERNALS - if (sizeof(digit) <= sizeof(signed PY_LONG_LONG)) { - switch (Py_SIZE(x)) { - case 0: return 0; - case 1: return +(signed PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - case -1: return -(signed PY_LONG_LONG) ((PyLongObject*)x)->ob_digit[0]; - } - } -#endif -#endif - return (signed PY_LONG_LONG)PyLong_AsLongLong(x); - } - } else { - signed PY_LONG_LONG val; - PyObject *tmp = __Pyx_PyNumber_Int(x); - if (!tmp) return (signed PY_LONG_LONG)-1; - val = __Pyx_PyInt_AsSignedLongLong(tmp); - Py_DECREF(tmp); - return val; - } -} - -static int __Pyx_check_binary_version(void) { - char ctversion[4], rtversion[4]; - PyOS_snprintf(ctversion, 4, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION); - PyOS_snprintf(rtversion, 4, "%s", Py_GetVersion()); - if (ctversion[0] != rtversion[0] || ctversion[2] != rtversion[2]) { - char message[200]; - PyOS_snprintf(message, sizeof(message), - "compiletime version %s of module '%.100s' " - "does not match runtime version %s", - ctversion, __Pyx_MODULE_NAME, rtversion); - #if PY_VERSION_HEX < 0x02050000 - return PyErr_Warn(NULL, message); - #else - return PyErr_WarnEx(NULL, message, 1); - #endif - } - return 0; -} - -static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) { - int start = 0, mid = 0, end = count - 1; - if (end >= 0 && code_line > entries[end].code_line) { - return count; - } - while (start < end) { - mid = (start + end) / 2; - if (code_line < entries[mid].code_line) { - end = mid; - } else if (code_line > entries[mid].code_line) { - start = mid + 1; - } else { - return mid; - } - } - if (code_line <= entries[mid].code_line) { - return mid; - } else { - return mid + 1; - } -} -static PyCodeObject *__pyx_find_code_object(int code_line) { - PyCodeObject* code_object; - int pos; - if (unlikely(!code_line) || unlikely(!__pyx_code_cache.entries)) { - return NULL; - } - pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); - if (unlikely(pos >= __pyx_code_cache.count) || unlikely(__pyx_code_cache.entries[pos].code_line != code_line)) { - return NULL; - } - code_object = __pyx_code_cache.entries[pos].code_object; - Py_INCREF(code_object); - return code_object; -} -static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) { - int pos, i; - __Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries; - if (unlikely(!code_line)) { - return; - } - if (unlikely(!entries)) { - entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry)); - if (likely(entries)) { - __pyx_code_cache.entries = entries; - __pyx_code_cache.max_count = 64; - __pyx_code_cache.count = 1; - entries[0].code_line = code_line; - entries[0].code_object = code_object; - Py_INCREF(code_object); - } - return; - } - pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); - if ((pos < __pyx_code_cache.count) && unlikely(__pyx_code_cache.entries[pos].code_line == code_line)) { - PyCodeObject* tmp = entries[pos].code_object; - entries[pos].code_object = code_object; - Py_DECREF(tmp); - return; - } - if (__pyx_code_cache.count == __pyx_code_cache.max_count) { - int new_max = __pyx_code_cache.max_count + 64; - entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc( - __pyx_code_cache.entries, new_max*sizeof(__Pyx_CodeObjectCacheEntry)); - if (unlikely(!entries)) { - return; - } - __pyx_code_cache.entries = entries; - __pyx_code_cache.max_count = new_max; - } - for (i=__pyx_code_cache.count; i>pos; i--) { - entries[i] = entries[i-1]; - } - entries[pos].code_line = code_line; - entries[pos].code_object = code_object; - __pyx_code_cache.count++; - Py_INCREF(code_object); -} - -#include "compile.h" -#include "frameobject.h" -#include "traceback.h" -static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( - const char *funcname, int c_line, - int py_line, const char *filename) { - PyCodeObject *py_code = 0; - PyObject *py_srcfile = 0; - PyObject *py_funcname = 0; - #if PY_MAJOR_VERSION < 3 - py_srcfile = PyString_FromString(filename); - #else - py_srcfile = PyUnicode_FromString(filename); - #endif - if (!py_srcfile) goto bad; - if (c_line) { - #if PY_MAJOR_VERSION < 3 - py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); - #else - py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); - #endif - } - else { - #if PY_MAJOR_VERSION < 3 - py_funcname = PyString_FromString(funcname); - #else - py_funcname = PyUnicode_FromString(funcname); - #endif - } - if (!py_funcname) goto bad; - py_code = __Pyx_PyCode_New( - 0, /*int argcount,*/ - 0, /*int kwonlyargcount,*/ - 0, /*int nlocals,*/ - 0, /*int stacksize,*/ - 0, /*int flags,*/ - __pyx_empty_bytes, /*PyObject *code,*/ - __pyx_empty_tuple, /*PyObject *consts,*/ - __pyx_empty_tuple, /*PyObject *names,*/ - __pyx_empty_tuple, /*PyObject *varnames,*/ - __pyx_empty_tuple, /*PyObject *freevars,*/ - __pyx_empty_tuple, /*PyObject *cellvars,*/ - py_srcfile, /*PyObject *filename,*/ - py_funcname, /*PyObject *name,*/ - py_line, /*int firstlineno,*/ - __pyx_empty_bytes /*PyObject *lnotab*/ - ); - Py_DECREF(py_srcfile); - Py_DECREF(py_funcname); - return py_code; -bad: - Py_XDECREF(py_srcfile); - Py_XDECREF(py_funcname); - return NULL; -} -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename) { - PyCodeObject *py_code = 0; - PyObject *py_globals = 0; - PyFrameObject *py_frame = 0; - py_code = __pyx_find_code_object(c_line ? c_line : py_line); - if (!py_code) { - py_code = __Pyx_CreateCodeObjectForTraceback( - funcname, c_line, py_line, filename); - if (!py_code) goto bad; - __pyx_insert_code_object(c_line ? c_line : py_line, py_code); - } - py_globals = PyModule_GetDict(__pyx_m); - if (!py_globals) goto bad; - py_frame = PyFrame_New( - PyThreadState_GET(), /*PyThreadState *tstate,*/ - py_code, /*PyCodeObject *code,*/ - py_globals, /*PyObject *globals,*/ - 0 /*PyObject *locals*/ - ); - if (!py_frame) goto bad; - py_frame->f_lineno = py_line; - PyTraceBack_Here(py_frame); -bad: - Py_XDECREF(py_code); - Py_XDECREF(py_frame); -} - -static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { - while (t->p) { - #if PY_MAJOR_VERSION < 3 - if (t->is_unicode) { - *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL); - } else if (t->intern) { - *t->p = PyString_InternFromString(t->s); - } else { - *t->p = PyString_FromStringAndSize(t->s, t->n - 1); - } - #else /* Python 3+ has unicode identifiers */ - if (t->is_unicode | t->is_str) { - if (t->intern) { - *t->p = PyUnicode_InternFromString(t->s); - } else if (t->encoding) { - *t->p = PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL); - } else { - *t->p = PyUnicode_FromStringAndSize(t->s, t->n - 1); - } - } else { - *t->p = PyBytes_FromStringAndSize(t->s, t->n - 1); - } - #endif - if (!*t->p) - return -1; - ++t; - } - return 0; -} - -static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(char* c_str) { - return __Pyx_PyUnicode_FromStringAndSize(c_str, strlen(c_str)); -} -static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) { - Py_ssize_t ignore; - return __Pyx_PyObject_AsStringAndSize(o, &ignore); -} -static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) { -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT - if ( -#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - __Pyx_sys_getdefaultencoding_not_ascii && -#endif - PyUnicode_Check(o)) { -#if PY_VERSION_HEX < 0x03030000 - char* defenc_c; - PyObject* defenc = _PyUnicode_AsDefaultEncodedString(o, NULL); - if (!defenc) return NULL; - defenc_c = PyBytes_AS_STRING(defenc); -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - { - char* end = defenc_c + PyBytes_GET_SIZE(defenc); - char* c; - for (c = defenc_c; c < end; c++) { - if ((unsigned char) (*c) >= 128) { - PyUnicode_AsASCIIString(o); - return NULL; - } - } - } -#endif /*__PYX_DEFAULT_STRING_ENCODING_IS_ASCII*/ - *length = PyBytes_GET_SIZE(defenc); - return defenc_c; -#else /* PY_VERSION_HEX < 0x03030000 */ - if (PyUnicode_READY(o) == -1) return NULL; -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - if (PyUnicode_IS_ASCII(o)) { - *length = PyUnicode_GET_DATA_SIZE(o); - return PyUnicode_AsUTF8(o); - } else { - PyUnicode_AsASCIIString(o); - return NULL; - } -#else /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII */ - return PyUnicode_AsUTF8AndSize(o, length); -#endif /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII */ -#endif /* PY_VERSION_HEX < 0x03030000 */ - } else -#endif /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT */ - { - char* result; - int r = PyBytes_AsStringAndSize(o, &result, length); - if (r < 0) { - return NULL; - } else { - return result; - } - } -} -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { - int is_true = x == Py_True; - if (is_true | (x == Py_False) | (x == Py_None)) return is_true; - else return PyObject_IsTrue(x); -} -static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) { - PyNumberMethods *m; - const char *name = NULL; - PyObject *res = NULL; -#if PY_MAJOR_VERSION < 3 - if (PyInt_Check(x) || PyLong_Check(x)) -#else - if (PyLong_Check(x)) -#endif - return Py_INCREF(x), x; - m = Py_TYPE(x)->tp_as_number; -#if PY_MAJOR_VERSION < 3 - if (m && m->nb_int) { - name = "int"; - res = PyNumber_Int(x); - } - else if (m && m->nb_long) { - name = "long"; - res = PyNumber_Long(x); - } -#else - if (m && m->nb_int) { - name = "int"; - res = PyNumber_Long(x); - } -#endif - if (res) { -#if PY_MAJOR_VERSION < 3 - if (!PyInt_Check(res) && !PyLong_Check(res)) { -#else - if (!PyLong_Check(res)) { -#endif - PyErr_Format(PyExc_TypeError, - "__%s__ returned non-%s (type %.200s)", - name, name, Py_TYPE(res)->tp_name); - Py_DECREF(res); - return NULL; - } - } - else if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, - "an integer is required"); - } - return res; -} -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { - Py_ssize_t ival; - PyObject* x = PyNumber_Index(b); - if (!x) return -1; - ival = PyInt_AsSsize_t(x); - Py_DECREF(x); - return ival; -} -static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) { -#if PY_VERSION_HEX < 0x02050000 - if (ival <= LONG_MAX) - return PyInt_FromLong((long)ival); - else { - unsigned char *bytes = (unsigned char *) &ival; - int one = 1; int little = (int)*(unsigned char*)&one; - return _PyLong_FromByteArray(bytes, sizeof(size_t), little, 0); - } -#else - return PyInt_FromSize_t(ival); -#endif -} -static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject* x) { - unsigned PY_LONG_LONG val = __Pyx_PyInt_AsUnsignedLongLong(x); - if (unlikely(val != (unsigned PY_LONG_LONG)(size_t)val)) { - if ((val != (unsigned PY_LONG_LONG)-1) || !PyErr_Occurred()) - PyErr_SetString(PyExc_OverflowError, - "value too large to convert to size_t"); - return (size_t)-1; - } - return (size_t)val; -} - - -#endif /* Py_PYTHON_H */ diff --git a/src/misaka.pyx b/src/misaka.pyx deleted file mode 100644 index 5a65925..0000000 --- a/src/misaka.pyx +++ /dev/null @@ -1,247 +0,0 @@ -cimport sundown -cimport wrapper - -from libc.stdint cimport uint8_t - - -__version__ = '1.0.3' - - -# Markdown extensions -EXT_NO_INTRA_EMPHASIS = (1 << 0) -EXT_TABLES = (1 << 1) -EXT_FENCED_CODE = (1 << 2) -EXT_AUTOLINK = (1 << 3) -EXT_STRIKETHROUGH = (1 << 4) -EXT_SPACE_HEADERS = (1 << 6) -EXT_SUPERSCRIPT = (1 << 7) -EXT_LAX_SPACING = (1 << 8) - -# HTML Render flags -HTML_SKIP_HTML = (1 << 0) -HTML_SKIP_STYLE = (1 << 1) -HTML_SKIP_IMAGES = (1 << 2) -HTML_SKIP_LINKS = (1 << 3) -HTML_EXPAND_TABS = (1 << 4) -HTML_SAFELINK = (1 << 5) -HTML_TOC = (1 << 6) -HTML_HARD_WRAP = (1 << 7) -HTML_USE_XHTML = (1 << 8) -HTML_ESCAPE = (1 << 9) - -# Extra HTML render flags - these are not from Sundown -HTML_SMARTYPANTS = (1 << 10) # An extra flag to enable Smartypants -HTML_TOC_TREE = (1 << 11) # Only render a table of contents tree - -# Other flags -TABLE_ALIGN_L = 1 # MKD_TABLE_ALIGN_L -TABLE_ALIGN_R = 2 # MKD_TABLE_ALIGN_R -TABLE_ALIGN_C = 3 # MKD_TABLE_ALIGN_CENTER -TABLE_ALIGNMASK = 3 # MKD_TABLE_ALIGNMASK -TABLE_HEADER = 4 # MKD_TABLE_HEADER - - -def html(object text, unsigned int extensions=0, unsigned int render_flags=0): - """Convert markdown text to (X)HTML. - - Returns a unicode string. - - :param text: A byte or unicode string. - :param extensions: Enable additional Markdown extensions with the ``EXT_*`` constants. - :param render_flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants. - """ - - if render_flags & HTML_TOC_TREE: - renderer = HtmlTocRenderer(render_flags) - else: - renderer = HtmlRenderer(render_flags) - - markdown = Markdown(renderer, extensions) - result = markdown.render(text) - - if render_flags & HTML_SMARTYPANTS: - result = SmartyPants().postprocess(result) - - return result - - -cdef class SmartyPants: - """Smartypants is a post-processor for (X)HTML renderers and can be used - standalone or as a mixin. It adds a methode named ``postprocess`` to the - renderer. - - ================================== ======== - Source Result - ================================== ======== - `'s` (s, t, m, d, re, ll, ve) [1]_ ’s - `--` — - `-` – - `...` … - `. . .` … - `(c)` © - `(r)` ® - `(tm)` ™ - `3/4` ¾ - `1/2` ½ - `1/4` ¼ - ================================== ======== - - .. [1] A ``'`` followed by a ``s``, ``t``, ``m``, ``d``, ``re``, ``ll`` or - ``ve`` will be turned into ``’s``, ``’t``, and so on. - """ - def postprocess(self, object text): - """Process the input text. - - Returns a unicode string. - - :param text: A byte or unicode string. - """ - # Convert string - cdef bytes py_string - if hasattr(text, 'encode'): - py_string = text.encode('UTF-8', 'strict') - else: - py_string = text - cdef char *c_string = py_string - - cdef sundown.buf *ob = sundown.bufnew(128) - sundown.sdhtml_smartypants(ob, - c_string, len(c_string)) - - try: - return ( ob.data)[:ob.size].decode('UTF-8', 'strict') - finally: - sundown.bufrelease(ob) - - -cdef class BaseRenderer: - """The ``BaseRenderer`` is boilerplate code for creating your own renderers by - sublassing `BaseRenderer`. It takes care of setting the callbacks and flags. - - :param flags: Available as a read-only, integer type attribute named ``self.flags``. - """ - - cdef sundown.sd_callbacks callbacks - cdef wrapper.renderopt options - - #: Read-only render flags - cdef readonly int flags - - def __init__(self, int flags=0): - self.options.self = self - self.flags = flags - self.setup() - - # Set callbacks - cdef void **source = &wrapper.callback_funcs - cdef void **dest = &self.callbacks - - cdef unicode method_name - for i from 0 <= i < wrapper.method_count by 1: - # In Python 3 ``wrapper.method_names[i]`` is a byte string. - # This means hasattr can't find any method in the renderer, so - # ``wrapper.method_names[i]`` is converted to a normal string first. - method_name = wrapper.method_names[i].decode('utf-8') - if hasattr(self, method_name): - dest[i] = source[i] - - def setup(self): - """A method that can be overridden by the renderer that sublasses ``BaseRenderer``. - It's called everytime an instance of a renderer is created. - """ - pass - - -cdef class HtmlRenderer(BaseRenderer): - """The HTML renderer that's included in Sundown. - - Do you override the ``setup`` method when subclassing ``HtmlRenderer``. If - you do make sure to call parent class' ``setup`` method first. - - :param flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants. - """ - def setup(self): - self.options.html.flags = self.flags - sundown.sdhtml_renderer( - &self.callbacks, - &self.options.html, - self.options.html.flags) - - -cdef class HtmlTocRenderer(BaseRenderer): - """The HTML table of contents renderer that's included in Sundown. - - Do you override the ``setup`` method when subclassing ``HtmlTocRenderer``. - If you do make sure to call parent class' ``setup`` method first. - - :param flags: Adjust HTML rendering behaviour with the ``HTML_*`` constants. - """ - def setup(self, int flags=0): - sundown.sdhtml_toc_renderer( - &self.callbacks, - &self.options.html) - - -cdef class Markdown: - """The Markdown parser. - - :param renderer: An instance of ``BaseRenderer``. - :param extensions: Enable additional Markdown extensions with the ``EXT_*`` constants. - """ - - cdef sundown.sd_markdown *markdown - cdef BaseRenderer renderer - - def __cinit__(self, object renderer, int extensions=0): - if not isinstance(renderer, BaseRenderer): - raise ValueError('expected instance of BaseRenderer, %s found' % \ - renderer.__class__.__name__) - - self.renderer = renderer - self.markdown = sundown.sd_markdown_new( - extensions, 16, - &self.renderer.callbacks, - &self.renderer.options) - - def render(self, object text): - """Render the Markdon text. - - Returns a unicode string. - - :param text: A byte or unicode string. - """ - if hasattr(self.renderer, 'preprocess'): - text = self.renderer.preprocess(text) - - # Convert string - cdef bytes py_string - if hasattr(text, 'encode'): - py_string = text.encode('UTF-8', 'strict') - else: - py_string = text # If it's a byte string it's assumed it's UTF-8 - cdef char *c_string = py_string - - # Buffers - cdef sundown.buf *ib = sundown.bufnew(128) - sundown.bufputs(ib, c_string) - - cdef sundown.buf *ob = sundown.bufnew(128) - sundown.bufgrow(ob, (ib.size * 1.4)) - - # Parse! And make a unicode string - sundown.sd_markdown_render(ob, ib.data, ib.size, self.markdown) - text = ( ob.data)[:ob.size].decode('UTF-8', 'strict') - - if hasattr(self.renderer, 'postprocess'): - text = self.renderer.postprocess(text) - - # Return a string and release buffers - try: - return text - finally: - sundown.bufrelease(ob) - sundown.bufrelease(ib) - - def __dealloc__(self): - if self.markdown is not NULL: - sundown.sd_markdown_free(self.markdown) diff --git a/src/sundown.pxd b/src/sundown.pxd deleted file mode 100644 index 02a30d9..0000000 --- a/src/sundown.pxd +++ /dev/null @@ -1,97 +0,0 @@ -from libc.stdint cimport uint8_t - - -cdef extern from 'sundown/buffer.h': - struct buf: - uint8_t *data - size_t size - size_t asize - size_t unit - - buf* bufnew(size_t) - int bufgrow(buf *, size_t) - void bufcstr(buf *) - void bufrelease(buf *) - void bufputs(buf *, char *) - - -cdef extern from 'sundown/html.h': - struct _toc_data_st: - int header_count - int current_level - - struct html_renderopt: - _toc_data_st toc_data - unsigned int flags - void (*link_attributes)(buf *ob, buf *url, void *self) - - void sdhtml_renderer( - sd_callbacks *callbacks, - html_renderopt *options_ptr, - unsigned int render_flags) - void sdhtml_toc_renderer( - sd_callbacks *callbacks, - html_renderopt *options_ptr) - void sdhtml_smartypants( - buf *ob, - uint8_t *text, - size_t size) - - -cdef extern from 'sundown/markdown.h': - enum mkd_autolink: - pass - - struct sd_callbacks: - # Block level callbacks - NULL skips the block - void (*blockcode)(buf *ob, buf *text, buf *lang, void *opaque) - void (*blockquote)(buf *ob, buf *text, void *opaque) - void (*blockhtml)(buf *ob, buf *text, void *opaque) - void (*header)(buf *ob, buf *text, int level, void *opaque) - void (*hrule)(buf *ob, void *opaque) - void (*list)(buf *ob, buf *text, int flags, void *opaque) - void (*listitem)(buf *ob, buf *text, int flags, void *opaque) - void (*paragraph)(buf *ob, buf *text, void *opaque) - void (*table)(buf *ob, buf *header, buf *body, void *opaque) - void (*table_row)(buf *ob, buf *text, void *opaque) - void (*table_cell)(buf *ob, buf *text, int flags, void *opaque) - - # Span level callbacks - NULL or return 0 prints the span verbatim - int (*autolink)(buf *ob, buf *link, mkd_autolink type, void *opaque) - int (*codespan)(buf *ob, buf *text, void *opaque) - int (*double_emphasis)(buf *ob, buf *text, void *opaque) - int (*emphasis)(buf *ob, buf *text, void *opaque) - int (*image)(buf *ob, buf *link, buf *title, buf *alt, void *opaque) - int (*linebreak)(buf *ob, void *opaque) - int (*link)(buf *ob, buf *link, buf *title, buf *content, void *opaque) - int (*raw_html_tag)(buf *ob, buf *tag, void *opaque) - int (*triple_emphasis)(buf *ob, buf *text, void *opaque) - int (*strikethrough)(buf *ob, buf *text, void *opaque) - int (*superscript)(buf *ob, buf *text, void *opaque) - - # Low level callbacks - NULL copies input directly into the output - void (*entity)(buf *ob, buf *entity, void *opaque) - void (*normal_text)(buf *ob, buf *text, void *opaque) - - # Header and footer - void (*doc_header)(buf *ob, void *opaque) - void (*doc_footer)(buf *ob, void *opaque) - - enum mkd_autolink: - pass - - struct sd_markdown: - pass - - sd_markdown *sd_markdown_new( - unsigned int extensions, - size_t max_nesting, - sd_callbacks *callbacks, - html_renderopt *opaque) - void sd_markdown_render( - buf *ob, - uint8_t *document, - size_t doc_size, - sd_markdown *md) - void sd_markdown_free(sd_markdown *md) - void sd_version(int *major, int *minor, int *revision) diff --git a/src/sundown/autolink.h b/src/sundown/autolink.h deleted file mode 100644 index 65e0fe6..0000000 --- a/src/sundown/autolink.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2011, Vicent Marti - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UPSKIRT_AUTOLINK_H -#define UPSKIRT_AUTOLINK_H - -#include "buffer.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - SD_AUTOLINK_SHORT_DOMAINS = (1 << 0), -}; - -int -sd_autolink_issafe(const uint8_t *link, size_t link_len); - -size_t -sd_autolink__www(size_t *rewind_p, struct buf *link, - uint8_t *data, size_t offset, size_t size, unsigned int flags); - -size_t -sd_autolink__email(size_t *rewind_p, struct buf *link, - uint8_t *data, size_t offset, size_t size, unsigned int flags); - -size_t -sd_autolink__url(size_t *rewind_p, struct buf *link, - uint8_t *data, size_t offset, size_t size, unsigned int flags); - -#ifdef __cplusplus -} -#endif - -#endif - -/* vim: set filetype=c: */ diff --git a/src/sundown/buffer.c b/src/sundown/buffer.c deleted file mode 100644 index 47b40ce..0000000 --- a/src/sundown/buffer.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2008, Natacha Porté - * Copyright (c) 2011, Vicent Martí - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb - -#include "buffer.h" - -#include -#include -#include -#include - -/* MSVC compat */ -#if defined(_MSC_VER) -# define _buf_vsnprintf _vsnprintf -#else -# define _buf_vsnprintf vsnprintf -#endif - -int -bufprefix(const struct buf *buf, const char *prefix) -{ - size_t i; - assert(buf && buf->unit); - - for (i = 0; i < buf->size; ++i) { - if (prefix[i] == 0) - return 0; - - if (buf->data[i] != prefix[i]) - return buf->data[i] - prefix[i]; - } - - return 0; -} - -/* bufgrow: increasing the allocated size to the given value */ -int -bufgrow(struct buf *buf, size_t neosz) -{ - size_t neoasz; - void *neodata; - - assert(buf && buf->unit); - - if (neosz > BUFFER_MAX_ALLOC_SIZE) - return BUF_ENOMEM; - - if (buf->asize >= neosz) - return BUF_OK; - - neoasz = buf->asize + buf->unit; - while (neoasz < neosz) - neoasz += buf->unit; - - neodata = realloc(buf->data, neoasz); - if (!neodata) - return BUF_ENOMEM; - - buf->data = neodata; - buf->asize = neoasz; - return BUF_OK; -} - - -/* bufnew: allocation of a new buffer */ -struct buf * -bufnew(size_t unit) -{ - struct buf *ret; - ret = malloc(sizeof (struct buf)); - - if (ret) { - ret->data = 0; - ret->size = ret->asize = 0; - ret->unit = unit; - } - return ret; -} - -/* bufnullterm: NULL-termination of the string array */ -const char * -bufcstr(struct buf *buf) -{ - assert(buf && buf->unit); - - if (buf->size < buf->asize && buf->data[buf->size] == 0) - return (char *)buf->data; - - if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) { - buf->data[buf->size] = 0; - return (char *)buf->data; - } - - return NULL; -} - -/* bufprintf: formatted printing to a buffer */ -void -bufprintf(struct buf *buf, const char *fmt, ...) -{ - va_list ap; - int n; - - assert(buf && buf->unit); - - if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < 0) - return; - - va_start(ap, fmt); - n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); - va_end(ap); - - if (n < 0) { -#ifdef _MSC_VER - va_start(ap, fmt); - n = _vscprintf(fmt, ap); - va_end(ap); -#else - return; -#endif - } - - if ((size_t)n >= buf->asize - buf->size) { - if (bufgrow(buf, buf->size + n + 1) < 0) - return; - - va_start(ap, fmt); - n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); - va_end(ap); - } - - if (n < 0) - return; - - buf->size += n; -} - -/* bufput: appends raw data to a buffer */ -void -bufput(struct buf *buf, const void *data, size_t len) -{ - assert(buf && buf->unit); - - if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0) - return; - - memcpy(buf->data + buf->size, data, len); - buf->size += len; -} - -/* bufputs: appends a NUL-terminated string to a buffer */ -void -bufputs(struct buf *buf, const char *str) -{ - bufput(buf, str, strlen(str)); -} - - -/* bufputc: appends a single uint8_t to a buffer */ -void -bufputc(struct buf *buf, int c) -{ - assert(buf && buf->unit); - - if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0) - return; - - buf->data[buf->size] = c; - buf->size += 1; -} - -/* bufrelease: decrease the reference count and free the buffer if needed */ -void -bufrelease(struct buf *buf) -{ - if (!buf) - return; - - free(buf->data); - free(buf); -} - - -/* bufreset: frees internal data of the buffer */ -void -bufreset(struct buf *buf) -{ - if (!buf) - return; - - free(buf->data); - buf->data = NULL; - buf->size = buf->asize = 0; -} - -/* bufslurp: removes a given number of bytes from the head of the array */ -void -bufslurp(struct buf *buf, size_t len) -{ - assert(buf && buf->unit); - - if (len >= buf->size) { - buf->size = 0; - return; - } - - buf->size -= len; - memmove(buf->data, buf->data + len, buf->size); -} - diff --git a/src/sundown/buffer.h b/src/sundown/buffer.h deleted file mode 100644 index 221d142..0000000 --- a/src/sundown/buffer.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2008, Natacha Porté - * Copyright (c) 2011, Vicent Martí - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef BUFFER_H__ -#define BUFFER_H__ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(_MSC_VER) -#define __attribute__(x) -#define inline -#endif - -typedef enum { - BUF_OK = 0, - BUF_ENOMEM = -1, -} buferror_t; - -/* struct buf: character array buffer */ -struct buf { - uint8_t *data; /* actual character data */ - size_t size; /* size of the string */ - size_t asize; /* allocated size (0 = volatile buffer) */ - size_t unit; /* reallocation unit size (0 = read-only buffer) */ -}; - -/* CONST_BUF: global buffer from a string litteral */ -#define BUF_STATIC(string) \ - { (uint8_t *)string, sizeof string -1, sizeof string, 0, 0 } - -/* VOLATILE_BUF: macro for creating a volatile buffer on the stack */ -#define BUF_VOLATILE(strname) \ - { (uint8_t *)strname, strlen(strname), 0, 0, 0 } - -/* BUFPUTSL: optimized bufputs of a string litteral */ -#define BUFPUTSL(output, literal) \ - bufput(output, literal, sizeof literal - 1) - -/* bufgrow: increasing the allocated size to the given value */ -int bufgrow(struct buf *, size_t); - -/* bufnew: allocation of a new buffer */ -struct buf *bufnew(size_t) __attribute__ ((malloc)); - -/* bufnullterm: NUL-termination of the string array (making a C-string) */ -const char *bufcstr(struct buf *); - -/* bufprefix: compare the beginning of a buffer with a string */ -int bufprefix(const struct buf *buf, const char *prefix); - -/* bufput: appends raw data to a buffer */ -void bufput(struct buf *, const void *, size_t); - -/* bufputs: appends a NUL-terminated string to a buffer */ -void bufputs(struct buf *, const char *); - -/* bufputc: appends a single char to a buffer */ -void bufputc(struct buf *, int); - -/* bufrelease: decrease the reference count and free the buffer if needed */ -void bufrelease(struct buf *); - -/* bufreset: frees internal data of the buffer */ -void bufreset(struct buf *); - -/* bufslurp: removes a given number of bytes from the head of the array */ -void bufslurp(struct buf *, size_t); - -/* bufprintf: formatted printing to a buffer */ -void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3))); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/sundown/houdini.h b/src/sundown/houdini.h deleted file mode 100644 index b4954c0..0000000 --- a/src/sundown/houdini.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef HOUDINI_H__ -#define HOUDINI_H__ - -#include "buffer.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef HOUDINI_USE_LOCALE -# define _isxdigit(c) isxdigit(c) -# define _isdigit(c) isdigit(c) -#else -/* - * Helper _isdigit methods -- do not trust the current locale - * */ -# define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL) -# define _isdigit(c) ((c) >= '0' && (c) <= '9') -#endif - -extern void houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure); -extern void houdini_unescape_html(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_escape_xml(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_escape_uri(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_escape_url(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_unescape_uri(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_unescape_url(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_escape_js(struct buf *ob, const uint8_t *src, size_t size); -extern void houdini_unescape_js(struct buf *ob, const uint8_t *src, size_t size); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/sundown/houdini_href_e.c b/src/sundown/houdini_href_e.c deleted file mode 100644 index 981b3b1..0000000 --- a/src/sundown/houdini_href_e.c +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include -#include - -#include "houdini.h" - -#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) - -/* - * The following characters will not be escaped: - * - * -_.+!*'(),%#@?=;:/,+&$ alphanum - * - * Note that this character set is the addition of: - * - * - The characters which are safe to be in an URL - * - The characters which are *not* safe to be in - * an URL because they are RESERVED characters. - * - * We asume (lazily) that any RESERVED char that - * appears inside an URL is actually meant to - * have its native function (i.e. as an URL - * component/separator) and hence needs no escaping. - * - * There are two exceptions: the chacters & (amp) - * and ' (single quote) do not appear in the table. - * They are meant to appear in the URL as components, - * yet they require special HTML-entity escaping - * to generate valid HTML markup. - * - * All other characters will be escaped to %XX. - * - */ -static const char HREF_SAFE[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -void -houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size) -{ - static const char hex_chars[] = "0123456789ABCDEF"; - size_t i = 0, org; - char hex_str[3]; - - bufgrow(ob, ESCAPE_GROW_FACTOR(size)); - hex_str[0] = '%'; - - while (i < size) { - org = i; - while (i < size && HREF_SAFE[src[i]] != 0) - i++; - - if (i > org) - bufput(ob, src + org, i - org); - - /* escaping */ - if (i >= size) - break; - - switch (src[i]) { - /* amp appears all the time in URLs, but needs - * HTML-entity escaping to be inside an href */ - case '&': - BUFPUTSL(ob, "&"); - break; - - /* the single quote is a valid URL character - * according to the standard; it needs HTML - * entity escaping too */ - case '\'': - BUFPUTSL(ob, "'"); - break; - - /* the space can be escaped to %20 or a plus - * sign. we're going with the generic escape - * for now. the plus thing is more commonly seen - * when building GET strings */ -#if 0 - case ' ': - bufputc(ob, '+'); - break; -#endif - - /* every other character goes with a %XX escaping */ - default: - hex_str[1] = hex_chars[(src[i] >> 4) & 0xF]; - hex_str[2] = hex_chars[src[i] & 0xF]; - bufput(ob, hex_str, 3); - } - - i++; - } -} diff --git a/src/sundown/houdini_html_e.c b/src/sundown/houdini_html_e.c deleted file mode 100644 index d9bbf18..0000000 --- a/src/sundown/houdini_html_e.c +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include - -#include "houdini.h" - -#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) /* this is very scientific, yes */ - -/** - * According to the OWASP rules: - * - * & --> & - * < --> < - * > --> > - * " --> " - * ' --> ' ' is not recommended - * / --> / forward slash is included as it helps end an HTML entity - * - */ -static const char HTML_ESCAPE_TABLE[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static const char *HTML_ESCAPES[] = { - "", - """, - "&", - "'", - "/", - "<", - ">" -}; - -void -houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure) -{ - size_t i = 0, org, esc = 0; - - bufgrow(ob, ESCAPE_GROW_FACTOR(size)); - - while (i < size) { - org = i; - while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0) - i++; - - if (i > org) - bufput(ob, src + org, i - org); - - /* escaping */ - if (i >= size) - break; - - /* The forward slash is only escaped in secure mode */ - if (src[i] == '/' && !secure) { - bufputc(ob, '/'); - } else { - bufputs(ob, HTML_ESCAPES[esc]); - } - - i++; - } -} - -void -houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size) -{ - houdini_escape_html0(ob, src, size, 1); -} - diff --git a/src/sundown/html.c b/src/sundown/html.c deleted file mode 100644 index 7f08ee8..0000000 --- a/src/sundown/html.c +++ /dev/null @@ -1,635 +0,0 @@ -/* - * Copyright (c) 2009, Natacha Porté - * Copyright (c) 2011, Vicent Marti - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "markdown.h" -#include "html.h" - -#include -#include -#include -#include - -#include "houdini.h" - -#define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML) - -int -sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname) -{ - size_t i; - int closed = 0; - - if (tag_size < 3 || tag_data[0] != '<') - return HTML_TAG_NONE; - - i = 1; - - if (tag_data[i] == '/') { - closed = 1; - i++; - } - - for (; i < tag_size; ++i, ++tagname) { - if (*tagname == 0) - break; - - if (tag_data[i] != *tagname) - return HTML_TAG_NONE; - } - - if (i == tag_size) - return HTML_TAG_NONE; - - if (isspace(tag_data[i]) || tag_data[i] == '>') - return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN; - - return HTML_TAG_NONE; -} - -static inline void escape_html(struct buf *ob, const uint8_t *source, size_t length) -{ - houdini_escape_html0(ob, source, length, 0); -} - -static inline void escape_href(struct buf *ob, const uint8_t *source, size_t length) -{ - houdini_escape_href(ob, source, length); -} - -/******************** - * GENERIC RENDERER * - ********************/ -static int -rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque) -{ - struct html_renderopt *options = opaque; - - if (!link || !link->size) - return 0; - - if ((options->flags & HTML_SAFELINK) != 0 && - !sd_autolink_issafe(link->data, link->size) && - type != MKDA_EMAIL) - return 0; - - BUFPUTSL(ob, "data, link->size); - - if (options->link_attributes) { - bufputc(ob, '\"'); - options->link_attributes(ob, link, opaque); - bufputc(ob, '>'); - } else { - BUFPUTSL(ob, "\">"); - } - - /* - * Pretty printing: if we get an email address as - * an actual URI, e.g. `mailto:foo@bar.com`, we don't - * want to print the `mailto:` prefix - */ - if (bufprefix(link, "mailto:") == 0) { - escape_html(ob, link->data + 7, link->size - 7); - } else { - escape_html(ob, link->data, link->size); - } - - BUFPUTSL(ob, ""); - - return 1; -} - -static void -rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque) -{ - if (ob->size) bufputc(ob, '\n'); - - if (lang && lang->size) { - size_t i, cls; - BUFPUTSL(ob, "
    size; ++i, ++cls) {
    -			while (i < lang->size && isspace(lang->data[i]))
    -				i++;
    -
    -			if (i < lang->size) {
    -				size_t org = i;
    -				while (i < lang->size && !isspace(lang->data[i]))
    -					i++;
    -
    -				if (lang->data[org] == '.')
    -					org++;
    -
    -				if (cls) bufputc(ob, ' ');
    -				escape_html(ob, lang->data + org, i - org);
    -			}
    -		}
    -
    -		BUFPUTSL(ob, "\">");
    -	} else
    -		BUFPUTSL(ob, "
    ");
    -
    -	if (text)
    -		escape_html(ob, text->data, text->size);
    -
    -	BUFPUTSL(ob, "
    \n"); -} - -static void -rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque) -{ - if (ob->size) bufputc(ob, '\n'); - BUFPUTSL(ob, "
    \n"); - if (text) bufput(ob, text->data, text->size); - BUFPUTSL(ob, "
    \n"); -} - -static int -rndr_codespan(struct buf *ob, const struct buf *text, void *opaque) -{ - BUFPUTSL(ob, ""); - if (text) escape_html(ob, text->data, text->size); - BUFPUTSL(ob, ""); - return 1; -} - -static int -rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque) -{ - if (!text || !text->size) - return 0; - - BUFPUTSL(ob, ""); - bufput(ob, text->data, text->size); - BUFPUTSL(ob, ""); - return 1; -} - -static int -rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque) -{ - if (!text || !text->size) - return 0; - - BUFPUTSL(ob, ""); - bufput(ob, text->data, text->size); - BUFPUTSL(ob, ""); - - return 1; -} - -static int -rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque) -{ - if (!text || !text->size) return 0; - BUFPUTSL(ob, ""); - if (text) bufput(ob, text->data, text->size); - BUFPUTSL(ob, ""); - return 1; -} - -static int -rndr_linebreak(struct buf *ob, void *opaque) -{ - struct html_renderopt *options = opaque; - bufputs(ob, USE_XHTML(options) ? "
    \n" : "
    \n"); - return 1; -} - -static void -rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque) -{ - struct html_renderopt *options = opaque; - - if (ob->size) - bufputc(ob, '\n'); - - if (options->flags & HTML_TOC) - bufprintf(ob, "", level, options->toc_data.header_count++); - else - bufprintf(ob, "", level); - - if (text) bufput(ob, text->data, text->size); - bufprintf(ob, "\n", level); -} - -static int -rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque) -{ - struct html_renderopt *options = opaque; - - if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size)) - return 0; - - BUFPUTSL(ob, "size) - escape_href(ob, link->data, link->size); - - if (title && title->size) { - BUFPUTSL(ob, "\" title=\""); - escape_html(ob, title->data, title->size); - } - - if (options->link_attributes) { - bufputc(ob, '\"'); - options->link_attributes(ob, link, opaque); - bufputc(ob, '>'); - } else { - BUFPUTSL(ob, "\">"); - } - - if (content && content->size) bufput(ob, content->data, content->size); - BUFPUTSL(ob, ""); - return 1; -} - -static void -rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque) -{ - if (ob->size) bufputc(ob, '\n'); - bufput(ob, flags & MKD_LIST_ORDERED ? "
      \n" : "
        \n", 5); - if (text) bufput(ob, text->data, text->size); - bufput(ob, flags & MKD_LIST_ORDERED ? "
    \n" : "\n", 6); -} - -static void -rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque) -{ - BUFPUTSL(ob, "
  • "); - if (text) { - size_t size = text->size; - while (size && text->data[size - 1] == '\n') - size--; - - bufput(ob, text->data, size); - } - BUFPUTSL(ob, "
  • \n"); -} - -static void -rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque) -{ - struct html_renderopt *options = opaque; - size_t i = 0; - - if (ob->size) bufputc(ob, '\n'); - - if (!text || !text->size) - return; - - while (i < text->size && isspace(text->data[i])) i++; - - if (i == text->size) - return; - - BUFPUTSL(ob, "

    "); - if (options->flags & HTML_HARD_WRAP) { - size_t org; - while (i < text->size) { - org = i; - while (i < text->size && text->data[i] != '\n') - i++; - - if (i > org) - bufput(ob, text->data + org, i - org); - - /* - * do not insert a line break if this newline - * is the last character on the paragraph - */ - if (i >= text->size - 1) - break; - - rndr_linebreak(ob, opaque); - i++; - } - } else { - bufput(ob, &text->data[i], text->size - i); - } - BUFPUTSL(ob, "

    \n"); -} - -static void -rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque) -{ - size_t org, sz; - if (!text) return; - sz = text->size; - while (sz > 0 && text->data[sz - 1] == '\n') sz--; - org = 0; - while (org < sz && text->data[org] == '\n') org++; - if (org >= sz) return; - if (ob->size) bufputc(ob, '\n'); - bufput(ob, text->data + org, sz - org); - bufputc(ob, '\n'); -} - -static int -rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque) -{ - if (!text || !text->size) return 0; - BUFPUTSL(ob, ""); - bufput(ob, text->data, text->size); - BUFPUTSL(ob, ""); - return 1; -} - -static void -rndr_hrule(struct buf *ob, void *opaque) -{ - struct html_renderopt *options = opaque; - if (ob->size) bufputc(ob, '\n'); - bufputs(ob, USE_XHTML(options) ? "
    \n" : "
    \n"); -} - -static int -rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque) -{ - struct html_renderopt *options = opaque; - if (!link || !link->size) return 0; - - BUFPUTSL(ob, "data, link->size); - BUFPUTSL(ob, "\" alt=\""); - - if (alt && alt->size) - escape_html(ob, alt->data, alt->size); - - if (title && title->size) { - BUFPUTSL(ob, "\" title=\""); - escape_html(ob, title->data, title->size); } - - bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">"); - return 1; -} - -static int -rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque) -{ - struct html_renderopt *options = opaque; - - /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES - * It doens't see if there are any valid tags, just escape all of them. */ - if((options->flags & HTML_ESCAPE) != 0) { - escape_html(ob, text->data, text->size); - return 1; - } - - if ((options->flags & HTML_SKIP_HTML) != 0) - return 1; - - if ((options->flags & HTML_SKIP_STYLE) != 0 && - sdhtml_is_tag(text->data, text->size, "style")) - return 1; - - if ((options->flags & HTML_SKIP_LINKS) != 0 && - sdhtml_is_tag(text->data, text->size, "a")) - return 1; - - if ((options->flags & HTML_SKIP_IMAGES) != 0 && - sdhtml_is_tag(text->data, text->size, "img")) - return 1; - - bufput(ob, text->data, text->size); - return 1; -} - -static void -rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque) -{ - if (ob->size) bufputc(ob, '\n'); - BUFPUTSL(ob, "\n"); - if (header) - bufput(ob, header->data, header->size); - BUFPUTSL(ob, "\n"); - if (body) - bufput(ob, body->data, body->size); - BUFPUTSL(ob, "
    \n"); -} - -static void -rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque) -{ - BUFPUTSL(ob, "\n"); - if (text) - bufput(ob, text->data, text->size); - BUFPUTSL(ob, "\n"); -} - -static void -rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque) -{ - if (flags & MKD_TABLE_HEADER) { - BUFPUTSL(ob, ""); - break; - - case MKD_TABLE_ALIGN_L: - BUFPUTSL(ob, " align=\"left\">"); - break; - - case MKD_TABLE_ALIGN_R: - BUFPUTSL(ob, " align=\"right\">"); - break; - - default: - BUFPUTSL(ob, ">"); - } - - if (text) - bufput(ob, text->data, text->size); - - if (flags & MKD_TABLE_HEADER) { - BUFPUTSL(ob, "\n"); - } else { - BUFPUTSL(ob, "\n"); - } -} - -static int -rndr_superscript(struct buf *ob, const struct buf *text, void *opaque) -{ - if (!text || !text->size) return 0; - BUFPUTSL(ob, ""); - bufput(ob, text->data, text->size); - BUFPUTSL(ob, ""); - return 1; -} - -static void -rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque) -{ - if (text) - escape_html(ob, text->data, text->size); -} - -static void -toc_header(struct buf *ob, const struct buf *text, int level, void *opaque) -{ - struct html_renderopt *options = opaque; - - /* set the level offset if this is the first header - * we're parsing for the document */ - if (options->toc_data.current_level == 0) { - options->toc_data.level_offset = level - 1; - } - level -= options->toc_data.level_offset; - - if (level > options->toc_data.current_level) { - while (level > options->toc_data.current_level) { - BUFPUTSL(ob, "
      \n
    • \n"); - options->toc_data.current_level++; - } - } else if (level < options->toc_data.current_level) { - BUFPUTSL(ob, "
    • \n"); - while (level < options->toc_data.current_level) { - BUFPUTSL(ob, "
    \n\n"); - options->toc_data.current_level--; - } - BUFPUTSL(ob,"
  • \n"); - } else { - BUFPUTSL(ob,"
  • \n
  • \n"); - } - - bufprintf(ob, "", options->toc_data.header_count++); - if (text) - escape_html(ob, text->data, text->size); - BUFPUTSL(ob, "\n"); -} - -static int -toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque) -{ - if (content && content->size) - bufput(ob, content->data, content->size); - return 1; -} - -static void -toc_finalize(struct buf *ob, void *opaque) -{ - struct html_renderopt *options = opaque; - - while (options->toc_data.current_level > 0) { - BUFPUTSL(ob, "
  • \n\n"); - options->toc_data.current_level--; - } -} - -void -sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options) -{ - static const struct sd_callbacks cb_default = { - NULL, - NULL, - NULL, - toc_header, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - - NULL, - rndr_codespan, - rndr_double_emphasis, - rndr_emphasis, - NULL, - NULL, - toc_link, - NULL, - rndr_triple_emphasis, - rndr_strikethrough, - rndr_superscript, - - NULL, - NULL, - - NULL, - toc_finalize, - }; - - memset(options, 0x0, sizeof(struct html_renderopt)); - options->flags = HTML_TOC; - - memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks)); -} - -void -sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags) -{ - static const struct sd_callbacks cb_default = { - rndr_blockcode, - rndr_blockquote, - rndr_raw_block, - rndr_header, - rndr_hrule, - rndr_list, - rndr_listitem, - rndr_paragraph, - rndr_table, - rndr_tablerow, - rndr_tablecell, - - rndr_autolink, - rndr_codespan, - rndr_double_emphasis, - rndr_emphasis, - rndr_image, - rndr_linebreak, - rndr_link, - rndr_raw_html, - rndr_triple_emphasis, - rndr_strikethrough, - rndr_superscript, - - NULL, - rndr_normal_text, - - NULL, - NULL, - }; - - /* Prepare the options pointer */ - memset(options, 0x0, sizeof(struct html_renderopt)); - options->flags = render_flags; - - /* Prepare the callbacks */ - memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks)); - - if (render_flags & HTML_SKIP_IMAGES) - callbacks->image = NULL; - - if (render_flags & HTML_SKIP_LINKS) { - callbacks->link = NULL; - callbacks->autolink = NULL; - } - - if (render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE) - callbacks->blockhtml = NULL; -} diff --git a/src/sundown/html.h b/src/sundown/html.h deleted file mode 100644 index 4c8810d..0000000 --- a/src/sundown/html.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2011, Vicent Marti - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UPSKIRT_HTML_H -#define UPSKIRT_HTML_H - -#include "markdown.h" -#include "buffer.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct html_renderopt { - struct { - int header_count; - int current_level; - int level_offset; - } toc_data; - - unsigned int flags; - - /* extra callbacks */ - void (*link_attributes)(struct buf *ob, const struct buf *url, void *self); -}; - -typedef enum { - HTML_SKIP_HTML = (1 << 0), - HTML_SKIP_STYLE = (1 << 1), - HTML_SKIP_IMAGES = (1 << 2), - HTML_SKIP_LINKS = (1 << 3), - HTML_EXPAND_TABS = (1 << 4), - HTML_SAFELINK = (1 << 5), - HTML_TOC = (1 << 6), - HTML_HARD_WRAP = (1 << 7), - HTML_USE_XHTML = (1 << 8), - HTML_ESCAPE = (1 << 9), -} html_render_mode; - -typedef enum { - HTML_TAG_NONE = 0, - HTML_TAG_OPEN, - HTML_TAG_CLOSE, -} html_tag; - -int -sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname); - -extern void -sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags); - -extern void -sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr); - -extern void -sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/src/sundown/html_blocks.h b/src/sundown/html_blocks.h deleted file mode 100644 index 09a758f..0000000 --- a/src/sundown/html_blocks.h +++ /dev/null @@ -1,206 +0,0 @@ -/* C code produced by gperf version 3.0.3 */ -/* Command-line: gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case html_block_names.txt */ -/* Computed positions: -k'1-2' */ - -#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ - && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ - && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ - && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ - && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ - && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ - && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ - && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ - && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ - && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ - && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ - && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ - && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ - && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ - && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ - && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ - && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ - && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ - && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ - && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ - && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ - && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ - && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) -/* The character set is not based on ISO-646. */ -error "gperf generated tables don't work with this execution character set. Please report a bug to ." -#endif - -/* maximum key range = 37, duplicates = 0 */ - -#ifndef GPERF_DOWNCASE -#define GPERF_DOWNCASE 1 -static unsigned char gperf_downcase[256] = - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, - 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, - 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, - 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, - 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, - 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, - 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, - 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, - 255 - }; -#endif - -#ifndef GPERF_CASE_STRNCMP -#define GPERF_CASE_STRNCMP 1 -static int -gperf_case_strncmp (s1, s2, n) - register const char *s1; - register const char *s2; - register unsigned int n; -{ - for (; n > 0;) - { - unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; - unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; - if (c1 != 0 && c1 == c2) - { - n--; - continue; - } - return (int)c1 - (int)c2; - } - return 0; -} -#endif - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static unsigned int -hash_block_tag (str, len) - register const char *str; - register unsigned int len; -{ - static const unsigned char asso_values[] = - { - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 8, 30, 25, 20, 15, 10, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 0, 38, 0, 38, - 5, 5, 5, 15, 0, 38, 38, 0, 15, 10, - 0, 38, 38, 15, 0, 5, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, - 0, 38, 5, 5, 5, 15, 0, 38, 38, 0, - 15, 10, 0, 38, 38, 15, 0, 5, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38 - }; - register int hval = len; - - switch (hval) - { - default: - hval += asso_values[(unsigned char)str[1]+1]; - /*FALLTHROUGH*/ - case 1: - hval += asso_values[(unsigned char)str[0]]; - break; - } - return hval; -} - -#ifdef __GNUC__ -__inline -#ifdef __GNUC_STDC_INLINE__ -__attribute__ ((__gnu_inline__)) -#endif -#endif -const char * -find_block_tag (str, len) - register const char *str; - register unsigned int len; -{ - enum - { - TOTAL_KEYWORDS = 24, - MIN_WORD_LENGTH = 1, - MAX_WORD_LENGTH = 10, - MIN_HASH_VALUE = 1, - MAX_HASH_VALUE = 37 - }; - - static const char * const wordlist[] = - { - "", - "p", - "dl", - "div", - "math", - "table", - "", - "ul", - "del", - "form", - "blockquote", - "figure", - "ol", - "fieldset", - "", - "h1", - "", - "h6", - "pre", - "", "", - "script", - "h5", - "noscript", - "", - "style", - "iframe", - "h4", - "ins", - "", "", "", - "h3", - "", "", "", "", - "h2" - }; - - if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) - { - register int key = hash_block_tag (str, len); - - if (key <= MAX_HASH_VALUE && key >= 0) - { - register const char *s = wordlist[key]; - - if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strncmp (str, s, len) && s[len] == '\0') - return s; - } - } - return 0; -} diff --git a/src/sundown/html_smartypants.c b/src/sundown/html_smartypants.c deleted file mode 100644 index 367c26a..0000000 --- a/src/sundown/html_smartypants.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2011, Vicent Marti - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "buffer.h" -#include "html.h" - -#include -#include -#include -#include - -#if defined(_WIN32) -#define snprintf _snprintf -#endif - -struct smartypants_data { - int in_squote; - int in_dquote; -}; - -static size_t smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); -static size_t smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); - -static size_t (*smartypants_cb_ptrs[]) - (struct buf *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) = -{ - NULL, /* 0 */ - smartypants_cb__dash, /* 1 */ - smartypants_cb__parens, /* 2 */ - smartypants_cb__squote, /* 3 */ - smartypants_cb__dquote, /* 4 */ - smartypants_cb__amp, /* 5 */ - smartypants_cb__period, /* 6 */ - smartypants_cb__number, /* 7 */ - smartypants_cb__ltag, /* 8 */ - smartypants_cb__backtick, /* 9 */ - smartypants_cb__escape, /* 10 */ -}; - -static const uint8_t smartypants_cb_chars[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0, - 0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static inline int -word_boundary(uint8_t c) -{ - return c == 0 || isspace(c) || ispunct(c); -} - -static int -smartypants_quotes(struct buf *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open) -{ - char ent[8]; - - if (*is_open && !word_boundary(next_char)) - return 0; - - if (!(*is_open) && !word_boundary(previous_char)) - return 0; - - snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote); - *is_open = !(*is_open); - bufputs(ob, ent); - return 1; -} - -static size_t -smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (size >= 2) { - uint8_t t1 = tolower(text[1]); - - if (t1 == '\'') { - if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) - return 1; - } - - if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && - (size == 3 || word_boundary(text[2]))) { - BUFPUTSL(ob, "’"); - return 0; - } - - if (size >= 3) { - uint8_t t2 = tolower(text[2]); - - if (((t1 == 'r' && t2 == 'e') || - (t1 == 'l' && t2 == 'l') || - (t1 == 'v' && t2 == 'e')) && - (size == 4 || word_boundary(text[3]))) { - BUFPUTSL(ob, "’"); - return 0; - } - } - } - - if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote)) - return 0; - - bufputc(ob, text[0]); - return 0; -} - -static size_t -smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (size >= 3) { - uint8_t t1 = tolower(text[1]); - uint8_t t2 = tolower(text[2]); - - if (t1 == 'c' && t2 == ')') { - BUFPUTSL(ob, "©"); - return 2; - } - - if (t1 == 'r' && t2 == ')') { - BUFPUTSL(ob, "®"); - return 2; - } - - if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') { - BUFPUTSL(ob, "™"); - return 3; - } - } - - bufputc(ob, text[0]); - return 0; -} - -static size_t -smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (size >= 3 && text[1] == '-' && text[2] == '-') { - BUFPUTSL(ob, "—"); - return 2; - } - - if (size >= 2 && text[1] == '-') { - BUFPUTSL(ob, "–"); - return 1; - } - - bufputc(ob, text[0]); - return 0; -} - -static size_t -smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (size >= 6 && memcmp(text, """, 6) == 0) { - if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote)) - return 5; - } - - if (size >= 4 && memcmp(text, "�", 4) == 0) - return 3; - - bufputc(ob, '&'); - return 0; -} - -static size_t -smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (size >= 3 && text[1] == '.' && text[2] == '.') { - BUFPUTSL(ob, "…"); - return 2; - } - - if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') { - BUFPUTSL(ob, "…"); - return 4; - } - - bufputc(ob, text[0]); - return 0; -} - -static size_t -smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (size >= 2 && text[1] == '`') { - if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) - return 1; - } - - return 0; -} - -static size_t -smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (word_boundary(previous_char) && size >= 3) { - if (text[0] == '1' && text[1] == '/' && text[2] == '2') { - if (size == 3 || word_boundary(text[3])) { - BUFPUTSL(ob, "½"); - return 2; - } - } - - if (text[0] == '1' && text[1] == '/' && text[2] == '4') { - if (size == 3 || word_boundary(text[3]) || - (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) { - BUFPUTSL(ob, "¼"); - return 2; - } - } - - if (text[0] == '3' && text[1] == '/' && text[2] == '4') { - if (size == 3 || word_boundary(text[3]) || - (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) { - BUFPUTSL(ob, "¾"); - return 2; - } - } - } - - bufputc(ob, text[0]); - return 0; -} - -static size_t -smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote)) - BUFPUTSL(ob, """); - - return 0; -} - -static size_t -smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - static const char *skip_tags[] = { - "pre", "code", "var", "samp", "kbd", "math", "script", "style" - }; - static const size_t skip_tags_count = 8; - - size_t tag, i = 0; - - while (i < size && text[i] != '>') - i++; - - for (tag = 0; tag < skip_tags_count; ++tag) { - if (sdhtml_is_tag(text, size, skip_tags[tag]) == HTML_TAG_OPEN) - break; - } - - if (tag < skip_tags_count) { - for (;;) { - while (i < size && text[i] != '<') - i++; - - if (i == size) - break; - - if (sdhtml_is_tag(text + i, size - i, skip_tags[tag]) == HTML_TAG_CLOSE) - break; - - i++; - } - - while (i < size && text[i] != '>') - i++; - } - - bufput(ob, text, i + 1); - return i; -} - -static size_t -smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) -{ - if (size < 2) - return 0; - - switch (text[1]) { - case '\\': - case '"': - case '\'': - case '.': - case '-': - case '`': - bufputc(ob, text[1]); - return 1; - - default: - bufputc(ob, '\\'); - return 0; - } -} - -#if 0 -static struct { - uint8_t c0; - const uint8_t *pattern; - const uint8_t *entity; - int skip; -} smartypants_subs[] = { - { '\'', "'s>", "’", 0 }, - { '\'', "'t>", "’", 0 }, - { '\'', "'re>", "’", 0 }, - { '\'', "'ll>", "’", 0 }, - { '\'', "'ve>", "’", 0 }, - { '\'', "'m>", "’", 0 }, - { '\'', "'d>", "’", 0 }, - { '-', "--", "—", 1 }, - { '-', "<->", "–", 0 }, - { '.', "...", "…", 2 }, - { '.', ". . .", "…", 4 }, - { '(', "(c)", "©", 2 }, - { '(', "(r)", "®", 2 }, - { '(', "(tm)", "™", 3 }, - { '3', "<3/4>", "¾", 2 }, - { '3', "<3/4ths>", "¾", 2 }, - { '1', "<1/2>", "½", 2 }, - { '1', "<1/4>", "¼", 2 }, - { '1', "<1/4th>", "¼", 2 }, - { '&', "�", 0, 3 }, -}; -#endif - -void -sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size) -{ - size_t i; - struct smartypants_data smrt = {0, 0}; - - if (!text) - return; - - bufgrow(ob, size); - - for (i = 0; i < size; ++i) { - size_t org; - uint8_t action = 0; - - org = i; - while (i < size && (action = smartypants_cb_chars[text[i]]) == 0) - i++; - - if (i > org) - bufput(ob, text + org, i - org); - - if (i < size) { - i += smartypants_cb_ptrs[(int)action] - (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i); - } - } -} - - diff --git a/src/sundown/markdown.c b/src/sundown/markdown.c deleted file mode 100644 index ea3cf23..0000000 --- a/src/sundown/markdown.c +++ /dev/null @@ -1,2556 +0,0 @@ -/* markdown.c - generic markdown parser */ - -/* - * Copyright (c) 2009, Natacha Porté - * Copyright (c) 2011, Vicent Marti - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "markdown.h" -#include "stack.h" - -#include -#include -#include -#include - -#if defined(_WIN32) -#define strncasecmp _strnicmp -#endif - -#define REF_TABLE_SIZE 8 - -#define BUFFER_BLOCK 0 -#define BUFFER_SPAN 1 - -#define MKD_LI_END 8 /* internal list flag */ - -#define gperf_case_strncmp(s1, s2, n) strncasecmp(s1, s2, n) -#define GPERF_DOWNCASE 1 -#define GPERF_CASE_STRNCMP 1 -#include "html_blocks.h" - -/*************** - * LOCAL TYPES * - ***************/ - -/* link_ref: reference to a link */ -struct link_ref { - unsigned int id; - - struct buf *link; - struct buf *title; - - struct link_ref *next; -}; - -/* char_trigger: function pointer to render active chars */ -/* returns the number of chars taken care of */ -/* data is the pointer of the beginning of the span */ -/* offset is the number of valid chars before data */ -struct sd_markdown; -typedef size_t -(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); - -static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); -static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); - -enum markdown_char_t { - MD_CHAR_NONE = 0, - MD_CHAR_EMPHASIS, - MD_CHAR_CODESPAN, - MD_CHAR_LINEBREAK, - MD_CHAR_LINK, - MD_CHAR_LANGLE, - MD_CHAR_ESCAPE, - MD_CHAR_ENTITITY, - MD_CHAR_AUTOLINK_URL, - MD_CHAR_AUTOLINK_EMAIL, - MD_CHAR_AUTOLINK_WWW, - MD_CHAR_SUPERSCRIPT, -}; - -static char_trigger markdown_char_ptrs[] = { - NULL, - &char_emphasis, - &char_codespan, - &char_linebreak, - &char_link, - &char_langle_tag, - &char_escape, - &char_entity, - &char_autolink_url, - &char_autolink_email, - &char_autolink_www, - &char_superscript, -}; - -/* render • structure containing one particular render */ -struct sd_markdown { - struct sd_callbacks cb; - void *opaque; - - struct link_ref *refs[REF_TABLE_SIZE]; - uint8_t active_char[256]; - struct stack work_bufs[2]; - unsigned int ext_flags; - size_t max_nesting; - int in_link_body; -}; - -/*************************** - * HELPER FUNCTIONS * - ***************************/ - -static inline struct buf * -rndr_newbuf(struct sd_markdown *rndr, int type) -{ - static const size_t buf_size[2] = {256, 64}; - struct buf *work = NULL; - struct stack *pool = &rndr->work_bufs[type]; - - if (pool->size < pool->asize && - pool->item[pool->size] != NULL) { - work = pool->item[pool->size++]; - work->size = 0; - } else { - work = bufnew(buf_size[type]); - stack_push(pool, work); - } - - return work; -} - -static inline void -rndr_popbuf(struct sd_markdown *rndr, int type) -{ - rndr->work_bufs[type].size--; -} - -static void -unscape_text(struct buf *ob, struct buf *src) -{ - size_t i = 0, org; - while (i < src->size) { - org = i; - while (i < src->size && src->data[i] != '\\') - i++; - - if (i > org) - bufput(ob, src->data + org, i - org); - - if (i + 1 >= src->size) - break; - - bufputc(ob, src->data[i + 1]); - i += 2; - } -} - -static unsigned int -hash_link_ref(const uint8_t *link_ref, size_t length) -{ - size_t i; - unsigned int hash = 0; - - for (i = 0; i < length; ++i) - hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash; - - return hash; -} - -static struct link_ref * -add_link_ref( - struct link_ref **references, - const uint8_t *name, size_t name_size) -{ - struct link_ref *ref = calloc(1, sizeof(struct link_ref)); - - if (!ref) - return NULL; - - ref->id = hash_link_ref(name, name_size); - ref->next = references[ref->id % REF_TABLE_SIZE]; - - references[ref->id % REF_TABLE_SIZE] = ref; - return ref; -} - -static struct link_ref * -find_link_ref(struct link_ref **references, uint8_t *name, size_t length) -{ - unsigned int hash = hash_link_ref(name, length); - struct link_ref *ref = NULL; - - ref = references[hash % REF_TABLE_SIZE]; - - while (ref != NULL) { - if (ref->id == hash) - return ref; - - ref = ref->next; - } - - return NULL; -} - -static void -free_link_refs(struct link_ref **references) -{ - size_t i; - - for (i = 0; i < REF_TABLE_SIZE; ++i) { - struct link_ref *r = references[i]; - struct link_ref *next; - - while (r) { - next = r->next; - bufrelease(r->link); - bufrelease(r->title); - free(r); - r = next; - } - } -} - -/* - * Check whether a char is a Markdown space. - - * Right now we only consider spaces the actual - * space and a newline: tabs and carriage returns - * are filtered out during the preprocessing phase. - * - * If we wanted to actually be UTF-8 compliant, we - * should instead extract an Unicode codepoint from - * this character and check for space properties. - */ -static inline int -_isspace(int c) -{ - return c == ' ' || c == '\n'; -} - -/**************************** - * INLINE PARSING FUNCTIONS * - ****************************/ - -/* is_mail_autolink • looks for the address part of a mail autolink and '>' */ -/* this is less strict than the original markdown e-mail address matching */ -static size_t -is_mail_autolink(uint8_t *data, size_t size) -{ - size_t i = 0, nb = 0; - - /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */ - for (i = 0; i < size; ++i) { - if (isalnum(data[i])) - continue; - - switch (data[i]) { - case '@': - nb++; - - case '-': - case '.': - case '_': - break; - - case '>': - return (nb == 1) ? i + 1 : 0; - - default: - return 0; - } - } - - return 0; -} - -/* tag_length • returns the length of the given tag, or 0 is it's not valid */ -static size_t -tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink) -{ - size_t i, j; - - /* a valid tag can't be shorter than 3 chars */ - if (size < 3) return 0; - - /* begins with a '<' optionally followed by '/', followed by letter or number */ - if (data[0] != '<') return 0; - i = (data[1] == '/') ? 2 : 1; - - if (!isalnum(data[i])) - return 0; - - /* scheme test */ - *autolink = MKDA_NOT_AUTOLINK; - - /* try to find the beginning of an URI */ - while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-')) - i++; - - if (i > 1 && data[i] == '@') { - if ((j = is_mail_autolink(data + i, size - i)) != 0) { - *autolink = MKDA_EMAIL; - return i + j; - } - } - - if (i > 2 && data[i] == ':') { - *autolink = MKDA_NORMAL; - i++; - } - - /* completing autolink test: no whitespace or ' or " */ - if (i >= size) - *autolink = MKDA_NOT_AUTOLINK; - - else if (*autolink) { - j = i; - - while (i < size) { - if (data[i] == '\\') i += 2; - else if (data[i] == '>' || data[i] == '\'' || - data[i] == '"' || data[i] == ' ' || data[i] == '\n') - break; - else i++; - } - - if (i >= size) return 0; - if (i > j && data[i] == '>') return i + 1; - /* one of the forbidden chars has been found */ - *autolink = MKDA_NOT_AUTOLINK; - } - - /* looking for sometinhg looking like a tag end */ - while (i < size && data[i] != '>') i++; - if (i >= size) return 0; - return i + 1; -} - -/* parse_inline • parses inline markdown elements */ -static void -parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - size_t i = 0, end = 0; - uint8_t action = 0; - struct buf work = { 0, 0, 0, 0 }; - - if (rndr->work_bufs[BUFFER_SPAN].size + - rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting) - return; - - while (i < size) { - /* copying inactive chars into the output */ - while (end < size && (action = rndr->active_char[data[end]]) == 0) { - end++; - } - - if (rndr->cb.normal_text) { - work.data = data + i; - work.size = end - i; - rndr->cb.normal_text(ob, &work, rndr->opaque); - } - else - bufput(ob, data + i, end - i); - - if (end >= size) break; - i = end; - - end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i); - if (!end) /* no action from the callback */ - end = i + 1; - else { - i += end; - end = i; - } - } -} - -/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */ -static size_t -find_emph_char(uint8_t *data, size_t size, uint8_t c) -{ - size_t i = 1; - - while (i < size) { - while (i < size && data[i] != c && data[i] != '`' && data[i] != '[') - i++; - - if (i == size) - return 0; - - if (data[i] == c) - return i; - - /* not counting escaped chars */ - if (i && data[i - 1] == '\\') { - i++; continue; - } - - if (data[i] == '`') { - size_t span_nb = 0, bt; - size_t tmp_i = 0; - - /* counting the number of opening backticks */ - while (i < size && data[i] == '`') { - i++; span_nb++; - } - - if (i >= size) return 0; - - /* finding the matching closing sequence */ - bt = 0; - while (i < size && bt < span_nb) { - if (!tmp_i && data[i] == c) tmp_i = i; - if (data[i] == '`') bt++; - else bt = 0; - i++; - } - - if (i >= size) return tmp_i; - } - /* skipping a link */ - else if (data[i] == '[') { - size_t tmp_i = 0; - uint8_t cc; - - i++; - while (i < size && data[i] != ']') { - if (!tmp_i && data[i] == c) tmp_i = i; - i++; - } - - i++; - while (i < size && (data[i] == ' ' || data[i] == '\n')) - i++; - - if (i >= size) - return tmp_i; - - switch (data[i]) { - case '[': - cc = ']'; break; - - case '(': - cc = ')'; break; - - default: - if (tmp_i) - return tmp_i; - else - continue; - } - - i++; - while (i < size && data[i] != cc) { - if (!tmp_i && data[i] == c) tmp_i = i; - i++; - } - - if (i >= size) - return tmp_i; - - i++; - } - } - - return 0; -} - -/* parse_emph1 • parsing single emphase */ -/* closed by a symbol not preceded by whitespace and not followed by symbol */ -static size_t -parse_emph1(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c) -{ - size_t i = 0, len; - struct buf *work = 0; - int r; - - if (!rndr->cb.emphasis) return 0; - - /* skipping one symbol if coming from emph3 */ - if (size > 1 && data[0] == c && data[1] == c) i = 1; - - while (i < size) { - len = find_emph_char(data + i, size - i, c); - if (!len) return 0; - i += len; - if (i >= size) return 0; - - if (data[i] == c && !_isspace(data[i - 1])) { - - if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) { - if (i + 1 < size && isalnum(data[i + 1])) - continue; - } - - work = rndr_newbuf(rndr, BUFFER_SPAN); - parse_inline(work, rndr, data, i); - r = rndr->cb.emphasis(ob, work, rndr->opaque); - rndr_popbuf(rndr, BUFFER_SPAN); - return r ? i + 1 : 0; - } - } - - return 0; -} - -/* parse_emph2 • parsing single emphase */ -static size_t -parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c) -{ - int (*render_method)(struct buf *ob, const struct buf *text, void *opaque); - size_t i = 0, len; - struct buf *work = 0; - int r; - - render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis; - - if (!render_method) - return 0; - - while (i < size) { - len = find_emph_char(data + i, size - i, c); - if (!len) return 0; - i += len; - - if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) { - work = rndr_newbuf(rndr, BUFFER_SPAN); - parse_inline(work, rndr, data, i); - r = render_method(ob, work, rndr->opaque); - rndr_popbuf(rndr, BUFFER_SPAN); - return r ? i + 2 : 0; - } - i++; - } - return 0; -} - -/* parse_emph3 • parsing single emphase */ -/* finds the first closing tag, and delegates to the other emph */ -static size_t -parse_emph3(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c) -{ - size_t i = 0, len; - int r; - - while (i < size) { - len = find_emph_char(data + i, size - i, c); - if (!len) return 0; - i += len; - - /* skip whitespace preceded symbols */ - if (data[i] != c || _isspace(data[i - 1])) - continue; - - if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) { - /* triple symbol found */ - struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN); - - parse_inline(work, rndr, data, i); - r = rndr->cb.triple_emphasis(ob, work, rndr->opaque); - rndr_popbuf(rndr, BUFFER_SPAN); - return r ? i + 3 : 0; - - } else if (i + 1 < size && data[i + 1] == c) { - /* double symbol found, handing over to emph1 */ - len = parse_emph1(ob, rndr, data - 2, size + 2, c); - if (!len) return 0; - else return len - 2; - - } else { - /* single symbol found, handing over to emph2 */ - len = parse_emph2(ob, rndr, data - 1, size + 1, c); - if (!len) return 0; - else return len - 1; - } - } - return 0; -} - -/* char_emphasis • single and double emphasis parsing */ -static size_t -char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - uint8_t c = data[0]; - size_t ret; - - if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) { - if (offset > 0 && !_isspace(data[-1]) && data[-1] != '>') - return 0; - } - - if (size > 2 && data[1] != c) { - /* whitespace cannot follow an opening emphasis; - * strikethrough only takes two characters '~~' */ - if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0) - return 0; - - return ret + 1; - } - - if (size > 3 && data[1] == c && data[2] != c) { - if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0) - return 0; - - return ret + 2; - } - - if (size > 4 && data[1] == c && data[2] == c && data[3] != c) { - if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0) - return 0; - - return ret + 3; - } - - return 0; -} - - -/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */ -static size_t -char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - if (offset < 2 || data[-1] != ' ' || data[-2] != ' ') - return 0; - - /* removing the last space from ob and rendering */ - while (ob->size && ob->data[ob->size - 1] == ' ') - ob->size--; - - return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0; -} - - -/* char_codespan • '`' parsing a code span (assuming codespan != 0) */ -static size_t -char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - size_t end, nb = 0, i, f_begin, f_end; - - /* counting the number of backticks in the delimiter */ - while (nb < size && data[nb] == '`') - nb++; - - /* finding the next delimiter */ - i = 0; - for (end = nb; end < size && i < nb; end++) { - if (data[end] == '`') i++; - else i = 0; - } - - if (i < nb && end >= size) - return 0; /* no matching delimiter */ - - /* trimming outside whitespaces */ - f_begin = nb; - while (f_begin < end && data[f_begin] == ' ') - f_begin++; - - f_end = end - nb; - while (f_end > nb && data[f_end-1] == ' ') - f_end--; - - /* real code span */ - if (f_begin < f_end) { - struct buf work = { data + f_begin, f_end - f_begin, 0, 0 }; - if (!rndr->cb.codespan(ob, &work, rndr->opaque)) - end = 0; - } else { - if (!rndr->cb.codespan(ob, 0, rndr->opaque)) - end = 0; - } - - return end; -} - - -/* char_escape • '\\' backslash escape */ -static size_t -char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~"; - struct buf work = { 0, 0, 0, 0 }; - - if (size > 1) { - if (strchr(escape_chars, data[1]) == NULL) - return 0; - - if (rndr->cb.normal_text) { - work.data = data + 1; - work.size = 1; - rndr->cb.normal_text(ob, &work, rndr->opaque); - } - else bufputc(ob, data[1]); - } else if (size == 1) { - bufputc(ob, data[0]); - } - - return 2; -} - -/* char_entity • '&' escaped when it doesn't belong to an entity */ -/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */ -static size_t -char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - size_t end = 1; - struct buf work = { 0, 0, 0, 0 }; - - if (end < size && data[end] == '#') - end++; - - while (end < size && isalnum(data[end])) - end++; - - if (end < size && data[end] == ';') - end++; /* real entity */ - else - return 0; /* lone '&' */ - - if (rndr->cb.entity) { - work.data = data; - work.size = end; - rndr->cb.entity(ob, &work, rndr->opaque); - } - else bufput(ob, data, end); - - return end; -} - -/* char_langle_tag • '<' when tags or autolinks are allowed */ -static size_t -char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - enum mkd_autolink altype = MKDA_NOT_AUTOLINK; - size_t end = tag_length(data, size, &altype); - struct buf work = { data, end, 0, 0 }; - int ret = 0; - - if (end > 2) { - if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) { - struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN); - work.data = data + 1; - work.size = end - 2; - unscape_text(u_link, &work); - ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque); - rndr_popbuf(rndr, BUFFER_SPAN); - } - else if (rndr->cb.raw_html_tag) - ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque); - } - - if (!ret) return 0; - else return end; -} - -static size_t -char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - struct buf *link, *link_url, *link_text; - size_t link_len, rewind; - - if (!rndr->cb.link || rndr->in_link_body) - return 0; - - link = rndr_newbuf(rndr, BUFFER_SPAN); - - if ((link_len = sd_autolink__www(&rewind, link, data, offset, size, 0)) > 0) { - link_url = rndr_newbuf(rndr, BUFFER_SPAN); - BUFPUTSL(link_url, "http://"); - bufput(link_url, link->data, link->size); - - ob->size -= rewind; - if (rndr->cb.normal_text) { - link_text = rndr_newbuf(rndr, BUFFER_SPAN); - rndr->cb.normal_text(link_text, link, rndr->opaque); - rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque); - rndr_popbuf(rndr, BUFFER_SPAN); - } else { - rndr->cb.link(ob, link_url, NULL, link, rndr->opaque); - } - rndr_popbuf(rndr, BUFFER_SPAN); - } - - rndr_popbuf(rndr, BUFFER_SPAN); - return link_len; -} - -static size_t -char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - struct buf *link; - size_t link_len, rewind; - - if (!rndr->cb.autolink || rndr->in_link_body) - return 0; - - link = rndr_newbuf(rndr, BUFFER_SPAN); - - if ((link_len = sd_autolink__email(&rewind, link, data, offset, size, 0)) > 0) { - ob->size -= rewind; - rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque); - } - - rndr_popbuf(rndr, BUFFER_SPAN); - return link_len; -} - -static size_t -char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - struct buf *link; - size_t link_len, rewind; - - if (!rndr->cb.autolink || rndr->in_link_body) - return 0; - - link = rndr_newbuf(rndr, BUFFER_SPAN); - - if ((link_len = sd_autolink__url(&rewind, link, data, offset, size, 0)) > 0) { - ob->size -= rewind; - rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque); - } - - rndr_popbuf(rndr, BUFFER_SPAN); - return link_len; -} - -/* char_link • '[': parsing a link or an image */ -static size_t -char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - int is_img = (offset && data[-1] == '!'), level; - size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0; - struct buf *content = 0; - struct buf *link = 0; - struct buf *title = 0; - struct buf *u_link = 0; - size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size; - int text_has_nl = 0, ret = 0; - int in_title = 0, qtype = 0; - - /* checking whether the correct renderer exists */ - if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link)) - goto cleanup; - - /* looking for the matching closing bracket */ - for (level = 1; i < size; i++) { - if (data[i] == '\n') - text_has_nl = 1; - - else if (data[i - 1] == '\\') - continue; - - else if (data[i] == '[') - level++; - - else if (data[i] == ']') { - level--; - if (level <= 0) - break; - } - } - - if (i >= size) - goto cleanup; - - txt_e = i; - i++; - - /* skip any amount of whitespace or newline */ - /* (this is much more laxist than original markdown syntax) */ - while (i < size && _isspace(data[i])) - i++; - - /* inline style link */ - if (i < size && data[i] == '(') { - /* skipping initial whitespace */ - i++; - - while (i < size && _isspace(data[i])) - i++; - - link_b = i; - - /* looking for link end: ' " ) */ - while (i < size) { - if (data[i] == '\\') i += 2; - else if (data[i] == ')') break; - else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break; - else i++; - } - - if (i >= size) goto cleanup; - link_e = i; - - /* looking for title end if present */ - if (data[i] == '\'' || data[i] == '"') { - qtype = data[i]; - in_title = 1; - i++; - title_b = i; - - while (i < size) { - if (data[i] == '\\') i += 2; - else if (data[i] == qtype) {in_title = 0; i++;} - else if ((data[i] == ')') && !in_title) break; - else i++; - } - - if (i >= size) goto cleanup; - - /* skipping whitespaces after title */ - title_e = i - 1; - while (title_e > title_b && _isspace(data[title_e])) - title_e--; - - /* checking for closing quote presence */ - if (data[title_e] != '\'' && data[title_e] != '"') { - title_b = title_e = 0; - link_e = i; - } - } - - /* remove whitespace at the end of the link */ - while (link_e > link_b && _isspace(data[link_e - 1])) - link_e--; - - /* remove optional angle brackets around the link */ - if (data[link_b] == '<') link_b++; - if (data[link_e - 1] == '>') link_e--; - - /* building escaped link and title */ - if (link_e > link_b) { - link = rndr_newbuf(rndr, BUFFER_SPAN); - bufput(link, data + link_b, link_e - link_b); - } - - if (title_e > title_b) { - title = rndr_newbuf(rndr, BUFFER_SPAN); - bufput(title, data + title_b, title_e - title_b); - } - - i++; - } - - /* reference style link */ - else if (i < size && data[i] == '[') { - struct buf id = { 0, 0, 0, 0 }; - struct link_ref *lr; - - /* looking for the id */ - i++; - link_b = i; - while (i < size && data[i] != ']') i++; - if (i >= size) goto cleanup; - link_e = i; - - /* finding the link_ref */ - if (link_b == link_e) { - if (text_has_nl) { - struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN); - size_t j; - - for (j = 1; j < txt_e; j++) { - if (data[j] != '\n') - bufputc(b, data[j]); - else if (data[j - 1] != ' ') - bufputc(b, ' '); - } - - id.data = b->data; - id.size = b->size; - } else { - id.data = data + 1; - id.size = txt_e - 1; - } - } else { - id.data = data + link_b; - id.size = link_e - link_b; - } - - lr = find_link_ref(rndr->refs, id.data, id.size); - if (!lr) - goto cleanup; - - /* keeping link and title from link_ref */ - link = lr->link; - title = lr->title; - i++; - } - - /* shortcut reference style link */ - else { - struct buf id = { 0, 0, 0, 0 }; - struct link_ref *lr; - - /* crafting the id */ - if (text_has_nl) { - struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN); - size_t j; - - for (j = 1; j < txt_e; j++) { - if (data[j] != '\n') - bufputc(b, data[j]); - else if (data[j - 1] != ' ') - bufputc(b, ' '); - } - - id.data = b->data; - id.size = b->size; - } else { - id.data = data + 1; - id.size = txt_e - 1; - } - - /* finding the link_ref */ - lr = find_link_ref(rndr->refs, id.data, id.size); - if (!lr) - goto cleanup; - - /* keeping link and title from link_ref */ - link = lr->link; - title = lr->title; - - /* rewinding the whitespace */ - i = txt_e + 1; - } - - /* building content: img alt is escaped, link content is parsed */ - if (txt_e > 1) { - content = rndr_newbuf(rndr, BUFFER_SPAN); - if (is_img) { - bufput(content, data + 1, txt_e - 1); - } else { - /* disable autolinking when parsing inline the - * content of a link */ - rndr->in_link_body = 1; - parse_inline(content, rndr, data + 1, txt_e - 1); - rndr->in_link_body = 0; - } - } - - if (link) { - u_link = rndr_newbuf(rndr, BUFFER_SPAN); - unscape_text(u_link, link); - } - - /* calling the relevant rendering function */ - if (is_img) { - if (ob->size && ob->data[ob->size - 1] == '!') - ob->size -= 1; - - ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque); - } else { - ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque); - } - - /* cleanup */ -cleanup: - rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size; - return ret ? i : 0; -} - -static size_t -char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) -{ - size_t sup_start, sup_len; - struct buf *sup; - - if (!rndr->cb.superscript) - return 0; - - if (size < 2) - return 0; - - if (data[1] == '(') { - sup_start = sup_len = 2; - - while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\') - sup_len++; - - if (sup_len == size) - return 0; - } else { - sup_start = sup_len = 1; - - while (sup_len < size && !_isspace(data[sup_len])) - sup_len++; - } - - if (sup_len - sup_start == 0) - return (sup_start == 2) ? 3 : 0; - - sup = rndr_newbuf(rndr, BUFFER_SPAN); - parse_inline(sup, rndr, data + sup_start, sup_len - sup_start); - rndr->cb.superscript(ob, sup, rndr->opaque); - rndr_popbuf(rndr, BUFFER_SPAN); - - return (sup_start == 2) ? sup_len + 1 : sup_len; -} - -/********************************* - * BLOCK-LEVEL PARSING FUNCTIONS * - *********************************/ - -/* is_empty • returns the line length when it is empty, 0 otherwise */ -static size_t -is_empty(uint8_t *data, size_t size) -{ - size_t i; - - for (i = 0; i < size && data[i] != '\n'; i++) - if (data[i] != ' ') - return 0; - - return i + 1; -} - -/* is_hrule • returns whether a line is a horizontal rule */ -static int -is_hrule(uint8_t *data, size_t size) -{ - size_t i = 0, n = 0; - uint8_t c; - - /* skipping initial spaces */ - if (size < 3) return 0; - if (data[0] == ' ') { i++; - if (data[1] == ' ') { i++; - if (data[2] == ' ') { i++; } } } - - /* looking at the hrule uint8_t */ - if (i + 2 >= size - || (data[i] != '*' && data[i] != '-' && data[i] != '_')) - return 0; - c = data[i]; - - /* the whole line must be the char or whitespace */ - while (i < size && data[i] != '\n') { - if (data[i] == c) n++; - else if (data[i] != ' ') - return 0; - - i++; - } - - return n >= 3; -} - -/* check if a line begins with a code fence; return the - * width of the code fence */ -static size_t -prefix_codefence(uint8_t *data, size_t size) -{ - size_t i = 0, n = 0; - uint8_t c; - - /* skipping initial spaces */ - if (size < 3) return 0; - if (data[0] == ' ') { i++; - if (data[1] == ' ') { i++; - if (data[2] == ' ') { i++; } } } - - /* looking at the hrule uint8_t */ - if (i + 2 >= size || !(data[i] == '~' || data[i] == '`')) - return 0; - - c = data[i]; - - /* the whole line must be the uint8_t or whitespace */ - while (i < size && data[i] == c) { - n++; i++; - } - - if (n < 3) - return 0; - - return i; -} - -/* check if a line is a code fence; return its size if it is */ -static size_t -is_codefence(uint8_t *data, size_t size, struct buf *syntax) -{ - size_t i = 0, syn_len = 0; - uint8_t *syn_start; - - i = prefix_codefence(data, size); - if (i == 0) - return 0; - - while (i < size && data[i] == ' ') - i++; - - syn_start = data + i; - - if (i < size && data[i] == '{') { - i++; syn_start++; - - while (i < size && data[i] != '}' && data[i] != '\n') { - syn_len++; i++; - } - - if (i == size || data[i] != '}') - return 0; - - /* strip all whitespace at the beginning and the end - * of the {} block */ - while (syn_len > 0 && _isspace(syn_start[0])) { - syn_start++; syn_len--; - } - - while (syn_len > 0 && _isspace(syn_start[syn_len - 1])) - syn_len--; - - i++; - } else { - while (i < size && !_isspace(data[i])) { - syn_len++; i++; - } - } - - if (syntax) { - syntax->data = syn_start; - syntax->size = syn_len; - } - - while (i < size && data[i] != '\n') { - if (!_isspace(data[i])) - return 0; - - i++; - } - - return i + 1; -} - -/* is_atxheader • returns whether the line is a hash-prefixed header */ -static int -is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - if (data[0] != '#') - return 0; - - if (rndr->ext_flags & MKDEXT_SPACE_HEADERS) { - size_t level = 0; - - while (level < size && level < 6 && data[level] == '#') - level++; - - if (level < size && data[level] != ' ') - return 0; - } - - return 1; -} - -/* is_headerline • returns whether the line is a setext-style hdr underline */ -static int -is_headerline(uint8_t *data, size_t size) -{ - size_t i = 0; - - /* test of level 1 header */ - if (data[i] == '=') { - for (i = 1; i < size && data[i] == '='; i++); - while (i < size && data[i] == ' ') i++; - return (i >= size || data[i] == '\n') ? 1 : 0; } - - /* test of level 2 header */ - if (data[i] == '-') { - for (i = 1; i < size && data[i] == '-'; i++); - while (i < size && data[i] == ' ') i++; - return (i >= size || data[i] == '\n') ? 2 : 0; } - - return 0; -} - -static int -is_next_headerline(uint8_t *data, size_t size) -{ - size_t i = 0; - - while (i < size && data[i] != '\n') - i++; - - if (++i >= size) - return 0; - - return is_headerline(data + i, size - i); -} - -/* prefix_quote • returns blockquote prefix length */ -static size_t -prefix_quote(uint8_t *data, size_t size) -{ - size_t i = 0; - if (i < size && data[i] == ' ') i++; - if (i < size && data[i] == ' ') i++; - if (i < size && data[i] == ' ') i++; - - if (i < size && data[i] == '>') { - if (i + 1 < size && data[i + 1] == ' ') - return i + 2; - - return i + 1; - } - - return 0; -} - -/* prefix_code • returns prefix length for block code*/ -static size_t -prefix_code(uint8_t *data, size_t size) -{ - if (size > 3 && data[0] == ' ' && data[1] == ' ' - && data[2] == ' ' && data[3] == ' ') return 4; - - return 0; -} - -/* prefix_oli • returns ordered list item prefix */ -static size_t -prefix_oli(uint8_t *data, size_t size) -{ - size_t i = 0; - - if (i < size && data[i] == ' ') i++; - if (i < size && data[i] == ' ') i++; - if (i < size && data[i] == ' ') i++; - - if (i >= size || data[i] < '0' || data[i] > '9') - return 0; - - while (i < size && data[i] >= '0' && data[i] <= '9') - i++; - - if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ') - return 0; - - if (is_next_headerline(data + i, size - i)) - return 0; - - return i + 2; -} - -/* prefix_uli • returns ordered list item prefix */ -static size_t -prefix_uli(uint8_t *data, size_t size) -{ - size_t i = 0; - - if (i < size && data[i] == ' ') i++; - if (i < size && data[i] == ' ') i++; - if (i < size && data[i] == ' ') i++; - - if (i + 1 >= size || - (data[i] != '*' && data[i] != '+' && data[i] != '-') || - data[i + 1] != ' ') - return 0; - - if (is_next_headerline(data + i, size - i)) - return 0; - - return i + 2; -} - - -/* parse_block • parsing of one block, returning next uint8_t to parse */ -static void parse_block(struct buf *ob, struct sd_markdown *rndr, - uint8_t *data, size_t size); - - -/* parse_blockquote • handles parsing of a blockquote fragment */ -static size_t -parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - size_t beg, end = 0, pre, work_size = 0; - uint8_t *work_data = 0; - struct buf *out = 0; - - out = rndr_newbuf(rndr, BUFFER_BLOCK); - beg = 0; - while (beg < size) { - for (end = beg + 1; end < size && data[end - 1] != '\n'; end++); - - pre = prefix_quote(data + beg, end - beg); - - if (pre) - beg += pre; /* skipping prefix */ - - /* empty line followed by non-quote line */ - else if (is_empty(data + beg, end - beg) && - (end >= size || (prefix_quote(data + end, size - end) == 0 && - !is_empty(data + end, size - end)))) - break; - - if (beg < end) { /* copy into the in-place working buffer */ - /* bufput(work, data + beg, end - beg); */ - if (!work_data) - work_data = data + beg; - else if (data + beg != work_data + work_size) - memmove(work_data + work_size, data + beg, end - beg); - work_size += end - beg; - } - beg = end; - } - - parse_block(out, rndr, work_data, work_size); - if (rndr->cb.blockquote) - rndr->cb.blockquote(ob, out, rndr->opaque); - rndr_popbuf(rndr, BUFFER_BLOCK); - return end; -} - -static size_t -parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render); - -/* parse_blockquote • handles parsing of a regular paragraph */ -static size_t -parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - size_t i = 0, end = 0; - int level = 0; - struct buf work = { data, 0, 0, 0 }; - - while (i < size) { - for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */; - - if (is_empty(data + i, size - i)) - break; - - if ((level = is_headerline(data + i, size - i)) != 0) - break; - - if (is_atxheader(rndr, data + i, size - i) || - is_hrule(data + i, size - i) || - prefix_quote(data + i, size - i)) { - end = i; - break; - } - - /* - * Early termination of a paragraph with the same logic - * as Markdown 1.0.0. If this logic is applied, the - * Markdown 1.0.3 test suite won't pass cleanly - * - * :: If the first character in a new line is not a letter, - * let's check to see if there's some kind of block starting - * here - */ - if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !isalnum(data[i])) { - if (prefix_oli(data + i, size - i) || - prefix_uli(data + i, size - i)) { - end = i; - break; - } - - /* see if an html block starts here */ - if (data[i] == '<' && rndr->cb.blockhtml && - parse_htmlblock(ob, rndr, data + i, size - i, 0)) { - end = i; - break; - } - - /* see if a code fence starts here */ - if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 && - is_codefence(data + i, size - i, NULL) != 0) { - end = i; - break; - } - } - - i = end; - } - - work.size = i; - while (work.size && data[work.size - 1] == '\n') - work.size--; - - if (!level) { - struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK); - parse_inline(tmp, rndr, work.data, work.size); - if (rndr->cb.paragraph) - rndr->cb.paragraph(ob, tmp, rndr->opaque); - rndr_popbuf(rndr, BUFFER_BLOCK); - } else { - struct buf *header_work; - - if (work.size) { - size_t beg; - i = work.size; - work.size -= 1; - - while (work.size && data[work.size] != '\n') - work.size -= 1; - - beg = work.size + 1; - while (work.size && data[work.size - 1] == '\n') - work.size -= 1; - - if (work.size > 0) { - struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK); - parse_inline(tmp, rndr, work.data, work.size); - - if (rndr->cb.paragraph) - rndr->cb.paragraph(ob, tmp, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_BLOCK); - work.data += beg; - work.size = i - beg; - } - else work.size = i; - } - - header_work = rndr_newbuf(rndr, BUFFER_SPAN); - parse_inline(header_work, rndr, work.data, work.size); - - if (rndr->cb.header) - rndr->cb.header(ob, header_work, (int)level, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_SPAN); - } - - return end; -} - -/* parse_fencedcode • handles parsing of a block-level code fragment */ -static size_t -parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - size_t beg, end; - struct buf *work = 0; - struct buf lang = { 0, 0, 0, 0 }; - - beg = is_codefence(data, size, &lang); - if (beg == 0) return 0; - - work = rndr_newbuf(rndr, BUFFER_BLOCK); - - while (beg < size) { - size_t fence_end; - struct buf fence_trail = { 0, 0, 0, 0 }; - - fence_end = is_codefence(data + beg, size - beg, &fence_trail); - if (fence_end != 0 && fence_trail.size == 0) { - beg += fence_end; - break; - } - - for (end = beg + 1; end < size && data[end - 1] != '\n'; end++); - - if (beg < end) { - /* verbatim copy to the working buffer, - escaping entities */ - if (is_empty(data + beg, end - beg)) - bufputc(work, '\n'); - else bufput(work, data + beg, end - beg); - } - beg = end; - } - - if (work->size && work->data[work->size - 1] != '\n') - bufputc(work, '\n'); - - if (rndr->cb.blockcode) - rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_BLOCK); - return beg; -} - -static size_t -parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - size_t beg, end, pre; - struct buf *work = 0; - - work = rndr_newbuf(rndr, BUFFER_BLOCK); - - beg = 0; - while (beg < size) { - for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {}; - pre = prefix_code(data + beg, end - beg); - - if (pre) - beg += pre; /* skipping prefix */ - else if (!is_empty(data + beg, end - beg)) - /* non-empty non-prefixed line breaks the pre */ - break; - - if (beg < end) { - /* verbatim copy to the working buffer, - escaping entities */ - if (is_empty(data + beg, end - beg)) - bufputc(work, '\n'); - else bufput(work, data + beg, end - beg); - } - beg = end; - } - - while (work->size && work->data[work->size - 1] == '\n') - work->size -= 1; - - bufputc(work, '\n'); - - if (rndr->cb.blockcode) - rndr->cb.blockcode(ob, work, NULL, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_BLOCK); - return beg; -} - -/* parse_listitem • parsing of a single list item */ -/* assuming initial prefix is already removed */ -static size_t -parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags) -{ - struct buf *work = 0, *inter = 0; - size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; - int in_empty = 0, has_inside_empty = 0, in_fence = 0; - - /* keeping track of the first indentation prefix */ - while (orgpre < 3 && orgpre < size && data[orgpre] == ' ') - orgpre++; - - beg = prefix_uli(data, size); - if (!beg) - beg = prefix_oli(data, size); - - if (!beg) - return 0; - - /* skipping to the beginning of the following line */ - end = beg; - while (end < size && data[end - 1] != '\n') - end++; - - /* getting working buffers */ - work = rndr_newbuf(rndr, BUFFER_SPAN); - inter = rndr_newbuf(rndr, BUFFER_SPAN); - - /* putting the first line into the working buffer */ - bufput(work, data + beg, end - beg); - beg = end; - - /* process the following lines */ - while (beg < size) { - size_t has_next_uli = 0, has_next_oli = 0; - - end++; - - while (end < size && data[end - 1] != '\n') - end++; - - /* process an empty line */ - if (is_empty(data + beg, end - beg)) { - in_empty = 1; - beg = end; - continue; - } - - /* calculating the indentation */ - i = 0; - while (i < 4 && beg + i < end && data[beg + i] == ' ') - i++; - - pre = i; - - if (rndr->ext_flags & MKDEXT_FENCED_CODE) { - if (is_codefence(data + beg + i, end - beg - i, NULL) != 0) - in_fence = !in_fence; - } - - /* Only check for new list items if we are **not** inside - * a fenced code block */ - if (!in_fence) { - has_next_uli = prefix_uli(data + beg + i, end - beg - i); - has_next_oli = prefix_oli(data + beg + i, end - beg - i); - } - - /* checking for ul/ol switch */ - if (in_empty && ( - ((*flags & MKD_LIST_ORDERED) && has_next_uli) || - (!(*flags & MKD_LIST_ORDERED) && has_next_oli))){ - *flags |= MKD_LI_END; - break; /* the following item must have same list type */ - } - - /* checking for a new item */ - if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) { - if (in_empty) - has_inside_empty = 1; - - if (pre == orgpre) /* the following item must have */ - break; /* the same indentation */ - - if (!sublist) - sublist = work->size; - } - /* joining only indented stuff after empty lines; - * note that now we only require 1 space of indentation - * to continue a list */ - else if (in_empty && pre == 0) { - *flags |= MKD_LI_END; - break; - } - else if (in_empty) { - bufputc(work, '\n'); - has_inside_empty = 1; - } - - in_empty = 0; - - /* adding the line without prefix into the working buffer */ - bufput(work, data + beg + i, end - beg - i); - beg = end; - } - - /* render of li contents */ - if (has_inside_empty) - *flags |= MKD_LI_BLOCK; - - if (*flags & MKD_LI_BLOCK) { - /* intermediate render of block li */ - if (sublist && sublist < work->size) { - parse_block(inter, rndr, work->data, sublist); - parse_block(inter, rndr, work->data + sublist, work->size - sublist); - } - else - parse_block(inter, rndr, work->data, work->size); - } else { - /* intermediate render of inline li */ - if (sublist && sublist < work->size) { - parse_inline(inter, rndr, work->data, sublist); - parse_block(inter, rndr, work->data + sublist, work->size - sublist); - } - else - parse_inline(inter, rndr, work->data, work->size); - } - - /* render of li itself */ - if (rndr->cb.listitem) - rndr->cb.listitem(ob, inter, *flags, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_SPAN); - rndr_popbuf(rndr, BUFFER_SPAN); - return beg; -} - - -/* parse_list • parsing ordered or unordered list block */ -static size_t -parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags) -{ - struct buf *work = 0; - size_t i = 0, j; - - work = rndr_newbuf(rndr, BUFFER_BLOCK); - - while (i < size) { - j = parse_listitem(work, rndr, data + i, size - i, &flags); - i += j; - - if (!j || (flags & MKD_LI_END)) - break; - } - - if (rndr->cb.list) - rndr->cb.list(ob, work, flags, rndr->opaque); - rndr_popbuf(rndr, BUFFER_BLOCK); - return i; -} - -/* parse_atxheader • parsing of atx-style headers */ -static size_t -parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - size_t level = 0; - size_t i, end, skip; - - while (level < size && level < 6 && data[level] == '#') - level++; - - for (i = level; i < size && data[i] == ' '; i++); - - for (end = i; end < size && data[end] != '\n'; end++); - skip = end; - - while (end && data[end - 1] == '#') - end--; - - while (end && data[end - 1] == ' ') - end--; - - if (end > i) { - struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN); - - parse_inline(work, rndr, data + i, end - i); - - if (rndr->cb.header) - rndr->cb.header(ob, work, (int)level, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_SPAN); - } - - return skip; -} - - -/* htmlblock_end • checking end of HTML block : [ \t]*\n[ \t*]\n */ -/* returns the length on match, 0 otherwise */ -static size_t -htmlblock_end_tag( - const char *tag, - size_t tag_len, - struct sd_markdown *rndr, - uint8_t *data, - size_t size) -{ - size_t i, w; - - /* checking if tag is a match */ - if (tag_len + 3 >= size || - strncasecmp((char *)data + 2, tag, tag_len) != 0 || - data[tag_len + 2] != '>') - return 0; - - /* checking white lines */ - i = tag_len + 3; - w = 0; - if (i < size && (w = is_empty(data + i, size - i)) == 0) - return 0; /* non-blank after tag */ - i += w; - w = 0; - - if (i < size) - w = is_empty(data + i, size - i); - - return i + w; -} - -static size_t -htmlblock_end(const char *curtag, - struct sd_markdown *rndr, - uint8_t *data, - size_t size, - int start_of_line) -{ - size_t tag_size = strlen(curtag); - size_t i = 1, end_tag; - int block_lines = 0; - - while (i < size) { - i++; - while (i < size && !(data[i - 1] == '<' && data[i] == '/')) { - if (data[i] == '\n') - block_lines++; - - i++; - } - - /* If we are only looking for unindented tags, skip the tag - * if it doesn't follow a newline. - * - * The only exception to this is if the tag is still on the - * initial line; in that case it still counts as a closing - * tag - */ - if (start_of_line && block_lines > 0 && data[i - 2] != '\n') - continue; - - if (i + 2 + tag_size >= size) - break; - - end_tag = htmlblock_end_tag(curtag, tag_size, rndr, data + i - 1, size - i + 1); - if (end_tag) - return i + end_tag - 1; - } - - return 0; -} - - -/* parse_htmlblock • parsing of inline HTML block */ -static size_t -parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render) -{ - size_t i, j = 0, tag_end; - const char *curtag = NULL; - struct buf work = { data, 0, 0, 0 }; - - /* identification of the opening tag */ - if (size < 2 || data[0] != '<') - return 0; - - i = 1; - while (i < size && data[i] != '>' && data[i] != ' ') - i++; - - if (i < size) - curtag = find_block_tag((char *)data + 1, (int)i - 1); - - /* handling of special cases */ - if (!curtag) { - - /* HTML comment, laxist form */ - if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') { - i = 5; - - while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>')) - i++; - - i++; - - if (i < size) - j = is_empty(data + i, size - i); - - if (j) { - work.size = i + j; - if (do_render && rndr->cb.blockhtml) - rndr->cb.blockhtml(ob, &work, rndr->opaque); - return work.size; - } - } - - /* HR, which is the only self-closing block tag considered */ - if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) { - i = 3; - while (i < size && data[i] != '>') - i++; - - if (i + 1 < size) { - i++; - j = is_empty(data + i, size - i); - if (j) { - work.size = i + j; - if (do_render && rndr->cb.blockhtml) - rndr->cb.blockhtml(ob, &work, rndr->opaque); - return work.size; - } - } - } - - /* no special case recognised */ - return 0; - } - - /* looking for an unindented matching closing tag */ - /* followed by a blank line */ - tag_end = htmlblock_end(curtag, rndr, data, size, 1); - - /* if not found, trying a second pass looking for indented match */ - /* but not if tag is "ins" or "del" (following original Markdown.pl) */ - if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) { - tag_end = htmlblock_end(curtag, rndr, data, size, 0); - } - - if (!tag_end) - return 0; - - /* the end of the block has been found */ - work.size = tag_end; - if (do_render && rndr->cb.blockhtml) - rndr->cb.blockhtml(ob, &work, rndr->opaque); - - return tag_end; -} - -static void -parse_table_row( - struct buf *ob, - struct sd_markdown *rndr, - uint8_t *data, - size_t size, - size_t columns, - int *col_data, - int header_flag) -{ - size_t i = 0, col; - struct buf *row_work = 0; - - if (!rndr->cb.table_cell || !rndr->cb.table_row) - return; - - row_work = rndr_newbuf(rndr, BUFFER_SPAN); - - if (i < size && data[i] == '|') - i++; - - for (col = 0; col < columns && i < size; ++col) { - size_t cell_start, cell_end; - struct buf *cell_work; - - cell_work = rndr_newbuf(rndr, BUFFER_SPAN); - - while (i < size && _isspace(data[i])) - i++; - - cell_start = i; - - while (i < size && data[i] != '|') - i++; - - cell_end = i - 1; - - while (cell_end > cell_start && _isspace(data[cell_end])) - cell_end--; - - parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start); - rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_SPAN); - i++; - } - - for (; col < columns; ++col) { - struct buf empty_cell = { 0, 0, 0, 0 }; - rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque); - } - - rndr->cb.table_row(ob, row_work, rndr->opaque); - - rndr_popbuf(rndr, BUFFER_SPAN); -} - -static size_t -parse_table_header( - struct buf *ob, - struct sd_markdown *rndr, - uint8_t *data, - size_t size, - size_t *columns, - int **column_data) -{ - int pipes; - size_t i = 0, col, header_end, under_end; - - pipes = 0; - while (i < size && data[i] != '\n') - if (data[i++] == '|') - pipes++; - - if (i == size || pipes == 0) - return 0; - - header_end = i; - - while (header_end > 0 && _isspace(data[header_end - 1])) - header_end--; - - if (data[0] == '|') - pipes--; - - if (header_end && data[header_end - 1] == '|') - pipes--; - - *columns = pipes + 1; - *column_data = calloc(*columns, sizeof(int)); - - /* Parse the header underline */ - i++; - if (i < size && data[i] == '|') - i++; - - under_end = i; - while (under_end < size && data[under_end] != '\n') - under_end++; - - for (col = 0; col < *columns && i < under_end; ++col) { - size_t dashes = 0; - - while (i < under_end && data[i] == ' ') - i++; - - if (data[i] == ':') { - i++; (*column_data)[col] |= MKD_TABLE_ALIGN_L; - dashes++; - } - - while (i < under_end && data[i] == '-') { - i++; dashes++; - } - - if (i < under_end && data[i] == ':') { - i++; (*column_data)[col] |= MKD_TABLE_ALIGN_R; - dashes++; - } - - while (i < under_end && data[i] == ' ') - i++; - - if (i < under_end && data[i] != '|') - break; - - if (dashes < 3) - break; - - i++; - } - - if (col < *columns) - return 0; - - parse_table_row( - ob, rndr, data, - header_end, - *columns, - *column_data, - MKD_TABLE_HEADER - ); - - return under_end + 1; -} - -static size_t -parse_table( - struct buf *ob, - struct sd_markdown *rndr, - uint8_t *data, - size_t size) -{ - size_t i; - - struct buf *header_work = 0; - struct buf *body_work = 0; - - size_t columns; - int *col_data = NULL; - - header_work = rndr_newbuf(rndr, BUFFER_SPAN); - body_work = rndr_newbuf(rndr, BUFFER_BLOCK); - - i = parse_table_header(header_work, rndr, data, size, &columns, &col_data); - if (i > 0) { - - while (i < size) { - size_t row_start; - int pipes = 0; - - row_start = i; - - while (i < size && data[i] != '\n') - if (data[i++] == '|') - pipes++; - - if (pipes == 0 || i == size) { - i = row_start; - break; - } - - parse_table_row( - body_work, - rndr, - data + row_start, - i - row_start, - columns, - col_data, 0 - ); - - i++; - } - - if (rndr->cb.table) - rndr->cb.table(ob, header_work, body_work, rndr->opaque); - } - - free(col_data); - rndr_popbuf(rndr, BUFFER_SPAN); - rndr_popbuf(rndr, BUFFER_BLOCK); - return i; -} - -/* parse_block • parsing of one block, returning next uint8_t to parse */ -static void -parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) -{ - size_t beg, end, i; - uint8_t *txt_data; - beg = 0; - - if (rndr->work_bufs[BUFFER_SPAN].size + - rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting) - return; - - while (beg < size) { - txt_data = data + beg; - end = size - beg; - - if (is_atxheader(rndr, txt_data, end)) - beg += parse_atxheader(ob, rndr, txt_data, end); - - else if (data[beg] == '<' && rndr->cb.blockhtml && - (i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0) - beg += i; - - else if ((i = is_empty(txt_data, end)) != 0) - beg += i; - - else if (is_hrule(txt_data, end)) { - if (rndr->cb.hrule) - rndr->cb.hrule(ob, rndr->opaque); - - while (beg < size && data[beg] != '\n') - beg++; - - beg++; - } - - else if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 && - (i = parse_fencedcode(ob, rndr, txt_data, end)) != 0) - beg += i; - - else if ((rndr->ext_flags & MKDEXT_TABLES) != 0 && - (i = parse_table(ob, rndr, txt_data, end)) != 0) - beg += i; - - else if (prefix_quote(txt_data, end)) - beg += parse_blockquote(ob, rndr, txt_data, end); - - else if (prefix_code(txt_data, end)) - beg += parse_blockcode(ob, rndr, txt_data, end); - - else if (prefix_uli(txt_data, end)) - beg += parse_list(ob, rndr, txt_data, end, 0); - - else if (prefix_oli(txt_data, end)) - beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED); - - else - beg += parse_paragraph(ob, rndr, txt_data, end); - } -} - - - -/********************* - * REFERENCE PARSING * - *********************/ - -/* is_ref • returns whether a line is a reference or not */ -static int -is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs) -{ -/* int n; */ - size_t i = 0; - size_t id_offset, id_end; - size_t link_offset, link_end; - size_t title_offset, title_end; - size_t line_end; - - /* up to 3 optional leading spaces */ - if (beg + 3 >= end) return 0; - if (data[beg] == ' ') { i = 1; - if (data[beg + 1] == ' ') { i = 2; - if (data[beg + 2] == ' ') { i = 3; - if (data[beg + 3] == ' ') return 0; } } } - i += beg; - - /* id part: anything but a newline between brackets */ - if (data[i] != '[') return 0; - i++; - id_offset = i; - while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') - i++; - if (i >= end || data[i] != ']') return 0; - id_end = i; - - /* spacer: colon (space | tab)* newline? (space | tab)* */ - i++; - if (i >= end || data[i] != ':') return 0; - i++; - while (i < end && data[i] == ' ') i++; - if (i < end && (data[i] == '\n' || data[i] == '\r')) { - i++; - if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; } - while (i < end && data[i] == ' ') i++; - if (i >= end) return 0; - - /* link: whitespace-free sequence, optionally between angle brackets */ - if (data[i] == '<') - i++; - - link_offset = i; - - while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r') - i++; - - if (data[i - 1] == '>') link_end = i - 1; - else link_end = i; - - /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */ - while (i < end && data[i] == ' ') i++; - if (i < end && data[i] != '\n' && data[i] != '\r' - && data[i] != '\'' && data[i] != '"' && data[i] != '(') - return 0; - line_end = 0; - /* computing end-of-line */ - if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i; - if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') - line_end = i + 1; - - /* optional (space|tab)* spacer after a newline */ - if (line_end) { - i = line_end + 1; - while (i < end && data[i] == ' ') i++; } - - /* optional title: any non-newline sequence enclosed in '"() - alone on its line */ - title_offset = title_end = 0; - if (i + 1 < end - && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) { - i++; - title_offset = i; - /* looking for EOL */ - while (i < end && data[i] != '\n' && data[i] != '\r') i++; - if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') - title_end = i + 1; - else title_end = i; - /* stepping back */ - i -= 1; - while (i > title_offset && data[i] == ' ') - i -= 1; - if (i > title_offset - && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) { - line_end = title_end; - title_end = i; } } - - if (!line_end || link_end == link_offset) - return 0; /* garbage after the link empty link */ - - /* a valid ref has been found, filling-in return structures */ - if (last) - *last = line_end; - - if (refs) { - struct link_ref *ref; - - ref = add_link_ref(refs, data + id_offset, id_end - id_offset); - if (!ref) - return 0; - - ref->link = bufnew(link_end - link_offset); - bufput(ref->link, data + link_offset, link_end - link_offset); - - if (title_end > title_offset) { - ref->title = bufnew(title_end - title_offset); - bufput(ref->title, data + title_offset, title_end - title_offset); - } - } - - return 1; -} - -static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size) -{ - size_t i = 0, tab = 0; - - while (i < size) { - size_t org = i; - - while (i < size && line[i] != '\t') { - i++; tab++; - } - - if (i > org) - bufput(ob, line + org, i - org); - - if (i >= size) - break; - - do { - bufputc(ob, ' '); tab++; - } while (tab % 4); - - i++; - } -} - -/********************** - * EXPORTED FUNCTIONS * - **********************/ - -struct sd_markdown * -sd_markdown_new( - unsigned int extensions, - size_t max_nesting, - const struct sd_callbacks *callbacks, - void *opaque) -{ - struct sd_markdown *md = NULL; - - assert(max_nesting > 0 && callbacks); - - md = malloc(sizeof(struct sd_markdown)); - if (!md) - return NULL; - - memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks)); - - stack_init(&md->work_bufs[BUFFER_BLOCK], 4); - stack_init(&md->work_bufs[BUFFER_SPAN], 8); - - memset(md->active_char, 0x0, 256); - - if (md->cb.emphasis || md->cb.double_emphasis || md->cb.triple_emphasis) { - md->active_char['*'] = MD_CHAR_EMPHASIS; - md->active_char['_'] = MD_CHAR_EMPHASIS; - if (extensions & MKDEXT_STRIKETHROUGH) - md->active_char['~'] = MD_CHAR_EMPHASIS; - } - - if (md->cb.codespan) - md->active_char['`'] = MD_CHAR_CODESPAN; - - if (md->cb.linebreak) - md->active_char['\n'] = MD_CHAR_LINEBREAK; - - if (md->cb.image || md->cb.link) - md->active_char['['] = MD_CHAR_LINK; - - md->active_char['<'] = MD_CHAR_LANGLE; - md->active_char['\\'] = MD_CHAR_ESCAPE; - md->active_char['&'] = MD_CHAR_ENTITITY; - - if (extensions & MKDEXT_AUTOLINK) { - md->active_char[':'] = MD_CHAR_AUTOLINK_URL; - md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL; - md->active_char['w'] = MD_CHAR_AUTOLINK_WWW; - } - - if (extensions & MKDEXT_SUPERSCRIPT) - md->active_char['^'] = MD_CHAR_SUPERSCRIPT; - - /* Extension data */ - md->ext_flags = extensions; - md->opaque = opaque; - md->max_nesting = max_nesting; - md->in_link_body = 0; - - return md; -} - -void -sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md) -{ -#define MARKDOWN_GROW(x) ((x) + ((x) >> 1)) - static const char UTF8_BOM[] = {0xEF, 0xBB, 0xBF}; - - struct buf *text; - size_t beg, end; - - text = bufnew(64); - if (!text) - return; - - /* Preallocate enough space for our buffer to avoid expanding while copying */ - bufgrow(text, doc_size); - - /* reset the references table */ - memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); - - /* first pass: looking for references, copying everything else */ - beg = 0; - - /* Skip a possible UTF-8 BOM, even though the Unicode standard - * discourages having these in UTF-8 documents */ - if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0) - beg += 3; - - while (beg < doc_size) /* iterating over lines */ - if (is_ref(document, beg, doc_size, &end, md->refs)) - beg = end; - else { /* skipping to the next line */ - end = beg; - while (end < doc_size && document[end] != '\n' && document[end] != '\r') - end++; - - /* adding the line body if present */ - if (end > beg) - expand_tabs(text, document + beg, end - beg); - - while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) { - /* add one \n per newline */ - if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n')) - bufputc(text, '\n'); - end++; - } - - beg = end; - } - - /* pre-grow the output buffer to minimize allocations */ - bufgrow(ob, MARKDOWN_GROW(text->size)); - - /* second pass: actual rendering */ - if (md->cb.doc_header) - md->cb.doc_header(ob, md->opaque); - - if (text->size) { - /* adding a final newline if not already present */ - if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') - bufputc(text, '\n'); - - parse_block(ob, md, text->data, text->size); - } - - if (md->cb.doc_footer) - md->cb.doc_footer(ob, md->opaque); - - /* clean-up */ - bufrelease(text); - free_link_refs(md->refs); - - assert(md->work_bufs[BUFFER_SPAN].size == 0); - assert(md->work_bufs[BUFFER_BLOCK].size == 0); -} - -void -sd_markdown_free(struct sd_markdown *md) -{ - size_t i; - - for (i = 0; i < (size_t)md->work_bufs[BUFFER_SPAN].asize; ++i) - bufrelease(md->work_bufs[BUFFER_SPAN].item[i]); - - for (i = 0; i < (size_t)md->work_bufs[BUFFER_BLOCK].asize; ++i) - bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]); - - stack_free(&md->work_bufs[BUFFER_SPAN]); - stack_free(&md->work_bufs[BUFFER_BLOCK]); - - free(md); -} - -void -sd_version(int *ver_major, int *ver_minor, int *ver_revision) -{ - *ver_major = SUNDOWN_VER_MAJOR; - *ver_minor = SUNDOWN_VER_MINOR; - *ver_revision = SUNDOWN_VER_REVISION; -} - -/* vim: set filetype=c: */ diff --git a/src/sundown/markdown.h b/src/sundown/markdown.h deleted file mode 100644 index 6f6553e..0000000 --- a/src/sundown/markdown.h +++ /dev/null @@ -1,138 +0,0 @@ -/* markdown.h - generic markdown parser */ - -/* - * Copyright (c) 2009, Natacha Porté - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UPSKIRT_MARKDOWN_H -#define UPSKIRT_MARKDOWN_H - -#include "buffer.h" -#include "autolink.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define SUNDOWN_VERSION "1.16.0" -#define SUNDOWN_VER_MAJOR 1 -#define SUNDOWN_VER_MINOR 16 -#define SUNDOWN_VER_REVISION 0 - -/******************** - * TYPE DEFINITIONS * - ********************/ - -/* mkd_autolink - type of autolink */ -enum mkd_autolink { - MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/ - MKDA_NORMAL, /* normal http/http/ftp/mailto/etc link */ - MKDA_EMAIL, /* e-mail link without explit mailto: */ -}; - -enum mkd_tableflags { - MKD_TABLE_ALIGN_L = 1, - MKD_TABLE_ALIGN_R = 2, - MKD_TABLE_ALIGN_CENTER = 3, - MKD_TABLE_ALIGNMASK = 3, - MKD_TABLE_HEADER = 4 -}; - -enum mkd_extensions { - MKDEXT_NO_INTRA_EMPHASIS = (1 << 0), - MKDEXT_TABLES = (1 << 1), - MKDEXT_FENCED_CODE = (1 << 2), - MKDEXT_AUTOLINK = (1 << 3), - MKDEXT_STRIKETHROUGH = (1 << 4), - MKDEXT_SPACE_HEADERS = (1 << 6), - MKDEXT_SUPERSCRIPT = (1 << 7), - MKDEXT_LAX_SPACING = (1 << 8), -}; - -/* sd_callbacks - functions for rendering parsed data */ -struct sd_callbacks { - /* block level callbacks - NULL skips the block */ - void (*blockcode)(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque); - void (*blockquote)(struct buf *ob, const struct buf *text, void *opaque); - void (*blockhtml)(struct buf *ob,const struct buf *text, void *opaque); - void (*header)(struct buf *ob, const struct buf *text, int level, void *opaque); - void (*hrule)(struct buf *ob, void *opaque); - void (*list)(struct buf *ob, const struct buf *text, int flags, void *opaque); - void (*listitem)(struct buf *ob, const struct buf *text, int flags, void *opaque); - void (*paragraph)(struct buf *ob, const struct buf *text, void *opaque); - void (*table)(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque); - void (*table_row)(struct buf *ob, const struct buf *text, void *opaque); - void (*table_cell)(struct buf *ob, const struct buf *text, int flags, void *opaque); - - - /* span level callbacks - NULL or return 0 prints the span verbatim */ - int (*autolink)(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque); - int (*codespan)(struct buf *ob, const struct buf *text, void *opaque); - int (*double_emphasis)(struct buf *ob, const struct buf *text, void *opaque); - int (*emphasis)(struct buf *ob, const struct buf *text, void *opaque); - int (*image)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque); - int (*linebreak)(struct buf *ob, void *opaque); - int (*link)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque); - int (*raw_html_tag)(struct buf *ob, const struct buf *tag, void *opaque); - int (*triple_emphasis)(struct buf *ob, const struct buf *text, void *opaque); - int (*strikethrough)(struct buf *ob, const struct buf *text, void *opaque); - int (*superscript)(struct buf *ob, const struct buf *text, void *opaque); - - /* low level callbacks - NULL copies input directly into the output */ - void (*entity)(struct buf *ob, const struct buf *entity, void *opaque); - void (*normal_text)(struct buf *ob, const struct buf *text, void *opaque); - - /* header and footer */ - void (*doc_header)(struct buf *ob, void *opaque); - void (*doc_footer)(struct buf *ob, void *opaque); -}; - -struct sd_markdown; - -/********* - * FLAGS * - *********/ - -/* list/listitem flags */ -#define MKD_LIST_ORDERED 1 -#define MKD_LI_BLOCK 2 /*
  • containing block data */ - -/********************** - * EXPORTED FUNCTIONS * - **********************/ - -extern struct sd_markdown * -sd_markdown_new( - unsigned int extensions, - size_t max_nesting, - const struct sd_callbacks *callbacks, - void *opaque); - -extern void -sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md); - -extern void -sd_markdown_free(struct sd_markdown *md); - -extern void -sd_version(int *major, int *minor, int *revision); - -#ifdef __cplusplus -} -#endif - -#endif - -/* vim: set filetype=c: */ diff --git a/src/sundown/stack.c b/src/sundown/stack.c deleted file mode 100644 index ce069ff..0000000 --- a/src/sundown/stack.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "stack.h" -#include - -int -stack_grow(struct stack *st, size_t new_size) -{ - void **new_st; - - if (st->asize >= new_size) - return 0; - - new_st = realloc(st->item, new_size * sizeof(void *)); - if (new_st == NULL) - return -1; - - memset(new_st + st->asize, 0x0, - (new_size - st->asize) * sizeof(void *)); - - st->item = new_st; - st->asize = new_size; - - if (st->size > new_size) - st->size = new_size; - - return 0; -} - -void -stack_free(struct stack *st) -{ - if (!st) - return; - - free(st->item); - - st->item = NULL; - st->size = 0; - st->asize = 0; -} - -int -stack_init(struct stack *st, size_t initial_size) -{ - st->item = NULL; - st->size = 0; - st->asize = 0; - - if (!initial_size) - initial_size = 8; - - return stack_grow(st, initial_size); -} - -void * -stack_pop(struct stack *st) -{ - if (!st->size) - return NULL; - - return st->item[--st->size]; -} - -int -stack_push(struct stack *st, void *item) -{ - if (stack_grow(st, st->size * 2) < 0) - return -1; - - st->item[st->size++] = item; - return 0; -} - -void * -stack_top(struct stack *st) -{ - if (!st->size) - return NULL; - - return st->item[st->size - 1]; -} - diff --git a/src/sundown/stack.h b/src/sundown/stack.h deleted file mode 100644 index 08ff030..0000000 --- a/src/sundown/stack.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef STACK_H__ -#define STACK_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct stack { - void **item; - size_t size; - size_t asize; -}; - -void stack_free(struct stack *); -int stack_grow(struct stack *, size_t); -int stack_init(struct stack *, size_t); - -int stack_push(struct stack *, void *); - -void *stack_pop(struct stack *); -void *stack_top(struct stack *); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/wrapper.c b/src/wrapper.c deleted file mode 100644 index 62129cd..0000000 --- a/src/wrapper.c +++ /dev/null @@ -1,336 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include - -#include "sundown/markdown.h" -#include "sundown/buffer.h" - -#include "wrapper.h" - - -#define PROCESS_SPAN(method_name, ...) {\ - struct renderopt *opt = opaque;\ - PyObject *ret = PyObject_CallMethodObjArgs(\ - (PyObject *) opt->self, PyUnicode_FromString(method_name),\ - __VA_ARGS__);\ - if (ret == NULL || ret == Py_None) {\ - PyObject *r_ex = PyErr_Occurred();\ - if (r_ex != NULL)\ - PyErr_Print();\ - return 0;\ - }\ - if (PyUnicode_Check(ret)) {\ - PyObject *byte_string = PyUnicode_AsEncodedString(ret, "utf-8", "strict");\ - bufputs(ob, PyBytes_AsString(byte_string));\ - } else {\ - bufputs(ob, PyBytes_AsString(ret));\ - }\ - return 1;\ -} - - -#define PROCESS_BLOCK(method_name, ...) {\ - struct renderopt *opt = opaque;\ - PyObject *ret = PyObject_CallMethodObjArgs(\ - (PyObject *) opt->self, PyUnicode_FromString(method_name),\ - __VA_ARGS__);\ - if (ret == NULL || ret == Py_None) {\ - PyObject *r_ex = PyErr_Occurred();\ - if (r_ex != NULL)\ - PyErr_Print();\ - return;\ - }\ - if (PyUnicode_Check(ret)) {\ - PyObject *byte_string = PyUnicode_AsEncodedString(ret, "utf-8", "strict");\ - bufputs(ob, PyBytes_AsString(byte_string));\ - } else {\ - bufputs(ob, PyBytes_AsString(ret));\ - }\ -} - - -#define PY_STR(b) (b != NULL ? PyUnicode_FromStringAndSize((const char *) b->data, (int) b->size) : Py_None) - -#if PY_MAJOR_VERSION >= 3 - #define PY_INT(i) PyLong_FromLong(i) -#else - #define PY_INT(i) PyInt_FromLong(i) -#endif - - -/* Block level - ----------- */ - - -static void -rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque) -{ - PROCESS_BLOCK("block_code", PY_STR(text), PY_STR(lang), NULL); -} - - -static void -rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_BLOCK("block_quote", PY_STR(text), NULL); -} - - -static void -rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_BLOCK("block_html", PY_STR(text), NULL); -} - - -static void -rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque) -{ - PROCESS_BLOCK("header", PY_STR(text), PY_INT(level), NULL); -} - - -static void -rndr_hrule(struct buf *ob, void *opaque) -{ - PROCESS_BLOCK("hrule", NULL); -} - - -static void -rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque) -{ - PyObject *is_ordered = Py_False; - if (flags & MKD_LIST_ORDERED) { - is_ordered = Py_True; - } - - PROCESS_BLOCK("list", PY_STR(text), is_ordered, NULL); -} - - -static void -rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque) -{ - PyObject *is_ordered = Py_False; - if (flags & MKD_LIST_ORDERED) { - is_ordered = Py_True; - } - - PROCESS_BLOCK("list_item", PY_STR(text), is_ordered, NULL); -} - - -static void -rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_BLOCK("paragraph", PY_STR(text), NULL); -} - - -static void -rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque) -{ - PROCESS_BLOCK("table", PY_STR(header), PY_STR(body), NULL); -} - - -static void -rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_BLOCK("table_row", PY_STR(text), NULL); -} - - -static void -rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque) -{ - PROCESS_BLOCK("table_cell", PY_STR(text), PY_INT(flags), NULL); -} - - -/* Span level - ---------- */ - - -static int -rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque) -{ - PyObject *is_email = Py_False; - if (type == MKDA_EMAIL) { - is_email = Py_True; - } - - PROCESS_SPAN("autolink", PY_STR(link), is_email, NULL); -} - - -static int -rndr_codespan(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_SPAN("codespan", PY_STR(text), NULL); -} - - -static int -rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_SPAN("double_emphasis", PY_STR(text), NULL); -} - - -static int -rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_SPAN("emphasis", PY_STR(text), NULL); -} - - -static int -rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque) -{ - PROCESS_SPAN("image", PY_STR(link), PY_STR(title), PY_STR(alt), NULL); -} - - -static int -rndr_linebreak(struct buf *ob, void *opaque) -{ - PROCESS_SPAN("linebreak", NULL); -} - - -static int -rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque) -{ - PROCESS_SPAN("link", PY_STR(link), PY_STR(title), PY_STR(content), NULL); -} - - -static int -rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_SPAN("raw_html", PY_STR(text), NULL); -} - - -static int -rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_SPAN("triple_emphasis", PY_STR(text), NULL); -} - - -static int -rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_SPAN("strikethrough", PY_STR(text), NULL); -} - - -static int -rndr_superscript(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_SPAN("superscript", PY_STR(text), NULL); -} - - -/* Direct writes - ------------- */ - - -static void -rndr_entity(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_BLOCK("entity", PY_STR(text), NULL); -} - - -static void -rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque) -{ - PROCESS_BLOCK("normal_text", PY_STR(text), NULL); -} - - -static void -rndr_doc_header(struct buf *ob, void *opaque) -{ - PROCESS_BLOCK("doc_header", NULL); -} - - -static void -rndr_doc_footer(struct buf *ob, void *opaque) -{ - PROCESS_BLOCK("doc_footer", NULL); -} - - -struct sd_callbacks callback_funcs = { - rndr_blockcode, - rndr_blockquote, - rndr_raw_block, - rndr_header, - rndr_hrule, - rndr_list, - rndr_listitem, - rndr_paragraph, - rndr_table, - rndr_tablerow, - rndr_tablecell, - - rndr_autolink, - rndr_codespan, - rndr_double_emphasis, - rndr_emphasis, - rndr_image, - rndr_linebreak, - rndr_link, - rndr_raw_html, - rndr_triple_emphasis, - rndr_strikethrough, - rndr_superscript, - - rndr_entity, - rndr_normal_text, - - rndr_doc_header, - rndr_doc_footer, -}; - - -const char *method_names[] = { - "block_code", - "block_quote", - "block_html", - "header", - "hrule", - "list", - "list_item", - "paragraph", - "table", - "table_row", - "table_cell", - - "autolink", - "codespan", - "double_emphasis", - "emphasis", - "image", - "linebreak", - "link", - "raw_html", - "triple_emphasis", - "strikethrough", - "superscript", - - "entity", - "normal_text", - - "doc_header", - "doc_footer" -}; - - -const size_t method_count = sizeof( - method_names)/sizeof(char *); diff --git a/src/wrapper.h b/src/wrapper.h deleted file mode 100644 index f731530..0000000 --- a/src/wrapper.h +++ /dev/null @@ -1,15 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include - -#include "sundown/html.h" - - -struct renderopt { - struct html_renderopt html; - void *self; -}; - - -extern struct sd_callbacks callback_funcs; -extern const char *method_names[]; -extern const size_t method_count; diff --git a/src/wrapper.pxd b/src/wrapper.pxd deleted file mode 100644 index a1e6709..0000000 --- a/src/wrapper.pxd +++ /dev/null @@ -1,16 +0,0 @@ -from sundown cimport buf, html_renderopt, mkd_autolink, sd_callbacks - - -cdef extern from *: - ctypedef char* const_char_ptr "const char *" - ctypedef char* const_size_t "const size_t" - - -cdef extern from 'wrapper.h': - struct renderopt: - html_renderopt html - void *self - - sd_callbacks callback_funcs - const_char_ptr method_names[] - const_size_t method_count diff --git a/vendor/sundown b/vendor/sundown deleted file mode 160000 index 37728fb..0000000 --- a/vendor/sundown +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 37728fb2d7137ff7c37d0a474cb827a8d6d846d8