├── Source ├── cmark_gfm │ ├── include │ │ ├── table.h │ │ ├── mention.h │ │ ├── autolink.h │ │ ├── tagfilter.h │ │ ├── checkbox.h │ │ ├── cmark_version.h │ │ ├── strikethrough.h │ │ ├── registry.h │ │ ├── iterator.h │ │ ├── footnotes.h │ │ ├── references.h │ │ ├── cmark_ctype.h │ │ ├── plugin.h │ │ ├── html.h │ │ ├── utf8.h │ │ ├── ext_scanners.h │ │ ├── inlines.h │ │ ├── map.h │ │ ├── libcmark_gfm.h │ │ ├── core-extensions.h │ │ ├── cmark_export.h │ │ ├── cmarkextensions_export.h │ │ ├── syntax_extension.h │ │ ├── config.h │ │ ├── houdini.h │ │ ├── render.h │ │ ├── parser.h │ │ ├── node.h │ │ ├── buffer.h │ │ ├── scanners.h │ │ └── chunk.h │ ├── module.modulemap │ ├── linked_list.c │ ├── core-extensions.c │ ├── plugin.c │ ├── footnotes.c │ ├── references.c │ ├── tagfilter.c │ ├── cmark.c │ ├── registry.c │ ├── cmark_ctype.c │ ├── houdini_html_e.c │ ├── arena.c │ ├── map.c │ ├── houdini_href_e.c │ ├── mention.c │ ├── houdini_html_u.c │ ├── checkbox.c │ ├── iterator.c │ ├── syntax_extension.c │ ├── strikethrough.c │ ├── xml.c │ ├── plaintext.c │ ├── man.c │ ├── render.c │ ├── buffer.c │ ├── scanners.re │ ├── utf8.c │ └── latex.c ├── TableRow.swift ├── ListElement.swift ├── cmark-gfm-swift.h ├── Block+TextElement.swift ├── Element.swift ├── TextElement.swift ├── Block+TableRow.swift ├── Info.plist ├── Block+ListElement.swift ├── Inline+TextElement.swift ├── Node+Elements.swift ├── Node.swift ├── ASTOperations.swift └── SwiftAST.swift ├── cmark-gfm-swift.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── cmark_gfm_swiftTests.xcscheme │ └── cmark-gfm-swift.xcscheme ├── .travis.yml ├── README.md ├── cmark_gfm_swiftTests └── Info.plist ├── .gitignore ├── LICENSE └── cmark-gfm-swift.podspec /Source/cmark_gfm/include/table.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLE_H 2 | #define TABLE_H 3 | 4 | #include "core-extensions.h" 5 | 6 | cmark_syntax_extension *create_table_extension(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/mention.h: -------------------------------------------------------------------------------- 1 | #ifndef MENTION_H 2 | #define MENTION_H 3 | 4 | #include "core-extensions.h" 5 | 6 | cmark_syntax_extension *create_mention_extension(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/autolink.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTOLINK_H 2 | #define AUTOLINK_H 3 | 4 | #include "core-extensions.h" 5 | 6 | cmark_syntax_extension *create_autolink_extension(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/tagfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef TAGFILTER_H 2 | #define TAGFILTER_H 3 | 4 | #include "core-extensions.h" 5 | 6 | cmark_syntax_extension *create_tagfilter_extension(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /Source/cmark_gfm/module.modulemap: -------------------------------------------------------------------------------- 1 | module cmark_gfm [system][extern_c] { 2 | header "include/cmark.h" 3 | header "include/node.h" 4 | header "include/core-extensions.h" 5 | export * 6 | } 7 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/checkbox.h: -------------------------------------------------------------------------------- 1 | #ifndef CHECKBOX_H 2 | #define CHECKBOX_H 3 | 4 | #include "core-extensions.h" 5 | 6 | cmark_syntax_extension *create_checkbox_extension(void); 7 | 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /cmark-gfm-swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/cmark_version.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_VERSION_H 2 | #define CMARK_VERSION_H 3 | 4 | #define CMARK_VERSION ((0 << 24) | (28 << 16) | (3 << 8) | 11) 5 | #define CMARK_VERSION_STRING "0.28.3.gfm.11" 6 | #define CMARK_GFM_VERSION 11 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode9.3 3 | script: 4 | - xcodebuild clean test -project cmark-gfm-swift.xcodeproj -scheme cmark-gfm-swift -destination "platform=iOS Simulator,name=iPhone X,OS=11.3" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO -quiet -------------------------------------------------------------------------------- /Source/TableRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableRow.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum TableRow { 11 | case header(cells: [TextLine]) 12 | case row(cells: [TextLine]) 13 | } 14 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/strikethrough.h: -------------------------------------------------------------------------------- 1 | #ifndef STRIKETHROUGH_H 2 | #define STRIKETHROUGH_H 3 | 4 | #include "core-extensions.h" 5 | 6 | //extern cmark_node_type CMARK_NODE_STRIKETHROUGH; 7 | cmark_syntax_extension *create_strikethrough_extension(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /Source/ListElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListElement.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum ListElement { 11 | case text(text: TextLine) 12 | case list(children: [[ListElement]], type: ListType, level: Int) 13 | } 14 | -------------------------------------------------------------------------------- /cmark-gfm-swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Source/cmark-gfm-swift.h: -------------------------------------------------------------------------------- 1 | // 2 | // cmark-gfm-swift.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | @import Foundation; 9 | 10 | //! Project version number for cmark-gfm-swift. 11 | FOUNDATION_EXPORT double cmark_gfm_swiftVersionNumber; 12 | 13 | //! Project version string for cmark-gfm-swift. 14 | FOUNDATION_EXPORT const unsigned char cmark_gfm_swiftVersionString[]; -------------------------------------------------------------------------------- /Source/cmark_gfm/include/registry.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_REGISTRY_H 2 | #define CMARK_REGISTRY_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "cmark.h" 9 | #include "plugin.h" 10 | 11 | CMARK_EXPORT 12 | void cmark_register_plugin(cmark_plugin_init_func reg_fn); 13 | 14 | CMARK_EXPORT 15 | void cmark_release_plugins(void); 16 | 17 | CMARK_EXPORT 18 | cmark_llist *cmark_list_syntax_extensions(cmark_mem *mem); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/iterator.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_ITERATOR_H 2 | #define CMARK_ITERATOR_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "cmark.h" 9 | #include "memory.h" 10 | 11 | typedef struct { 12 | cmark_event_type ev_type; 13 | cmark_node *node; 14 | } cmark_iter_state; 15 | 16 | struct cmark_iter { 17 | cmark_mem *mem; 18 | cmark_node *root; 19 | cmark_iter_state cur; 20 | cmark_iter_state next; 21 | }; 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/footnotes.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_FOOTNOTES_H 2 | #define CMARK_FOOTNOTES_H 3 | 4 | #include "map.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | struct cmark_footnote { 11 | cmark_map_entry entry; 12 | cmark_node *node; 13 | unsigned int ix; 14 | }; 15 | 16 | typedef struct cmark_footnote cmark_footnote; 17 | 18 | void cmark_footnote_create(cmark_map *map, cmark_node *node); 19 | cmark_map *cmark_footnote_map_new(cmark_mem *mem); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /Source/Block+TextElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Block+TextElement.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Block { 11 | var textElements: [TextElement]? { 12 | if case .paragraph(let text) = self { 13 | return text.textElements 14 | } 15 | return nil 16 | } 17 | } 18 | 19 | extension Sequence where Iterator.Element == Block { 20 | var textElements: [[TextElement]] { return compactMap { $0.textElements } } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Element.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Element.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/29/18. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Element { 11 | case text(items: TextLine) 12 | case quote(items: TextLine, level: Int) 13 | case image(title: String, url: String) 14 | case html(text: String) 15 | case table(rows: [TableRow]) 16 | case hr 17 | case codeBlock(text: String, language: String?) 18 | case heading(text: TextLine, level: Int) 19 | case list(items: [[ListElement]], type: ListType) 20 | } 21 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/references.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_REFERENCES_H 2 | #define CMARK_REFERENCES_H 3 | 4 | #include "map.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | struct cmark_reference { 11 | cmark_map_entry entry; 12 | cmark_chunk url; 13 | cmark_chunk title; 14 | }; 15 | 16 | typedef struct cmark_reference cmark_reference; 17 | 18 | void cmark_reference_create(cmark_map *map, cmark_chunk *label, 19 | cmark_chunk *url, cmark_chunk *title); 20 | cmark_map *cmark_reference_map_new(cmark_mem *mem); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/cmark_ctype.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_CMARK_CTYPE_H 2 | #define CMARK_CMARK_CTYPE_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "cmark_export.h" 9 | 10 | /** Locale-independent versions of functions from ctype.h. 11 | * We want cmark to behave the same no matter what the system locale. 12 | */ 13 | 14 | CMARK_EXPORT 15 | int cmark_isspace(char c); 16 | 17 | CMARK_EXPORT 18 | int cmark_ispunct(char c); 19 | 20 | CMARK_EXPORT 21 | int cmark_isalnum(char c); 22 | 23 | CMARK_EXPORT 24 | int cmark_isdigit(char c); 25 | 26 | CMARK_EXPORT 27 | int cmark_isalpha(char c); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Source/TextElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextElement.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | 10 | public typealias TextLine = [TextElement] 11 | 12 | public enum TextElement { 13 | case text(text: String) 14 | case softBreak 15 | case lineBreak 16 | case code(text: String) 17 | case emphasis(children: [TextElement]) 18 | case strong(children: [TextElement]) 19 | case link(children: [TextElement], title: String?, url: String?) 20 | case strikethrough(children: [TextElement]) 21 | case mention(login: String) 22 | case checkbox(checked: Bool, originalRange: NSRange) 23 | } 24 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_PLUGIN_H 2 | #define CMARK_PLUGIN_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "cmark.h" 9 | #include "cmark_extension_api.h" 10 | 11 | /** 12 | * cmark_plugin: 13 | * 14 | * A plugin structure, which should be filled by plugin's 15 | * init functions. 16 | */ 17 | struct cmark_plugin { 18 | cmark_llist *syntax_extensions; 19 | }; 20 | 21 | cmark_llist * 22 | cmark_plugin_steal_syntax_extensions(cmark_plugin *plugin); 23 | 24 | cmark_plugin * 25 | cmark_plugin_new(void); 26 | 27 | void 28 | cmark_plugin_free(cmark_plugin *plugin); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Source/Block+TableRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Block+TableRow.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Block { 11 | var tableCell: TextLine? { 12 | switch self { 13 | case .tableCell(let items): return items.textElements 14 | default: return nil 15 | } 16 | } 17 | var tableRow: TableRow? { 18 | switch self { 19 | case .tableRow(let items): return .row(cells: items.compactMap { $0.tableCell }) 20 | case .tableHeader(let items): return .header(cells: items.compactMap { $0.tableCell }) 21 | default: return nil 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cmark-gfm-swift 2 | 3 | ⚠️ WIP ⚠ 4 | 5 | A Swift wrapper of cmark with GitHub Flavored Markdown extensions. 6 | 7 | ### Resources 8 | 9 | - [GFM spec](https://github.github.com/gfm/) with [blog post](https://githubengineering.com/a-formal-spec-for-github-markdown/) 10 | - [CommonMark extensions](https://github.com/commonmark/CommonMark/wiki/Deployed-Extensions) 11 | - [Using cmark gfm extensions](https://medium.com/@krisgbaker/using-cmark-gfm-extensions-aad759894a89) 12 | 13 | ### Acknowledgements 14 | 15 | - [cmark](https://github.com/commonmark/cmark) 16 | - [GitHub cmark fork](https://github.com/github/cmark) 17 | - [commonmark-swift](https://github.com/chriseidhof/commonmark-swift) 18 | - [libcmark_gfm](https://github.com/KristopherGBaker/libcmark_gfm) -------------------------------------------------------------------------------- /cmark_gfm_swiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/html.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_HTML_H 2 | #define CMARK_HTML_H 3 | 4 | #include "buffer.h" 5 | #include "node.h" 6 | 7 | CMARK_INLINE 8 | static void cmark_html_render_cr(cmark_strbuf *html) { 9 | if (html->size && html->ptr[html->size - 1] != '\n') 10 | cmark_strbuf_putc(html, '\n'); 11 | } 12 | 13 | #define BUFFER_SIZE 100 14 | 15 | CMARK_INLINE 16 | static void cmark_html_render_sourcepos(cmark_node *node, cmark_strbuf *html, int options) { 17 | char buffer[BUFFER_SIZE]; 18 | if (CMARK_OPT_SOURCEPOS & options) { 19 | snprintf(buffer, BUFFER_SIZE, " data-sourcepos=\"%d:%d-%d:%d\"", 20 | cmark_node_get_start_line(node), cmark_node_get_start_column(node), 21 | cmark_node_get_end_line(node), cmark_node_get_end_column(node)); 22 | cmark_strbuf_puts(html, buffer); 23 | } 24 | } 25 | 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /Source/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/utf8.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_UTF8_H 2 | #define CMARK_UTF8_H 3 | 4 | #include 5 | #include "buffer.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | CMARK_EXPORT 12 | void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str, 13 | bufsize_t len); 14 | 15 | CMARK_EXPORT 16 | void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf); 17 | 18 | CMARK_EXPORT 19 | int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, int32_t *dst); 20 | 21 | CMARK_EXPORT 22 | void cmark_utf8proc_check(cmark_strbuf *dest, const uint8_t *line, 23 | bufsize_t size); 24 | 25 | CMARK_EXPORT 26 | int cmark_utf8proc_is_space(int32_t uc); 27 | 28 | CMARK_EXPORT 29 | int cmark_utf8proc_is_punctuation(int32_t uc); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/ext_scanners.h: -------------------------------------------------------------------------------- 1 | #include "chunk.h" 2 | #include "cmark.h" 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | bufsize_t _ext_scan_at(bufsize_t (*scanner)(const unsigned char *), 9 | unsigned char *ptr, int len, bufsize_t offset); 10 | bufsize_t _scan_table_start(const unsigned char *p); 11 | bufsize_t _scan_table_cell(const unsigned char *p); 12 | bufsize_t _scan_table_cell_end(const unsigned char *p); 13 | bufsize_t _scan_table_row_end(const unsigned char *p); 14 | 15 | #define scan_table_start(c, l, n) _ext_scan_at(&_scan_table_start, c, l, n) 16 | #define scan_table_cell(c, l, n) _ext_scan_at(&_scan_table_cell, c, l, n) 17 | #define scan_table_cell_end(c, l, n) _ext_scan_at(&_scan_table_cell_end, c, l, n) 18 | #define scan_table_row_end(c, l, n) _ext_scan_at(&_scan_table_row_end, c, l, n) 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/inlines.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_INLINES_H 2 | #define CMARK_INLINES_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "references.h" 9 | 10 | cmark_chunk cmark_clean_url(cmark_mem *mem, cmark_chunk *url); 11 | cmark_chunk cmark_clean_title(cmark_mem *mem, cmark_chunk *title); 12 | 13 | CMARK_EXPORT 14 | void cmark_parse_inlines(cmark_parser *parser, 15 | cmark_node *parent, 16 | cmark_map *refmap, 17 | int options); 18 | 19 | bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_chunk *input, 20 | cmark_map *refmap); 21 | 22 | void cmark_inlines_add_special_character(unsigned char c, bool emphasis); 23 | void cmark_inlines_remove_special_character(unsigned char c, bool emphasis); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /Source/cmark_gfm/linked_list.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cmark.h" 4 | 5 | cmark_llist *cmark_llist_append(cmark_mem *mem, cmark_llist *head, void *data) { 6 | cmark_llist *tmp; 7 | cmark_llist *new_node = (cmark_llist *) mem->calloc(1, sizeof(cmark_llist)); 8 | 9 | new_node->data = data; 10 | new_node->next = NULL; 11 | 12 | if (!head) 13 | return new_node; 14 | 15 | for (tmp = head; tmp->next; tmp=tmp->next); 16 | 17 | tmp->next = new_node; 18 | 19 | return head; 20 | } 21 | 22 | void cmark_llist_free_full(cmark_mem *mem, cmark_llist *head, cmark_free_func free_func) { 23 | cmark_llist *tmp, *prev; 24 | 25 | for (tmp = head; tmp;) { 26 | if (free_func) 27 | free_func(mem, tmp->data); 28 | 29 | prev = tmp; 30 | tmp = tmp->next; 31 | mem->free(prev); 32 | } 33 | } 34 | 35 | void cmark_llist_free(cmark_mem *mem, cmark_llist *head) { 36 | cmark_llist_free_full(mem, head, NULL); 37 | } 38 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/map.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_MAP_H 2 | #define CMARK_MAP_H 3 | 4 | #include "memory.h" 5 | #include "chunk.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | struct cmark_map_entry { 12 | struct cmark_map_entry *next; 13 | unsigned char *label; 14 | unsigned int age; 15 | }; 16 | 17 | typedef struct cmark_map_entry cmark_map_entry; 18 | 19 | struct cmark_map; 20 | 21 | typedef void (*cmark_map_free_f)(struct cmark_map *, cmark_map_entry *); 22 | 23 | struct cmark_map { 24 | cmark_mem *mem; 25 | cmark_map_entry *refs; 26 | cmark_map_entry **sorted; 27 | unsigned int size; 28 | cmark_map_free_f free; 29 | }; 30 | 31 | typedef struct cmark_map cmark_map; 32 | 33 | unsigned char *normalize_map_label(cmark_mem *mem, cmark_chunk *ref); 34 | cmark_map *cmark_map_new(cmark_mem *mem, cmark_map_free_f free); 35 | void cmark_map_free(cmark_map *map); 36 | cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /Source/cmark_gfm/core-extensions.c: -------------------------------------------------------------------------------- 1 | #include "core-extensions.h" 2 | #include "autolink.h" 3 | #include "strikethrough.h" 4 | #include "table.h" 5 | #include "tagfilter.h" 6 | #include "registry.h" 7 | #include "plugin.h" 8 | #include "mention.h" 9 | #include "checkbox.h" 10 | 11 | static int core_extensions_registration(cmark_plugin *plugin) { 12 | cmark_plugin_register_syntax_extension(plugin, create_table_extension()); 13 | cmark_plugin_register_syntax_extension(plugin, 14 | create_strikethrough_extension()); 15 | cmark_plugin_register_syntax_extension(plugin, create_autolink_extension()); 16 | cmark_plugin_register_syntax_extension(plugin, create_tagfilter_extension()); 17 | cmark_plugin_register_syntax_extension(plugin, create_mention_extension()); 18 | cmark_plugin_register_syntax_extension(plugin, create_checkbox_extension()); 19 | return 1; 20 | } 21 | 22 | void core_extensions_ensure_registered(void) { 23 | static int registered = 0; 24 | 25 | if (!registered) { 26 | cmark_register_plugin(core_extensions_registration); 27 | registered = 1; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/libcmark_gfm.h: -------------------------------------------------------------------------------- 1 | // 2 | // libcmark_gfm.h 3 | // libcmark_gfm 4 | // 5 | // Created by Kris Baker on 12/23/17. 6 | // Copyright © 2017 Kristopher Baker. All rights reserved. 7 | // 8 | 9 | #ifndef libcmark_gfm_h 10 | #define libcmark_gfm_h 11 | 12 | #include "autolink.h" 13 | #include "buffer.h" 14 | #include "chunk.h" 15 | #include "cmark.h" 16 | #include "cmark_ctype.h" 17 | #include "cmark_export.h" 18 | #include "cmark_extension_api.h" 19 | #include "cmark_version.h" 20 | #include "cmarkextensions_export.h" 21 | #include "config.h" 22 | #include "core-extensions.h" 23 | #include "ext_scanners.h" 24 | #include "footnotes.h" 25 | #include "houdini.h" 26 | #include "html.h" 27 | #include "inlines.h" 28 | #include "iterator.h" 29 | #include "map.h" 30 | #include "node.h" 31 | #include "parser.h" 32 | #include "plugin.h" 33 | #include "references.h" 34 | #include "registry.h" 35 | #include "render.h" 36 | #include "scanners.h" 37 | #include "strikethrough.h" 38 | #include "syntax_extension.h" 39 | #include "table.h" 40 | #include "tagfilter.h" 41 | #include "utf8.h" 42 | 43 | #endif /* libcmark_gfm_h */ 44 | -------------------------------------------------------------------------------- /Source/Block+ListElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Block+ListElement.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Block { 11 | func listElement(_ level: Int) -> ListElement? { 12 | switch self { 13 | case .paragraph(let text): 14 | return .text(text: text.textElements) 15 | case .blockQuote(let items): 16 | return .text(text: items.textElements.flatMap { $0 }) 17 | case .custom(let literal): 18 | return .text(text: [.text(text: literal)]) 19 | case .codeBlock(let text, _): 20 | return .text(text: [.code(text: text)]) 21 | case .list(let items, let type): 22 | let deeper = level + 1 23 | return .list(children: items.compactMap { $0.listElements(deeper) }, type: type, level: deeper) 24 | default: return nil 25 | } 26 | } 27 | } 28 | 29 | extension Sequence where Iterator.Element == Block { 30 | func listElements(_ level: Int) -> [ListElement] { 31 | return compactMap { $0.listElement(level) } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Ryan Nystrom 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/core-extensions.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_EXTENSIONS_H 2 | #define CORE_EXTENSIONS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "cmark_extension_api.h" 9 | #include "cmarkextensions_export.h" 10 | #include 11 | 12 | CMARKEXTENSIONS_EXPORT 13 | void core_extensions_ensure_registered(void); 14 | 15 | CMARKEXTENSIONS_EXPORT 16 | uint16_t cmarkextensions_get_table_columns(cmark_node *node); 17 | 18 | CMARKEXTENSIONS_EXPORT 19 | uint8_t *cmarkextensions_get_table_alignments(cmark_node *node); 20 | 21 | extern cmark_node_type CMARK_NODE_TABLE; 22 | extern cmark_node_type CMARK_NODE_TABLE_ROW; 23 | extern cmark_node_type CMARK_NODE_TABLE_CELL; 24 | extern cmark_node_type CMARK_NODE_STRIKETHROUGH; 25 | extern cmark_node_type CMARK_NODE_MENTION; 26 | extern const char *cmark_node_get_mention_login(cmark_node *node); 27 | extern cmark_node_type CMARK_NODE_CHECKBOX; 28 | extern int cmark_node_get_checkbox_checked(cmark_node *node); 29 | extern int cmark_node_get_checkbox_location(cmark_node *node); 30 | extern int cmark_node_get_checkbox_length(cmark_node *node); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Source/cmark_gfm/plugin.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "plugin.h" 4 | 5 | extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR; 6 | 7 | int cmark_plugin_register_syntax_extension(cmark_plugin * plugin, 8 | cmark_syntax_extension * extension) { 9 | plugin->syntax_extensions = cmark_llist_append(&CMARK_DEFAULT_MEM_ALLOCATOR, plugin->syntax_extensions, extension); 10 | return 1; 11 | } 12 | 13 | cmark_plugin * 14 | cmark_plugin_new(void) { 15 | cmark_plugin *res = (cmark_plugin *) CMARK_DEFAULT_MEM_ALLOCATOR.calloc(1, sizeof(cmark_plugin)); 16 | 17 | res->syntax_extensions = NULL; 18 | 19 | return res; 20 | } 21 | 22 | void 23 | cmark_plugin_free(cmark_plugin *plugin) { 24 | cmark_llist_free_full(&CMARK_DEFAULT_MEM_ALLOCATOR, 25 | plugin->syntax_extensions, 26 | (cmark_free_func) cmark_syntax_extension_free); 27 | CMARK_DEFAULT_MEM_ALLOCATOR.free(plugin); 28 | } 29 | 30 | cmark_llist * 31 | cmark_plugin_steal_syntax_extensions(cmark_plugin *plugin) { 32 | cmark_llist *res = plugin->syntax_extensions; 33 | 34 | plugin->syntax_extensions = NULL; 35 | return res; 36 | } 37 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/cmark_export.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CMARK_EXPORT_H 3 | #define CMARK_EXPORT_H 4 | 5 | #ifdef CMARK_STATIC_DEFINE 6 | # define CMARK_EXPORT 7 | # define CMARK_NO_EXPORT 8 | #else 9 | # ifndef CMARK_EXPORT 10 | # ifdef libcmark_gfm_EXPORTS 11 | /* We are building this library */ 12 | # define CMARK_EXPORT __attribute__((visibility("default"))) 13 | # else 14 | /* We are using this library */ 15 | # define CMARK_EXPORT __attribute__((visibility("default"))) 16 | # endif 17 | # endif 18 | 19 | # ifndef CMARK_NO_EXPORT 20 | # define CMARK_NO_EXPORT __attribute__((visibility("hidden"))) 21 | # endif 22 | #endif 23 | 24 | #ifndef CMARK_DEPRECATED 25 | # define CMARK_DEPRECATED __attribute__ ((__deprecated__)) 26 | #endif 27 | 28 | #ifndef CMARK_DEPRECATED_EXPORT 29 | # define CMARK_DEPRECATED_EXPORT CMARK_EXPORT CMARK_DEPRECATED 30 | #endif 31 | 32 | #ifndef CMARK_DEPRECATED_NO_EXPORT 33 | # define CMARK_DEPRECATED_NO_EXPORT CMARK_NO_EXPORT CMARK_DEPRECATED 34 | #endif 35 | 36 | #if 0 /* DEFINE_NO_DEPRECATED */ 37 | # ifndef CMARK_NO_DEPRECATED 38 | # define CMARK_NO_DEPRECATED 39 | # endif 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /Source/cmark_gfm/footnotes.c: -------------------------------------------------------------------------------- 1 | #include "cmark.h" 2 | #include "parser.h" 3 | #include "footnotes.h" 4 | #include "inlines.h" 5 | #include "chunk.h" 6 | 7 | static void footnote_free(cmark_map *map, cmark_map_entry *_ref) { 8 | cmark_footnote *ref = (cmark_footnote *)_ref; 9 | cmark_mem *mem = map->mem; 10 | if (ref != NULL) { 11 | mem->free(ref->entry.label); 12 | if (ref->node) 13 | cmark_node_free(ref->node); 14 | mem->free(ref); 15 | } 16 | } 17 | 18 | void cmark_footnote_create(cmark_map *map, cmark_node *node) { 19 | cmark_footnote *ref; 20 | unsigned char *reflabel = normalize_map_label(map->mem, &node->as.literal); 21 | 22 | /* empty footnote name, or composed from only whitespace */ 23 | if (reflabel == NULL) 24 | return; 25 | 26 | assert(map->sorted == NULL); 27 | 28 | ref = (cmark_footnote *)map->mem->calloc(1, sizeof(*ref)); 29 | ref->entry.label = reflabel; 30 | ref->node = node; 31 | ref->entry.age = map->size; 32 | ref->entry.next = map->refs; 33 | 34 | map->refs = (cmark_map_entry *)ref; 35 | map->size++; 36 | } 37 | 38 | cmark_map *cmark_footnote_map_new(cmark_mem *mem) { 39 | return cmark_map_new(mem, footnote_free); 40 | } 41 | -------------------------------------------------------------------------------- /cmark-gfm-swift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint cmark-gfm-swift.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'cmark-gfm-swift' 11 | s.version = '0.1.0' 12 | s.summary = 'A short description of cmark-gfm-swift.' 13 | 14 | s.homepage = 'https://github.com/githawkapp/cmark-gfm-swift' 15 | s.license = { :type => 'MIT', :file => 'LICENSE' } 16 | s.author = { 'Ryan Nystrom' => 'rnystrom@whoisryannystrom.com' } 17 | s.source = { :git => 'https://github.com/githawkapp/cmark-gfm-swift.git', :tag => s.version.to_s } 18 | 19 | s.ios.deployment_target = '8.0' 20 | s.osx.deployment_target = '10.10' 21 | 22 | s.source_files = 'Source/**/*' 23 | s.public_header_files = 'Source/*.h' 24 | s.exclude_files = "Source/Info.plist" 25 | s.preserve_path = 'cmark-gfm-swift/Source/cmark_gfm/module.modulemap' 26 | s.pod_target_xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(SRCROOT)/cmark-gfm-swift/Source/cmark_gfm/**' } 27 | 28 | end 29 | -------------------------------------------------------------------------------- /Source/cmark_gfm/references.c: -------------------------------------------------------------------------------- 1 | #include "cmark.h" 2 | #include "parser.h" 3 | #include "references.h" 4 | #include "inlines.h" 5 | #include "chunk.h" 6 | 7 | static void reference_free(cmark_map *map, cmark_map_entry *_ref) { 8 | cmark_reference *ref = (cmark_reference *)_ref; 9 | cmark_mem *mem = map->mem; 10 | if (ref != NULL) { 11 | mem->free(ref->entry.label); 12 | cmark_chunk_free(mem, &ref->url); 13 | cmark_chunk_free(mem, &ref->title); 14 | mem->free(ref); 15 | } 16 | } 17 | 18 | void cmark_reference_create(cmark_map *map, cmark_chunk *label, 19 | cmark_chunk *url, cmark_chunk *title) { 20 | cmark_reference *ref; 21 | unsigned char *reflabel = normalize_map_label(map->mem, label); 22 | 23 | /* empty reference name, or composed from only whitespace */ 24 | if (reflabel == NULL) 25 | return; 26 | 27 | assert(map->sorted == NULL); 28 | 29 | ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref)); 30 | ref->entry.label = reflabel; 31 | ref->url = cmark_clean_url(map->mem, url); 32 | ref->title = cmark_clean_title(map->mem, title); 33 | ref->entry.age = map->size; 34 | ref->entry.next = map->refs; 35 | 36 | map->refs = (cmark_map_entry *)ref; 37 | map->size++; 38 | } 39 | 40 | cmark_map *cmark_reference_map_new(cmark_mem *mem) { 41 | return cmark_map_new(mem, reference_free); 42 | } 43 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/cmarkextensions_export.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CMARKEXTENSIONS_EXPORT_H 3 | #define CMARKEXTENSIONS_EXPORT_H 4 | 5 | #ifdef CMARKEXTENSIONS_STATIC_DEFINE 6 | # define CMARKEXTENSIONS_EXPORT 7 | # define CMARKEXTENSIONS_NO_EXPORT 8 | #else 9 | # ifndef CMARKEXTENSIONS_EXPORT 10 | # ifdef libcmark_gfmextensions_EXPORTS 11 | /* We are building this library */ 12 | # define CMARKEXTENSIONS_EXPORT __attribute__((visibility("default"))) 13 | # else 14 | /* We are using this library */ 15 | # define CMARKEXTENSIONS_EXPORT __attribute__((visibility("default"))) 16 | # endif 17 | # endif 18 | 19 | # ifndef CMARKEXTENSIONS_NO_EXPORT 20 | # define CMARKEXTENSIONS_NO_EXPORT __attribute__((visibility("hidden"))) 21 | # endif 22 | #endif 23 | 24 | #ifndef CMARKEXTENSIONS_DEPRECATED 25 | # define CMARKEXTENSIONS_DEPRECATED __attribute__ ((__deprecated__)) 26 | #endif 27 | 28 | #ifndef CMARKEXTENSIONS_DEPRECATED_EXPORT 29 | # define CMARKEXTENSIONS_DEPRECATED_EXPORT CMARKEXTENSIONS_EXPORT CMARKEXTENSIONS_DEPRECATED 30 | #endif 31 | 32 | #ifndef CMARKEXTENSIONS_DEPRECATED_NO_EXPORT 33 | # define CMARKEXTENSIONS_DEPRECATED_NO_EXPORT CMARKEXTENSIONS_NO_EXPORT CMARKEXTENSIONS_DEPRECATED 34 | #endif 35 | 36 | #if 0 /* DEFINE_NO_DEPRECATED */ 37 | # ifndef CMARKEXTENSIONS_NO_DEPRECATED 38 | # define CMARKEXTENSIONS_NO_DEPRECATED 39 | # endif 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/syntax_extension.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_SYNTAX_EXTENSION_H 2 | #define CMARK_SYNTAX_EXTENSION_H 3 | 4 | #include "cmark.h" 5 | #include "cmark_extension_api.h" 6 | #include "config.h" 7 | 8 | struct cmark_syntax_extension { 9 | cmark_match_block_func last_block_matches; 10 | cmark_open_block_func try_opening_block; 11 | cmark_match_inline_func match_inline; 12 | cmark_inline_from_delim_func insert_inline_from_delim; 13 | cmark_llist * special_inline_chars; 14 | char * name; 15 | void * priv; 16 | bool emphasis; 17 | cmark_free_func free_function; 18 | cmark_get_type_string_func get_type_string_func; 19 | cmark_can_contain_func can_contain_func; 20 | cmark_contains_inlines_func contains_inlines_func; 21 | cmark_common_render_func commonmark_render_func; 22 | cmark_common_render_func plaintext_render_func; 23 | cmark_common_render_func latex_render_func; 24 | cmark_common_render_func man_render_func; 25 | cmark_html_render_func html_render_func; 26 | cmark_html_filter_func html_filter_func; 27 | cmark_postprocess_func postprocess_func; 28 | cmark_opaque_free_func opaque_free_func; 29 | cmark_commonmark_escape_func commonmark_escape_func; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /Source/cmark_gfm/tagfilter.c: -------------------------------------------------------------------------------- 1 | #include "tagfilter.h" 2 | #include "parser.h" 3 | #include 4 | 5 | static const char *blacklist[] = { 6 | "title", "textarea", "style", "xmp", "iframe", 7 | "noembed", "noframes", "script", "plaintext", NULL, 8 | }; 9 | 10 | static int is_tag(const unsigned char *tag_data, size_t tag_size, 11 | const char *tagname) { 12 | size_t i; 13 | 14 | if (tag_size < 3 || tag_data[0] != '<') 15 | return 0; 16 | 17 | i = 1; 18 | 19 | if (tag_data[i] == '/') { 20 | i++; 21 | } 22 | 23 | for (; i < tag_size; ++i, ++tagname) { 24 | if (*tagname == 0) 25 | break; 26 | 27 | if (tolower(tag_data[i]) != *tagname) 28 | return 0; 29 | } 30 | 31 | if (i == tag_size) 32 | return 0; 33 | 34 | if (cmark_isspace(tag_data[i]) || tag_data[i] == '>') 35 | return 1; 36 | 37 | if (tag_data[i] == '/' && tag_size >= i + 2 && tag_data[i + 1] == '>') 38 | return 1; 39 | 40 | return 0; 41 | } 42 | 43 | static int filter(cmark_syntax_extension *ext, const unsigned char *tag, 44 | size_t tag_len) { 45 | const char **it; 46 | 47 | for (it = blacklist; *it; ++it) { 48 | if (is_tag(tag, tag_len, *it)) { 49 | return 0; 50 | } 51 | } 52 | 53 | return 1; 54 | } 55 | 56 | cmark_syntax_extension *create_tagfilter_extension(void) { 57 | cmark_syntax_extension *ext = cmark_syntax_extension_new("tagfilter"); 58 | cmark_syntax_extension_set_html_filter_func(ext, filter); 59 | return ext; 60 | } 61 | -------------------------------------------------------------------------------- /Source/cmark_gfm/cmark.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "registry.h" 5 | #include "node.h" 6 | #include "houdini.h" 7 | #include "cmark.h" 8 | #include "buffer.h" 9 | 10 | cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FOOTNOTE_DEFINITION; 11 | cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_FOOTNOTE_REFERENCE; 12 | 13 | int cmark_version() { return CMARK_VERSION; } 14 | 15 | const char *cmark_version_string() { return CMARK_VERSION_STRING; } 16 | 17 | static void *xcalloc(size_t nmem, size_t size) { 18 | void *ptr = calloc(nmem, size); 19 | if (!ptr) { 20 | fprintf(stderr, "[cmark] calloc returned null pointer, aborting\n"); 21 | abort(); 22 | } 23 | return ptr; 24 | } 25 | 26 | static void *xrealloc(void *ptr, size_t size) { 27 | void *new_ptr = realloc(ptr, size); 28 | if (!new_ptr) { 29 | fprintf(stderr, "[cmark] realloc returned null pointer, aborting\n"); 30 | abort(); 31 | } 32 | return new_ptr; 33 | } 34 | 35 | static void xfree(void *ptr) { 36 | free(ptr); 37 | } 38 | 39 | cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, xfree}; 40 | 41 | cmark_mem *cmark_get_default_mem_allocator() { 42 | return &CMARK_DEFAULT_MEM_ALLOCATOR; 43 | } 44 | 45 | char *cmark_markdown_to_html(const char *text, size_t len, int options) { 46 | cmark_node *doc; 47 | char *result; 48 | 49 | doc = cmark_parse_document(text, len, options); 50 | 51 | result = cmark_render_html(doc, options, NULL); 52 | cmark_node_free(doc); 53 | 54 | return result; 55 | } 56 | -------------------------------------------------------------------------------- /Source/Inline+TextElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Inline+TextElement.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Inline { 11 | var textElement: TextElement? { 12 | switch self { 13 | case .text(let text): 14 | return text.isEmpty ? nil : .text(text: text) 15 | case .softBreak: 16 | return .softBreak 17 | case .lineBreak: 18 | return .lineBreak 19 | case .code(let text): 20 | return text.isEmpty ? nil : .code(text: text) 21 | case .emphasis(let children): 22 | return .emphasis(children: children.compactMap { $0.textElement }) 23 | case .strong(let children): 24 | return .strong(children: children.compactMap { $0.textElement }) 25 | case .custom(let literal): 26 | return literal.isEmpty ? nil : .text(text: literal) 27 | case .link(let children, let title, let url): 28 | return .link(children: children.compactMap { $0.textElement }, title: title, url: url) 29 | case .strikethrough(let children): 30 | return .strikethrough(children: children.compactMap { $0.textElement }) 31 | case .mention(let login): 32 | return .mention(login: login) 33 | case .checkbox(let checked, let originalRange): 34 | return .checkbox(checked: checked, originalRange: originalRange) 35 | case .image, .html: 36 | return nil 37 | } 38 | } 39 | } 40 | 41 | extension Sequence where Iterator.Element == Inline { 42 | var textElements: [TextElement] { return compactMap { $0.textElement } } 43 | } 44 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_CONFIG_H 2 | #define CMARK_CONFIG_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #define HAVE_STDBOOL_H 9 | 10 | #ifdef HAVE_STDBOOL_H 11 | #include 12 | #elif !defined(__cplusplus) 13 | typedef char bool; 14 | #endif 15 | 16 | #define HAVE___BUILTIN_EXPECT 17 | 18 | #define HAVE___ATTRIBUTE__ 19 | 20 | #ifdef HAVE___ATTRIBUTE__ 21 | #define CMARK_ATTRIBUTE(list) __attribute__ (list) 22 | #else 23 | #define CMARK_ATTRIBUTE(list) 24 | #endif 25 | 26 | #ifndef CMARK_INLINE 27 | #if defined(_MSC_VER) && !defined(__cplusplus) 28 | #define CMARK_INLINE __inline 29 | #else 30 | #define CMARK_INLINE inline 31 | #endif 32 | #endif 33 | 34 | /* snprintf and vsnprintf fallbacks for MSVC before 2015, 35 | due to Valentin Milea http://stackoverflow.com/questions/2915672/ 36 | */ 37 | 38 | #if defined(_MSC_VER) && _MSC_VER < 1900 39 | 40 | #include 41 | #include 42 | 43 | #define snprintf c99_snprintf 44 | #define vsnprintf c99_vsnprintf 45 | 46 | CMARK_INLINE int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) 47 | { 48 | int count = -1; 49 | 50 | if (size != 0) 51 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); 52 | if (count == -1) 53 | count = _vscprintf(format, ap); 54 | 55 | return count; 56 | } 57 | 58 | CMARK_INLINE int c99_snprintf(char *outBuf, size_t size, const char *format, ...) 59 | { 60 | int count; 61 | va_list ap; 62 | 63 | va_start(ap, format); 64 | count = c99_vsnprintf(outBuf, size, format, ap); 65 | va_end(ap); 66 | 67 | return count; 68 | } 69 | 70 | #endif 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/houdini.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_HOUDINI_H 2 | #define CMARK_HOUDINI_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include "config.h" 10 | #include "buffer.h" 11 | 12 | #ifdef HAVE___BUILTIN_EXPECT 13 | #define likely(x) __builtin_expect((x), 1) 14 | #define unlikely(x) __builtin_expect((x), 0) 15 | #else 16 | #define likely(x) (x) 17 | #define unlikely(x) (x) 18 | #endif 19 | 20 | #ifdef HOUDINI_USE_LOCALE 21 | #define _isxdigit(c) isxdigit(c) 22 | #define _isdigit(c) isdigit(c) 23 | #else 24 | /* 25 | * Helper _isdigit methods -- do not trust the current locale 26 | * */ 27 | #define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL) 28 | #define _isdigit(c) ((c) >= '0' && (c) <= '9') 29 | #endif 30 | 31 | #define HOUDINI_ESCAPED_SIZE(x) (((x)*12) / 10) 32 | #define HOUDINI_UNESCAPED_SIZE(x) (x) 33 | 34 | CMARK_EXPORT 35 | bufsize_t houdini_unescape_ent(cmark_strbuf *ob, const uint8_t *src, 36 | bufsize_t size); 37 | CMARK_EXPORT 38 | int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src, 39 | bufsize_t size); 40 | CMARK_EXPORT 41 | int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src, 42 | bufsize_t size, int secure); 43 | CMARK_EXPORT 44 | int houdini_unescape_html(cmark_strbuf *ob, const uint8_t *src, 45 | bufsize_t size); 46 | CMARK_EXPORT 47 | void houdini_unescape_html_f(cmark_strbuf *ob, const uint8_t *src, 48 | bufsize_t size); 49 | CMARK_EXPORT 50 | int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src, 51 | bufsize_t size); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /Source/cmark_gfm/registry.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "config.h" 6 | #include "cmark.h" 7 | #include "syntax_extension.h" 8 | #include "registry.h" 9 | #include "plugin.h" 10 | 11 | extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR; 12 | 13 | static cmark_llist *syntax_extensions = NULL; 14 | 15 | void cmark_register_plugin(cmark_plugin_init_func reg_fn) { 16 | cmark_plugin *plugin = cmark_plugin_new(); 17 | 18 | if (!reg_fn(plugin)) { 19 | cmark_plugin_free(plugin); 20 | return; 21 | } 22 | 23 | cmark_llist *syntax_extensions_list = cmark_plugin_steal_syntax_extensions(plugin), 24 | *it; 25 | 26 | for (it = syntax_extensions_list; it; it = it->next) { 27 | syntax_extensions = cmark_llist_append(&CMARK_DEFAULT_MEM_ALLOCATOR, syntax_extensions, it->data); 28 | } 29 | 30 | cmark_llist_free(&CMARK_DEFAULT_MEM_ALLOCATOR, syntax_extensions_list); 31 | cmark_plugin_free(plugin); 32 | } 33 | 34 | void cmark_release_plugins(void) { 35 | if (syntax_extensions) { 36 | cmark_llist_free_full( 37 | &CMARK_DEFAULT_MEM_ALLOCATOR, 38 | syntax_extensions, 39 | (cmark_free_func) cmark_syntax_extension_free); 40 | syntax_extensions = NULL; 41 | } 42 | } 43 | 44 | cmark_llist *cmark_list_syntax_extensions(cmark_mem *mem) { 45 | cmark_llist *it; 46 | cmark_llist *res = NULL; 47 | 48 | for (it = syntax_extensions; it; it = it->next) { 49 | res = cmark_llist_append(mem, res, it->data); 50 | } 51 | return res; 52 | } 53 | 54 | cmark_syntax_extension *cmark_find_syntax_extension(const char *name) { 55 | cmark_llist *tmp; 56 | 57 | for (tmp = syntax_extensions; tmp; tmp = tmp->next) { 58 | cmark_syntax_extension *ext = (cmark_syntax_extension *) tmp->data; 59 | if (!strcmp(ext->name, name)) 60 | return ext; 61 | } 62 | return NULL; 63 | } 64 | -------------------------------------------------------------------------------- /Source/cmark_gfm/cmark_ctype.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cmark_ctype.h" 4 | 5 | /** 1 = space, 2 = punct, 3 = digit, 4 = alpha, 0 = other 6 | */ 7 | static const uint8_t cmark_ctype_class[256] = { 8 | /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ 9 | /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 10 | /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 | /* 2 */ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12 | /* 3 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 13 | /* 4 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 14 | /* 5 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 15 | /* 6 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16 | /* 7 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0, 17 | /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18 | /* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19 | /* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20 | /* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21 | /* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 | /* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 | /* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 | /* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 25 | 26 | /** 27 | * Returns 1 if c is a "whitespace" character as defined by the spec. 28 | */ 29 | int cmark_isspace(char c) { return cmark_ctype_class[(uint8_t)c] == 1; } 30 | 31 | /** 32 | * Returns 1 if c is an ascii punctuation character. 33 | */ 34 | int cmark_ispunct(char c) { return cmark_ctype_class[(uint8_t)c] == 2; } 35 | 36 | int cmark_isalnum(char c) { 37 | uint8_t result; 38 | result = cmark_ctype_class[(uint8_t)c]; 39 | return (result == 3 || result == 4); 40 | } 41 | 42 | int cmark_isdigit(char c) { return cmark_ctype_class[(uint8_t)c] == 3; } 43 | 44 | int cmark_isalpha(char c) { return cmark_ctype_class[(uint8_t)c] == 4; } 45 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/render.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_RENDER_H 2 | #define CMARK_RENDER_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include "buffer.h" 10 | #include "chunk.h" 11 | #include "memory.h" 12 | 13 | typedef enum { LITERAL, NORMAL, TITLE, URL } cmark_escaping; 14 | 15 | struct cmark_renderer { 16 | cmark_mem *mem; 17 | cmark_strbuf *buffer; 18 | cmark_strbuf *prefix; 19 | int column; 20 | int width; 21 | int need_cr; 22 | bufsize_t last_breakable; 23 | bool begin_line; 24 | bool begin_content; 25 | bool no_linebreaks; 26 | bool in_tight_list_item; 27 | void (*outc)(struct cmark_renderer *, cmark_node *, cmark_escaping, int32_t, unsigned char); 28 | void (*cr)(struct cmark_renderer *); 29 | void (*blankline)(struct cmark_renderer *); 30 | void (*out)(struct cmark_renderer *, cmark_node *, const char *, bool, cmark_escaping); 31 | unsigned int footnote_ix; 32 | }; 33 | 34 | typedef struct cmark_renderer cmark_renderer; 35 | 36 | struct cmark_html_renderer { 37 | cmark_strbuf *html; 38 | cmark_node *plain; 39 | cmark_llist *filter_extensions; 40 | unsigned int footnote_ix; 41 | unsigned int written_footnote_ix; 42 | void *opaque; 43 | }; 44 | 45 | typedef struct cmark_html_renderer cmark_html_renderer; 46 | 47 | void cmark_render_ascii(cmark_renderer *renderer, const char *s); 48 | 49 | void cmark_render_code_point(cmark_renderer *renderer, uint32_t c); 50 | 51 | char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width, 52 | void (*outc)(cmark_renderer *, cmark_node *, 53 | cmark_escaping, int32_t, 54 | unsigned char), 55 | int (*render_node)(cmark_renderer *renderer, 56 | cmark_node *node, 57 | cmark_event_type ev_type, int options)); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_PARSER_H 2 | #define CMARK_PARSER_H 3 | 4 | #include 5 | #include "node.h" 6 | #include "buffer.h" 7 | #include "memory.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #define MAX_LINK_LABEL_LENGTH 1000 14 | 15 | struct cmark_parser { 16 | struct cmark_mem *mem; 17 | /* A hashtable of urls in the current document for cross-references */ 18 | struct cmark_map *refmap; 19 | /* The root node of the parser, always a CMARK_NODE_DOCUMENT */ 20 | struct cmark_node *root; 21 | /* The last open block after a line is fully processed */ 22 | struct cmark_node *current; 23 | /* See the documentation for cmark_parser_get_line_number() in cmark.h */ 24 | int line_number; 25 | /* See the documentation for cmark_parser_get_offset() in cmark.h */ 26 | bufsize_t offset; 27 | /* See the documentation for cmark_parser_get_column() in cmark.h */ 28 | bufsize_t column; 29 | /* See the documentation for cmark_parser_get_first_nonspace() in cmark.h */ 30 | bufsize_t first_nonspace; 31 | /* See the documentation for cmark_parser_get_first_nonspace_column() in cmark.h */ 32 | bufsize_t first_nonspace_column; 33 | /* See the documentation for cmark_parser_get_indent() in cmark.h */ 34 | int indent; 35 | /* See the documentation for cmark_parser_is_blank() in cmark.h */ 36 | bool blank; 37 | /* See the documentation for cmark_parser_has_partially_consumed_tab() in cmark.h */ 38 | bool partially_consumed_tab; 39 | /* Contains the currently processed line */ 40 | cmark_strbuf curline; 41 | /* See the documentation for cmark_parser_get_last_line_length() in cmark.h */ 42 | bufsize_t last_line_length; 43 | /* FIXME: not sure about the difference with curline */ 44 | cmark_strbuf linebuf; 45 | /* Options set by the user, see the Options section in cmark.h */ 46 | int options; 47 | bool last_buffer_ended_with_cr; 48 | cmark_llist *syntax_extensions; 49 | cmark_llist *inline_syntax_extensions; 50 | cmark_ispunct_func backslash_ispunct; 51 | bufsize_t line_offset; 52 | }; 53 | 54 | #ifdef __cplusplus 55 | } 56 | #endif 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /cmark-gfm-swift.xcodeproj/xcshareddata/xcschemes/cmark_gfm_swiftTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Source/cmark_gfm/houdini_html_e.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "houdini.h" 6 | 7 | /** 8 | * According to the OWASP rules: 9 | * 10 | * & --> & 11 | * < --> < 12 | * > --> > 13 | * " --> " 14 | * ' --> ' ' is not recommended 15 | * / --> / forward slash is included as it helps end an HTML entity 16 | * 17 | */ 18 | static const char HTML_ESCAPE_TABLE[] = { 19 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, 21 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | }; 31 | 32 | static const char *HTML_ESCAPES[] = {"", """, "&", "'", 33 | "/", "<", ">"}; 34 | 35 | int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src, bufsize_t size, 36 | int secure) { 37 | bufsize_t i = 0, org, esc = 0; 38 | 39 | while (i < size) { 40 | org = i; 41 | while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0) 42 | i++; 43 | 44 | if (i > org) 45 | cmark_strbuf_put(ob, src + org, i - org); 46 | 47 | /* escaping */ 48 | if (unlikely(i >= size)) 49 | break; 50 | 51 | /* The forward slash and single quote are only escaped in secure mode */ 52 | if ((src[i] == '/' || src[i] == '\'') && !secure) { 53 | cmark_strbuf_putc(ob, src[i]); 54 | } else { 55 | cmark_strbuf_puts(ob, HTML_ESCAPES[esc]); 56 | } 57 | 58 | i++; 59 | } 60 | 61 | return 1; 62 | } 63 | 64 | int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) { 65 | return houdini_escape_html0(ob, src, size, 1); 66 | } 67 | -------------------------------------------------------------------------------- /Source/cmark_gfm/arena.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "cmark.h" 5 | #include "cmark_extension_api.h" 6 | 7 | static struct arena_chunk { 8 | size_t sz, used; 9 | uint8_t push_point; 10 | void *ptr; 11 | struct arena_chunk *prev; 12 | } *A = NULL; 13 | 14 | static struct arena_chunk *alloc_arena_chunk(size_t sz, struct arena_chunk *prev) { 15 | struct arena_chunk *c = (struct arena_chunk *)calloc(1, sizeof(*c)); 16 | if (!c) 17 | abort(); 18 | c->sz = sz; 19 | c->ptr = calloc(1, sz); 20 | if (!c->ptr) 21 | abort(); 22 | c->prev = prev; 23 | return c; 24 | } 25 | 26 | void cmark_arena_push(void) { 27 | if (!A) 28 | return; 29 | A->push_point = 1; 30 | A = alloc_arena_chunk(10240, A); 31 | } 32 | 33 | int cmark_arena_pop(void) { 34 | if (!A) 35 | return 0; 36 | while (A && !A->push_point) { 37 | free(A->ptr); 38 | struct arena_chunk *n = A->prev; 39 | free(A); 40 | A = n; 41 | } 42 | if (A) 43 | A->push_point = 0; 44 | return 1; 45 | } 46 | 47 | static void init_arena(void) { 48 | A = alloc_arena_chunk(4 * 1048576, NULL); 49 | } 50 | 51 | void cmark_arena_reset(void) { 52 | while (A) { 53 | free(A->ptr); 54 | struct arena_chunk *n = A->prev; 55 | free(A); 56 | A = n; 57 | } 58 | } 59 | 60 | static void *arena_calloc(size_t nmem, size_t size) { 61 | if (!A) 62 | init_arena(); 63 | 64 | size_t sz = nmem * size + sizeof(size_t); 65 | 66 | // Round allocation sizes to largest integer size to 67 | // ensure returned memory is correctly aligned 68 | const size_t align = sizeof(size_t) - 1; 69 | sz = (sz + align) & ~align; 70 | 71 | if (sz > A->sz) { 72 | A->prev = alloc_arena_chunk(sz, A->prev); 73 | return (uint8_t *) A->prev->ptr + sizeof(size_t); 74 | } 75 | if (sz > A->sz - A->used) { 76 | A = alloc_arena_chunk(A->sz + A->sz / 2, A); 77 | } 78 | void *ptr = (uint8_t *) A->ptr + A->used; 79 | A->used += sz; 80 | *((size_t *) ptr) = sz - sizeof(size_t); 81 | return (uint8_t *) ptr + sizeof(size_t); 82 | } 83 | 84 | static void *arena_realloc(void *ptr, size_t size) { 85 | if (!A) 86 | init_arena(); 87 | 88 | void *new_ptr = arena_calloc(1, size); 89 | if (ptr) 90 | memcpy(new_ptr, ptr, ((size_t *) ptr)[-1]); 91 | return new_ptr; 92 | } 93 | 94 | static void arena_free(void *ptr) { 95 | (void) ptr; 96 | /* no-op */ 97 | } 98 | 99 | cmark_mem CMARK_ARENA_MEM_ALLOCATOR = {arena_calloc, arena_realloc, arena_free}; 100 | 101 | cmark_mem *cmark_get_arena_mem_allocator() { 102 | return &CMARK_ARENA_MEM_ALLOCATOR; 103 | } 104 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/node.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_NODE_H 2 | #define CMARK_NODE_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #include "cmark.h" 12 | #include "cmark_extension_api.h" 13 | #include "buffer.h" 14 | #include "chunk.h" 15 | 16 | typedef struct { 17 | cmark_list_type list_type; 18 | int marker_offset; 19 | int padding; 20 | int start; 21 | cmark_delim_type delimiter; 22 | unsigned char bullet_char; 23 | bool tight; 24 | } cmark_list; 25 | 26 | typedef struct { 27 | cmark_chunk info; 28 | cmark_chunk literal; 29 | uint8_t fence_length; 30 | uint8_t fence_offset; 31 | unsigned char fence_char; 32 | int8_t fenced; 33 | } cmark_code; 34 | 35 | typedef struct { 36 | int level; 37 | bool setext; 38 | } cmark_heading; 39 | 40 | typedef struct { 41 | cmark_chunk url; 42 | cmark_chunk title; 43 | } cmark_link; 44 | 45 | typedef struct { 46 | cmark_chunk on_enter; 47 | cmark_chunk on_exit; 48 | } cmark_custom; 49 | 50 | enum cmark_node__internal_flags { 51 | CMARK_NODE__OPEN = (1 << 0), 52 | CMARK_NODE__LAST_LINE_BLANK = (1 << 1), 53 | }; 54 | 55 | struct cmark_node { 56 | cmark_strbuf content; 57 | 58 | struct cmark_node *next; 59 | struct cmark_node *prev; 60 | struct cmark_node *parent; 61 | struct cmark_node *first_child; 62 | struct cmark_node *last_child; 63 | 64 | void *user_data; 65 | cmark_free_func user_data_free_func; 66 | 67 | int start_line; 68 | int start_column; 69 | int end_line; 70 | int end_column; 71 | int internal_offset; 72 | bufsize_t origin_offset; 73 | uint16_t type; 74 | uint16_t flags; 75 | 76 | cmark_syntax_extension *extension; 77 | 78 | union { 79 | cmark_chunk literal; 80 | cmark_list list; 81 | cmark_code code; 82 | cmark_heading heading; 83 | cmark_link link; 84 | cmark_custom custom; 85 | int html_block_type; 86 | void *opaque; 87 | } as; 88 | }; 89 | 90 | static CMARK_INLINE cmark_mem *cmark_node_mem(cmark_node *node) { 91 | return node->content.mem; 92 | } 93 | CMARK_EXPORT int cmark_node_check(cmark_node *node, FILE *out); 94 | 95 | static CMARK_INLINE bool CMARK_NODE_TYPE_BLOCK_P(cmark_node_type node_type) { 96 | return (node_type & CMARK_NODE_TYPE_MASK) == CMARK_NODE_TYPE_BLOCK; 97 | } 98 | 99 | static CMARK_INLINE bool CMARK_NODE_BLOCK_P(cmark_node *node) { 100 | return node != NULL && CMARK_NODE_TYPE_BLOCK_P((cmark_node_type) node->type); 101 | } 102 | 103 | static CMARK_INLINE bool CMARK_NODE_TYPE_INLINE_P(cmark_node_type node_type) { 104 | return (node_type & CMARK_NODE_TYPE_MASK) == CMARK_NODE_TYPE_INLINE; 105 | } 106 | 107 | static CMARK_INLINE bool CMARK_NODE_INLINE_P(cmark_node *node) { 108 | return node != NULL && CMARK_NODE_TYPE_INLINE_P((cmark_node_type) node->type); 109 | } 110 | 111 | CMARK_EXPORT bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type); 112 | 113 | #ifdef __cplusplus 114 | } 115 | #endif 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_BUFFER_H 2 | #define CMARK_BUFFER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "config.h" 10 | #include "cmark.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | typedef struct { 17 | cmark_mem *mem; 18 | unsigned char *ptr; 19 | bufsize_t asize, size; 20 | } cmark_strbuf; 21 | 22 | extern unsigned char cmark_strbuf__initbuf[]; 23 | 24 | #define CMARK_BUF_INIT(mem) \ 25 | { mem, cmark_strbuf__initbuf, 0, 0 } 26 | 27 | /** 28 | * Initialize a cmark_strbuf structure. 29 | * 30 | * For the cases where CMARK_BUF_INIT cannot be used to do static 31 | * initialization. 32 | */ 33 | CMARK_EXPORT 34 | void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf, 35 | bufsize_t initial_size); 36 | 37 | /** 38 | * Grow the buffer to hold at least `target_size` bytes. 39 | */ 40 | CMARK_EXPORT 41 | void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size); 42 | 43 | CMARK_EXPORT 44 | void cmark_strbuf_free(cmark_strbuf *buf); 45 | 46 | CMARK_EXPORT 47 | void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b); 48 | 49 | CMARK_EXPORT 50 | bufsize_t cmark_strbuf_len(const cmark_strbuf *buf); 51 | 52 | CMARK_EXPORT 53 | int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b); 54 | 55 | CMARK_EXPORT 56 | unsigned char *cmark_strbuf_detach(cmark_strbuf *buf); 57 | 58 | CMARK_EXPORT 59 | void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize, 60 | const cmark_strbuf *buf); 61 | 62 | static CMARK_INLINE const char *cmark_strbuf_cstr(const cmark_strbuf *buf) { 63 | return (char *)buf->ptr; 64 | } 65 | 66 | #define cmark_strbuf_at(buf, n) ((buf)->ptr[n]) 67 | 68 | CMARK_EXPORT 69 | void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, 70 | bufsize_t len); 71 | 72 | CMARK_EXPORT 73 | void cmark_strbuf_sets(cmark_strbuf *buf, const char *string); 74 | 75 | CMARK_EXPORT 76 | void cmark_strbuf_putc(cmark_strbuf *buf, int c); 77 | 78 | CMARK_EXPORT 79 | void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, 80 | bufsize_t len); 81 | 82 | CMARK_EXPORT 83 | void cmark_strbuf_puts(cmark_strbuf *buf, const char *string); 84 | 85 | CMARK_EXPORT 86 | void cmark_strbuf_clear(cmark_strbuf *buf); 87 | 88 | CMARK_EXPORT 89 | bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos); 90 | 91 | CMARK_EXPORT 92 | bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos); 93 | 94 | CMARK_EXPORT 95 | void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n); 96 | 97 | CMARK_EXPORT 98 | void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len); 99 | 100 | CMARK_EXPORT 101 | void cmark_strbuf_rtrim(cmark_strbuf *buf); 102 | 103 | CMARK_EXPORT 104 | void cmark_strbuf_trim(cmark_strbuf *buf); 105 | 106 | CMARK_EXPORT 107 | void cmark_strbuf_normalize_whitespace(cmark_strbuf *s); 108 | 109 | CMARK_EXPORT 110 | void cmark_strbuf_unescape(cmark_strbuf *s); 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/scanners.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_SCANNERS_H 2 | #define CMARK_SCANNERS_H 3 | 4 | #include "cmark.h" 5 | #include "chunk.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c, 12 | bufsize_t offset); 13 | bufsize_t _scan_scheme(const unsigned char *p); 14 | bufsize_t _scan_autolink_uri(const unsigned char *p); 15 | bufsize_t _scan_autolink_email(const unsigned char *p); 16 | bufsize_t _scan_html_tag(const unsigned char *p); 17 | bufsize_t _scan_liberal_html_tag(const unsigned char *p); 18 | bufsize_t _scan_html_block_start(const unsigned char *p); 19 | bufsize_t _scan_html_block_start_7(const unsigned char *p); 20 | bufsize_t _scan_html_block_end_1(const unsigned char *p); 21 | bufsize_t _scan_html_block_end_2(const unsigned char *p); 22 | bufsize_t _scan_html_block_end_3(const unsigned char *p); 23 | bufsize_t _scan_html_block_end_4(const unsigned char *p); 24 | bufsize_t _scan_html_block_end_5(const unsigned char *p); 25 | bufsize_t _scan_link_title(const unsigned char *p); 26 | bufsize_t _scan_spacechars(const unsigned char *p); 27 | bufsize_t _scan_atx_heading_start(const unsigned char *p); 28 | bufsize_t _scan_setext_heading_line(const unsigned char *p); 29 | bufsize_t _scan_thematic_break(const unsigned char *p); 30 | bufsize_t _scan_open_code_fence(const unsigned char *p); 31 | bufsize_t _scan_close_code_fence(const unsigned char *p); 32 | bufsize_t _scan_entity(const unsigned char *p); 33 | bufsize_t _scan_dangerous_url(const unsigned char *p); 34 | bufsize_t _scan_footnote_definition(const unsigned char *p); 35 | 36 | #define scan_scheme(c, n) _scan_at(&_scan_scheme, c, n) 37 | #define scan_autolink_uri(c, n) _scan_at(&_scan_autolink_uri, c, n) 38 | #define scan_autolink_email(c, n) _scan_at(&_scan_autolink_email, c, n) 39 | #define scan_html_tag(c, n) _scan_at(&_scan_html_tag, c, n) 40 | #define scan_liberal_html_tag(c, n) _scan_at(&_scan_liberal_html_tag, c, n) 41 | #define scan_html_block_start(c, n) _scan_at(&_scan_html_block_start, c, n) 42 | #define scan_html_block_start_7(c, n) _scan_at(&_scan_html_block_start_7, c, n) 43 | #define scan_html_block_end_1(c, n) _scan_at(&_scan_html_block_end_1, c, n) 44 | #define scan_html_block_end_2(c, n) _scan_at(&_scan_html_block_end_2, c, n) 45 | #define scan_html_block_end_3(c, n) _scan_at(&_scan_html_block_end_3, c, n) 46 | #define scan_html_block_end_4(c, n) _scan_at(&_scan_html_block_end_4, c, n) 47 | #define scan_html_block_end_5(c, n) _scan_at(&_scan_html_block_end_5, c, n) 48 | #define scan_link_title(c, n) _scan_at(&_scan_link_title, c, n) 49 | #define scan_spacechars(c, n) _scan_at(&_scan_spacechars, c, n) 50 | #define scan_atx_heading_start(c, n) _scan_at(&_scan_atx_heading_start, c, n) 51 | #define scan_setext_heading_line(c, n) \ 52 | _scan_at(&_scan_setext_heading_line, c, n) 53 | #define scan_thematic_break(c, n) _scan_at(&_scan_thematic_break, c, n) 54 | #define scan_open_code_fence(c, n) _scan_at(&_scan_open_code_fence, c, n) 55 | #define scan_close_code_fence(c, n) _scan_at(&_scan_close_code_fence, c, n) 56 | #define scan_entity(c, n) _scan_at(&_scan_entity, c, n) 57 | #define scan_dangerous_url(c, n) _scan_at(&_scan_dangerous_url, c, n) 58 | #define scan_footnote_definition(c, n) _scan_at(&_scan_footnote_definition, c, n) 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /Source/cmark_gfm/map.c: -------------------------------------------------------------------------------- 1 | #include "map.h" 2 | #include "utf8.h" 3 | #include "parser.h" 4 | 5 | // normalize map label: collapse internal whitespace to single space, 6 | // remove leading/trailing whitespace, case fold 7 | // Return NULL if the label is actually empty (i.e. composed solely from 8 | // whitespace) 9 | unsigned char *normalize_map_label(cmark_mem *mem, cmark_chunk *ref) { 10 | cmark_strbuf normalized = CMARK_BUF_INIT(mem); 11 | unsigned char *result; 12 | 13 | if (ref == NULL) 14 | return NULL; 15 | 16 | if (ref->len == 0) 17 | return NULL; 18 | 19 | cmark_utf8proc_case_fold(&normalized, ref->data, ref->len); 20 | cmark_strbuf_trim(&normalized); 21 | cmark_strbuf_normalize_whitespace(&normalized); 22 | 23 | result = cmark_strbuf_detach(&normalized); 24 | assert(result); 25 | 26 | if (result[0] == '\0') { 27 | mem->free(result); 28 | return NULL; 29 | } 30 | 31 | return result; 32 | } 33 | 34 | static int 35 | labelcmp(const unsigned char *a, const unsigned char *b) { 36 | return strcmp((const char *)a, (const char *)b); 37 | } 38 | 39 | static int 40 | refcmp(const void *p1, const void *p2) { 41 | cmark_map_entry *r1 = *(cmark_map_entry **)p1; 42 | cmark_map_entry *r2 = *(cmark_map_entry **)p2; 43 | int res = labelcmp(r1->label, r2->label); 44 | return res ? res : ((int)r1->age - (int)r2->age); 45 | } 46 | 47 | static int 48 | refsearch(const void *label, const void *p2) { 49 | cmark_map_entry *ref = *(cmark_map_entry **)p2; 50 | return labelcmp((const unsigned char *)label, ref->label); 51 | } 52 | 53 | static void sort_map(cmark_map *map) { 54 | unsigned int i = 0, last = 0, size = map->size; 55 | cmark_map_entry *r = map->refs, **sorted = NULL; 56 | 57 | sorted = (cmark_map_entry **)map->mem->calloc(size, sizeof(cmark_map_entry *)); 58 | while (r) { 59 | sorted[i++] = r; 60 | r = r->next; 61 | } 62 | 63 | qsort(sorted, size, sizeof(cmark_map_entry *), refcmp); 64 | 65 | for (i = 1; i < size; i++) { 66 | if (labelcmp(sorted[i]->label, sorted[last]->label) != 0) 67 | sorted[++last] = sorted[i]; 68 | } 69 | 70 | map->sorted = sorted; 71 | map->size = last + 1; 72 | } 73 | 74 | cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label) { 75 | cmark_map_entry **ref = NULL; 76 | unsigned char *norm; 77 | 78 | if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH) 79 | return NULL; 80 | 81 | if (map == NULL || !map->size) 82 | return NULL; 83 | 84 | norm = normalize_map_label(map->mem, label); 85 | if (norm == NULL) 86 | return NULL; 87 | 88 | if (!map->sorted) 89 | sort_map(map); 90 | 91 | ref = (cmark_map_entry **)bsearch(norm, map->sorted, map->size, sizeof(cmark_map_entry *), refsearch); 92 | map->mem->free(norm); 93 | 94 | if (!ref) 95 | return NULL; 96 | 97 | return ref[0]; 98 | } 99 | 100 | void cmark_map_free(cmark_map *map) { 101 | cmark_map_entry *ref; 102 | 103 | if (map == NULL) 104 | return; 105 | 106 | ref = map->refs; 107 | while (ref) { 108 | cmark_map_entry *next = ref->next; 109 | map->free(map, ref); 110 | ref = next; 111 | } 112 | 113 | map->mem->free(map->sorted); 114 | map->mem->free(map); 115 | } 116 | 117 | cmark_map *cmark_map_new(cmark_mem *mem, cmark_map_free_f free) { 118 | cmark_map *map = (cmark_map *)mem->calloc(1, sizeof(cmark_map)); 119 | map->mem = mem; 120 | map->free = free; 121 | return map; 122 | } 123 | -------------------------------------------------------------------------------- /Source/cmark_gfm/houdini_href_e.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "houdini.h" 6 | 7 | /* 8 | * The following characters will not be escaped: 9 | * 10 | * -_.+!*'(),%#@?=;:/,+&$ alphanum 11 | * 12 | * Note that this character set is the addition of: 13 | * 14 | * - The characters which are safe to be in an URL 15 | * - The characters which are *not* safe to be in 16 | * an URL because they are RESERVED characters. 17 | * 18 | * We asume (lazily) that any RESERVED char that 19 | * appears inside an URL is actually meant to 20 | * have its native function (i.e. as an URL 21 | * component/separator) and hence needs no escaping. 22 | * 23 | * There are two exceptions: the chacters & (amp) 24 | * and ' (single quote) do not appear in the table. 25 | * They are meant to appear in the URL as components, 26 | * yet they require special HTML-entity escaping 27 | * to generate valid HTML markup. 28 | * 29 | * All other characters will be escaped to %XX. 30 | * 31 | */ 32 | static const char HREF_SAFE[] = { 33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 35 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 36 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 37 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 38 | 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44 | }; 45 | 46 | int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) { 47 | static const uint8_t hex_chars[] = "0123456789ABCDEF"; 48 | bufsize_t i = 0, org; 49 | uint8_t hex_str[3]; 50 | 51 | hex_str[0] = '%'; 52 | 53 | while (i < size) { 54 | org = i; 55 | while (i < size && HREF_SAFE[src[i]] != 0) 56 | i++; 57 | 58 | if (likely(i > org)) 59 | cmark_strbuf_put(ob, src + org, i - org); 60 | 61 | /* escaping */ 62 | if (i >= size) 63 | break; 64 | 65 | switch (src[i]) { 66 | /* amp appears all the time in URLs, but needs 67 | * HTML-entity escaping to be inside an href */ 68 | case '&': 69 | cmark_strbuf_puts(ob, "&"); 70 | break; 71 | 72 | /* the single quote is a valid URL character 73 | * according to the standard; it needs HTML 74 | * entity escaping too */ 75 | case '\'': 76 | cmark_strbuf_puts(ob, "'"); 77 | break; 78 | 79 | /* the space can be escaped to %20 or a plus 80 | * sign. we're going with the generic escape 81 | * for now. the plus thing is more commonly seen 82 | * when building GET strings */ 83 | #if 0 84 | case ' ': 85 | cmark_strbuf_putc(ob, '+'); 86 | break; 87 | #endif 88 | 89 | /* every other character goes with a %XX escaping */ 90 | default: 91 | hex_str[1] = hex_chars[(src[i] >> 4) & 0xF]; 92 | hex_str[2] = hex_chars[src[i] & 0xF]; 93 | cmark_strbuf_put(ob, hex_str, 3); 94 | } 95 | 96 | i++; 97 | } 98 | 99 | return 1; 100 | } 101 | -------------------------------------------------------------------------------- /Source/cmark_gfm/mention.c: -------------------------------------------------------------------------------- 1 | #include "mention.h" 2 | #include "parser.h" 3 | #include "render.h" 4 | 5 | cmark_node_type CMARK_NODE_MENTION; 6 | 7 | const char *cmark_node_get_mention_login(cmark_node *node) { 8 | if (node->type != CMARK_NODE_MENTION) { 9 | return NULL; 10 | } 11 | return cmark_chunk_to_cstr(cmark_node_mem(node), (cmark_chunk *)node->as.opaque); 12 | } 13 | 14 | static cmark_node *match(cmark_syntax_extension *self, cmark_parser *parser, 15 | cmark_node *parent, unsigned char character, 16 | cmark_inline_parser *inline_parser) { 17 | if (character != '@') 18 | return NULL; 19 | 20 | cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser); 21 | uint8_t *data = chunk->data; 22 | size_t size = chunk->len; 23 | int start = cmark_inline_parser_get_offset(inline_parser); 24 | int at = start + 1; 25 | int end = at; 26 | 27 | if (start > 0 && !cmark_isspace(data[start-1])) { 28 | return NULL; 29 | } 30 | 31 | while (end < size 32 | && (cmark_isalnum(data[end]) || data[end] == '-')) { 33 | end++; 34 | } 35 | 36 | if (end == at) { 37 | return NULL; 38 | } 39 | 40 | cmark_node *node = cmark_node_new_with_mem(CMARK_NODE_MENTION, parser->mem); 41 | 42 | cmark_chunk *mention_chunk; 43 | node->as.opaque = mention_chunk = parser->mem->calloc(1, sizeof(cmark_chunk)); 44 | mention_chunk->data = data + at; 45 | mention_chunk->len = end - at; 46 | 47 | cmark_inline_parser_set_offset(inline_parser, start + (end - start)); 48 | cmark_node_set_syntax_extension(node, self); 49 | 50 | return node; 51 | } 52 | 53 | static void html_render(cmark_syntax_extension *extension, 54 | cmark_html_renderer *renderer, cmark_node *node, 55 | cmark_event_type ev_type, int options) { 56 | const char *login = cmark_node_get_mention_login(node); 57 | if (login == NULL) { 58 | return; 59 | } 60 | if (ev_type != CMARK_EVENT_ENTER) { 61 | return; 62 | } 63 | 64 | cmark_strbuf *html = renderer->html; 65 | cmark_strbuf_puts(html, "@"); 68 | cmark_strbuf_puts(html, login); 69 | cmark_strbuf_puts(html, ""); 70 | } 71 | 72 | static const char *get_type_string(cmark_syntax_extension *extension, 73 | cmark_node *node) { 74 | return node->type == CMARK_NODE_MENTION ? "mention" : ""; 75 | } 76 | 77 | static int can_contain(cmark_syntax_extension *extension, cmark_node *node, 78 | cmark_node_type child_type) { 79 | if (node->type != CMARK_NODE_MENTION) 80 | return false; 81 | 82 | return CMARK_NODE_TYPE_INLINE_P(child_type); 83 | } 84 | 85 | static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) { 86 | if (node->type == CMARK_NODE_MENTION) { 87 | mem->free(node->as.opaque); 88 | } 89 | } 90 | 91 | cmark_syntax_extension *create_mention_extension(void) { 92 | cmark_syntax_extension *self = cmark_syntax_extension_new("mention"); 93 | cmark_llist *special_chars = NULL; 94 | 95 | cmark_syntax_extension_set_get_type_string_func(self, get_type_string); 96 | cmark_syntax_extension_set_can_contain_func(self, can_contain); 97 | cmark_syntax_extension_set_opaque_free_func(self, opaque_free); 98 | cmark_syntax_extension_set_html_render_func(self, html_render); 99 | 100 | CMARK_NODE_MENTION = cmark_syntax_extension_add_node(1); 101 | 102 | cmark_syntax_extension_set_match_inline_func(self, match); 103 | 104 | cmark_mem *mem = cmark_get_default_mem_allocator(); 105 | special_chars = cmark_llist_append(mem, special_chars, (void *)'@'); 106 | cmark_syntax_extension_set_special_inline_chars(self, special_chars); 107 | 108 | return self; 109 | } 110 | 111 | -------------------------------------------------------------------------------- /Source/cmark_gfm/include/chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef CMARK_CHUNK_H 2 | #define CMARK_CHUNK_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "cmark.h" 8 | #include "buffer.h" 9 | #include "memory.h" 10 | #include "cmark_ctype.h" 11 | 12 | #define CMARK_CHUNK_EMPTY \ 13 | { NULL, 0, 0 } 14 | 15 | typedef struct cmark_chunk { 16 | unsigned char *data; 17 | bufsize_t len; 18 | bufsize_t alloc; // also implies a NULL-terminated string 19 | } cmark_chunk; 20 | 21 | static CMARK_INLINE void cmark_chunk_free(cmark_mem *mem, cmark_chunk *c) { 22 | if (c->alloc) 23 | mem->free(c->data); 24 | 25 | c->data = NULL; 26 | c->alloc = 0; 27 | c->len = 0; 28 | } 29 | 30 | static CMARK_INLINE void cmark_chunk_ltrim(cmark_chunk *c) { 31 | assert(!c->alloc); 32 | 33 | while (c->len && cmark_isspace(c->data[0])) { 34 | c->data++; 35 | c->len--; 36 | } 37 | } 38 | 39 | static CMARK_INLINE void cmark_chunk_rtrim(cmark_chunk *c) { 40 | assert(!c->alloc); 41 | 42 | while (c->len > 0) { 43 | if (!cmark_isspace(c->data[c->len - 1])) 44 | break; 45 | 46 | c->len--; 47 | } 48 | } 49 | 50 | static CMARK_INLINE void cmark_chunk_trim(cmark_chunk *c) { 51 | cmark_chunk_ltrim(c); 52 | cmark_chunk_rtrim(c); 53 | } 54 | 55 | static CMARK_INLINE bufsize_t cmark_chunk_strchr(cmark_chunk *ch, int c, 56 | bufsize_t offset) { 57 | const unsigned char *p = 58 | (unsigned char *)memchr(ch->data + offset, c, ch->len - offset); 59 | return p ? (bufsize_t)(p - ch->data) : ch->len; 60 | } 61 | 62 | static CMARK_INLINE const char *cmark_chunk_to_cstr(cmark_mem *mem, 63 | cmark_chunk *c) { 64 | unsigned char *str; 65 | 66 | if (c->alloc) { 67 | return (char *)c->data; 68 | } 69 | str = (unsigned char *)mem->calloc(c->len + 1, 1); 70 | if (c->len > 0) { 71 | memcpy(str, c->data, c->len); 72 | } 73 | str[c->len] = 0; 74 | c->data = str; 75 | c->alloc = 1; 76 | 77 | return (char *)str; 78 | } 79 | 80 | static CMARK_INLINE void cmark_chunk_set_cstr(cmark_mem *mem, cmark_chunk *c, 81 | const char *str) { 82 | unsigned char *old = c->alloc ? c->data : NULL; 83 | if (str == NULL) { 84 | c->len = 0; 85 | c->data = NULL; 86 | c->alloc = 0; 87 | } else { 88 | c->len = (bufsize_t)strlen(str); 89 | c->data = (unsigned char *)mem->calloc(c->len + 1, 1); 90 | c->alloc = 1; 91 | memcpy(c->data, str, c->len + 1); 92 | } 93 | if (old != NULL) { 94 | mem->free(old); 95 | } 96 | } 97 | 98 | static CMARK_INLINE cmark_chunk cmark_chunk_literal(const char *data) { 99 | bufsize_t len = data ? (bufsize_t)strlen(data) : 0; 100 | cmark_chunk c = {(unsigned char *)data, len, 0}; 101 | return c; 102 | } 103 | 104 | static CMARK_INLINE cmark_chunk cmark_chunk_dup(const cmark_chunk *ch, 105 | bufsize_t pos, bufsize_t len) { 106 | cmark_chunk c = {ch->data + pos, len, 0}; 107 | return c; 108 | } 109 | 110 | static CMARK_INLINE cmark_chunk cmark_chunk_buf_detach(cmark_strbuf *buf) { 111 | cmark_chunk c; 112 | 113 | c.len = buf->size; 114 | c.data = cmark_strbuf_detach(buf); 115 | c.alloc = 1; 116 | 117 | return c; 118 | } 119 | 120 | /* trim_new variants are to be used when the source chunk may or may not be 121 | * allocated; forces a newly allocated chunk. */ 122 | static CMARK_INLINE cmark_chunk cmark_chunk_ltrim_new(cmark_mem *mem, cmark_chunk *c) { 123 | cmark_chunk r = cmark_chunk_dup(c, 0, c->len); 124 | cmark_chunk_ltrim(&r); 125 | cmark_chunk_to_cstr(mem, &r); 126 | return r; 127 | } 128 | 129 | static CMARK_INLINE cmark_chunk cmark_chunk_rtrim_new(cmark_mem *mem, cmark_chunk *c) { 130 | cmark_chunk r = cmark_chunk_dup(c, 0, c->len); 131 | cmark_chunk_rtrim(&r); 132 | cmark_chunk_to_cstr(mem, &r); 133 | return r; 134 | } 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /Source/Node+Elements.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Node+Elements.swift 3 | // cmark-gfm-swift 4 | // 5 | // Created by Ryan Nystrom on 3/31/18. 6 | // 7 | 8 | import Foundation 9 | import cmark_gfm 10 | 11 | struct FoldingOptions { 12 | var quoteLevel: Int 13 | } 14 | 15 | extension Block { 16 | func folded(_ options: FoldingOptions) -> [Element] { 17 | switch self { 18 | case .blockQuote(let items): 19 | var deeper = options 20 | deeper.quoteLevel += 1 21 | return items.flatMap { $0.folded(deeper) } 22 | case .codeBlock(let text, let language): 23 | return [.codeBlock(text: text, language: language)] 24 | case .custom: 25 | return [] 26 | case .heading(let text, let level): 27 | return [.heading(text: text.textElements, level: level)] 28 | case .html(let text): 29 | return [.html(text: text)] 30 | case .list(let items, let type): 31 | return [.list(items: items.compactMap { $0.listElements(0) }, type: type)] 32 | case .paragraph(let text): 33 | let builder = InlineBuilder(options: options) 34 | text.forEach { $0.fold(builder: builder) } 35 | // clean up and append leftover text elements 36 | var els = builder.elements 37 | if let currentText = builder.currentText { 38 | els.append(currentText) 39 | } 40 | return els 41 | case .table(let items): 42 | return [.table(rows: items.compactMap { $0.tableRow })] 43 | case .tableHeader, .tableRow, .tableCell: 44 | return [] // handled in flattening .table 45 | case .thematicBreak: 46 | return [.hr] 47 | } 48 | } 49 | } 50 | 51 | class InlineBuilder { 52 | let options: FoldingOptions 53 | var elements = [Element]() 54 | var text = [TextElement]() 55 | init(options: FoldingOptions) { 56 | self.options = options 57 | } 58 | var currentText: Element? { 59 | guard text.count > 0 else { return nil } 60 | return options.quoteLevel > 0 61 | ? .quote(items: text, level: options.quoteLevel) 62 | : .text(items: text) 63 | } 64 | func pushNonText(_ el: Element) { 65 | if let currentText = self.currentText { 66 | elements.append(currentText) 67 | text.removeAll() 68 | } 69 | elements.append(el) 70 | } 71 | } 72 | 73 | extension Inline { 74 | /// Collapse all text elements, break by image elements 75 | func fold(builder: InlineBuilder) { 76 | switch self { 77 | case .text, .softBreak, .lineBreak, .code, .emphasis, .strong, 78 | .custom, .link, .strikethrough, .mention, .checkbox: 79 | if let el = textElement { 80 | builder.text.append(el) 81 | } 82 | case .image(_, let title, let url): 83 | if let title = title, let url = url { 84 | builder.pushNonText(.image(title: title, url: url)) 85 | } 86 | case .html: 87 | // handled by converting blocks containing html into full html elements 88 | break 89 | } 90 | } 91 | } 92 | 93 | private extension Node { 94 | var containsHTML: Bool { 95 | if type == CMARK_NODE_HTML_BLOCK || type == CMARK_NODE_HTML_INLINE { 96 | return true 97 | } 98 | for child in children { 99 | if child.containsHTML { return true } 100 | } 101 | return false 102 | } 103 | } 104 | 105 | public extension Node { 106 | 107 | var flatElements: [Element] { 108 | let options = FoldingOptions(quoteLevel: 0) 109 | return children.reduce([Element]()) { 110 | if $1.containsHTML { 111 | return $0 + [.html(text: $1.html)] 112 | } else { 113 | if let block = Block($1) { 114 | return $0 + block.folded(options) 115 | } else { 116 | return $0 117 | } 118 | } 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /cmark-gfm-swift.xcodeproj/xcshareddata/xcschemes/cmark-gfm-swift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Source/cmark_gfm/houdini_html_u.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "buffer.h" 6 | #include "houdini.h" 7 | #include "utf8.h" 8 | #include "entities.inc" 9 | 10 | /* Binary tree lookup code for entities added by JGM */ 11 | 12 | static const unsigned char *S_lookup(int i, int low, int hi, 13 | const unsigned char *s, int len) { 14 | int j; 15 | int cmp = 16 | strncmp((const char *)s, (const char *)cmark_entities[i].entity, len); 17 | if (cmp == 0 && cmark_entities[i].entity[len] == 0) { 18 | return (const unsigned char *)cmark_entities[i].bytes; 19 | } else if (cmp <= 0 && i > low) { 20 | j = i - ((i - low) / 2); 21 | if (j == i) 22 | j -= 1; 23 | return S_lookup(j, low, i - 1, s, len); 24 | } else if (cmp > 0 && i < hi) { 25 | j = i + ((hi - i) / 2); 26 | if (j == i) 27 | j += 1; 28 | return S_lookup(j, i + 1, hi, s, len); 29 | } else { 30 | return NULL; 31 | } 32 | } 33 | 34 | static const unsigned char *S_lookup_entity(const unsigned char *s, int len) { 35 | return S_lookup(CMARK_NUM_ENTITIES / 2, 0, CMARK_NUM_ENTITIES - 1, s, len); 36 | } 37 | 38 | bufsize_t houdini_unescape_ent(cmark_strbuf *ob, const uint8_t *src, 39 | bufsize_t size) { 40 | bufsize_t i = 0; 41 | 42 | if (size >= 3 && src[0] == '#') { 43 | int codepoint = 0; 44 | int num_digits = 0; 45 | 46 | if (_isdigit(src[1])) { 47 | for (i = 1; i < size && _isdigit(src[i]); ++i) { 48 | codepoint = (codepoint * 10) + (src[i] - '0'); 49 | 50 | if (codepoint >= 0x110000) { 51 | // Keep counting digits but 52 | // avoid integer overflow. 53 | codepoint = 0x110000; 54 | } 55 | } 56 | 57 | num_digits = i - 1; 58 | } 59 | 60 | else if (src[1] == 'x' || src[1] == 'X') { 61 | for (i = 2; i < size && _isxdigit(src[i]); ++i) { 62 | codepoint = (codepoint * 16) + ((src[i] | 32) % 39 - 9); 63 | 64 | if (codepoint >= 0x110000) { 65 | // Keep counting digits but 66 | // avoid integer overflow. 67 | codepoint = 0x110000; 68 | } 69 | } 70 | 71 | num_digits = i - 2; 72 | } 73 | 74 | if (num_digits >= 1 && num_digits <= 8 && i < size && src[i] == ';') { 75 | if (codepoint == 0 || (codepoint >= 0xD800 && codepoint < 0xE000) || 76 | codepoint >= 0x110000) { 77 | codepoint = 0xFFFD; 78 | } 79 | cmark_utf8proc_encode_char(codepoint, ob); 80 | return i + 1; 81 | } 82 | } 83 | 84 | else { 85 | if (size > CMARK_ENTITY_MAX_LENGTH) 86 | size = CMARK_ENTITY_MAX_LENGTH; 87 | 88 | for (i = CMARK_ENTITY_MIN_LENGTH; i < size; ++i) { 89 | if (src[i] == ' ') 90 | break; 91 | 92 | if (src[i] == ';') { 93 | const unsigned char *entity = S_lookup_entity(src, i); 94 | 95 | if (entity != NULL) { 96 | cmark_strbuf_puts(ob, (const char *)entity); 97 | return i + 1; 98 | } 99 | 100 | break; 101 | } 102 | } 103 | } 104 | 105 | return 0; 106 | } 107 | 108 | int houdini_unescape_html(cmark_strbuf *ob, const uint8_t *src, 109 | bufsize_t size) { 110 | bufsize_t i = 0, org, ent; 111 | 112 | while (i < size) { 113 | org = i; 114 | while (i < size && src[i] != '&') 115 | i++; 116 | 117 | if (likely(i > org)) { 118 | if (unlikely(org == 0)) { 119 | if (i >= size) 120 | return 0; 121 | 122 | cmark_strbuf_grow(ob, HOUDINI_UNESCAPED_SIZE(size)); 123 | } 124 | 125 | cmark_strbuf_put(ob, src + org, i - org); 126 | } 127 | 128 | /* escaping */ 129 | if (i >= size) 130 | break; 131 | 132 | i++; 133 | 134 | ent = houdini_unescape_ent(ob, src + i, size - i); 135 | i += ent; 136 | 137 | /* not really an entity */ 138 | if (ent == 0) 139 | cmark_strbuf_putc(ob, '&'); 140 | } 141 | 142 | return 1; 143 | } 144 | 145 | void houdini_unescape_html_f(cmark_strbuf *ob, const uint8_t *src, 146 | bufsize_t size) { 147 | if (!houdini_unescape_html(ob, src, size)) 148 | cmark_strbuf_put(ob, src, size); 149 | } 150 | -------------------------------------------------------------------------------- /Source/cmark_gfm/checkbox.c: -------------------------------------------------------------------------------- 1 | #include "checkbox.h" 2 | #include "parser.h" 3 | #include "render.h" 4 | 5 | cmark_node_type CMARK_NODE_CHECKBOX; 6 | 7 | typedef struct { 8 | int checked; 9 | int location; 10 | int length; 11 | } checkbox_data; 12 | 13 | int cmark_node_get_checkbox_checked(cmark_node *node) { 14 | if (node->type != CMARK_NODE_CHECKBOX) { 15 | return -1; 16 | } 17 | return ((checkbox_data *)node->as.opaque)->checked; 18 | } 19 | 20 | int cmark_node_get_checkbox_location(cmark_node *node) { 21 | if (node->type != CMARK_NODE_CHECKBOX) { 22 | return -1; 23 | } 24 | return ((checkbox_data *)node->as.opaque)->location; 25 | } 26 | 27 | int cmark_node_get_checkbox_length(cmark_node *node) { 28 | if (node->type != CMARK_NODE_CHECKBOX) { 29 | return -1; 30 | } 31 | return ((checkbox_data *)node->as.opaque)->length; 32 | } 33 | 34 | static cmark_node *match(cmark_syntax_extension *self, cmark_parser *parser, 35 | cmark_node *parent, unsigned char character, 36 | cmark_inline_parser *inline_parser) { 37 | if (parent->parent == NULL 38 | || parent->parent->type != CMARK_NODE_ITEM 39 | || !( 40 | character == ' ' 41 | || character == 'x' 42 | || character == 'X') 43 | ) { 44 | return NULL; 45 | } 46 | 47 | // must be the second character in a list item paragraph block 48 | int index = cmark_inline_parser_get_offset(inline_parser); 49 | if (index != 1) { 50 | return NULL; 51 | } 52 | 53 | cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser); 54 | size_t size = chunk->len; 55 | 56 | uint8_t *data = chunk->data; 57 | 58 | int len = 3; 59 | int leftBracket = index - 1; 60 | if (size - leftBracket < len 61 | || data[leftBracket] != '[' 62 | || data[index + 1] != ']') { 63 | return NULL; 64 | } 65 | 66 | cmark_node *node = cmark_node_new_with_mem(CMARK_NODE_CHECKBOX, parser->mem); 67 | 68 | checkbox_data *checkbox; 69 | node->as.opaque = checkbox = parser->mem->calloc(1, sizeof(checkbox_data)); 70 | checkbox->checked = character != ' '; 71 | checkbox->location = parent->origin_offset - leftBracket; 72 | checkbox->length = len; 73 | 74 | cmark_inline_parser_set_offset(inline_parser, leftBracket + len); 75 | // undo the left bracket being a text node 76 | cmark_node_unput(parent, 1); 77 | cmark_node_set_syntax_extension(node, self); 78 | 79 | return node; 80 | } 81 | 82 | static void html_render(cmark_syntax_extension *extension, 83 | cmark_html_renderer *renderer, cmark_node *node, 84 | cmark_event_type ev_type, int options) { 85 | const int checked = cmark_node_get_checkbox_checked(node); 86 | if (checked < 0) { 87 | return; 88 | } 89 | if (ev_type != CMARK_EVENT_ENTER) { 90 | return; 91 | } 92 | 93 | cmark_strbuf *html = renderer->html; 94 | cmark_strbuf_puts(html, ""); 99 | } 100 | 101 | static const char *get_type_string(cmark_syntax_extension *extension, 102 | cmark_node *node) { 103 | return node->type == CMARK_NODE_CHECKBOX ? "checkbox" : ""; 104 | } 105 | 106 | static int can_contain(cmark_syntax_extension *extension, cmark_node *node, 107 | cmark_node_type child_type) { 108 | if (node->type != CMARK_NODE_CHECKBOX) { 109 | return false; 110 | } 111 | return CMARK_NODE_TYPE_INLINE_P(child_type); 112 | } 113 | 114 | static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) { 115 | if (node->type == CMARK_NODE_CHECKBOX) { 116 | mem->free(node->as.opaque); 117 | } 118 | } 119 | 120 | cmark_syntax_extension *create_checkbox_extension(void) { 121 | cmark_syntax_extension *self = cmark_syntax_extension_new("checkbox"); 122 | cmark_llist *special_chars = NULL; 123 | 124 | cmark_syntax_extension_set_get_type_string_func(self, get_type_string); 125 | cmark_syntax_extension_set_can_contain_func(self, can_contain); 126 | cmark_syntax_extension_set_opaque_free_func(self, opaque_free); 127 | cmark_syntax_extension_set_html_render_func(self, html_render); 128 | 129 | CMARK_NODE_CHECKBOX = cmark_syntax_extension_add_node(1); 130 | 131 | cmark_syntax_extension_set_match_inline_func(self, match); 132 | 133 | cmark_mem *mem = cmark_get_default_mem_allocator(); 134 | special_chars = cmark_llist_append(mem, special_chars, (void *)' '); 135 | special_chars = cmark_llist_append(mem, special_chars, (void *)'x'); 136 | special_chars = cmark_llist_append(mem, special_chars, (void *)'X'); 137 | cmark_syntax_extension_set_special_inline_chars(self, special_chars); 138 | 139 | return self; 140 | } 141 | 142 | -------------------------------------------------------------------------------- /Source/cmark_gfm/iterator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "config.h" 5 | #include "node.h" 6 | #include "cmark.h" 7 | #include "iterator.h" 8 | 9 | cmark_iter *cmark_iter_new(cmark_node *root) { 10 | if (root == NULL) { 11 | return NULL; 12 | } 13 | cmark_mem *mem = root->content.mem; 14 | cmark_iter *iter = (cmark_iter *)mem->calloc(1, sizeof(cmark_iter)); 15 | iter->mem = mem; 16 | iter->root = root; 17 | iter->cur.ev_type = CMARK_EVENT_NONE; 18 | iter->cur.node = NULL; 19 | iter->next.ev_type = CMARK_EVENT_ENTER; 20 | iter->next.node = root; 21 | return iter; 22 | } 23 | 24 | void cmark_iter_free(cmark_iter *iter) { iter->mem->free(iter); } 25 | 26 | static bool S_is_leaf(cmark_node *node) { 27 | switch (node->type) { 28 | case CMARK_NODE_HTML_BLOCK: 29 | case CMARK_NODE_THEMATIC_BREAK: 30 | case CMARK_NODE_CODE_BLOCK: 31 | case CMARK_NODE_TEXT: 32 | case CMARK_NODE_SOFTBREAK: 33 | case CMARK_NODE_LINEBREAK: 34 | case CMARK_NODE_CODE: 35 | case CMARK_NODE_HTML_INLINE: 36 | return 1; 37 | } 38 | return 0; 39 | } 40 | 41 | cmark_event_type cmark_iter_next(cmark_iter *iter) { 42 | cmark_event_type ev_type = iter->next.ev_type; 43 | cmark_node *node = iter->next.node; 44 | 45 | iter->cur.ev_type = ev_type; 46 | iter->cur.node = node; 47 | 48 | if (ev_type == CMARK_EVENT_DONE) { 49 | return ev_type; 50 | } 51 | 52 | /* roll forward to next item, setting both fields */ 53 | if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) { 54 | if (node->first_child == NULL) { 55 | /* stay on this node but exit */ 56 | iter->next.ev_type = CMARK_EVENT_EXIT; 57 | } else { 58 | iter->next.ev_type = CMARK_EVENT_ENTER; 59 | iter->next.node = node->first_child; 60 | } 61 | } else if (node == iter->root) { 62 | /* don't move past root */ 63 | iter->next.ev_type = CMARK_EVENT_DONE; 64 | iter->next.node = NULL; 65 | } else if (node->next) { 66 | iter->next.ev_type = CMARK_EVENT_ENTER; 67 | iter->next.node = node->next; 68 | } else if (node->parent) { 69 | iter->next.ev_type = CMARK_EVENT_EXIT; 70 | iter->next.node = node->parent; 71 | } else { 72 | assert(false); 73 | iter->next.ev_type = CMARK_EVENT_DONE; 74 | iter->next.node = NULL; 75 | } 76 | 77 | return ev_type; 78 | } 79 | 80 | void cmark_iter_reset(cmark_iter *iter, cmark_node *current, 81 | cmark_event_type event_type) { 82 | iter->next.ev_type = event_type; 83 | iter->next.node = current; 84 | cmark_iter_next(iter); 85 | } 86 | 87 | cmark_node *cmark_iter_get_node(cmark_iter *iter) { return iter->cur.node; } 88 | 89 | cmark_event_type cmark_iter_get_event_type(cmark_iter *iter) { 90 | return iter->cur.ev_type; 91 | } 92 | 93 | cmark_node *cmark_iter_get_root(cmark_iter *iter) { return iter->root; } 94 | 95 | void cmark_consolidate_text_nodes(cmark_node *root) { 96 | if (root == NULL) { 97 | return; 98 | } 99 | cmark_iter *iter = cmark_iter_new(root); 100 | cmark_strbuf buf = CMARK_BUF_INIT(iter->mem); 101 | cmark_event_type ev_type; 102 | cmark_node *cur, *tmp, *next; 103 | 104 | while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { 105 | cur = cmark_iter_get_node(iter); 106 | if (ev_type == CMARK_EVENT_ENTER && cur->type == CMARK_NODE_TEXT && 107 | cur->next && cur->next->type == CMARK_NODE_TEXT) { 108 | cmark_strbuf_clear(&buf); 109 | cmark_strbuf_put(&buf, cur->as.literal.data, cur->as.literal.len); 110 | tmp = cur->next; 111 | while (tmp && tmp->type == CMARK_NODE_TEXT) { 112 | cmark_iter_next(iter); // advance pointer 113 | cmark_strbuf_put(&buf, tmp->as.literal.data, tmp->as.literal.len); 114 | cur->end_column = tmp->end_column; 115 | next = tmp->next; 116 | cmark_node_free(tmp); 117 | tmp = next; 118 | } 119 | cmark_chunk_free(iter->mem, &cur->as.literal); 120 | cur->as.literal = cmark_chunk_buf_detach(&buf); 121 | } 122 | } 123 | 124 | cmark_strbuf_free(&buf); 125 | cmark_iter_free(iter); 126 | } 127 | 128 | void cmark_node_own(cmark_node *root) { 129 | if (root == NULL) { 130 | return; 131 | } 132 | cmark_iter *iter = cmark_iter_new(root); 133 | cmark_event_type ev_type; 134 | cmark_node *cur; 135 | 136 | while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { 137 | cur = cmark_iter_get_node(iter); 138 | if (ev_type == CMARK_EVENT_ENTER) { 139 | switch (cur->type) { 140 | case CMARK_NODE_TEXT: 141 | case CMARK_NODE_HTML_INLINE: 142 | case CMARK_NODE_CODE: 143 | case CMARK_NODE_HTML_BLOCK: 144 | cmark_chunk_to_cstr(iter->mem, &cur->as.literal); 145 | break; 146 | case CMARK_NODE_LINK: 147 | cmark_chunk_to_cstr(iter->mem, &cur->as.link.url); 148 | cmark_chunk_to_cstr(iter->mem, &cur->as.link.title); 149 | break; 150 | case CMARK_NODE_CUSTOM_INLINE: 151 | cmark_chunk_to_cstr(iter->mem, &cur->as.custom.on_enter); 152 | cmark_chunk_to_cstr(iter->mem, &cur->as.custom.on_exit); 153 | break; 154 | } 155 | } 156 | } 157 | 158 | cmark_iter_free(iter); 159 | } 160 | -------------------------------------------------------------------------------- /Source/cmark_gfm/syntax_extension.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "cmark.h" 5 | #include "syntax_extension.h" 6 | #include "buffer.h" 7 | 8 | extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR; 9 | 10 | static cmark_mem *_mem = &CMARK_DEFAULT_MEM_ALLOCATOR; 11 | 12 | void cmark_syntax_extension_free(cmark_mem *mem, cmark_syntax_extension *extension) { 13 | if (extension->free_function && extension->priv) { 14 | extension->free_function(mem, extension->priv); 15 | } 16 | 17 | cmark_llist_free(mem, extension->special_inline_chars); 18 | mem->free(extension->name); 19 | mem->free(extension); 20 | } 21 | 22 | cmark_syntax_extension *cmark_syntax_extension_new(const char *name) { 23 | cmark_syntax_extension *res = (cmark_syntax_extension *) _mem->calloc(1, sizeof(cmark_syntax_extension)); 24 | res->name = (char *) _mem->calloc(1, sizeof(char) * (strlen(name)) + 1); 25 | strcpy(res->name, name); 26 | return res; 27 | } 28 | 29 | cmark_node_type cmark_syntax_extension_add_node(int is_inline) { 30 | cmark_node_type *ref = !is_inline ? &CMARK_NODE_LAST_BLOCK : &CMARK_NODE_LAST_INLINE; 31 | 32 | if ((*ref & CMARK_NODE_VALUE_MASK) == CMARK_NODE_VALUE_MASK) { 33 | assert(false); 34 | return (cmark_node_type) 0; 35 | } 36 | 37 | return *ref = (cmark_node_type) ((int) *ref + 1); 38 | } 39 | 40 | void cmark_syntax_extension_set_emphasis(cmark_syntax_extension *extension, 41 | int emphasis) { 42 | extension->emphasis = emphasis == 1; 43 | } 44 | 45 | void cmark_syntax_extension_set_open_block_func(cmark_syntax_extension *extension, 46 | cmark_open_block_func func) { 47 | extension->try_opening_block = func; 48 | } 49 | 50 | void cmark_syntax_extension_set_match_block_func(cmark_syntax_extension *extension, 51 | cmark_match_block_func func) { 52 | extension->last_block_matches = func; 53 | } 54 | 55 | void cmark_syntax_extension_set_match_inline_func(cmark_syntax_extension *extension, 56 | cmark_match_inline_func func) { 57 | extension->match_inline = func; 58 | } 59 | 60 | void cmark_syntax_extension_set_inline_from_delim_func(cmark_syntax_extension *extension, 61 | cmark_inline_from_delim_func func) { 62 | extension->insert_inline_from_delim = func; 63 | } 64 | 65 | void cmark_syntax_extension_set_special_inline_chars(cmark_syntax_extension *extension, 66 | cmark_llist *special_chars) { 67 | extension->special_inline_chars = special_chars; 68 | } 69 | 70 | void cmark_syntax_extension_set_get_type_string_func(cmark_syntax_extension *extension, 71 | cmark_get_type_string_func func) { 72 | extension->get_type_string_func = func; 73 | } 74 | 75 | void cmark_syntax_extension_set_can_contain_func(cmark_syntax_extension *extension, 76 | cmark_can_contain_func func) { 77 | extension->can_contain_func = func; 78 | } 79 | 80 | void cmark_syntax_extension_set_contains_inlines_func(cmark_syntax_extension *extension, 81 | cmark_contains_inlines_func func) { 82 | extension->contains_inlines_func = func; 83 | } 84 | 85 | void cmark_syntax_extension_set_commonmark_render_func(cmark_syntax_extension *extension, 86 | cmark_common_render_func func) { 87 | extension->commonmark_render_func = func; 88 | } 89 | 90 | void cmark_syntax_extension_set_plaintext_render_func(cmark_syntax_extension *extension, 91 | cmark_common_render_func func) { 92 | extension->plaintext_render_func = func; 93 | } 94 | 95 | void cmark_syntax_extension_set_latex_render_func(cmark_syntax_extension *extension, 96 | cmark_common_render_func func) { 97 | extension->latex_render_func = func; 98 | } 99 | 100 | void cmark_syntax_extension_set_man_render_func(cmark_syntax_extension *extension, 101 | cmark_common_render_func func) { 102 | extension->man_render_func = func; 103 | } 104 | 105 | void cmark_syntax_extension_set_html_render_func(cmark_syntax_extension *extension, 106 | cmark_html_render_func func) { 107 | extension->html_render_func = func; 108 | } 109 | 110 | void cmark_syntax_extension_set_html_filter_func(cmark_syntax_extension *extension, 111 | cmark_html_filter_func func) { 112 | extension->html_filter_func = func; 113 | } 114 | 115 | void cmark_syntax_extension_set_postprocess_func(cmark_syntax_extension *extension, 116 | cmark_postprocess_func func) { 117 | extension->postprocess_func = func; 118 | } 119 | 120 | void cmark_syntax_extension_set_private(cmark_syntax_extension *extension, 121 | void *priv, 122 | cmark_free_func free_func) { 123 | extension->priv = priv; 124 | extension->free_function = free_func; 125 | } 126 | 127 | void *cmark_syntax_extension_get_private(cmark_syntax_extension *extension) { 128 | return extension->priv; 129 | } 130 | 131 | void cmark_syntax_extension_set_opaque_free_func(cmark_syntax_extension *extension, 132 | cmark_opaque_free_func func) { 133 | extension->opaque_free_func = func; 134 | } 135 | 136 | void cmark_syntax_extension_set_commonmark_escape_func(cmark_syntax_extension *extension, 137 | cmark_commonmark_escape_func func) { 138 | extension->commonmark_escape_func = func; 139 | } 140 | -------------------------------------------------------------------------------- /Source/cmark_gfm/strikethrough.c: -------------------------------------------------------------------------------- 1 | #include "strikethrough.h" 2 | #include "parser.h" 3 | #include "render.h" 4 | 5 | cmark_node_type CMARK_NODE_STRIKETHROUGH; 6 | 7 | static cmark_node *match(cmark_syntax_extension *self, cmark_parser *parser, 8 | cmark_node *parent, unsigned char character, 9 | cmark_inline_parser *inline_parser) { 10 | cmark_node *res = NULL; 11 | int left_flanking, right_flanking, punct_before, punct_after, delims; 12 | char buffer[101]; 13 | 14 | if (character != '~') 15 | return NULL; 16 | 17 | delims = cmark_inline_parser_scan_delimiters( 18 | inline_parser, sizeof(buffer) - 1, '~', 19 | &left_flanking, 20 | &right_flanking, &punct_before, &punct_after); 21 | 22 | memset(buffer, '~', delims); 23 | buffer[delims] = 0; 24 | 25 | res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem); 26 | cmark_node_set_literal(res, buffer); 27 | res->start_line = res->end_line = cmark_inline_parser_get_line(inline_parser); 28 | res->start_column = cmark_inline_parser_get_column(inline_parser) - delims; 29 | 30 | if (left_flanking || right_flanking) { 31 | cmark_inline_parser_push_delimiter(inline_parser, character, left_flanking, 32 | right_flanking, res); 33 | } 34 | 35 | return res; 36 | } 37 | 38 | static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser, 39 | cmark_inline_parser *inline_parser, delimiter *opener, 40 | delimiter *closer) { 41 | cmark_node *strikethrough; 42 | cmark_node *tmp, *next; 43 | delimiter *delim, *tmp_delim; 44 | delimiter *res = closer->next; 45 | 46 | strikethrough = opener->inl_text; 47 | 48 | if (!cmark_node_set_type(strikethrough, CMARK_NODE_STRIKETHROUGH)) 49 | goto done; 50 | 51 | cmark_node_set_syntax_extension(strikethrough, self); 52 | 53 | cmark_node_set_string_content(strikethrough, "~"); 54 | tmp = cmark_node_next(opener->inl_text); 55 | 56 | while (tmp) { 57 | if (tmp == closer->inl_text) 58 | break; 59 | next = cmark_node_next(tmp); 60 | cmark_node_append_child(strikethrough, tmp); 61 | tmp = next; 62 | } 63 | 64 | strikethrough->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1; 65 | cmark_node_free(closer->inl_text); 66 | 67 | delim = closer; 68 | while (delim != NULL && delim != opener) { 69 | tmp_delim = delim->previous; 70 | cmark_inline_parser_remove_delimiter(inline_parser, delim); 71 | delim = tmp_delim; 72 | } 73 | 74 | cmark_inline_parser_remove_delimiter(inline_parser, opener); 75 | 76 | done: 77 | return res; 78 | } 79 | 80 | static const char *get_type_string(cmark_syntax_extension *extension, 81 | cmark_node *node) { 82 | return node->type == CMARK_NODE_STRIKETHROUGH ? "strikethrough" : ""; 83 | } 84 | 85 | static int can_contain(cmark_syntax_extension *extension, cmark_node *node, 86 | cmark_node_type child_type) { 87 | if (node->type != CMARK_NODE_STRIKETHROUGH) 88 | return false; 89 | 90 | return CMARK_NODE_TYPE_INLINE_P(child_type); 91 | } 92 | 93 | static void commonmark_render(cmark_syntax_extension *extension, 94 | cmark_renderer *renderer, cmark_node *node, 95 | cmark_event_type ev_type, int options) { 96 | renderer->out(renderer, node, cmark_node_get_string_content(node), false, LITERAL); 97 | } 98 | 99 | static void latex_render(cmark_syntax_extension *extension, 100 | cmark_renderer *renderer, cmark_node *node, 101 | cmark_event_type ev_type, int options) { 102 | // requires \usepackage{ulem} 103 | bool entering = (ev_type == CMARK_EVENT_ENTER); 104 | if (entering) { 105 | renderer->out(renderer, node, "\\sout{", false, LITERAL); 106 | } else { 107 | renderer->out(renderer, node, "}", false, LITERAL); 108 | } 109 | } 110 | 111 | static void man_render(cmark_syntax_extension *extension, 112 | cmark_renderer *renderer, cmark_node *node, 113 | cmark_event_type ev_type, int options) { 114 | bool entering = (ev_type == CMARK_EVENT_ENTER); 115 | if (entering) { 116 | renderer->cr(renderer); 117 | renderer->out(renderer, node, ".ST \"", false, LITERAL); 118 | } else { 119 | renderer->out(renderer, node, "\"", false, LITERAL); 120 | renderer->cr(renderer); 121 | } 122 | } 123 | 124 | static void html_render(cmark_syntax_extension *extension, 125 | cmark_html_renderer *renderer, cmark_node *node, 126 | cmark_event_type ev_type, int options) { 127 | bool entering = (ev_type == CMARK_EVENT_ENTER); 128 | if (entering) { 129 | cmark_strbuf_puts(renderer->html, ""); 130 | } else { 131 | cmark_strbuf_puts(renderer->html, ""); 132 | } 133 | } 134 | 135 | cmark_syntax_extension *create_strikethrough_extension(void) { 136 | cmark_syntax_extension *ext = cmark_syntax_extension_new("strikethrough"); 137 | cmark_llist *special_chars = NULL; 138 | 139 | cmark_syntax_extension_set_get_type_string_func(ext, get_type_string); 140 | cmark_syntax_extension_set_can_contain_func(ext, can_contain); 141 | cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render); 142 | cmark_syntax_extension_set_latex_render_func(ext, latex_render); 143 | cmark_syntax_extension_set_man_render_func(ext, man_render); 144 | cmark_syntax_extension_set_html_render_func(ext, html_render); 145 | CMARK_NODE_STRIKETHROUGH = cmark_syntax_extension_add_node(1); 146 | 147 | cmark_syntax_extension_set_match_inline_func(ext, match); 148 | cmark_syntax_extension_set_inline_from_delim_func(ext, insert); 149 | 150 | cmark_mem *mem = cmark_get_default_mem_allocator(); 151 | special_chars = cmark_llist_append(mem, special_chars, (void *)'~'); 152 | cmark_syntax_extension_set_special_inline_chars(ext, special_chars); 153 | 154 | cmark_syntax_extension_set_emphasis(ext, 1); 155 | 156 | return ext; 157 | } 158 | -------------------------------------------------------------------------------- /Source/cmark_gfm/xml.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "config.h" 7 | #include "cmark.h" 8 | #include "node.h" 9 | #include "buffer.h" 10 | #include "houdini.h" 11 | 12 | #define BUFFER_SIZE 100 13 | 14 | // Functions to convert cmark_nodes to XML strings. 15 | 16 | static void escape_xml(cmark_strbuf *dest, const unsigned char *source, 17 | bufsize_t length) { 18 | houdini_escape_html0(dest, source, length, 0); 19 | } 20 | 21 | struct render_state { 22 | cmark_strbuf *xml; 23 | int indent; 24 | }; 25 | 26 | static CMARK_INLINE void indent(struct render_state *state) { 27 | int i; 28 | for (i = 0; i < state->indent; i++) { 29 | cmark_strbuf_putc(state->xml, ' '); 30 | } 31 | } 32 | 33 | static int S_render_node(cmark_node *node, cmark_event_type ev_type, 34 | struct render_state *state, int options) { 35 | cmark_strbuf *xml = state->xml; 36 | bool literal = false; 37 | cmark_delim_type delim; 38 | bool entering = (ev_type == CMARK_EVENT_ENTER); 39 | char buffer[BUFFER_SIZE]; 40 | 41 | if (entering) { 42 | indent(state); 43 | cmark_strbuf_putc(xml, '<'); 44 | cmark_strbuf_puts(xml, cmark_node_get_type_string(node)); 45 | 46 | if (options & CMARK_OPT_SOURCEPOS && node->start_line != 0) { 47 | snprintf(buffer, BUFFER_SIZE, " sourcepos=\"%d:%d-%d:%d\"", 48 | node->start_line, node->start_column, node->end_line, 49 | node->end_column); 50 | cmark_strbuf_puts(xml, buffer); 51 | } 52 | 53 | literal = false; 54 | 55 | switch (node->type) { 56 | case CMARK_NODE_DOCUMENT: 57 | cmark_strbuf_puts(xml, " xmlns=\"http://commonmark.org/xml/1.0\""); 58 | break; 59 | case CMARK_NODE_TEXT: 60 | case CMARK_NODE_CODE: 61 | case CMARK_NODE_HTML_BLOCK: 62 | case CMARK_NODE_HTML_INLINE: 63 | cmark_strbuf_puts(xml, ">"); 64 | escape_xml(xml, node->as.literal.data, node->as.literal.len); 65 | cmark_strbuf_puts(xml, "as.heading.level); 95 | cmark_strbuf_puts(xml, buffer); 96 | break; 97 | case CMARK_NODE_CODE_BLOCK: 98 | if (node->as.code.info.len > 0) { 99 | cmark_strbuf_puts(xml, " info=\""); 100 | escape_xml(xml, node->as.code.info.data, node->as.code.info.len); 101 | cmark_strbuf_putc(xml, '"'); 102 | } 103 | cmark_strbuf_puts(xml, ">"); 104 | escape_xml(xml, node->as.code.literal.data, node->as.code.literal.len); 105 | cmark_strbuf_puts(xml, "as.custom.on_enter.data, 113 | node->as.custom.on_enter.len); 114 | cmark_strbuf_putc(xml, '"'); 115 | cmark_strbuf_puts(xml, " on_exit=\""); 116 | escape_xml(xml, node->as.custom.on_exit.data, 117 | node->as.custom.on_exit.len); 118 | cmark_strbuf_putc(xml, '"'); 119 | break; 120 | case CMARK_NODE_LINK: 121 | case CMARK_NODE_IMAGE: 122 | cmark_strbuf_puts(xml, " destination=\""); 123 | escape_xml(xml, node->as.link.url.data, node->as.link.url.len); 124 | cmark_strbuf_putc(xml, '"'); 125 | cmark_strbuf_puts(xml, " title=\""); 126 | escape_xml(xml, node->as.link.title.data, node->as.link.title.len); 127 | cmark_strbuf_putc(xml, '"'); 128 | break; 129 | default: 130 | break; 131 | } 132 | if (node->first_child) { 133 | state->indent += 2; 134 | } else if (!literal) { 135 | cmark_strbuf_puts(xml, " /"); 136 | } 137 | cmark_strbuf_puts(xml, ">\n"); 138 | 139 | } else if (node->first_child) { 140 | state->indent -= 2; 141 | indent(state); 142 | cmark_strbuf_puts(xml, "\n"); 145 | } 146 | 147 | return 1; 148 | } 149 | 150 | char *cmark_render_xml(cmark_node *root, int options) { 151 | return cmark_render_xml_with_mem(root, options, cmark_node_mem(root)); 152 | } 153 | 154 | char *cmark_render_xml_with_mem(cmark_node *root, int options, cmark_mem *mem) { 155 | char *result; 156 | cmark_strbuf xml = CMARK_BUF_INIT(mem); 157 | cmark_event_type ev_type; 158 | cmark_node *cur; 159 | struct render_state state = {&xml, 0}; 160 | 161 | cmark_iter *iter = cmark_iter_new(root); 162 | 163 | cmark_strbuf_puts(state.xml, "\n"); 164 | cmark_strbuf_puts(state.xml, 165 | "\n"); 166 | while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { 167 | cur = cmark_iter_get_node(iter); 168 | S_render_node(cur, ev_type, &state, options); 169 | } 170 | result = (char *)cmark_strbuf_detach(&xml); 171 | 172 | cmark_iter_free(iter); 173 | return result; 174 | } 175 | -------------------------------------------------------------------------------- /Source/Node.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonMark.swift 3 | // CommonMark 4 | // 5 | // Created by Chris Eidhof on 22/05/15. 6 | // Copyright (c) 2015 Unsigned Integer. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import cmark_gfm 11 | 12 | public func markdownToHtml(string: String) -> String { 13 | let outString = cmark_markdown_to_html(string, string.utf8.count, 0)! 14 | defer { free(outString) } 15 | return String(cString: outString) 16 | } 17 | 18 | struct Markdown { 19 | var string: String 20 | 21 | init(_ string: String) { 22 | self.string = string 23 | } 24 | 25 | var html: String { 26 | let outString = cmark_markdown_to_html(string, string.utf8.count, 0)! 27 | return String(cString: outString) 28 | } 29 | } 30 | 31 | extension String { 32 | init?(unsafeCString: UnsafePointer!) { 33 | guard let cString = unsafeCString else { return nil } 34 | self.init(cString: cString) 35 | } 36 | } 37 | 38 | /// A node in a Markdown document. 39 | /// 40 | /// Can represent a full Markdown document (i.e. the document's root node) or 41 | /// just some part of a document. 42 | public class Node: CustomStringConvertible { 43 | let node: UnsafeMutablePointer 44 | 45 | init(node: UnsafeMutablePointer) { 46 | self.node = node 47 | } 48 | 49 | public init?(markdown: String) { 50 | core_extensions_ensure_registered() 51 | 52 | guard let parser = cmark_parser_new(0) else { return nil } 53 | defer { cmark_parser_free(parser) } 54 | 55 | if let ext = cmark_find_syntax_extension("table") { 56 | cmark_parser_attach_syntax_extension(parser, ext) 57 | } 58 | 59 | if let ext = cmark_find_syntax_extension("autolink") { 60 | cmark_parser_attach_syntax_extension(parser, ext) 61 | } 62 | 63 | if let ext = cmark_find_syntax_extension("strikethrough") { 64 | cmark_parser_attach_syntax_extension(parser, ext) 65 | } 66 | 67 | if let ext = cmark_find_syntax_extension("mention") { 68 | cmark_parser_attach_syntax_extension(parser, ext) 69 | } 70 | 71 | if let ext = cmark_find_syntax_extension("checkbox") { 72 | cmark_parser_attach_syntax_extension(parser, ext) 73 | } 74 | 75 | cmark_parser_feed(parser, markdown, markdown.utf8.count) 76 | guard let node = cmark_parser_finish(parser) else { return nil } 77 | self.node = node 78 | } 79 | 80 | deinit { 81 | guard type == CMARK_NODE_DOCUMENT else { return } 82 | cmark_node_free(node) 83 | } 84 | 85 | var type: cmark_node_type { 86 | return cmark_node_get_type(node) 87 | } 88 | 89 | var listType: cmark_list_type { 90 | get { return cmark_node_get_list_type(node) } 91 | set { cmark_node_set_list_type(node, newValue) } 92 | } 93 | 94 | var listStart: Int { 95 | get { return Int(cmark_node_get_list_start(node)) } 96 | set { cmark_node_set_list_start(node, Int32(newValue)) } 97 | } 98 | 99 | var typeString: String { 100 | return String(cString: cmark_node_get_type_string(node)!) 101 | } 102 | 103 | var literal: String? { 104 | get { return String(unsafeCString: cmark_node_get_literal(node)) } 105 | set { 106 | if let value = newValue { 107 | cmark_node_set_literal(node, value) 108 | } else { 109 | cmark_node_set_literal(node, nil) 110 | } 111 | } 112 | } 113 | 114 | var headerLevel: Int { 115 | get { return Int(cmark_node_get_heading_level(node)) } 116 | set { cmark_node_set_heading_level(node, Int32(newValue)) } 117 | } 118 | 119 | var login: String? { 120 | return String(unsafeCString: cmark_node_get_mention_login(node)) 121 | } 122 | 123 | var checked: Bool { 124 | return cmark_node_get_checkbox_checked(node) == 1 125 | } 126 | 127 | var checkedRange: NSRange { 128 | return NSRange( 129 | location: Int(cmark_node_get_checkbox_location(node)), 130 | length: Int(cmark_node_get_checkbox_length(node)) 131 | ) 132 | } 133 | 134 | var fenceInfo: String? { 135 | get { 136 | return String(unsafeCString: cmark_node_get_fence_info(node)) } 137 | set { 138 | if let value = newValue { 139 | cmark_node_set_fence_info(node, value) 140 | } else { 141 | cmark_node_set_fence_info(node, nil) 142 | } 143 | } 144 | } 145 | 146 | var urlString: String? { 147 | get { return String(unsafeCString: cmark_node_get_url(node)) } 148 | set { 149 | if let value = newValue { 150 | cmark_node_set_url(node, value) 151 | } else { 152 | cmark_node_set_url(node, nil) 153 | } 154 | } 155 | } 156 | 157 | var title: String? { 158 | get { return String(unsafeCString: cmark_node_get_title(node)) } 159 | set { 160 | if let value = newValue { 161 | cmark_node_set_title(node, value) 162 | } else { 163 | cmark_node_set_title(node, nil) 164 | } 165 | } 166 | } 167 | 168 | var children: [Node] { 169 | var result: [Node] = [] 170 | 171 | var child = cmark_node_first_child(node) 172 | while let unwrapped = child { 173 | result.append(Node(node: unwrapped)) 174 | child = cmark_node_next(child) 175 | } 176 | return result 177 | } 178 | 179 | /// Renders the HTML representation 180 | public var html: String { 181 | return String(cString: cmark_render_html(node, 0, nil)) 182 | } 183 | 184 | /// Renders the XML representation 185 | public var xml: String { 186 | return String(cString: cmark_render_xml(node, 0)) 187 | } 188 | 189 | /// Renders the CommonMark representation 190 | public var commonMark: String { 191 | return String(cString: cmark_render_commonmark(node, CMARK_OPT_DEFAULT, 80)) 192 | } 193 | 194 | /// Renders the LaTeX representation 195 | public var latex: String { 196 | return String(cString: cmark_render_latex(node, CMARK_OPT_DEFAULT, 80)) 197 | } 198 | 199 | public var description: String { 200 | return "\(typeString) {\n \(literal ?? String())\(Array(children).description) \n}" 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /Source/cmark_gfm/plaintext.c: -------------------------------------------------------------------------------- 1 | #include "node.h" 2 | #include "syntax_extension.h" 3 | #include "render.h" 4 | 5 | #define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping) 6 | #define LIT(s) renderer->out(renderer, node, s, false, LITERAL) 7 | #define CR() renderer->cr(renderer) 8 | #define BLANKLINE() renderer->blankline(renderer) 9 | #define LISTMARKER_SIZE 20 10 | 11 | // Functions to convert cmark_nodes to plain text strings. 12 | 13 | static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node, 14 | cmark_escaping escape, 15 | int32_t c, unsigned char nextc) { 16 | cmark_render_code_point(renderer, c); 17 | } 18 | 19 | // if node is a block node, returns node. 20 | // otherwise returns first block-level node that is an ancestor of node. 21 | // if there is no block-level ancestor, returns NULL. 22 | static cmark_node *get_containing_block(cmark_node *node) { 23 | while (node) { 24 | if (CMARK_NODE_BLOCK_P(node)) { 25 | return node; 26 | } else { 27 | node = node->parent; 28 | } 29 | } 30 | return NULL; 31 | } 32 | 33 | static int S_render_node(cmark_renderer *renderer, cmark_node *node, 34 | cmark_event_type ev_type, int options) { 35 | cmark_node *tmp; 36 | int list_number; 37 | cmark_delim_type list_delim; 38 | int i; 39 | bool entering = (ev_type == CMARK_EVENT_ENTER); 40 | char listmarker[LISTMARKER_SIZE]; 41 | bool first_in_list_item; 42 | bufsize_t marker_width; 43 | bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) && 44 | !(CMARK_OPT_HARDBREAKS & options); 45 | 46 | // Don't adjust tight list status til we've started the list. 47 | // Otherwise we loose the blank line between a paragraph and 48 | // a following list. 49 | if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) { 50 | tmp = get_containing_block(node); 51 | renderer->in_tight_list_item = 52 | tmp && // tmp might be NULL if there is no containing block 53 | ((tmp->type == CMARK_NODE_ITEM && 54 | cmark_node_get_list_tight(tmp->parent)) || 55 | (tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM && 56 | cmark_node_get_list_tight(tmp->parent->parent))); 57 | } 58 | 59 | if (node->extension && node->extension->plaintext_render_func) { 60 | node->extension->plaintext_render_func(node->extension, renderer, node, ev_type, options); 61 | return 1; 62 | } 63 | 64 | switch (node->type) { 65 | case CMARK_NODE_DOCUMENT: 66 | break; 67 | 68 | case CMARK_NODE_BLOCK_QUOTE: 69 | break; 70 | 71 | case CMARK_NODE_LIST: 72 | if (!entering && node->next && (node->next->type == CMARK_NODE_CODE_BLOCK || 73 | node->next->type == CMARK_NODE_LIST)) { 74 | CR(); 75 | } 76 | break; 77 | 78 | case CMARK_NODE_ITEM: 79 | if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { 80 | marker_width = 4; 81 | } else { 82 | list_number = cmark_node_get_list_start(node->parent); 83 | list_delim = cmark_node_get_list_delim(node->parent); 84 | tmp = node; 85 | while (tmp->prev) { 86 | tmp = tmp->prev; 87 | list_number += 1; 88 | } 89 | // we ensure a width of at least 4 so 90 | // we get nice transition from single digits 91 | // to double 92 | snprintf(listmarker, LISTMARKER_SIZE, "%d%s%s", list_number, 93 | list_delim == CMARK_PAREN_DELIM ? ")" : ".", 94 | list_number < 10 ? " " : " "); 95 | marker_width = (bufsize_t)strlen(listmarker); 96 | } 97 | if (entering) { 98 | if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { 99 | LIT(" - "); 100 | renderer->begin_content = true; 101 | } else { 102 | LIT(listmarker); 103 | renderer->begin_content = true; 104 | } 105 | for (i = marker_width; i--;) { 106 | cmark_strbuf_putc(renderer->prefix, ' '); 107 | } 108 | } else { 109 | cmark_strbuf_truncate(renderer->prefix, 110 | renderer->prefix->size - marker_width); 111 | CR(); 112 | } 113 | break; 114 | 115 | case CMARK_NODE_HEADING: 116 | if (entering) { 117 | renderer->begin_content = true; 118 | renderer->no_linebreaks = true; 119 | } else { 120 | renderer->no_linebreaks = false; 121 | BLANKLINE(); 122 | } 123 | break; 124 | 125 | case CMARK_NODE_CODE_BLOCK: 126 | first_in_list_item = node->prev == NULL && node->parent && 127 | node->parent->type == CMARK_NODE_ITEM; 128 | 129 | if (!first_in_list_item) { 130 | BLANKLINE(); 131 | } 132 | OUT(cmark_node_get_literal(node), false, LITERAL); 133 | BLANKLINE(); 134 | break; 135 | 136 | case CMARK_NODE_HTML_BLOCK: 137 | break; 138 | 139 | case CMARK_NODE_CUSTOM_BLOCK: 140 | break; 141 | 142 | case CMARK_NODE_THEMATIC_BREAK: 143 | BLANKLINE(); 144 | break; 145 | 146 | case CMARK_NODE_PARAGRAPH: 147 | if (!entering) { 148 | BLANKLINE(); 149 | } 150 | break; 151 | 152 | case CMARK_NODE_TEXT: 153 | OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); 154 | break; 155 | 156 | case CMARK_NODE_LINEBREAK: 157 | CR(); 158 | break; 159 | 160 | case CMARK_NODE_SOFTBREAK: 161 | if (CMARK_OPT_HARDBREAKS & options) { 162 | CR(); 163 | } else if (!renderer->no_linebreaks && renderer->width == 0 && 164 | !(CMARK_OPT_HARDBREAKS & options) && 165 | !(CMARK_OPT_NOBREAKS & options)) { 166 | CR(); 167 | } else { 168 | OUT(" ", allow_wrap, LITERAL); 169 | } 170 | break; 171 | 172 | case CMARK_NODE_CODE: 173 | OUT(cmark_node_get_literal(node), allow_wrap, LITERAL); 174 | break; 175 | 176 | case CMARK_NODE_HTML_INLINE: 177 | break; 178 | 179 | case CMARK_NODE_CUSTOM_INLINE: 180 | break; 181 | 182 | case CMARK_NODE_STRONG: 183 | break; 184 | 185 | case CMARK_NODE_EMPH: 186 | break; 187 | 188 | case CMARK_NODE_LINK: 189 | break; 190 | 191 | case CMARK_NODE_IMAGE: 192 | break; 193 | 194 | default: 195 | assert(false); 196 | break; 197 | } 198 | 199 | return 1; 200 | } 201 | 202 | char *cmark_render_plaintext(cmark_node *root, int options, int width) { 203 | return cmark_render_plaintext_with_mem(root, options, width, cmark_node_mem(root)); 204 | } 205 | 206 | char *cmark_render_plaintext_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) { 207 | if (options & CMARK_OPT_HARDBREAKS) { 208 | // disable breaking on width, since it has 209 | // a different meaning with OPT_HARDBREAKS 210 | width = 0; 211 | } 212 | return cmark_render(mem, root, options, width, outc, S_render_node); 213 | } 214 | -------------------------------------------------------------------------------- /Source/cmark_gfm/man.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "config.h" 7 | #include "cmark.h" 8 | #include "node.h" 9 | #include "buffer.h" 10 | #include "utf8.h" 11 | #include "render.h" 12 | #include "syntax_extension.h" 13 | 14 | #define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping) 15 | #define LIT(s) renderer->out(renderer, node, s, false, LITERAL) 16 | #define CR() renderer->cr(renderer) 17 | #define BLANKLINE() renderer->blankline(renderer) 18 | #define LIST_NUMBER_SIZE 20 19 | 20 | // Functions to convert cmark_nodes to groff man strings. 21 | static void S_outc(cmark_renderer *renderer, cmark_node *node, 22 | cmark_escaping escape, int32_t c, 23 | unsigned char nextc) { 24 | (void)(nextc); 25 | 26 | if (escape == LITERAL) { 27 | cmark_render_code_point(renderer, c); 28 | return; 29 | } 30 | 31 | switch (c) { 32 | case 46: 33 | if (renderer->begin_line) { 34 | cmark_render_ascii(renderer, "\\&."); 35 | } else { 36 | cmark_render_code_point(renderer, c); 37 | } 38 | break; 39 | case 39: 40 | if (renderer->begin_line) { 41 | cmark_render_ascii(renderer, "\\&'"); 42 | } else { 43 | cmark_render_code_point(renderer, c); 44 | } 45 | break; 46 | case 45: 47 | cmark_render_ascii(renderer, "\\-"); 48 | break; 49 | case 92: 50 | cmark_render_ascii(renderer, "\\e"); 51 | break; 52 | case 8216: // left single quote 53 | cmark_render_ascii(renderer, "\\[oq]"); 54 | break; 55 | case 8217: // right single quote 56 | cmark_render_ascii(renderer, "\\[cq]"); 57 | break; 58 | case 8220: // left double quote 59 | cmark_render_ascii(renderer, "\\[lq]"); 60 | break; 61 | case 8221: // right double quote 62 | cmark_render_ascii(renderer, "\\[rq]"); 63 | break; 64 | case 8212: // em dash 65 | cmark_render_ascii(renderer, "\\[em]"); 66 | break; 67 | case 8211: // en dash 68 | cmark_render_ascii(renderer, "\\[en]"); 69 | break; 70 | default: 71 | cmark_render_code_point(renderer, c); 72 | } 73 | } 74 | 75 | static int S_render_node(cmark_renderer *renderer, cmark_node *node, 76 | cmark_event_type ev_type, int options) { 77 | cmark_node *tmp; 78 | int list_number; 79 | bool entering = (ev_type == CMARK_EVENT_ENTER); 80 | bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options); 81 | 82 | if (node->extension && node->extension->man_render_func) { 83 | node->extension->man_render_func(node->extension, renderer, node, ev_type, options); 84 | return 1; 85 | } 86 | 87 | switch (node->type) { 88 | case CMARK_NODE_DOCUMENT: 89 | if (entering) { 90 | /* Define a strikethrough macro */ 91 | /* Commenting out because this makes tests fail 92 | LIT(".de ST"); 93 | CR(); 94 | LIT(".nr ww \\w'\\\\$1'"); 95 | CR(); 96 | LIT("\\Z@\\v'-.25m'\\l'\\\\n[ww]u'@\\\\$1"); 97 | CR(); 98 | LIT(".."); 99 | CR(); 100 | */ 101 | } 102 | break; 103 | 104 | case CMARK_NODE_BLOCK_QUOTE: 105 | if (entering) { 106 | CR(); 107 | LIT(".RS"); 108 | CR(); 109 | } else { 110 | CR(); 111 | LIT(".RE"); 112 | CR(); 113 | } 114 | break; 115 | 116 | case CMARK_NODE_LIST: 117 | break; 118 | 119 | case CMARK_NODE_ITEM: 120 | if (entering) { 121 | CR(); 122 | LIT(".IP "); 123 | if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { 124 | LIT("\\[bu] 2"); 125 | } else { 126 | list_number = cmark_node_get_list_start(node->parent); 127 | tmp = node; 128 | while (tmp->prev) { 129 | tmp = tmp->prev; 130 | list_number += 1; 131 | } 132 | char list_number_s[LIST_NUMBER_SIZE]; 133 | snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number); 134 | LIT(list_number_s); 135 | } 136 | CR(); 137 | } else { 138 | CR(); 139 | } 140 | break; 141 | 142 | case CMARK_NODE_HEADING: 143 | if (entering) { 144 | CR(); 145 | LIT(cmark_node_get_heading_level(node) == 1 ? ".SH" : ".SS"); 146 | CR(); 147 | } else { 148 | CR(); 149 | } 150 | break; 151 | 152 | case CMARK_NODE_CODE_BLOCK: 153 | CR(); 154 | LIT(".IP\n.nf\n\\f[C]\n"); 155 | OUT(cmark_node_get_literal(node), false, NORMAL); 156 | CR(); 157 | LIT("\\f[]\n.fi"); 158 | CR(); 159 | break; 160 | 161 | case CMARK_NODE_HTML_BLOCK: 162 | break; 163 | 164 | case CMARK_NODE_CUSTOM_BLOCK: 165 | CR(); 166 | OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), 167 | false, LITERAL); 168 | CR(); 169 | break; 170 | 171 | case CMARK_NODE_THEMATIC_BREAK: 172 | CR(); 173 | LIT(".PP\n * * * * *"); 174 | CR(); 175 | break; 176 | 177 | case CMARK_NODE_PARAGRAPH: 178 | if (entering) { 179 | // no blank line if first paragraph in list: 180 | if (node->parent && node->parent->type == CMARK_NODE_ITEM && 181 | node->prev == NULL) { 182 | // no blank line or .PP 183 | } else { 184 | CR(); 185 | LIT(".PP"); 186 | CR(); 187 | } 188 | } else { 189 | CR(); 190 | } 191 | break; 192 | 193 | case CMARK_NODE_TEXT: 194 | OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); 195 | break; 196 | 197 | case CMARK_NODE_LINEBREAK: 198 | LIT(".PD 0\n.P\n.PD"); 199 | CR(); 200 | break; 201 | 202 | case CMARK_NODE_SOFTBREAK: 203 | if (options & CMARK_OPT_HARDBREAKS) { 204 | LIT(".PD 0\n.P\n.PD"); 205 | CR(); 206 | } else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) { 207 | CR(); 208 | } else { 209 | OUT(" ", allow_wrap, LITERAL); 210 | } 211 | break; 212 | 213 | case CMARK_NODE_CODE: 214 | LIT("\\f[C]"); 215 | OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); 216 | LIT("\\f[]"); 217 | break; 218 | 219 | case CMARK_NODE_HTML_INLINE: 220 | break; 221 | 222 | case CMARK_NODE_CUSTOM_INLINE: 223 | OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), 224 | false, LITERAL); 225 | break; 226 | 227 | case CMARK_NODE_STRONG: 228 | if (entering) { 229 | LIT("\\f[B]"); 230 | } else { 231 | LIT("\\f[]"); 232 | } 233 | break; 234 | 235 | case CMARK_NODE_EMPH: 236 | if (entering) { 237 | LIT("\\f[I]"); 238 | } else { 239 | LIT("\\f[]"); 240 | } 241 | break; 242 | 243 | case CMARK_NODE_LINK: 244 | if (!entering) { 245 | LIT(" ("); 246 | OUT(cmark_node_get_url(node), allow_wrap, URL); 247 | LIT(")"); 248 | } 249 | break; 250 | 251 | case CMARK_NODE_IMAGE: 252 | if (entering) { 253 | LIT("[IMAGE: "); 254 | } else { 255 | LIT("]"); 256 | } 257 | break; 258 | 259 | case CMARK_NODE_FOOTNOTE_DEFINITION: 260 | case CMARK_NODE_FOOTNOTE_REFERENCE: 261 | // TODO 262 | break; 263 | 264 | default: 265 | assert(false); 266 | break; 267 | } 268 | 269 | return 1; 270 | } 271 | 272 | char *cmark_render_man(cmark_node *root, int options, int width) { 273 | return cmark_render_man_with_mem(root, options, width, cmark_node_mem(root)); 274 | } 275 | 276 | char *cmark_render_man_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) { 277 | return cmark_render(mem, root, options, width, S_outc, S_render_node); 278 | } 279 | -------------------------------------------------------------------------------- /Source/cmark_gfm/render.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "buffer.h" 3 | #include "chunk.h" 4 | #include "cmark.h" 5 | #include "utf8.h" 6 | #include "render.h" 7 | #include "node.h" 8 | #include "syntax_extension.h" 9 | 10 | static CMARK_INLINE void S_cr(cmark_renderer *renderer) { 11 | if (renderer->need_cr < 1) { 12 | renderer->need_cr = 1; 13 | } 14 | } 15 | 16 | static CMARK_INLINE void S_blankline(cmark_renderer *renderer) { 17 | if (renderer->need_cr < 2) { 18 | renderer->need_cr = 2; 19 | } 20 | } 21 | 22 | static void S_out(cmark_renderer *renderer, cmark_node *node, 23 | const char *source, bool wrap, 24 | cmark_escaping escape) { 25 | int length = (int)strlen(source); 26 | unsigned char nextc; 27 | int32_t c; 28 | int i = 0; 29 | int last_nonspace; 30 | int len; 31 | cmark_chunk remainder = cmark_chunk_literal(""); 32 | int k = renderer->buffer->size - 1; 33 | 34 | cmark_syntax_extension *ext = NULL; 35 | cmark_node *n = node; 36 | while (n && !ext) { 37 | ext = n->extension; 38 | if (!ext) 39 | n = n->parent; 40 | } 41 | if (ext && !ext->commonmark_escape_func) 42 | ext = NULL; 43 | 44 | wrap = wrap && !renderer->no_linebreaks; 45 | 46 | if (renderer->in_tight_list_item && renderer->need_cr > 1) { 47 | renderer->need_cr = 1; 48 | } 49 | while (renderer->need_cr) { 50 | if (k < 0 || renderer->buffer->ptr[k] == '\n') { 51 | k -= 1; 52 | } else { 53 | cmark_strbuf_putc(renderer->buffer, '\n'); 54 | if (renderer->need_cr > 1) { 55 | cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr, 56 | renderer->prefix->size); 57 | } 58 | } 59 | renderer->column = 0; 60 | renderer->begin_line = true; 61 | renderer->begin_content = true; 62 | renderer->need_cr -= 1; 63 | } 64 | 65 | while (i < length) { 66 | if (renderer->begin_line) { 67 | cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr, 68 | renderer->prefix->size); 69 | // note: this assumes prefix is ascii: 70 | renderer->column = renderer->prefix->size; 71 | } 72 | 73 | len = cmark_utf8proc_iterate((const uint8_t *)source + i, length - i, &c); 74 | if (len == -1) { // error condition 75 | return; // return without rendering rest of string 76 | } 77 | 78 | if (ext && ext->commonmark_escape_func(ext, node, c)) 79 | cmark_strbuf_putc(renderer->buffer, '\\'); 80 | 81 | nextc = source[i + len]; 82 | if (c == 32 && wrap) { 83 | if (!renderer->begin_line) { 84 | last_nonspace = renderer->buffer->size; 85 | cmark_strbuf_putc(renderer->buffer, ' '); 86 | renderer->column += 1; 87 | renderer->begin_line = false; 88 | renderer->begin_content = false; 89 | // skip following spaces 90 | while (source[i + 1] == ' ') { 91 | i++; 92 | } 93 | // We don't allow breaks that make a digit the first character 94 | // because this causes problems with commonmark output. 95 | if (!cmark_isdigit(source[i + 1])) { 96 | renderer->last_breakable = last_nonspace; 97 | } 98 | } 99 | 100 | } else if (c == 10) { 101 | cmark_strbuf_putc(renderer->buffer, '\n'); 102 | renderer->column = 0; 103 | renderer->begin_line = true; 104 | renderer->begin_content = true; 105 | renderer->last_breakable = 0; 106 | } else if (escape == LITERAL) { 107 | cmark_render_code_point(renderer, c); 108 | renderer->begin_line = false; 109 | // we don't set 'begin_content' to false til we've 110 | // finished parsing a digit. Reason: in commonmark 111 | // we need to escape a potential list marker after 112 | // a digit: 113 | renderer->begin_content = 114 | renderer->begin_content && cmark_isdigit((char)c) == 1; 115 | } else { 116 | (renderer->outc)(renderer, node, escape, c, nextc); 117 | renderer->begin_line = false; 118 | renderer->begin_content = 119 | renderer->begin_content && cmark_isdigit((char)c) == 1; 120 | } 121 | 122 | // If adding the character went beyond width, look for an 123 | // earlier place where the line could be broken: 124 | if (renderer->width > 0 && renderer->column > renderer->width && 125 | !renderer->begin_line && renderer->last_breakable > 0) { 126 | 127 | // copy from last_breakable to remainder 128 | cmark_chunk_set_cstr(renderer->mem, &remainder, 129 | (char *)renderer->buffer->ptr + 130 | renderer->last_breakable + 1); 131 | // truncate at last_breakable 132 | cmark_strbuf_truncate(renderer->buffer, renderer->last_breakable); 133 | // add newline, prefix, and remainder 134 | cmark_strbuf_putc(renderer->buffer, '\n'); 135 | cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr, 136 | renderer->prefix->size); 137 | cmark_strbuf_put(renderer->buffer, remainder.data, remainder.len); 138 | renderer->column = renderer->prefix->size + remainder.len; 139 | cmark_chunk_free(renderer->mem, &remainder); 140 | renderer->last_breakable = 0; 141 | renderer->begin_line = false; 142 | renderer->begin_content = false; 143 | } 144 | 145 | i += len; 146 | } 147 | } 148 | 149 | // Assumes no newlines, assumes ascii content: 150 | void cmark_render_ascii(cmark_renderer *renderer, const char *s) { 151 | int origsize = renderer->buffer->size; 152 | cmark_strbuf_puts(renderer->buffer, s); 153 | renderer->column += renderer->buffer->size - origsize; 154 | } 155 | 156 | void cmark_render_code_point(cmark_renderer *renderer, uint32_t c) { 157 | cmark_utf8proc_encode_char(c, renderer->buffer); 158 | renderer->column += 1; 159 | } 160 | 161 | char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width, 162 | void (*outc)(cmark_renderer *, cmark_node *, 163 | cmark_escaping, int32_t, 164 | unsigned char), 165 | int (*render_node)(cmark_renderer *renderer, 166 | cmark_node *node, 167 | cmark_event_type ev_type, int options)) { 168 | cmark_strbuf pref = CMARK_BUF_INIT(mem); 169 | cmark_strbuf buf = CMARK_BUF_INIT(mem); 170 | cmark_node *cur; 171 | cmark_event_type ev_type; 172 | char *result; 173 | cmark_iter *iter = cmark_iter_new(root); 174 | 175 | cmark_renderer renderer = {mem, &buf, &pref, 0, width, 176 | 0, 0, true, true, false, 177 | false, outc, S_cr, S_blankline, S_out, 178 | 0}; 179 | 180 | while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { 181 | cur = cmark_iter_get_node(iter); 182 | if (!render_node(&renderer, cur, ev_type, options)) { 183 | // a false value causes us to skip processing 184 | // the node's contents. this is used for 185 | // autolinks. 186 | cmark_iter_reset(iter, cur, CMARK_EVENT_EXIT); 187 | } 188 | } 189 | 190 | // ensure final newline 191 | if (renderer.buffer->ptr[renderer.buffer->size - 1] != '\n') { 192 | cmark_strbuf_putc(renderer.buffer, '\n'); 193 | } 194 | 195 | result = (char *)cmark_strbuf_detach(renderer.buffer); 196 | 197 | cmark_iter_free(iter); 198 | cmark_strbuf_free(renderer.prefix); 199 | cmark_strbuf_free(renderer.buffer); 200 | 201 | return result; 202 | } 203 | -------------------------------------------------------------------------------- /Source/ASTOperations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASTOperations.swift 3 | // CommonMark 4 | // 5 | // Created by Chris Eidhof on 23/05/15. 6 | // Copyright (c) 2015 Unsigned Integer. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Sequence where Iterator.Element == Block { 12 | /// Apply a transformation to each block-level element in the sequence. 13 | /// Performs a deep traversal of the element tree. 14 | /// 15 | /// - parameter f: The transformation function that will be recursively applied 16 | /// to each block-level element in `elements`. 17 | /// 18 | /// The function returns an array of elements, which allows you to transform 19 | /// one element into several (or none). Return an array containing only the 20 | /// unchanged element to not transform that element at all. Return an empty 21 | /// array to delete an element from the result. 22 | /// - returns: A Markdown document containing the results of the transformation, 23 | /// represented as an array of block-level elements. 24 | 25 | public func deepApply(_ f: (Block) throws -> [Block]) rethrows -> [Block] { 26 | return try flatMap { try $0.deepApply(f) } 27 | } 28 | 29 | /// Apply a transformation to each inline element in the sequence 30 | /// Performs a deep traversal of the element tree. 31 | /// 32 | /// - parameter f: The transformation function that will be recursively applied 33 | /// to each inline element in `elements`. 34 | /// 35 | /// The function returns an array of elements, which allows you to transform 36 | /// one element into several (or none). Return an array containing only the 37 | /// unchanged element to not transform that element at all. Return an empty 38 | /// array to delete an element from the result. 39 | /// - returns: A Markdown document containing the results of the transformation, 40 | /// represented as an array of block-level elements. 41 | public func deepApply(_ f: (Inline) throws -> [Inline]) rethrows -> [Block] { 42 | return try flatMap { try $0.deepApply(f) } 43 | } 44 | } 45 | 46 | extension Block { 47 | public func deepApply(_ f: (Block) throws -> [Block]) rethrows -> [Block] { 48 | switch self { 49 | case let .list(items, type): 50 | let newItems = try items.map { item in try item.deepApply(f) } 51 | return try f(.list(items: newItems, type: type)) 52 | case .blockQuote(let items): 53 | return try f(.blockQuote(items: try items.deepApply(f))) 54 | default: 55 | return try f(self) 56 | } 57 | } 58 | 59 | public func deepApply(_ f: (Inline) throws -> [Inline]) rethrows -> [Block] { 60 | switch self { 61 | case .paragraph(let children): 62 | return [.paragraph(text: try children.deepApply(f))] 63 | case let .list(items, type): 64 | return [.list(items: try items.map { try $0.deepApply(f) }, type: type)] 65 | case .blockQuote(let items): 66 | return [.blockQuote(items: try items.deepApply(f))] 67 | case let .heading(text, level): 68 | return [.heading(text: try text.deepApply(f), level: level)] 69 | default: 70 | return [self] 71 | } 72 | 73 | } 74 | } 75 | 76 | extension Sequence where Iterator.Element == Inline { 77 | public func deepApply(_ f: (Inline) throws -> [Inline]) rethrows -> [Inline] { 78 | return try flatMap { try $0.deepApply(f) } 79 | } 80 | } 81 | 82 | 83 | extension Inline { 84 | public func deepApply(_ f: (Inline) throws -> [Inline]) rethrows -> [Inline] { 85 | switch self { 86 | case .emphasis(let children): 87 | return try f(.emphasis(children: try children.deepApply(f))) 88 | case .strong(let children): 89 | return try f(Inline.strong(children: try children.deepApply(f))) 90 | case let .link(children, title, url): 91 | return try f(Inline.link(children: try children.deepApply(f), title: title, url: url)) 92 | case let .image(children, title, url): 93 | return try f(Inline.image(children: try children.deepApply(f), title: title, url: url)) 94 | default: 95 | return try f(self) 96 | } 97 | } 98 | } 99 | 100 | 101 | extension Sequence where Iterator.Element == Block { 102 | /// Performs a deep 'flatMap' operation over all _block-level elements_ in a 103 | /// sequence. Performs a deep traversal over all block-level elements 104 | /// in the element tree, applies `f` to each element, and returns the flattened 105 | /// results. 106 | /// 107 | /// Use this function to extract data from a Markdown document. E.g. you could 108 | /// extract the texts and levels of all headers in a document to build a table 109 | /// of contents. 110 | /// 111 | /// - parameter f: The function that will be recursively applied to each 112 | /// block-level element in `elements`. 113 | /// 114 | /// The function returns an array, which allows you to extract zero, one, or 115 | /// multiple pieces of data from each element. Return an empty array to ignore 116 | /// this element in the result. 117 | /// - returns: A flattened array of the results of all invocations of `f`. 118 | public func deep(collect: (Block) throws -> [A]) rethrows -> [A] { 119 | return try flatMap { try $0.deep(collect: collect) } 120 | } 121 | 122 | public func deep(collect: (Inline) throws -> [A]) rethrows -> [A] { 123 | return try flatMap { try $0.deep(collect: collect) } 124 | } 125 | } 126 | 127 | extension Block { 128 | public func deep(collect: (Block) throws -> [A]) rethrows -> [A] { 129 | var result: [A] 130 | switch self { 131 | case .list(let items, _): 132 | result = try items.joined().deep(collect: collect) 133 | case .blockQuote(let items): 134 | result = try items.deep(collect: collect) 135 | default: 136 | result = [] 137 | } 138 | try result.append(contentsOf: collect(self)) 139 | return result 140 | } 141 | 142 | public func deep(collect: (Inline) throws -> [A]) rethrows -> [A] { 143 | var result: [A] 144 | switch self { 145 | case .list(let items, _): 146 | result = try items.joined().deep(collect: collect) 147 | case .blockQuote(let items): 148 | result = try items.deep(collect: collect) 149 | case .heading(let items, _): 150 | result = try items.deep(collect: collect) 151 | case .paragraph(let items): 152 | result = try items.deep(collect: collect) 153 | default: 154 | result = [] 155 | } 156 | return result 157 | } 158 | } 159 | 160 | extension Inline { 161 | public func deep(collect: (Inline) throws -> [A]) rethrows -> [A] { 162 | var result: [A] 163 | switch self { 164 | case .emphasis(let children): 165 | result = try children.deep(collect: collect) 166 | case .strong(let children): 167 | result = try children.deep(collect: collect) 168 | case let .link(children, _, _): 169 | result = try children.deep(collect: collect) 170 | case let .image(children, _, _): 171 | result = try children.deep(collect: collect) 172 | default: 173 | result = [] 174 | } 175 | result.append(contentsOf: try collect(self)) 176 | return result 177 | } 178 | } 179 | 180 | extension Sequence where Iterator.Element == Inline { 181 | public func deep(collect: (Inline) throws -> [A]) rethrows -> [A] { 182 | return try flatMap { try $0.deep(collect: collect) } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Source/cmark_gfm/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "config.h" 11 | #include "cmark_ctype.h" 12 | #include "buffer.h" 13 | #include "memory.h" 14 | 15 | /* Used as default value for cmark_strbuf->ptr so that people can always 16 | * assume ptr is non-NULL and zero terminated even for new cmark_strbufs. 17 | */ 18 | unsigned char cmark_strbuf__initbuf[1]; 19 | 20 | #ifndef MIN 21 | #define MIN(x, y) ((x < y) ? x : y) 22 | #endif 23 | 24 | void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf, 25 | bufsize_t initial_size) { 26 | buf->mem = mem; 27 | buf->asize = 0; 28 | buf->size = 0; 29 | buf->ptr = cmark_strbuf__initbuf; 30 | 31 | if (initial_size > 0) 32 | cmark_strbuf_grow(buf, initial_size); 33 | } 34 | 35 | static CMARK_INLINE void S_strbuf_grow_by(cmark_strbuf *buf, bufsize_t add) { 36 | cmark_strbuf_grow(buf, buf->size + add); 37 | } 38 | 39 | void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) { 40 | assert(target_size > 0); 41 | 42 | if (target_size < buf->asize) 43 | return; 44 | 45 | if (target_size > (bufsize_t)(INT32_MAX / 2)) { 46 | fprintf(stderr, 47 | "[cmark] cmark_strbuf_grow requests buffer with size > %d, aborting\n", 48 | (INT32_MAX / 2)); 49 | abort(); 50 | } 51 | 52 | /* Oversize the buffer by 50% to guarantee amortized linear time 53 | * complexity on append operations. */ 54 | bufsize_t new_size = target_size + target_size / 2; 55 | new_size += 1; 56 | new_size = (new_size + 7) & ~7; 57 | 58 | buf->ptr = (unsigned char *)buf->mem->realloc(buf->asize ? buf->ptr : NULL, 59 | new_size); 60 | buf->asize = new_size; 61 | } 62 | 63 | bufsize_t cmark_strbuf_len(const cmark_strbuf *buf) { return buf->size; } 64 | 65 | void cmark_strbuf_free(cmark_strbuf *buf) { 66 | if (!buf) 67 | return; 68 | 69 | if (buf->ptr != cmark_strbuf__initbuf) 70 | buf->mem->free(buf->ptr); 71 | 72 | cmark_strbuf_init(buf->mem, buf, 0); 73 | } 74 | 75 | void cmark_strbuf_clear(cmark_strbuf *buf) { 76 | buf->size = 0; 77 | 78 | if (buf->asize > 0) 79 | buf->ptr[0] = '\0'; 80 | } 81 | 82 | void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, 83 | bufsize_t len) { 84 | if (len <= 0 || data == NULL) { 85 | cmark_strbuf_clear(buf); 86 | } else { 87 | if (data != buf->ptr) { 88 | if (len >= buf->asize) 89 | cmark_strbuf_grow(buf, len); 90 | memmove(buf->ptr, data, len); 91 | } 92 | buf->size = len; 93 | buf->ptr[buf->size] = '\0'; 94 | } 95 | } 96 | 97 | void cmark_strbuf_sets(cmark_strbuf *buf, const char *string) { 98 | cmark_strbuf_set(buf, (const unsigned char *)string, 99 | string ? (bufsize_t)strlen(string) : 0); 100 | } 101 | 102 | void cmark_strbuf_putc(cmark_strbuf *buf, int c) { 103 | S_strbuf_grow_by(buf, 1); 104 | buf->ptr[buf->size++] = (unsigned char)(c & 0xFF); 105 | buf->ptr[buf->size] = '\0'; 106 | } 107 | 108 | void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, 109 | bufsize_t len) { 110 | if (len <= 0) 111 | return; 112 | 113 | S_strbuf_grow_by(buf, len); 114 | memmove(buf->ptr + buf->size, data, len); 115 | buf->size += len; 116 | buf->ptr[buf->size] = '\0'; 117 | } 118 | 119 | void cmark_strbuf_puts(cmark_strbuf *buf, const char *string) { 120 | cmark_strbuf_put(buf, (const unsigned char *)string, (bufsize_t)strlen(string)); 121 | } 122 | 123 | void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize, 124 | const cmark_strbuf *buf) { 125 | bufsize_t copylen; 126 | 127 | assert(buf); 128 | if (!data || datasize <= 0) 129 | return; 130 | 131 | data[0] = '\0'; 132 | 133 | if (buf->size == 0 || buf->asize <= 0) 134 | return; 135 | 136 | copylen = buf->size; 137 | if (copylen > datasize - 1) 138 | copylen = datasize - 1; 139 | memmove(data, buf->ptr, copylen); 140 | data[copylen] = '\0'; 141 | } 142 | 143 | void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b) { 144 | cmark_strbuf t = *buf_a; 145 | *buf_a = *buf_b; 146 | *buf_b = t; 147 | } 148 | 149 | unsigned char *cmark_strbuf_detach(cmark_strbuf *buf) { 150 | unsigned char *data = buf->ptr; 151 | 152 | if (buf->asize == 0) { 153 | /* return an empty string */ 154 | return (unsigned char *)buf->mem->calloc(1, 1); 155 | } 156 | 157 | cmark_strbuf_init(buf->mem, buf, 0); 158 | return data; 159 | } 160 | 161 | int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b) { 162 | int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size)); 163 | return (result != 0) ? result 164 | : (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0; 165 | } 166 | 167 | bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos) { 168 | if (pos >= buf->size) 169 | return -1; 170 | if (pos < 0) 171 | pos = 0; 172 | 173 | const unsigned char *p = 174 | (unsigned char *)memchr(buf->ptr + pos, c, buf->size - pos); 175 | if (!p) 176 | return -1; 177 | 178 | return (bufsize_t)(p - (const unsigned char *)buf->ptr); 179 | } 180 | 181 | bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos) { 182 | if (pos < 0 || buf->size == 0) 183 | return -1; 184 | if (pos >= buf->size) 185 | pos = buf->size - 1; 186 | 187 | bufsize_t i; 188 | for (i = pos; i >= 0; i--) { 189 | if (buf->ptr[i] == (unsigned char)c) 190 | return i; 191 | } 192 | 193 | return -1; 194 | } 195 | 196 | void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len) { 197 | if (len < 0) 198 | len = 0; 199 | 200 | if (len < buf->size) { 201 | buf->size = len; 202 | buf->ptr[buf->size] = '\0'; 203 | } 204 | } 205 | 206 | void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n) { 207 | if (n > 0) { 208 | if (n > buf->size) 209 | n = buf->size; 210 | buf->size = buf->size - n; 211 | if (buf->size) 212 | memmove(buf->ptr, buf->ptr + n, buf->size); 213 | 214 | buf->ptr[buf->size] = '\0'; 215 | } 216 | } 217 | 218 | void cmark_strbuf_rtrim(cmark_strbuf *buf) { 219 | if (!buf->size) 220 | return; 221 | 222 | while (buf->size > 0) { 223 | if (!cmark_isspace(buf->ptr[buf->size - 1])) 224 | break; 225 | 226 | buf->size--; 227 | } 228 | 229 | buf->ptr[buf->size] = '\0'; 230 | } 231 | 232 | void cmark_strbuf_trim(cmark_strbuf *buf) { 233 | bufsize_t i = 0; 234 | 235 | if (!buf->size) 236 | return; 237 | 238 | while (i < buf->size && cmark_isspace(buf->ptr[i])) 239 | i++; 240 | 241 | cmark_strbuf_drop(buf, i); 242 | 243 | cmark_strbuf_rtrim(buf); 244 | } 245 | 246 | // Destructively modify string, collapsing consecutive 247 | // space and newline characters into a single space. 248 | void cmark_strbuf_normalize_whitespace(cmark_strbuf *s) { 249 | bool last_char_was_space = false; 250 | bufsize_t r, w; 251 | 252 | for (r = 0, w = 0; r < s->size; ++r) { 253 | if (cmark_isspace(s->ptr[r])) { 254 | if (!last_char_was_space) { 255 | s->ptr[w++] = ' '; 256 | last_char_was_space = true; 257 | } 258 | } else { 259 | s->ptr[w++] = s->ptr[r]; 260 | last_char_was_space = false; 261 | } 262 | } 263 | 264 | cmark_strbuf_truncate(s, w); 265 | } 266 | 267 | // Destructively unescape a string: remove backslashes before punctuation chars. 268 | extern void cmark_strbuf_unescape(cmark_strbuf *buf) { 269 | bufsize_t r, w; 270 | 271 | for (r = 0, w = 0; r < buf->size; ++r) { 272 | if (buf->ptr[r] == '\\' && cmark_ispunct(buf->ptr[r + 1])) 273 | r++; 274 | 275 | buf->ptr[w++] = buf->ptr[r]; 276 | } 277 | 278 | cmark_strbuf_truncate(buf, w); 279 | } 280 | -------------------------------------------------------------------------------- /Source/SwiftAST.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftAST.swift 3 | // CommonMark 4 | // 5 | // Created by Chris Eidhof on 22/05/15. 6 | // Copyright (c) 2015 Unsigned Integer. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import cmark_gfm 11 | 12 | /// The type of a list in Markdown, represented by `Block.List`. 13 | public enum ListType { 14 | case unordered 15 | case ordered 16 | } 17 | 18 | /// An inline element in a Markdown abstract syntax tree. 19 | public enum Inline { 20 | case text(text: String) 21 | case softBreak 22 | case lineBreak 23 | case code(text: String) 24 | case html(text: String) 25 | case emphasis(children: [Inline]) 26 | case strong(children: [Inline]) 27 | case custom(literal: String) 28 | case link(children: [Inline], title: String?, url: String?) 29 | case image(children: [Inline], title: String?, url: String?) 30 | case strikethrough(children: [Inline]) 31 | case mention(login: String) 32 | case checkbox(checked: Bool, originalRange: NSRange) 33 | } 34 | 35 | enum InlineType: String { 36 | case code 37 | case custom_inline 38 | case emph 39 | case html_inline 40 | case image 41 | case linebreak 42 | case link 43 | case softbreak 44 | case strong 45 | case text 46 | case strikethrough 47 | case mention 48 | case checkbox 49 | } 50 | 51 | extension Inline: ExpressibleByStringLiteral { 52 | 53 | public init(extendedGraphemeClusterLiteral value: StringLiteralType) { 54 | self.init(stringLiteral: value) 55 | } 56 | 57 | public init(unicodeScalarLiteral value: StringLiteralType) { 58 | self.init(stringLiteral: value) 59 | } 60 | 61 | public init(stringLiteral: StringLiteralType) { 62 | self = Inline.text(text: stringLiteral) 63 | } 64 | } 65 | 66 | /// A block-level element in a Markdown abstract syntax tree. 67 | public enum Block { 68 | case list(items: [[Block]], type: ListType) 69 | case blockQuote(items: [Block]) 70 | case codeBlock(text: String, language: String?) 71 | case html(text: String) 72 | case paragraph(text: [Inline]) 73 | case heading(text: [Inline], level: Int) 74 | case custom(literal: String) 75 | case thematicBreak 76 | case table(items: [Block]) 77 | case tableHeader(items: [Block]) 78 | case tableRow(items: [Block]) 79 | case tableCell(items: [Inline]) 80 | } 81 | 82 | enum BlockType: String { 83 | case block_quote 84 | case code_block 85 | case custom_block 86 | case heading 87 | case html_block 88 | case list 89 | case paragraph 90 | case table 91 | case table_cell 92 | case table_header 93 | case table_row 94 | case thematic_break 95 | } 96 | 97 | extension Inline { 98 | init?(_ node: Node) { 99 | guard let type = InlineType(rawValue: node.typeString) else { 100 | return nil 101 | } 102 | let inlineChildren = { node.children.compactMap(Inline.init) } 103 | switch type { 104 | case .text: 105 | self = .text(text: node.literal!) 106 | case .softbreak: 107 | self = .softBreak 108 | case .linebreak: 109 | self = .lineBreak 110 | case .code: 111 | self = .code(text: node.literal!) 112 | case .html_inline: 113 | self = .html(text: node.literal!) 114 | case .custom_inline: 115 | self = .custom(literal: node.literal!) 116 | case .emph: 117 | self = .emphasis(children: inlineChildren()) 118 | case .strong: 119 | self = .strong(children: inlineChildren()) 120 | case .link: 121 | self = .link(children: inlineChildren(), title: node.title, url: node.urlString) 122 | case .image: 123 | self = .image(children: inlineChildren(), title: node.title, url: node.urlString) 124 | case .strikethrough: 125 | self = .strikethrough(children: inlineChildren()) 126 | case .mention: 127 | self = .mention(login: node.login ?? "") 128 | case .checkbox: 129 | self = .checkbox(checked: node.checked, originalRange: node.checkedRange) 130 | } 131 | } 132 | } 133 | 134 | extension Block { 135 | init?(_ node: Node) { 136 | guard let type = BlockType(rawValue: node.typeString) else { 137 | return nil 138 | } 139 | let parseInlineChildren = { node.children.compactMap(Inline.init) } 140 | let parseBlockChildren = { node.children.compactMap(Block.init) } 141 | switch type { 142 | case .paragraph: 143 | self = .paragraph(text: parseInlineChildren()) 144 | case .block_quote: 145 | self = .blockQuote(items: parseBlockChildren()) 146 | case .list: 147 | let type: ListType = node.listType == CMARK_BULLET_LIST ? .unordered : .ordered 148 | self = .list(items: node.children.compactMap { $0.listItem }, type: type) 149 | case .code_block: 150 | self = .codeBlock(text: node.literal!, language: node.fenceInfo) 151 | case .html_block: 152 | self = .html(text: node.literal!) 153 | case .custom_block: 154 | self = .custom(literal: node.literal!) 155 | case .heading: 156 | self = .heading(text: parseInlineChildren(), level: node.headerLevel) 157 | case .thematic_break: 158 | self = .thematicBreak 159 | case .table: 160 | self = .table(items: parseBlockChildren()) 161 | case .table_header: 162 | self = .tableHeader(items: parseBlockChildren()) 163 | case .table_row: 164 | self = .tableRow(items: parseBlockChildren()) 165 | case .table_cell: 166 | self = .tableCell(items: parseInlineChildren()) 167 | } 168 | } 169 | } 170 | 171 | extension Node { 172 | var listItem: [Block]? { 173 | switch type { 174 | case CMARK_NODE_ITEM: 175 | return children.compactMap(Block.init) 176 | default: 177 | return nil 178 | } 179 | } 180 | 181 | } 182 | 183 | extension Node { 184 | convenience init(type: cmark_node_type, children: [Node] = []) { 185 | self.init(node: cmark_node_new(type)) 186 | for child in children { 187 | cmark_node_append_child(node, child.node) 188 | } 189 | } 190 | } 191 | 192 | //extension Node { 193 | // convenience init(type: cmark_node_type, literal: String) { 194 | // self.init(type: type) 195 | // self.literal = literal 196 | // } 197 | // convenience init(type: cmark_node_type, blocks: [Block]) { 198 | // self.init(type: type, children: blocks.map(Node.init)) 199 | // } 200 | // convenience init(type: cmark_node_type, elements: [Inline]) { 201 | // self.init(type: type, children: elements.map(Node.init)) 202 | // } 203 | //} 204 | 205 | //extension Node { 206 | // public convenience init(blocks: [Block]) { 207 | // self.init(type: CMARK_NODE_DOCUMENT, blocks: blocks) 208 | // } 209 | //} 210 | 211 | extension Node { 212 | /// The abstract syntax tree representation of a Markdown document. 213 | /// - returns: an array of block-level elements. 214 | public var elements: [Block] { 215 | return children.compactMap(Block.init) 216 | } 217 | } 218 | 219 | func tableOfContents(document: String) -> [Block] { 220 | let blocks = Node(markdown: document)?.children.compactMap(Block.init) ?? [] 221 | return blocks.filter { 222 | switch $0 { 223 | case .heading(_, let level) where level < 3: return true 224 | default: return false 225 | } 226 | } 227 | } 228 | 229 | //extension Node { 230 | // convenience init(element: Inline) { 231 | // switch element { 232 | // case .text(let text): 233 | // self.init(type: CMARK_NODE_TEXT, literal: text) 234 | // case .emphasis(let children): 235 | // self.init(type: CMARK_NODE_EMPH, elements: children) 236 | // case .code(let text): 237 | // self.init(type: CMARK_NODE_CODE, literal: text) 238 | // case .strong(let children): 239 | // self.init(type: CMARK_NODE_STRONG, elements: children) 240 | // case .html(let text): 241 | // self.init(type: CMARK_NODE_HTML_INLINE, literal: text) 242 | // case .custom(let literal): 243 | // self.init(type: CMARK_NODE_CUSTOM_INLINE, literal: literal) 244 | // case let .link(children, title, url): 245 | // self.init(type: CMARK_NODE_LINK, elements: children) 246 | // self.title = title 247 | // self.urlString = url 248 | // case let .image(children, title, url): 249 | // self.init(type: CMARK_NODE_IMAGE, elements: children) 250 | // self.title = title 251 | // urlString = url 252 | // case .softBreak: 253 | // self.init(type: CMARK_NODE_SOFTBREAK) 254 | // case .lineBreak: 255 | // self.init(type: CMARK_NODE_LINEBREAK) 256 | // } 257 | // } 258 | //} 259 | // 260 | //extension Node { 261 | // convenience init(block: Block) { 262 | // switch block { 263 | // case .paragraph(let children): 264 | // self.init(type: CMARK_NODE_PARAGRAPH, elements: children) 265 | // case let .list(items, type): 266 | // let listItems = items.map { Node(type: CMARK_NODE_ITEM, blocks: $0) } 267 | // self.init(type: CMARK_NODE_LIST, children: listItems) 268 | // listType = type == .Unordered ? CMARK_BULLET_LIST : CMARK_ORDERED_LIST 269 | // case .blockQuote(let items): 270 | // self.init(type: CMARK_NODE_BLOCK_QUOTE, blocks: items) 271 | // case let .codeBlock(text, language): 272 | // self.init(type: CMARK_NODE_CODE_BLOCK, literal: text) 273 | // fenceInfo = language 274 | // case .html(let text): 275 | // self.init(type: CMARK_NODE_HTML_BLOCK, literal: text) 276 | // case .custom(let literal): 277 | // self.init(type: CMARK_NODE_CUSTOM_BLOCK, literal: literal) 278 | // case let .heading(text, level): 279 | // self.init(type: CMARK_NODE_HEADING, elements: text) 280 | // headerLevel = level 281 | // case .thematicBreak: 282 | // self.init(type: CMARK_NODE_THEMATIC_BREAK) 283 | // } 284 | // } 285 | //} 286 | 287 | -------------------------------------------------------------------------------- /Source/cmark_gfm/scanners.re: -------------------------------------------------------------------------------- 1 | #include 2 | #include "chunk.h" 3 | #include "scanners.h" 4 | 5 | bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c, bufsize_t offset) 6 | { 7 | bufsize_t res; 8 | unsigned char *ptr = (unsigned char *)c->data; 9 | 10 | if (ptr == NULL || offset > c->len) { 11 | return 0; 12 | } else { 13 | unsigned char lim = ptr[c->len]; 14 | 15 | ptr[c->len] = '\0'; 16 | res = scanner(ptr + offset); 17 | ptr[c->len] = lim; 18 | } 19 | 20 | return res; 21 | } 22 | 23 | /*!re2c 24 | re2c:define:YYCTYPE = "unsigned char"; 25 | re2c:define:YYCURSOR = p; 26 | re2c:define:YYMARKER = marker; 27 | re2c:define:YYCTXMARKER = marker; 28 | re2c:yyfill:enable = 0; 29 | 30 | wordchar = [^\x00-\x20]; 31 | 32 | spacechar = [ \t\v\f\r\n]; 33 | 34 | reg_char = [^\\()\x00-\x20]; 35 | 36 | escaped_char = [\\][!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~-]; 37 | 38 | tagname = [A-Za-z][A-Za-z0-9-]*; 39 | 40 | blocktagname = 'address'|'article'|'aside'|'base'|'basefont'|'blockquote'|'body'|'caption'|'center'|'col'|'colgroup'|'dd'|'details'|'dialog'|'dir'|'div'|'dl'|'dt'|'fieldset'|'figcaption'|'figure'|'footer'|'form'|'frame'|'frameset'|'h1'|'h2'|'h3'|'h4'|'h5'|'h6'|'head'|'header'|'hr'|'html'|'iframe'|'legend'|'li'|'link'|'main'|'menu'|'menuitem'|'meta'|'nav'|'noframes'|'ol'|'optgroup'|'option'|'p'|'param'|'section'|'source'|'title'|'summary'|'table'|'tbody'|'td'|'tfoot'|'th'|'thead'|'title'|'tr'|'track'|'ul'; 41 | 42 | attributename = [a-zA-Z_:][a-zA-Z0-9:._-]*; 43 | 44 | unquotedvalue = [^"'=<>`\x00]+; 45 | singlequotedvalue = ['][^'\x00]*[']; 46 | doublequotedvalue = ["][^"\x00]*["]; 47 | 48 | attributevalue = unquotedvalue | singlequotedvalue | doublequotedvalue; 49 | 50 | attributevaluespec = spacechar* [=] spacechar* attributevalue; 51 | 52 | attribute = spacechar+ attributename attributevaluespec?; 53 | 54 | opentag = tagname attribute* spacechar* [/]? [>]; 55 | closetag = [/] tagname spacechar* [>]; 56 | 57 | htmlcomment = "!---->" | ("!--" ([-]? [^\x00>-]) ([-]? [^\x00-])* "-->"); 58 | 59 | processinginstruction = "?" ([^?>\x00]+ | [?][^>\x00] | [>])* "?>"; 60 | 61 | declaration = "!" [A-Z]+ spacechar+ [^>\x00]* ">"; 62 | 63 | cdata = "![CDATA[" ([^\]\x00]+ | "]" [^\]\x00] | "]]" [^>\x00])* "]]>"; 64 | 65 | htmltag = opentag | closetag | htmlcomment | processinginstruction | 66 | declaration | cdata; 67 | 68 | in_parens_nosp = [(] (reg_char|escaped_char|[\\])* [)]; 69 | 70 | in_double_quotes = ["] (escaped_char|[^"\x00])* ["]; 71 | in_single_quotes = ['] (escaped_char|[^'\x00])* [']; 72 | in_parens = [(] (escaped_char|[^)\x00])* [)]; 73 | 74 | scheme = [A-Za-z][A-Za-z0-9.+-]{1,31}; 75 | */ 76 | 77 | // Try to match a scheme including colon. 78 | bufsize_t _scan_scheme(const unsigned char *p) 79 | { 80 | const unsigned char *marker = NULL; 81 | const unsigned char *start = p; 82 | /*!re2c 83 | scheme [:] { return (bufsize_t)(p - start); } 84 | * { return 0; } 85 | */ 86 | } 87 | 88 | // Try to match URI autolink after first <, returning number of chars matched. 89 | bufsize_t _scan_autolink_uri(const unsigned char *p) 90 | { 91 | const unsigned char *marker = NULL; 92 | const unsigned char *start = p; 93 | /*!re2c 94 | scheme [:][^\x00-\x20<>]*[>] { return (bufsize_t)(p - start); } 95 | * { return 0; } 96 | */ 97 | } 98 | 99 | // Try to match email autolink after first <, returning num of chars matched. 100 | bufsize_t _scan_autolink_email(const unsigned char *p) 101 | { 102 | const unsigned char *marker = NULL; 103 | const unsigned char *start = p; 104 | /*!re2c 105 | [a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+ 106 | [@] 107 | [a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? 108 | ([.][a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)* 109 | [>] { return (bufsize_t)(p - start); } 110 | * { return 0; } 111 | */ 112 | } 113 | 114 | // Try to match an HTML tag after first <, returning num of chars matched. 115 | bufsize_t _scan_html_tag(const unsigned char *p) 116 | { 117 | const unsigned char *marker = NULL; 118 | const unsigned char *start = p; 119 | /*!re2c 120 | htmltag { return (bufsize_t)(p - start); } 121 | * { return 0; } 122 | */ 123 | } 124 | 125 | // Try to (liberally) match an HTML tag after first <, returning num of chars matched. 126 | bufsize_t _scan_liberal_html_tag(const unsigned char *p) 127 | { 128 | const unsigned char *marker = NULL; 129 | const unsigned char *start = p; 130 | /*!re2c 131 | [^\n\x00]+ [>] { return (bufsize_t)(p - start); } 132 | * { return 0; } 133 | */ 134 | } 135 | 136 | // Try to match an HTML block tag start line, returning 137 | // an integer code for the type of block (1-6, matching the spec). 138 | // #7 is handled by a separate function, below. 139 | bufsize_t _scan_html_block_start(const unsigned char *p) 140 | { 141 | const unsigned char *marker = NULL; 142 | /*!re2c 143 | [<] ('script'|'pre'|'style') (spacechar | [>]) { return 1; } 144 | '' { return (bufsize_t)(p - start); } 182 | * { return 0; } 183 | */ 184 | } 185 | 186 | // Try to match an HTML block end line of type 3 187 | bufsize_t _scan_html_block_end_3(const unsigned char *p) 188 | { 189 | const unsigned char *marker = NULL; 190 | const unsigned char *start = p; 191 | /*!re2c 192 | [^\n\x00]* '?>' { return (bufsize_t)(p - start); } 193 | * { return 0; } 194 | */ 195 | } 196 | 197 | // Try to match an HTML block end line of type 4 198 | bufsize_t _scan_html_block_end_4(const unsigned char *p) 199 | { 200 | const unsigned char *marker = NULL; 201 | const unsigned char *start = p; 202 | /*!re2c 203 | [^\n\x00]* '>' { return (bufsize_t)(p - start); } 204 | * { return 0; } 205 | */ 206 | } 207 | 208 | // Try to match an HTML block end line of type 5 209 | bufsize_t _scan_html_block_end_5(const unsigned char *p) 210 | { 211 | const unsigned char *marker = NULL; 212 | const unsigned char *start = p; 213 | /*!re2c 214 | [^\n\x00]* ']]>' { return (bufsize_t)(p - start); } 215 | * { return 0; } 216 | */ 217 | } 218 | 219 | // Try to match a link title (in single quotes, in double quotes, or 220 | // in parentheses), returning number of chars matched. Allow one 221 | // level of internal nesting (quotes within quotes). 222 | bufsize_t _scan_link_title(const unsigned char *p) 223 | { 224 | const unsigned char *marker = NULL; 225 | const unsigned char *start = p; 226 | /*!re2c 227 | ["] (escaped_char|[^"\x00])* ["] { return (bufsize_t)(p - start); } 228 | ['] (escaped_char|[^'\x00])* ['] { return (bufsize_t)(p - start); } 229 | [(] (escaped_char|[^)\x00])* [)] { return (bufsize_t)(p - start); } 230 | * { return 0; } 231 | */ 232 | } 233 | 234 | // Match space characters, including newlines. 235 | bufsize_t _scan_spacechars(const unsigned char *p) 236 | { 237 | const unsigned char *start = p; \ 238 | /*!re2c 239 | [ \t\v\f\r\n]+ { return (bufsize_t)(p - start); } 240 | * { return 0; } 241 | */ 242 | } 243 | 244 | // Match ATX heading start. 245 | bufsize_t _scan_atx_heading_start(const unsigned char *p) 246 | { 247 | const unsigned char *marker = NULL; 248 | const unsigned char *start = p; 249 | /*!re2c 250 | [#]{1,6} ([ \t]+|[\r\n]) { return (bufsize_t)(p - start); } 251 | * { return 0; } 252 | */ 253 | } 254 | 255 | // Match setext heading line. Return 1 for level-1 heading, 256 | // 2 for level-2, 0 for no match. 257 | bufsize_t _scan_setext_heading_line(const unsigned char *p) 258 | { 259 | const unsigned char *marker = NULL; 260 | /*!re2c 261 | [=]+ [ \t]* [\r\n] { return 1; } 262 | [-]+ [ \t]* [\r\n] { return 2; } 263 | * { return 0; } 264 | */ 265 | } 266 | 267 | // Scan a thematic break line: "...three or more hyphens, asterisks, 268 | // or underscores on a line by themselves. If you wish, you may use 269 | // spaces between the hyphens or asterisks." 270 | bufsize_t _scan_thematic_break(const unsigned char *p) 271 | { 272 | const unsigned char *marker = NULL; 273 | const unsigned char *start = p; 274 | /*!re2c 275 | ([*][ \t]*){3,} [ \t]* [\r\n] { return (bufsize_t)(p - start); } 276 | ([_][ \t]*){3,} [ \t]* [\r\n] { return (bufsize_t)(p - start); } 277 | ([-][ \t]*){3,} [ \t]* [\r\n] { return (bufsize_t)(p - start); } 278 | * { return 0; } 279 | */ 280 | } 281 | 282 | // Scan an opening code fence. 283 | bufsize_t _scan_open_code_fence(const unsigned char *p) 284 | { 285 | const unsigned char *marker = NULL; 286 | const unsigned char *start = p; 287 | /*!re2c 288 | [`]{3,} / [^`\r\n\x00]*[\r\n] { return (bufsize_t)(p - start); } 289 | [~]{3,} / [^~\r\n\x00]*[\r\n] { return (bufsize_t)(p - start); } 290 | * { return 0; } 291 | */ 292 | } 293 | 294 | // Scan a closing code fence with length at least len. 295 | bufsize_t _scan_close_code_fence(const unsigned char *p) 296 | { 297 | const unsigned char *marker = NULL; 298 | const unsigned char *start = p; 299 | /*!re2c 300 | [`]{3,} / [ \t]*[\r\n] { return (bufsize_t)(p - start); } 301 | [~]{3,} / [ \t]*[\r\n] { return (bufsize_t)(p - start); } 302 | * { return 0; } 303 | */ 304 | } 305 | 306 | // Scans an entity. 307 | // Returns number of chars matched. 308 | bufsize_t _scan_entity(const unsigned char *p) 309 | { 310 | const unsigned char *marker = NULL; 311 | const unsigned char *start = p; 312 | /*!re2c 313 | [&] ([#] ([Xx][A-Fa-f0-9]{1,8}|[0-9]{1,8}) |[A-Za-z][A-Za-z0-9]{1,31} ) [;] 314 | { return (bufsize_t)(p - start); } 315 | * { return 0; } 316 | */ 317 | } 318 | 319 | // Returns positive value if a URL begins in a way that is potentially 320 | // dangerous, with javascript:, vbscript:, file:, or data:, otherwise 0. 321 | bufsize_t _scan_dangerous_url(const unsigned char *p) 322 | { 323 | const unsigned char *marker = NULL; 324 | const unsigned char *start = p; 325 | /*!re2c 326 | 'data:image/' ('png'|'gif'|'jpeg'|'webp') { return 0; } 327 | 'javascript:' | 'vbscript:' | 'file:' | 'data:' { return (bufsize_t)(p - start); } 328 | * { return 0; } 329 | */ 330 | } 331 | 332 | // Scans a footnote definition opening. 333 | bufsize_t _scan_footnote_definition(const unsigned char *p) 334 | { 335 | const unsigned char *marker = NULL; 336 | const unsigned char *start = p; 337 | /*!re2c 338 | '[^' ([^\] \r\n\x00\t]+) ']:' [ \t]* { return (bufsize_t)(p - start); } 339 | * { return 0; } 340 | */ 341 | } 342 | -------------------------------------------------------------------------------- /Source/cmark_gfm/utf8.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cmark_ctype.h" 6 | #include "utf8.h" 7 | 8 | static const int8_t utf8proc_utf8class[256] = { 9 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 14 | 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18 | 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19 | 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0}; 20 | 21 | static void encode_unknown(cmark_strbuf *buf) { 22 | static const uint8_t repl[] = {239, 191, 189}; 23 | cmark_strbuf_put(buf, repl, 3); 24 | } 25 | 26 | static int utf8proc_charlen(const uint8_t *str, bufsize_t str_len) { 27 | int length, i; 28 | 29 | if (!str_len) 30 | return 0; 31 | 32 | length = utf8proc_utf8class[str[0]]; 33 | 34 | if (!length) 35 | return -1; 36 | 37 | if (str_len >= 0 && (bufsize_t)length > str_len) 38 | return -str_len; 39 | 40 | for (i = 1; i < length; i++) { 41 | if ((str[i] & 0xC0) != 0x80) 42 | return -i; 43 | } 44 | 45 | return length; 46 | } 47 | 48 | // Validate a single UTF-8 character according to RFC 3629. 49 | static int utf8proc_valid(const uint8_t *str, bufsize_t str_len) { 50 | int length = utf8proc_utf8class[str[0]]; 51 | 52 | if (!length) 53 | return -1; 54 | 55 | if ((bufsize_t)length > str_len) 56 | return -str_len; 57 | 58 | switch (length) { 59 | case 2: 60 | if ((str[1] & 0xC0) != 0x80) 61 | return -1; 62 | if (str[0] < 0xC2) { 63 | // Overlong 64 | return -length; 65 | } 66 | break; 67 | 68 | case 3: 69 | if ((str[1] & 0xC0) != 0x80) 70 | return -1; 71 | if ((str[2] & 0xC0) != 0x80) 72 | return -2; 73 | if (str[0] == 0xE0) { 74 | if (str[1] < 0xA0) { 75 | // Overlong 76 | return -length; 77 | } 78 | } else if (str[0] == 0xED) { 79 | if (str[1] >= 0xA0) { 80 | // Surrogate 81 | return -length; 82 | } 83 | } 84 | break; 85 | 86 | case 4: 87 | if ((str[1] & 0xC0) != 0x80) 88 | return -1; 89 | if ((str[2] & 0xC0) != 0x80) 90 | return -2; 91 | if ((str[3] & 0xC0) != 0x80) 92 | return -3; 93 | if (str[0] == 0xF0) { 94 | if (str[1] < 0x90) { 95 | // Overlong 96 | return -length; 97 | } 98 | } else if (str[0] >= 0xF4) { 99 | if (str[0] > 0xF4 || str[1] >= 0x90) { 100 | // Above 0x10FFFF 101 | return -length; 102 | } 103 | } 104 | break; 105 | } 106 | 107 | return length; 108 | } 109 | 110 | void cmark_utf8proc_check(cmark_strbuf *ob, const uint8_t *line, 111 | bufsize_t size) { 112 | bufsize_t i = 0; 113 | 114 | while (i < size) { 115 | bufsize_t org = i; 116 | int charlen = 0; 117 | 118 | while (i < size) { 119 | if (line[i] < 0x80 && line[i] != 0) { 120 | i++; 121 | } else if (line[i] >= 0x80) { 122 | charlen = utf8proc_valid(line + i, size - i); 123 | if (charlen < 0) { 124 | charlen = -charlen; 125 | break; 126 | } 127 | i += charlen; 128 | } else if (line[i] == 0) { 129 | // ASCII NUL is technically valid but rejected 130 | // for security reasons. 131 | charlen = 1; 132 | break; 133 | } 134 | } 135 | 136 | if (i > org) { 137 | cmark_strbuf_put(ob, line + org, i - org); 138 | } 139 | 140 | if (i >= size) { 141 | break; 142 | } else { 143 | // Invalid UTF-8 144 | encode_unknown(ob); 145 | i += charlen; 146 | } 147 | } 148 | } 149 | 150 | int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, 151 | int32_t *dst) { 152 | int length; 153 | int32_t uc = -1; 154 | 155 | *dst = -1; 156 | length = utf8proc_charlen(str, str_len); 157 | if (length < 0) 158 | return -1; 159 | 160 | switch (length) { 161 | case 1: 162 | uc = str[0]; 163 | break; 164 | case 2: 165 | uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F); 166 | if (uc < 0x80) 167 | uc = -1; 168 | break; 169 | case 3: 170 | uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6) + (str[2] & 0x3F); 171 | if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000)) 172 | uc = -1; 173 | break; 174 | case 4: 175 | uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12) + 176 | ((str[2] & 0x3F) << 6) + (str[3] & 0x3F); 177 | if (uc < 0x10000 || uc >= 0x110000) 178 | uc = -1; 179 | break; 180 | } 181 | 182 | if (uc < 0) 183 | return -1; 184 | 185 | *dst = uc; 186 | return length; 187 | } 188 | 189 | void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf) { 190 | uint8_t dst[4]; 191 | bufsize_t len = 0; 192 | 193 | assert(uc >= 0); 194 | 195 | if (uc < 0x80) { 196 | dst[0] = (uint8_t)(uc); 197 | len = 1; 198 | } else if (uc < 0x800) { 199 | dst[0] = (uint8_t)(0xC0 + (uc >> 6)); 200 | dst[1] = 0x80 + (uc & 0x3F); 201 | len = 2; 202 | } else if (uc == 0xFFFF) { 203 | dst[0] = 0xFF; 204 | len = 1; 205 | } else if (uc == 0xFFFE) { 206 | dst[0] = 0xFE; 207 | len = 1; 208 | } else if (uc < 0x10000) { 209 | dst[0] = (uint8_t)(0xE0 + (uc >> 12)); 210 | dst[1] = 0x80 + ((uc >> 6) & 0x3F); 211 | dst[2] = 0x80 + (uc & 0x3F); 212 | len = 3; 213 | } else if (uc < 0x110000) { 214 | dst[0] = (uint8_t)(0xF0 + (uc >> 18)); 215 | dst[1] = 0x80 + ((uc >> 12) & 0x3F); 216 | dst[2] = 0x80 + ((uc >> 6) & 0x3F); 217 | dst[3] = 0x80 + (uc & 0x3F); 218 | len = 4; 219 | } else { 220 | encode_unknown(buf); 221 | return; 222 | } 223 | 224 | cmark_strbuf_put(buf, dst, len); 225 | } 226 | 227 | void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str, 228 | bufsize_t len) { 229 | int32_t c; 230 | 231 | #define bufpush(x) cmark_utf8proc_encode_char(x, dest) 232 | 233 | while (len > 0) { 234 | bufsize_t char_len = cmark_utf8proc_iterate(str, len, &c); 235 | 236 | if (char_len >= 0) { 237 | #include "case_fold_switch.inc" 238 | } else { 239 | encode_unknown(dest); 240 | char_len = -char_len; 241 | } 242 | 243 | str += char_len; 244 | len -= char_len; 245 | } 246 | } 247 | 248 | // matches anything in the Zs class, plus LF, CR, TAB, FF. 249 | int cmark_utf8proc_is_space(int32_t uc) { 250 | return (uc == 9 || uc == 10 || uc == 12 || uc == 13 || uc == 32 || 251 | uc == 160 || uc == 5760 || (uc >= 8192 && uc <= 8202) || uc == 8239 || 252 | uc == 8287 || uc == 12288); 253 | } 254 | 255 | // matches anything in the P[cdefios] classes. 256 | int cmark_utf8proc_is_punctuation(int32_t uc) { 257 | return ( 258 | (uc < 128 && cmark_ispunct((char)uc)) || uc == 161 || uc == 167 || 259 | uc == 171 || uc == 182 || uc == 183 || uc == 187 || uc == 191 || 260 | uc == 894 || uc == 903 || (uc >= 1370 && uc <= 1375) || uc == 1417 || 261 | uc == 1418 || uc == 1470 || uc == 1472 || uc == 1475 || uc == 1478 || 262 | uc == 1523 || uc == 1524 || uc == 1545 || uc == 1546 || uc == 1548 || 263 | uc == 1549 || uc == 1563 || uc == 1566 || uc == 1567 || 264 | (uc >= 1642 && uc <= 1645) || uc == 1748 || (uc >= 1792 && uc <= 1805) || 265 | (uc >= 2039 && uc <= 2041) || (uc >= 2096 && uc <= 2110) || uc == 2142 || 266 | uc == 2404 || uc == 2405 || uc == 2416 || uc == 2800 || uc == 3572 || 267 | uc == 3663 || uc == 3674 || uc == 3675 || (uc >= 3844 && uc <= 3858) || 268 | uc == 3860 || (uc >= 3898 && uc <= 3901) || uc == 3973 || 269 | (uc >= 4048 && uc <= 4052) || uc == 4057 || uc == 4058 || 270 | (uc >= 4170 && uc <= 4175) || uc == 4347 || (uc >= 4960 && uc <= 4968) || 271 | uc == 5120 || uc == 5741 || uc == 5742 || uc == 5787 || uc == 5788 || 272 | (uc >= 5867 && uc <= 5869) || uc == 5941 || uc == 5942 || 273 | (uc >= 6100 && uc <= 6102) || (uc >= 6104 && uc <= 6106) || 274 | (uc >= 6144 && uc <= 6154) || uc == 6468 || uc == 6469 || uc == 6686 || 275 | uc == 6687 || (uc >= 6816 && uc <= 6822) || (uc >= 6824 && uc <= 6829) || 276 | (uc >= 7002 && uc <= 7008) || (uc >= 7164 && uc <= 7167) || 277 | (uc >= 7227 && uc <= 7231) || uc == 7294 || uc == 7295 || 278 | (uc >= 7360 && uc <= 7367) || uc == 7379 || (uc >= 8208 && uc <= 8231) || 279 | (uc >= 8240 && uc <= 8259) || (uc >= 8261 && uc <= 8273) || 280 | (uc >= 8275 && uc <= 8286) || uc == 8317 || uc == 8318 || uc == 8333 || 281 | uc == 8334 || (uc >= 8968 && uc <= 8971) || uc == 9001 || uc == 9002 || 282 | (uc >= 10088 && uc <= 10101) || uc == 10181 || uc == 10182 || 283 | (uc >= 10214 && uc <= 10223) || (uc >= 10627 && uc <= 10648) || 284 | (uc >= 10712 && uc <= 10715) || uc == 10748 || uc == 10749 || 285 | (uc >= 11513 && uc <= 11516) || uc == 11518 || uc == 11519 || 286 | uc == 11632 || (uc >= 11776 && uc <= 11822) || 287 | (uc >= 11824 && uc <= 11842) || (uc >= 12289 && uc <= 12291) || 288 | (uc >= 12296 && uc <= 12305) || (uc >= 12308 && uc <= 12319) || 289 | uc == 12336 || uc == 12349 || uc == 12448 || uc == 12539 || uc == 42238 || 290 | uc == 42239 || (uc >= 42509 && uc <= 42511) || uc == 42611 || 291 | uc == 42622 || (uc >= 42738 && uc <= 42743) || 292 | (uc >= 43124 && uc <= 43127) || uc == 43214 || uc == 43215 || 293 | (uc >= 43256 && uc <= 43258) || uc == 43310 || uc == 43311 || 294 | uc == 43359 || (uc >= 43457 && uc <= 43469) || uc == 43486 || 295 | uc == 43487 || (uc >= 43612 && uc <= 43615) || uc == 43742 || 296 | uc == 43743 || uc == 43760 || uc == 43761 || uc == 44011 || uc == 64830 || 297 | uc == 64831 || (uc >= 65040 && uc <= 65049) || 298 | (uc >= 65072 && uc <= 65106) || (uc >= 65108 && uc <= 65121) || 299 | uc == 65123 || uc == 65128 || uc == 65130 || uc == 65131 || 300 | (uc >= 65281 && uc <= 65283) || (uc >= 65285 && uc <= 65290) || 301 | (uc >= 65292 && uc <= 65295) || uc == 65306 || uc == 65307 || 302 | uc == 65311 || uc == 65312 || (uc >= 65339 && uc <= 65341) || 303 | uc == 65343 || uc == 65371 || uc == 65373 || 304 | (uc >= 65375 && uc <= 65381) || (uc >= 65792 && uc <= 65794) || 305 | uc == 66463 || uc == 66512 || uc == 66927 || uc == 67671 || uc == 67871 || 306 | uc == 67903 || (uc >= 68176 && uc <= 68184) || uc == 68223 || 307 | (uc >= 68336 && uc <= 68342) || (uc >= 68409 && uc <= 68415) || 308 | (uc >= 68505 && uc <= 68508) || (uc >= 69703 && uc <= 69709) || 309 | uc == 69819 || uc == 69820 || (uc >= 69822 && uc <= 69825) || 310 | (uc >= 69952 && uc <= 69955) || uc == 70004 || uc == 70005 || 311 | (uc >= 70085 && uc <= 70088) || uc == 70093 || 312 | (uc >= 70200 && uc <= 70205) || uc == 70854 || 313 | (uc >= 71105 && uc <= 71113) || (uc >= 71233 && uc <= 71235) || 314 | (uc >= 74864 && uc <= 74868) || uc == 92782 || uc == 92783 || 315 | uc == 92917 || (uc >= 92983 && uc <= 92987) || uc == 92996 || 316 | uc == 113823); 317 | } 318 | -------------------------------------------------------------------------------- /Source/cmark_gfm/latex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "config.h" 7 | #include "cmark.h" 8 | #include "node.h" 9 | #include "buffer.h" 10 | #include "utf8.h" 11 | #include "scanners.h" 12 | #include "render.h" 13 | #include "syntax_extension.h" 14 | 15 | #define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping) 16 | #define LIT(s) renderer->out(renderer, node, s, false, LITERAL) 17 | #define CR() renderer->cr(renderer) 18 | #define BLANKLINE() renderer->blankline(renderer) 19 | #define LIST_NUMBER_STRING_SIZE 20 20 | 21 | static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node, 22 | cmark_escaping escape, 23 | int32_t c, unsigned char nextc) { 24 | if (escape == LITERAL) { 25 | cmark_render_code_point(renderer, c); 26 | return; 27 | } 28 | 29 | switch (c) { 30 | case 123: // '{' 31 | case 125: // '}' 32 | case 35: // '#' 33 | case 37: // '%' 34 | case 38: // '&' 35 | cmark_render_ascii(renderer, "\\"); 36 | cmark_render_code_point(renderer, c); 37 | break; 38 | case 36: // '$' 39 | case 95: // '_' 40 | if (escape == NORMAL) { 41 | cmark_render_ascii(renderer, "\\"); 42 | } 43 | cmark_render_code_point(renderer, c); 44 | break; 45 | case 45: // '-' 46 | if (nextc == 45) { // prevent ligature 47 | cmark_render_ascii(renderer, "-{}"); 48 | } else { 49 | cmark_render_ascii(renderer, "-"); 50 | } 51 | break; 52 | case 126: // '~' 53 | if (escape == NORMAL) { 54 | cmark_render_ascii(renderer, "\\textasciitilde{}"); 55 | } else { 56 | cmark_render_code_point(renderer, c); 57 | } 58 | break; 59 | case 94: // '^' 60 | cmark_render_ascii(renderer, "\\^{}"); 61 | break; 62 | case 92: // '\\' 63 | if (escape == URL) { 64 | // / acts as path sep even on windows: 65 | cmark_render_ascii(renderer, "/"); 66 | } else { 67 | cmark_render_ascii(renderer, "\\textbackslash{}"); 68 | } 69 | break; 70 | case 124: // '|' 71 | cmark_render_ascii(renderer, "\\textbar{}"); 72 | break; 73 | case 60: // '<' 74 | cmark_render_ascii(renderer, "\\textless{}"); 75 | break; 76 | case 62: // '>' 77 | cmark_render_ascii(renderer, "\\textgreater{}"); 78 | break; 79 | case 91: // '[' 80 | case 93: // ']' 81 | cmark_render_ascii(renderer, "{"); 82 | cmark_render_code_point(renderer, c); 83 | cmark_render_ascii(renderer, "}"); 84 | break; 85 | case 34: // '"' 86 | cmark_render_ascii(renderer, "\\textquotedbl{}"); 87 | // requires \usepackage[T1]{fontenc} 88 | break; 89 | case 39: // '\'' 90 | cmark_render_ascii(renderer, "\\textquotesingle{}"); 91 | // requires \usepackage{textcomp} 92 | break; 93 | case 160: // nbsp 94 | cmark_render_ascii(renderer, "~"); 95 | break; 96 | case 8230: // hellip 97 | cmark_render_ascii(renderer, "\\ldots{}"); 98 | break; 99 | case 8216: // lsquo 100 | if (escape == NORMAL) { 101 | cmark_render_ascii(renderer, "`"); 102 | } else { 103 | cmark_render_code_point(renderer, c); 104 | } 105 | break; 106 | case 8217: // rsquo 107 | if (escape == NORMAL) { 108 | cmark_render_ascii(renderer, "\'"); 109 | } else { 110 | cmark_render_code_point(renderer, c); 111 | } 112 | break; 113 | case 8220: // ldquo 114 | if (escape == NORMAL) { 115 | cmark_render_ascii(renderer, "``"); 116 | } else { 117 | cmark_render_code_point(renderer, c); 118 | } 119 | break; 120 | case 8221: // rdquo 121 | if (escape == NORMAL) { 122 | cmark_render_ascii(renderer, "''"); 123 | } else { 124 | cmark_render_code_point(renderer, c); 125 | } 126 | break; 127 | case 8212: // emdash 128 | if (escape == NORMAL) { 129 | cmark_render_ascii(renderer, "---"); 130 | } else { 131 | cmark_render_code_point(renderer, c); 132 | } 133 | break; 134 | case 8211: // endash 135 | if (escape == NORMAL) { 136 | cmark_render_ascii(renderer, "--"); 137 | } else { 138 | cmark_render_code_point(renderer, c); 139 | } 140 | break; 141 | default: 142 | cmark_render_code_point(renderer, c); 143 | } 144 | } 145 | 146 | typedef enum { 147 | NO_LINK, 148 | URL_AUTOLINK, 149 | EMAIL_AUTOLINK, 150 | NORMAL_LINK, 151 | INTERNAL_LINK 152 | } link_type; 153 | 154 | static link_type get_link_type(cmark_node *node) { 155 | size_t title_len, url_len; 156 | cmark_node *link_text; 157 | char *realurl; 158 | int realurllen; 159 | bool isemail = false; 160 | 161 | if (node->type != CMARK_NODE_LINK) { 162 | return NO_LINK; 163 | } 164 | 165 | const char *url = cmark_node_get_url(node); 166 | cmark_chunk url_chunk = cmark_chunk_literal(url); 167 | 168 | if (url && *url == '#') { 169 | return INTERNAL_LINK; 170 | } 171 | 172 | url_len = strlen(url); 173 | if (url_len == 0 || scan_scheme(&url_chunk, 0) == 0) { 174 | return NO_LINK; 175 | } 176 | 177 | const char *title = cmark_node_get_title(node); 178 | title_len = strlen(title); 179 | // if it has a title, we can't treat it as an autolink: 180 | if (title_len == 0) { 181 | 182 | link_text = node->first_child; 183 | cmark_consolidate_text_nodes(link_text); 184 | 185 | if (!link_text) 186 | return NO_LINK; 187 | 188 | realurl = (char *)url; 189 | realurllen = (int)url_len; 190 | if (strncmp(realurl, "mailto:", 7) == 0) { 191 | realurl += 7; 192 | realurllen -= 7; 193 | isemail = true; 194 | } 195 | if (realurllen == link_text->as.literal.len && 196 | strncmp(realurl, (char *)link_text->as.literal.data, 197 | link_text->as.literal.len) == 0) { 198 | if (isemail) { 199 | return EMAIL_AUTOLINK; 200 | } else { 201 | return URL_AUTOLINK; 202 | } 203 | } 204 | } 205 | 206 | return NORMAL_LINK; 207 | } 208 | 209 | static int S_get_enumlevel(cmark_node *node) { 210 | int enumlevel = 0; 211 | cmark_node *tmp = node; 212 | while (tmp) { 213 | if (tmp->type == CMARK_NODE_LIST && 214 | cmark_node_get_list_type(node) == CMARK_ORDERED_LIST) { 215 | enumlevel++; 216 | } 217 | tmp = tmp->parent; 218 | } 219 | return enumlevel; 220 | } 221 | 222 | static int S_render_node(cmark_renderer *renderer, cmark_node *node, 223 | cmark_event_type ev_type, int options) { 224 | int list_number; 225 | int enumlevel; 226 | char list_number_string[LIST_NUMBER_STRING_SIZE]; 227 | bool entering = (ev_type == CMARK_EVENT_ENTER); 228 | cmark_list_type list_type; 229 | bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options); 230 | 231 | if (node->extension && node->extension->latex_render_func) { 232 | node->extension->latex_render_func(node->extension, renderer, node, ev_type, options); 233 | return 1; 234 | } 235 | 236 | switch (node->type) { 237 | case CMARK_NODE_DOCUMENT: 238 | break; 239 | 240 | case CMARK_NODE_BLOCK_QUOTE: 241 | if (entering) { 242 | LIT("\\begin{quote}"); 243 | CR(); 244 | } else { 245 | LIT("\\end{quote}"); 246 | BLANKLINE(); 247 | } 248 | break; 249 | 250 | case CMARK_NODE_LIST: 251 | list_type = cmark_node_get_list_type(node); 252 | if (entering) { 253 | LIT("\\begin{"); 254 | LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize"); 255 | LIT("}"); 256 | CR(); 257 | list_number = cmark_node_get_list_start(node); 258 | if (list_number > 1) { 259 | enumlevel = S_get_enumlevel(node); 260 | // latex normally supports only five levels 261 | if (enumlevel >= 1 && enumlevel <= 5) { 262 | snprintf(list_number_string, LIST_NUMBER_STRING_SIZE, "%d", 263 | list_number); 264 | LIT("\\setcounter{enum"); 265 | switch (enumlevel) { 266 | case 1: LIT("i"); break; 267 | case 2: LIT("ii"); break; 268 | case 3: LIT("iii"); break; 269 | case 4: LIT("iv"); break; 270 | case 5: LIT("v"); break; 271 | default: LIT("i"); break; 272 | } 273 | LIT("}{"); 274 | OUT(list_number_string, false, NORMAL); 275 | LIT("}"); 276 | } 277 | CR(); 278 | } 279 | } else { 280 | LIT("\\end{"); 281 | LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize"); 282 | LIT("}"); 283 | BLANKLINE(); 284 | } 285 | break; 286 | 287 | case CMARK_NODE_ITEM: 288 | if (entering) { 289 | LIT("\\item "); 290 | } else { 291 | CR(); 292 | } 293 | break; 294 | 295 | case CMARK_NODE_HEADING: 296 | if (entering) { 297 | switch (cmark_node_get_heading_level(node)) { 298 | case 1: 299 | LIT("\\section"); 300 | break; 301 | case 2: 302 | LIT("\\subsection"); 303 | break; 304 | case 3: 305 | LIT("\\subsubsection"); 306 | break; 307 | case 4: 308 | LIT("\\paragraph"); 309 | break; 310 | case 5: 311 | LIT("\\subparagraph"); 312 | break; 313 | } 314 | LIT("{"); 315 | } else { 316 | LIT("}"); 317 | BLANKLINE(); 318 | } 319 | break; 320 | 321 | case CMARK_NODE_CODE_BLOCK: 322 | CR(); 323 | LIT("\\begin{verbatim}"); 324 | CR(); 325 | OUT(cmark_node_get_literal(node), false, LITERAL); 326 | CR(); 327 | LIT("\\end{verbatim}"); 328 | BLANKLINE(); 329 | break; 330 | 331 | case CMARK_NODE_HTML_BLOCK: 332 | break; 333 | 334 | case CMARK_NODE_CUSTOM_BLOCK: 335 | CR(); 336 | OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), 337 | false, LITERAL); 338 | CR(); 339 | break; 340 | 341 | case CMARK_NODE_THEMATIC_BREAK: 342 | BLANKLINE(); 343 | LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}"); 344 | BLANKLINE(); 345 | break; 346 | 347 | case CMARK_NODE_PARAGRAPH: 348 | if (!entering) { 349 | BLANKLINE(); 350 | } 351 | break; 352 | 353 | case CMARK_NODE_TEXT: 354 | OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); 355 | break; 356 | 357 | case CMARK_NODE_LINEBREAK: 358 | LIT("\\\\"); 359 | CR(); 360 | break; 361 | 362 | case CMARK_NODE_SOFTBREAK: 363 | if (options & CMARK_OPT_HARDBREAKS) { 364 | LIT("\\\\"); 365 | CR(); 366 | } else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) { 367 | CR(); 368 | } else { 369 | OUT(" ", allow_wrap, NORMAL); 370 | } 371 | break; 372 | 373 | case CMARK_NODE_CODE: 374 | LIT("\\texttt{"); 375 | OUT(cmark_node_get_literal(node), false, NORMAL); 376 | LIT("}"); 377 | break; 378 | 379 | case CMARK_NODE_HTML_INLINE: 380 | break; 381 | 382 | case CMARK_NODE_CUSTOM_INLINE: 383 | OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), 384 | false, LITERAL); 385 | break; 386 | 387 | case CMARK_NODE_STRONG: 388 | if (entering) { 389 | LIT("\\textbf{"); 390 | } else { 391 | LIT("}"); 392 | } 393 | break; 394 | 395 | case CMARK_NODE_EMPH: 396 | if (entering) { 397 | LIT("\\emph{"); 398 | } else { 399 | LIT("}"); 400 | } 401 | break; 402 | 403 | case CMARK_NODE_LINK: 404 | if (entering) { 405 | const char *url = cmark_node_get_url(node); 406 | // requires \usepackage{hyperref} 407 | switch (get_link_type(node)) { 408 | case URL_AUTOLINK: 409 | LIT("\\url{"); 410 | OUT(url, false, URL); 411 | LIT("}"); 412 | return 0; // Don't process further nodes to avoid double-rendering artefacts 413 | case EMAIL_AUTOLINK: 414 | LIT("\\href{"); 415 | OUT(url, false, URL); 416 | LIT("}\\nolinkurl{"); 417 | break; 418 | case NORMAL_LINK: 419 | LIT("\\href{"); 420 | OUT(url, false, URL); 421 | LIT("}{"); 422 | break; 423 | case INTERNAL_LINK: 424 | LIT("\\protect\\hyperlink{"); 425 | OUT(url + 1, false, URL); 426 | LIT("}{"); 427 | break; 428 | case NO_LINK: 429 | LIT("{"); // error? 430 | } 431 | } else { 432 | LIT("}"); 433 | } 434 | 435 | break; 436 | 437 | case CMARK_NODE_IMAGE: 438 | if (entering) { 439 | LIT("\\protect\\includegraphics{"); 440 | // requires \include{graphicx} 441 | OUT(cmark_node_get_url(node), false, URL); 442 | LIT("}"); 443 | return 0; 444 | } 445 | break; 446 | 447 | case CMARK_NODE_FOOTNOTE_DEFINITION: 448 | case CMARK_NODE_FOOTNOTE_REFERENCE: 449 | // TODO 450 | break; 451 | 452 | default: 453 | assert(false); 454 | break; 455 | } 456 | 457 | return 1; 458 | } 459 | 460 | char *cmark_render_latex(cmark_node *root, int options, int width) { 461 | return cmark_render_latex_with_mem(root, options, width, cmark_node_mem(root)); 462 | } 463 | 464 | char *cmark_render_latex_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) { 465 | return cmark_render(mem, root, options, width, outc, S_render_node); 466 | } 467 | --------------------------------------------------------------------------------