├── 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, "");
66 | cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
67 | literal = true;
68 | break;
69 | case CMARK_NODE_LIST:
70 | switch (cmark_node_get_list_type(node)) {
71 | case CMARK_ORDERED_LIST:
72 | cmark_strbuf_puts(xml, " type=\"ordered\"");
73 | snprintf(buffer, BUFFER_SIZE, " start=\"%d\"",
74 | cmark_node_get_list_start(node));
75 | cmark_strbuf_puts(xml, buffer);
76 | delim = cmark_node_get_list_delim(node);
77 | if (delim == CMARK_PAREN_DELIM) {
78 | cmark_strbuf_puts(xml, " delim=\"paren\"");
79 | } else if (delim == CMARK_PERIOD_DELIM) {
80 | cmark_strbuf_puts(xml, " delim=\"period\"");
81 | }
82 | break;
83 | case CMARK_BULLET_LIST:
84 | cmark_strbuf_puts(xml, " type=\"bullet\"");
85 | break;
86 | default:
87 | break;
88 | }
89 | snprintf(buffer, BUFFER_SIZE, " tight=\"%s\"",
90 | (cmark_node_get_list_tight(node) ? "true" : "false"));
91 | cmark_strbuf_puts(xml, buffer);
92 | break;
93 | case CMARK_NODE_HEADING:
94 | snprintf(buffer, BUFFER_SIZE, " level=\"%d\"", node->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, "");
106 | cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
107 | literal = true;
108 | break;
109 | case CMARK_NODE_CUSTOM_BLOCK:
110 | case CMARK_NODE_CUSTOM_INLINE:
111 | cmark_strbuf_puts(xml, " on_enter=\"");
112 | escape_xml(xml, node->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, "");
143 | cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
144 | 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 |
--------------------------------------------------------------------------------