char* CFCPerlPod_md_to_pod(const char *md, CFCClass *klass, int header_level) { int options = CMARK_OPT_SMART | CMARK_OPT_VALIDATE_UTF8; cmark_node *doc = cmark_parse_document(md, strlen(md), options); char *pod = S_node_to_pod(doc, klass, header_level); cmark_node_free(doc); return pod; }
char* CFCPerlPod_md_to_pod(CFCPerlPod *self, CFCClass *klass, const char *md) { (void)self; // unused cmark_node *doc = cmark_parse_document(md, strlen(md)); char *pod = S_nodes_to_pod(klass, doc); cmark_node_free(doc); return pod; }
char *cmark_markdown_to_html(const char *text, size_t len, int options) { cmark_node *doc; char *result; doc = cmark_parse_document(text, len, options); result = cmark_render_html(doc, options, NULL); cmark_node_free(doc); return result; }
char *cmark_markdown_to_html(const char *text, int len) { cmark_node *doc; char *result; doc = cmark_parse_document(text, len, CMARK_OPT_DEFAULT); result = cmark_render_html(doc, CMARK_OPT_DEFAULT); cmark_node_free(doc); return result; }
static void test_content(test_batch_runner *runner, cmark_node_type type, int allowed_content) { cmark_node *node = cmark_node_new(type); for (int i = 0; i < num_node_types; ++i) { cmark_node_type child_type = node_types[i]; cmark_node *child = cmark_node_new(child_type); int got = cmark_node_append_child(node, child); int expected = (allowed_content >> child_type) & 1; INT_EQ(runner, got, expected, "add %d as child of %d", child_type, type); cmark_node_free(child); } cmark_node_free(node); }
static void iterator_delete(test_batch_runner *runner) { static const char md[] = "a *b* c\n" "\n" "* item1\n" "* item2\n" "\n" "a `b` c\n" "\n" "* item1\n" "* item2\n"; cmark_node *doc = cmark_parse_document(md, sizeof(md) - 1, CMARK_OPT_DEFAULT); cmark_iter *iter = cmark_iter_new(doc); cmark_event_type ev_type; while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cmark_node *node = cmark_iter_get_node(iter); // Delete list, emph, and code nodes. if ((ev_type == CMARK_EVENT_EXIT && node->type == CMARK_NODE_LIST) || (ev_type == CMARK_EVENT_EXIT && node->type == CMARK_NODE_EMPH) || (ev_type == CMARK_EVENT_ENTER && node->type == CMARK_NODE_CODE)) { cmark_node_free(node); } } char *html = cmark_render_html(doc, CMARK_OPT_DEFAULT); static const char expected[] = "<p>a c</p>\n" "<p>a c</p>\n"; STR_EQ(runner, html, expected, "iterate and delete nodes"); free(html); cmark_iter_free(iter); cmark_node_free(doc); }
static void node_check(test_batch_runner *runner) { // Construct an incomplete tree. cmark_node *doc = cmark_node_new(CMARK_NODE_DOCUMENT); cmark_node *p1 = cmark_node_new(CMARK_NODE_PARAGRAPH); cmark_node *p2 = cmark_node_new(CMARK_NODE_PARAGRAPH); doc->first_child = p1; p1->next = p2; INT_EQ(runner, cmark_node_check(doc, NULL), 4, "node_check works"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "node_check fixes tree"); cmark_node_free(doc); }
static void md_escape_inline(cmark_iter *const iter) { for(;;) { cmark_event_type const event = cmark_iter_next(iter); if(CMARK_EVENT_DONE == event) break; if(CMARK_EVENT_ENTER != event) continue; cmark_node *const node = cmark_iter_get_node(iter); if(CMARK_NODE_INLINE_HTML != cmark_node_get_type(node)) continue; char const *const str = cmark_node_get_literal(node); cmark_node *text = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(text, str); cmark_node_insert_before(node, text); cmark_node_free(node); } }
static void iterator(test_batch_runner *runner) { cmark_node *doc = cmark_parse_document("> a *b*\n\nc", 10, CMARK_OPT_DEFAULT); int parnodes = 0; cmark_event_type ev_type; cmark_iter *iter = cmark_iter_new(doc); cmark_node *cur; while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cur = cmark_iter_get_node(iter); if (cur->type == CMARK_NODE_PARAGRAPH && ev_type == CMARK_EVENT_ENTER) { parnodes += 1; } } INT_EQ(runner, parnodes, 2, "iterate correctly counts paragraphs"); cmark_iter_free(iter); cmark_node_free(doc); }
char* CFCPerlPod_md_doc_to_pod(const char *module, const char *md) { int options = CMARK_OPT_SMART | CMARK_OPT_VALIDATE_UTF8; cmark_node *doc = cmark_parse_document(md, strlen(md), options); cmark_node *maybe_header = cmark_node_first_child(doc); char *name; char *desc; if (maybe_header && cmark_node_get_type(maybe_header) == CMARK_NODE_HEADER ) { cmark_node *header_child = cmark_node_first_child(maybe_header); char *short_desc = S_nodes_to_pod(header_child, NULL, 1); name = CFCUtil_sprintf("%s - %s", module, short_desc); FREEMEM(short_desc); cmark_node *remaining = cmark_node_next(maybe_header); desc = S_nodes_to_pod(remaining, NULL, 1); } else { // No header found. name = CFCUtil_strdup(module); desc = S_node_to_pod(doc, NULL, 1); } const char *pattern = "=head1 NAME\n" "\n" "%s\n" "\n" "=head1 DESCRIPTION\n" "\n" "%s"; char *retval = CFCUtil_sprintf(pattern, name, desc); FREEMEM(name); FREEMEM(desc); cmark_node_free(doc); return retval; }
static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser, cmark_inline_parser *inline_parser, delimiter *opener, delimiter *closer) { cmark_node *strikethrough; cmark_node *tmp, *next; delimiter *delim, *tmp_delim; delimiter *res = closer->next; strikethrough = opener->inl_text; if (!cmark_node_set_type(strikethrough, CMARK_NODE_STRIKETHROUGH)) goto done; cmark_node_set_syntax_extension(strikethrough, self); cmark_node_set_string_content(strikethrough, "~"); tmp = cmark_node_next(opener->inl_text); while (tmp) { if (tmp == closer->inl_text) break; next = cmark_node_next(tmp); cmark_node_append_child(strikethrough, tmp); tmp = next; } strikethrough->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1; cmark_node_free(closer->inl_text); delim = closer; while (delim != NULL && delim != opener) { tmp_delim = delim->previous; cmark_inline_parser_remove_delimiter(inline_parser, delim); delim = tmp_delim; } cmark_inline_parser_remove_delimiter(inline_parser, opener); done: return res; }
static void constructor(test_batch_runner *runner) { for (int i = 0; i < num_node_types; ++i) { cmark_node_type type = node_types[i]; cmark_node *node = cmark_node_new(type); OK(runner, node != NULL, "new type %d", type); INT_EQ(runner, cmark_node_get_type(node), type, "get_type %d", type); switch (node->type) { case CMARK_NODE_HEADER: INT_EQ(runner, cmark_node_get_header_level(node), 1, "default header level is 1"); node->as.header.level = 1; break; case CMARK_NODE_LIST: INT_EQ(runner, cmark_node_get_list_type(node), CMARK_BULLET_LIST, "default is list type is bullet"); INT_EQ(runner, cmark_node_get_list_delim(node), CMARK_NO_DELIM, "default is list delim is NO_DELIM"); INT_EQ(runner, cmark_node_get_list_start(node), 1, "default is list start is 1"); INT_EQ(runner, cmark_node_get_list_tight(node), 0, "default is list is loose"); break; default: break; } cmark_node_free(node); } }
int main(int argc, char *argv[]) { int i, numfps = 0; int *files; int ok; char buffer[4096]; cmark_parser *parser; size_t bytes; cmark_node *document; char *outfile = NULL; int options = CMARK_OPT_DEFAULT | CMARK_OPT_SAFE | CMARK_OPT_NORMALIZE; #if defined(_WIN32) && !defined(__CYGWIN__) _setmode(_fileno(stdout), _O_BINARY); #endif files = (int *)malloc(argc * sizeof(*files)); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--version") == 0) { printf("cmark %s", CMARK_VERSION_STRING); printf(" - CommonMark converter\n(C) 2014, 2015 John MacFarlane\n"); exit(0); } else if (strcmp(argv[i], "--sourcepos") == 0) { options |= CMARK_OPT_SOURCEPOS; } else if (strcmp(argv[i], "--hardbreaks") == 0) { options |= CMARK_OPT_HARDBREAKS; } else if (strcmp(argv[i], "--smart") == 0) { options |= CMARK_OPT_SMART; } else if (strcmp(argv[i], "--validate-utf8") == 0) { options |= CMARK_OPT_VALIDATE_UTF8; } else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) { print_usage(); exit(0); } else if ((strcmp(argv[i], "-o") == 0) || (strcmp(argv[i], "--output") == 0)) { i += 1; if (i < argc) { outfile = argv[i]; } else { fprintf(stderr, "No argument provided for %s\n", argv[i - 1]); exit(1); } } else if (*argv[i] == '-') { print_usage(); exit(1); } else { // treat as file argument files[numfps++] = i; } } if (!outfile) { fprintf(stderr, "Specify an output file with -o/--output\n"); exit(1); } parser = cmark_parser_new(options); for (i = 0; i < numfps; i++) { FILE *fp = fopen(argv[files[i]], "r"); if (fp == NULL) { fprintf(stderr, "Error opening file %s: %s\n", argv[files[i]], strerror(errno)); exit(1); } while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { cmark_parser_feed(parser, buffer, bytes); if (bytes < sizeof(buffer)) { break; } } fclose(fp); } if (numfps == 0) { while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0) { cmark_parser_feed(parser, buffer, bytes); if (bytes < sizeof(buffer)) { break; } } } document = cmark_parser_finish(parser); cmark_parser_free(parser); ok = cmark_render_pdf(document, options, outfile); free(files); cmark_node_free(document); return ok ? 0 : 1; }
static cmark_node *fixup_nodes(cmark_syntax_extension *self, cmark_parser *parser, cmark_inline_parser *inline_parser, cmark_node *parent, int start_offset, int size) { int node_text_len; cmark_node *prev = NULL; cmark_node *tmp; int name_size = size; cmark_strbuf *name; NamedLink *named_link; for (prev = cmark_node_last_child(parent); prev; prev = cmark_node_previous(prev)) { if (cmark_node_get_type(prev) == CMARK_NODE_TEXT) { const char *text = cmark_node_get_literal(prev); node_text_len = strlen(text); size -= node_text_len; if (size <= 0) { if (size < 0) { char *split_text = my_strndup(text, size * -1); cmark_node *split = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(split, split_text); free(split_text); split_text = my_strndup(text + (size * - 1), node_text_len - size); cmark_node_set_literal(prev, split_text); free(split_text); cmark_node_insert_before(prev, split); } break; } } else { return NULL; } } name = cmark_strbuf_new(name_size + 1); tmp = prev; while (tmp) { cmark_node *next = cmark_node_next(tmp); cmark_strbuf_puts(name, cmark_node_get_literal(tmp)); if (tmp != prev) cmark_node_free(tmp); tmp = next; } named_link = PRIV(self)->link_resolve_func(cmark_strbuf_get(name)); if (!named_link || !named_link->ref) { int actual_line, actual_col; translate_sourcepos(get_first_parent_block(parent), start_offset, &actual_line, &actual_col); cmark_strbuf *message = cmark_strbuf_new(0); cmark_strbuf_puts(message, "Trying to link to non-existing symbol ‘"); cmark_strbuf_puts(message, cmark_strbuf_get(name)); cmark_strbuf_puts(message, "’"); diagnose("gtk-doc-bad-link", cmark_strbuf_get(message), actual_line - 1, actual_col - 1); cmark_strbuf_free(message); cmark_node_set_literal(prev, cmark_strbuf_get(name)); cmark_strbuf_free(name); return prev; } free_named_link(named_link); cmark_node_set_type(prev, CMARK_NODE_LINK); cmark_node_set_url(prev, cmark_strbuf_get(name)); cmark_strbuf_free(name); return prev; }
// Return a link, an image, or a literal close bracket. static cmark_node *handle_close_bracket(subject *subj) { bufsize_t initial_pos, after_link_text_pos; bufsize_t endurl, starttitle, endtitle, endall; bufsize_t sps, n; cmark_reference *ref = NULL; cmark_chunk url_chunk, title_chunk; cmark_chunk url, title; bracket *opener; cmark_node *inl; cmark_chunk raw_label; int found_label; cmark_node *tmp, *tmpnext; bool is_image; advance(subj); // advance past ] initial_pos = subj->pos; // get last [ or ![ opener = subj->last_bracket; if (opener == NULL) { return make_str(subj->mem, cmark_chunk_literal("]")); } if (!opener->active) { // take delimiter off stack pop_bracket(subj); return make_str(subj->mem, cmark_chunk_literal("]")); } // If we got here, we matched a potential link/image text. // Now we check to see if it's a link/image. is_image = opener->image; after_link_text_pos = subj->pos; // First, look for an inline link. if (peek_char(subj) == '(' && ((sps = scan_spacechars(&subj->input, subj->pos + 1)) > -1) && ((n = manual_scan_link_url(&subj->input, subj->pos + 1 + sps, &url_chunk)) > -1)) { // try to parse an explicit link: endurl = subj->pos + 1 + sps + n; starttitle = endurl + scan_spacechars(&subj->input, endurl); // ensure there are spaces btw url and title endtitle = (starttitle == endurl) ? starttitle : starttitle + scan_link_title(&subj->input, starttitle); endall = endtitle + scan_spacechars(&subj->input, endtitle); if (peek_at(subj, endall) == ')') { subj->pos = endall + 1; title_chunk = cmark_chunk_dup(&subj->input, starttitle, endtitle - starttitle); url = cmark_clean_url(subj->mem, &url_chunk); title = cmark_clean_title(subj->mem, &title_chunk); cmark_chunk_free(subj->mem, &url_chunk); cmark_chunk_free(subj->mem, &title_chunk); goto match; } else { // it could still be a shortcut reference link subj->pos = after_link_text_pos; } } // Next, look for a following [link label] that matches in refmap. // skip spaces raw_label = cmark_chunk_literal(""); found_label = link_label(subj, &raw_label); if (!found_label) { // If we have a shortcut reference link, back up // to before the spacse we skipped. subj->pos = initial_pos; } if ((!found_label || raw_label.len == 0) && !opener->bracket_after) { cmark_chunk_free(subj->mem, &raw_label); raw_label = cmark_chunk_dup(&subj->input, opener->position, initial_pos - opener->position - 1); found_label = true; } if (found_label) { ref = cmark_reference_lookup(subj->refmap, &raw_label); cmark_chunk_free(subj->mem, &raw_label); } if (ref != NULL) { // found url = chunk_clone(subj->mem, &ref->url); title = chunk_clone(subj->mem, &ref->title); goto match; } else { goto noMatch; } noMatch: // If we fall through to here, it means we didn't match a link: pop_bracket(subj); // remove this opener from delimiter list subj->pos = initial_pos; return make_str(subj->mem, cmark_chunk_literal("]")); match: inl = make_simple(subj->mem, is_image ? CMARK_NODE_IMAGE : CMARK_NODE_LINK); inl->as.link.url = url; inl->as.link.title = title; cmark_node_insert_before(opener->inl_text, inl); // Add link text: tmp = opener->inl_text->next; while (tmp) { tmpnext = tmp->next; cmark_node_append_child(inl, tmp); tmp = tmpnext; } // Free the bracket [: cmark_node_free(opener->inl_text); process_emphasis(subj, opener->previous_delimiter); pop_bracket(subj); // Now, if we have a link, we also want to deactivate earlier link // delimiters. (This code can be removed if we decide to allow links // inside links.) if (!is_image) { opener = subj->last_bracket; while (opener != NULL) { if (!opener->image) { if (!opener->active) { break; } else { opener->active = false; } } opener = opener->previous; } } return NULL; }
static void create_tree(test_batch_runner *runner) { char *html; cmark_node *doc = cmark_node_new(CMARK_NODE_DOCUMENT); cmark_node *p = cmark_node_new(CMARK_NODE_PARAGRAPH); OK(runner, !cmark_node_insert_before(doc, p), "insert before root fails"); OK(runner, !cmark_node_insert_after(doc, p), "insert after root fails"); OK(runner, cmark_node_append_child(doc, p), "append1"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "append1 consistent"); OK(runner, cmark_node_parent(p) == doc, "node_parent"); cmark_node *emph = cmark_node_new(CMARK_NODE_EMPH); OK(runner, cmark_node_prepend_child(p, emph), "prepend1"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "prepend1 consistent"); cmark_node *str1 = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(str1, "Hello, "); OK(runner, cmark_node_prepend_child(p, str1), "prepend2"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "prepend2 consistent"); cmark_node *str3 = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(str3, "!"); OK(runner, cmark_node_append_child(p, str3), "append2"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "append2 consistent"); cmark_node *str2 = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(str2, "world"); OK(runner, cmark_node_append_child(emph, str2), "append3"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "append3 consistent"); html = cmark_render_html(doc, CMARK_OPT_DEFAULT); STR_EQ(runner, html, "<p>Hello, <em>world</em>!</p>\n", "render_html"); free(html); OK(runner, cmark_node_insert_before(str1, str3), "ins before1"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "ins before1 consistent"); // 31e OK(runner, cmark_node_first_child(p) == str3, "ins before1 works"); OK(runner, cmark_node_insert_before(str1, emph), "ins before2"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "ins before2 consistent"); // 3e1 OK(runner, cmark_node_last_child(p) == str1, "ins before2 works"); OK(runner, cmark_node_insert_after(str1, str3), "ins after1"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "ins after1 consistent"); // e13 OK(runner, cmark_node_next(str1) == str3, "ins after1 works"); OK(runner, cmark_node_insert_after(str1, emph), "ins after2"); INT_EQ(runner, cmark_node_check(doc, NULL), 0, "ins after2 consistent"); // 1e3 OK(runner, cmark_node_previous(emph) == str1, "ins after2 works"); cmark_node_unlink(emph); html = cmark_render_html(doc, CMARK_OPT_DEFAULT); STR_EQ(runner, html, "<p>Hello, !</p>\n", "render_html after shuffling"); free(html); cmark_node_free(doc); // TODO: Test that the contents of an unlinked inline are valid // after the parent block was destroyed. This doesn't work so far. cmark_node_free(emph); }
static cmark_node* finalize(cmark_parser *parser, cmark_node* b) { int pos; cmark_node* item; cmark_node* subitem; cmark_node* parent; parent = b->parent; assert(b->open); // shouldn't call finalize on closed blocks b->open = false; if (parser->curline->size == 0) { // end of input - line number has not been incremented b->end_line = parser->line_number; b->end_column = parser->last_line_length; } else if (b->type == NODE_DOCUMENT || (b->type == NODE_CODE_BLOCK && b->as.code.fenced) || (b->type == NODE_HEADER && b->as.header.setext)) { b->end_line = parser->line_number; b->end_column = parser->curline->size; if (b->end_column && parser->curline->ptr[b->end_column - 1] == '\n') b->end_column--; if (b->end_column && parser->curline->ptr[b->end_column - 1] == '\r') b->end_column--; } else { b->end_line = parser->line_number - 1; b->end_column = parser->last_line_length; } switch (b->type) { case NODE_PARAGRAPH: while (cmark_strbuf_at(&b->string_content, 0) == '[' && (pos = cmark_parse_reference_inline(&b->string_content, parser->refmap))) { cmark_strbuf_drop(&b->string_content, pos); } if (is_blank(&b->string_content, 0)) { // remove blank node (former reference def) cmark_node_free(b); } break; case NODE_CODE_BLOCK: if (!b->as.code.fenced) { // indented code remove_trailing_blank_lines(&b->string_content); cmark_strbuf_putc(&b->string_content, '\n'); } else { // first line of contents becomes info for (pos = 0; pos < b->string_content.size; ++pos) { if (b->string_content.ptr[pos] == '\r' || b->string_content.ptr[pos] == '\n') break; } assert(pos < b->string_content.size); cmark_strbuf tmp = GH_BUF_INIT; houdini_unescape_html_f( &tmp, b->string_content.ptr, pos ); cmark_strbuf_trim(&tmp); cmark_strbuf_unescape(&tmp); b->as.code.info = cmark_chunk_buf_detach(&tmp); if (b->string_content.ptr[pos] == '\r') pos += 1; if (b->string_content.ptr[pos] == '\n') pos += 1; cmark_strbuf_drop(&b->string_content, pos); } b->as.code.literal = cmark_chunk_buf_detach(&b->string_content); break; case NODE_HTML: b->as.literal = cmark_chunk_buf_detach(&b->string_content); break; case NODE_LIST: // determine tight/loose status b->as.list.tight = true; // tight by default item = b->first_child; while (item) { // check for non-final non-empty list item ending with blank line: if (item->last_line_blank && item->next) { b->as.list.tight = false; break; } // recurse into children of list item, to see if there are // spaces between them: subitem = item->first_child; while (subitem) { if (ends_with_blank_line(subitem) && (item->next || subitem->next)) { b->as.list.tight = false; break; } subitem = subitem->next; } if (!(b->as.list.tight)) { break; } item = item->next; } break; default: break; } return parent; }
static void accessors(test_batch_runner *runner) { static const char markdown[] = "## Header\n" "\n" "* Item 1\n" "* Item 2\n" "\n" "2. Item 1\n" "\n" "3. Item 2\n" "\n" "\n" " code\n" "\n" "``` lang\n" "fenced\n" "```\n" "\n" "<div>html</div>\n" "\n" "[link](url 'title')\n"; cmark_node *doc = cmark_parse_document(markdown, sizeof(markdown) - 1, CMARK_OPT_DEFAULT); // Getters cmark_node *header = cmark_node_first_child(doc); INT_EQ(runner, cmark_node_get_header_level(header), 2, "get_header_level"); cmark_node *bullet_list = cmark_node_next(header); INT_EQ(runner, cmark_node_get_list_type(bullet_list), CMARK_BULLET_LIST, "get_list_type bullet"); INT_EQ(runner, cmark_node_get_list_tight(bullet_list), 1, "get_list_tight tight"); cmark_node *ordered_list = cmark_node_next(bullet_list); INT_EQ(runner, cmark_node_get_list_type(ordered_list), CMARK_ORDERED_LIST, "get_list_type ordered"); INT_EQ(runner, cmark_node_get_list_delim(ordered_list), CMARK_PERIOD_DELIM, "get_list_delim ordered"); INT_EQ(runner, cmark_node_get_list_start(ordered_list), 2, "get_list_start"); INT_EQ(runner, cmark_node_get_list_tight(ordered_list), 0, "get_list_tight loose"); cmark_node *code = cmark_node_next(ordered_list); STR_EQ(runner, cmark_node_get_literal(code), "code\n", "get_literal indented code"); cmark_node *fenced = cmark_node_next(code); STR_EQ(runner, cmark_node_get_literal(fenced), "fenced\n", "get_literal fenced code"); STR_EQ(runner, cmark_node_get_fence_info(fenced), "lang", "get_fence_info"); cmark_node *html = cmark_node_next(fenced); STR_EQ(runner, cmark_node_get_literal(html), "<div>html</div>\n", "get_literal html"); cmark_node *paragraph = cmark_node_next(html); INT_EQ(runner, cmark_node_get_start_line(paragraph), 19, "get_start_line"); INT_EQ(runner, cmark_node_get_start_column(paragraph), 1, "get_start_column"); INT_EQ(runner, cmark_node_get_end_line(paragraph), 19, "get_end_line"); cmark_node *link = cmark_node_first_child(paragraph); STR_EQ(runner, cmark_node_get_url(link), "url", "get_url"); STR_EQ(runner, cmark_node_get_title(link), "title", "get_title"); cmark_node *string = cmark_node_first_child(link); STR_EQ(runner, cmark_node_get_literal(string), "link", "get_literal string"); // Setters OK(runner, cmark_node_set_header_level(header, 3), "set_header_level"); OK(runner, cmark_node_set_list_type(bullet_list, CMARK_ORDERED_LIST), "set_list_type ordered"); OK(runner, cmark_node_set_list_delim(bullet_list, CMARK_PAREN_DELIM), "set_list_delim paren"); OK(runner, cmark_node_set_list_start(bullet_list, 3), "set_list_start"); OK(runner, cmark_node_set_list_tight(bullet_list, 0), "set_list_tight loose"); OK(runner, cmark_node_set_list_type(ordered_list, CMARK_BULLET_LIST), "set_list_type bullet"); OK(runner, cmark_node_set_list_tight(ordered_list, 1), "set_list_tight tight"); OK(runner, cmark_node_set_literal(code, "CODE\n"), "set_literal indented code"); OK(runner, cmark_node_set_literal(fenced, "FENCED\n"), "set_literal fenced code"); OK(runner, cmark_node_set_fence_info(fenced, "LANG"), "set_fence_info"); OK(runner, cmark_node_set_literal(html, "<div>HTML</div>\n"), "set_literal html"); OK(runner, cmark_node_set_url(link, "URL"), "set_url"); OK(runner, cmark_node_set_title(link, "TITLE"), "set_title"); OK(runner, cmark_node_set_literal(string, "LINK"), "set_literal string"); char *rendered_html = cmark_render_html(doc, CMARK_OPT_DEFAULT); static const char expected_html[] = "<h3>Header</h3>\n" "<ol start=\"3\">\n" "<li>\n" "<p>Item 1</p>\n" "</li>\n" "<li>\n" "<p>Item 2</p>\n" "</li>\n" "</ol>\n" "<ul>\n" "<li>Item 1</li>\n" "<li>Item 2</li>\n" "</ul>\n" "<pre><code>CODE\n" "</code></pre>\n" "<pre><code class=\"language-LANG\">FENCED\n" "</code></pre>\n" "<div>HTML</div>\n" "<p><a href=\"URL\" title=\"TITLE\">LINK</a></p>\n"; STR_EQ(runner, rendered_html, expected_html, "setters work"); free(rendered_html); // Getter errors INT_EQ(runner, cmark_node_get_header_level(bullet_list), 0, "get_header_level error"); INT_EQ(runner, cmark_node_get_list_type(header), CMARK_NO_LIST, "get_list_type error"); INT_EQ(runner, cmark_node_get_list_start(code), 0, "get_list_start error"); INT_EQ(runner, cmark_node_get_list_tight(fenced), 0, "get_list_tight error"); OK(runner, cmark_node_get_literal(ordered_list) == NULL, "get_literal error"); OK(runner, cmark_node_get_fence_info(paragraph) == NULL, "get_fence_info error"); OK(runner, cmark_node_get_url(html) == NULL, "get_url error"); OK(runner, cmark_node_get_title(header) == NULL, "get_title error"); // Setter errors OK(runner, !cmark_node_set_header_level(bullet_list, 3), "set_header_level error"); OK(runner, !cmark_node_set_list_type(header, CMARK_ORDERED_LIST), "set_list_type error"); OK(runner, !cmark_node_set_list_start(code, 3), "set_list_start error"); OK(runner, !cmark_node_set_list_tight(fenced, 0), "set_list_tight error"); OK(runner, !cmark_node_set_literal(ordered_list, "content\n"), "set_literal error"); OK(runner, !cmark_node_set_fence_info(paragraph, "lang"), "set_fence_info error"); OK(runner, !cmark_node_set_url(html, "url"), "set_url error"); OK(runner, !cmark_node_set_title(header, "title"), "set_title error"); OK(runner, !cmark_node_set_header_level(header, 0), "set_header_level too small"); OK(runner, !cmark_node_set_header_level(header, 7), "set_header_level too large"); OK(runner, !cmark_node_set_list_type(bullet_list, CMARK_NO_LIST), "set_list_type invalid"); OK(runner, !cmark_node_set_list_start(bullet_list, -1), "set_list_start negative"); cmark_node_free(doc); }
static cmark_node* finalize(cmark_parser *parser, cmark_node* b, int line_number) { int firstlinelen; int pos; cmark_node* item; cmark_node* subitem; cmark_node* parent; parent = b->parent; // don't do anything if the cmark_node is already closed if (!b->open) return parent; b->open = false; if (line_number > b->start_line) { b->end_line = line_number - 1; } else { b->end_line = line_number; } switch (b->type) { case NODE_PARAGRAPH: while (cmark_strbuf_at(&b->string_content, 0) == '[' && (pos = cmark_parse_reference_inline(&b->string_content, parser->refmap))) { cmark_strbuf_drop(&b->string_content, pos); } if (is_blank(&b->string_content, 0)) { // remove blank node (former reference def) cmark_node_free(b); } break; case NODE_CODE_BLOCK: if (!b->as.code.fenced) { // indented code remove_trailing_blank_lines(&b->string_content); cmark_strbuf_putc(&b->string_content, '\n'); } else { // first line of contents becomes info firstlinelen = cmark_strbuf_strchr(&b->string_content, '\n', 0); cmark_strbuf tmp = GH_BUF_INIT; houdini_unescape_html_f( &tmp, b->string_content.ptr, firstlinelen ); cmark_strbuf_trim(&tmp); cmark_strbuf_unescape(&tmp); b->as.code.info = cmark_chunk_buf_detach(&tmp); cmark_strbuf_drop(&b->string_content, firstlinelen + 1); } b->as.code.literal = cmark_chunk_buf_detach(&b->string_content); break; case NODE_HTML: b->as.literal = cmark_chunk_buf_detach(&b->string_content); break; case NODE_LIST: // determine tight/loose status b->as.list.tight = true; // tight by default item = b->first_child; while (item) { // check for non-final non-empty list item ending with blank line: if (item->last_line_blank && item->next) { b->as.list.tight = false; break; } // recurse into children of list item, to see if there are // spaces between them: subitem = item->first_child; while (subitem) { if (ends_with_blank_line(subitem) && (item->next || subitem->next)) { b->as.list.tight = false; break; } subitem = subitem->next; } if (!(b->as.list.tight)) { break; } item = item->next; } break; default: break; } return parent; }
void hierarchy(test_batch_runner *runner) { cmark_node *bquote1 = cmark_node_new(CMARK_NODE_BLOCK_QUOTE); cmark_node *bquote2 = cmark_node_new(CMARK_NODE_BLOCK_QUOTE); cmark_node *bquote3 = cmark_node_new(CMARK_NODE_BLOCK_QUOTE); OK(runner, cmark_node_append_child(bquote1, bquote2), "append bquote2"); OK(runner, cmark_node_append_child(bquote2, bquote3), "append bquote3"); OK(runner, !cmark_node_append_child(bquote3, bquote3), "adding a node as child of itself fails"); OK(runner, !cmark_node_append_child(bquote3, bquote1), "adding a parent as child fails"); cmark_node_free(bquote1); int max_node_type = CMARK_NODE_LAST_BLOCK > CMARK_NODE_LAST_INLINE ? CMARK_NODE_LAST_BLOCK : CMARK_NODE_LAST_INLINE; OK(runner, max_node_type < 32, "all node types < 32"); int list_item_flag = 1 << CMARK_NODE_ITEM; int top_level_blocks = (1 << CMARK_NODE_BLOCK_QUOTE) | (1 << CMARK_NODE_LIST) | (1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_HTML) | (1 << CMARK_NODE_PARAGRAPH) | (1 << CMARK_NODE_HEADER) | (1 << CMARK_NODE_HRULE); int all_inlines = (1 << CMARK_NODE_TEXT) | (1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) | (1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_INLINE_HTML) | (1 << CMARK_NODE_EMPH) | (1 << CMARK_NODE_STRONG) | (1 << CMARK_NODE_LINK) | (1 << CMARK_NODE_IMAGE); test_content(runner, CMARK_NODE_DOCUMENT, top_level_blocks); test_content(runner, CMARK_NODE_BLOCK_QUOTE, top_level_blocks); test_content(runner, CMARK_NODE_LIST, list_item_flag); test_content(runner, CMARK_NODE_ITEM, top_level_blocks); test_content(runner, CMARK_NODE_CODE_BLOCK , 0); test_content(runner, CMARK_NODE_HTML, 0); test_content(runner, CMARK_NODE_PARAGRAPH, all_inlines); test_content(runner, CMARK_NODE_HEADER, all_inlines); test_content(runner, CMARK_NODE_HRULE, 0); test_content(runner, CMARK_NODE_TEXT, 0); test_content(runner, CMARK_NODE_SOFTBREAK, 0); test_content(runner, CMARK_NODE_LINEBREAK, 0); test_content(runner, CMARK_NODE_CODE, 0); test_content(runner, CMARK_NODE_INLINE_HTML, 0); test_content(runner, CMARK_NODE_EMPH, all_inlines); test_content(runner, CMARK_NODE_STRONG, all_inlines); test_content(runner, CMARK_NODE_LINK, all_inlines); test_content(runner, CMARK_NODE_IMAGE, all_inlines); }
static delimiter* S_insert_emph(subject *subj, delimiter *opener, delimiter *closer) { delimiter *delim, *tmp_delim; int use_delims; cmark_node *opener_inl = opener->inl_text; cmark_node *closer_inl = closer->inl_text; int opener_num_chars = opener_inl->as.literal.len; int closer_num_chars = closer_inl->as.literal.len; cmark_node *tmp, *emph, *first_child, *last_child; // calculate the actual number of characters used from this closer if (closer_num_chars < 3 || opener_num_chars < 3) { use_delims = closer_num_chars <= opener_num_chars ? closer_num_chars : opener_num_chars; } else { // closer and opener both have >= 3 characters use_delims = closer_num_chars % 2 == 0 ? 2 : 1; } // remove used characters from associated inlines. opener_num_chars -= use_delims; closer_num_chars -= use_delims; opener_inl->as.literal.len = opener_num_chars; closer_inl->as.literal.len = closer_num_chars; // free delimiters between opener and closer delim = closer->previous; while (delim != NULL && delim != opener) { tmp_delim = delim->previous; remove_delimiter(subj, delim); delim = tmp_delim; } first_child = opener_inl->next; last_child = closer_inl->prev; // if opener has 0 characters, remove it and its associated inline if (opener_num_chars == 0) { // replace empty opener inline with emph cmark_chunk_free(&(opener_inl->as.literal)); emph = opener_inl; emph->type = use_delims == 1 ? NODE_EMPH : NODE_STRONG; // remove opener from list remove_delimiter(subj, opener); } else { // create new emph or strong, and splice it in to our inlines // between the opener and closer emph = use_delims == 1 ? make_emph() : make_strong(); emph->parent = opener_inl->parent; emph->prev = opener_inl; opener_inl->next = emph; } // push children below emph emph->next = closer_inl; closer_inl->prev = emph; emph->first_child = first_child; emph->last_child = last_child; // fix children pointers first_child->prev = NULL; last_child->next = NULL; for (tmp = first_child; tmp != NULL; tmp = tmp->next) { tmp->parent = emph; } // if closer has 0 characters, remove it and its associated inline if (closer_num_chars == 0) { // remove empty closer inline cmark_node_free(closer_inl); // remove closer from list tmp_delim = closer->next; remove_delimiter(subj, closer); closer = tmp_delim; } return closer; }
static delimiter *S_insert_emph(subject *subj, delimiter *opener, delimiter *closer) { delimiter *delim, *tmp_delim; bufsize_t use_delims; cmark_node *opener_inl = opener->inl_text; cmark_node *closer_inl = closer->inl_text; bufsize_t opener_num_chars = opener_inl->as.literal.len; bufsize_t closer_num_chars = closer_inl->as.literal.len; cmark_node *tmp, *tmpnext, *emph; // calculate the actual number of characters used from this closer if (closer_num_chars < 3 || opener_num_chars < 3) { use_delims = closer_num_chars <= opener_num_chars ? closer_num_chars : opener_num_chars; } else { // closer and opener both have >= 3 characters use_delims = closer_num_chars % 2 == 0 ? 2 : 1; } // remove used characters from associated inlines. opener_num_chars -= use_delims; closer_num_chars -= use_delims; opener_inl->as.literal.len = opener_num_chars; closer_inl->as.literal.len = closer_num_chars; // free delimiters between opener and closer delim = closer->previous; while (delim != NULL && delim != opener) { tmp_delim = delim->previous; remove_delimiter(subj, delim); delim = tmp_delim; } // create new emph or strong, and splice it in to our inlines // between the opener and closer emph = use_delims == 1 ? make_emph(subj->mem) : make_strong(subj->mem); tmp = opener_inl->next; while (tmp && tmp != closer_inl) { tmpnext = tmp->next; cmark_node_append_child(emph, tmp); tmp = tmpnext; } cmark_node_insert_after(opener_inl, emph); // if opener has 0 characters, remove it and its associated inline if (opener_num_chars == 0) { cmark_node_free(opener_inl); remove_delimiter(subj, opener); } // if closer has 0 characters, remove it and its associated inline if (closer_num_chars == 0) { // remove empty closer inline cmark_node_free(closer_inl); // remove closer from list tmp_delim = closer->next; remove_delimiter(subj, closer); closer = tmp_delim; } return closer; }
int main(int argc, char *argv[]) { int i, numfps = 0; int *files; char buffer[4096]; cmark_parser *parser; size_t bytes; cmark_node *document; int width = 0; char *unparsed; writer_format writer = FORMAT_HTML; int options = CMARK_OPT_DEFAULT; #if defined(_WIN32) && !defined(__CYGWIN__) _setmode(_fileno(stdout), _O_BINARY); #endif files = (int *)malloc(argc * sizeof(*files)); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--version") == 0) { printf("cmark %s", CMARK_VERSION_STRING); printf(" - CommonMark converter\n(C) 2014, 2015 John MacFarlane\n"); exit(0); } else if (strcmp(argv[i], "--sourcepos") == 0) { options |= CMARK_OPT_SOURCEPOS; } else if (strcmp(argv[i], "--hardbreaks") == 0) { options |= CMARK_OPT_HARDBREAKS; } else if (strcmp(argv[i], "--smart") == 0) { options |= CMARK_OPT_SMART; } else if (strcmp(argv[i], "--safe") == 0) { options |= CMARK_OPT_SAFE; } else if (strcmp(argv[i], "--normalize") == 0) { options |= CMARK_OPT_NORMALIZE; } else if (strcmp(argv[i], "--validate-utf8") == 0) { options |= CMARK_OPT_VALIDATE_UTF8; } else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) { print_usage(); exit(0); } else if (strcmp(argv[i], "--width") == 0) { i += 1; if (i < argc) { width = (int)strtol(argv[i], &unparsed, 10); if (unparsed && strlen(unparsed) > 0) { fprintf(stderr, "failed parsing width '%s' at '%s'\n", argv[i], unparsed); exit(1); } } else { fprintf(stderr, "--width requires an argument\n"); exit(1); } } else if ((strcmp(argv[i], "-t") == 0) || (strcmp(argv[i], "--to") == 0)) { i += 1; if (i < argc) { if (strcmp(argv[i], "man") == 0) { writer = FORMAT_MAN; } else if (strcmp(argv[i], "html") == 0) { writer = FORMAT_HTML; } else if (strcmp(argv[i], "xml") == 0) { writer = FORMAT_XML; } else if (strcmp(argv[i], "commonmark") == 0) { writer = FORMAT_COMMONMARK; } else if (strcmp(argv[i], "latex") == 0) { writer = FORMAT_LATEX; } else { fprintf(stderr, "Unknown format %s\n", argv[i]); exit(1); } } else { fprintf(stderr, "No argument provided for %s\n", argv[i - 1]); exit(1); } } else if (*argv[i] == '-') { print_usage(); exit(1); } else { // treat as file argument files[numfps++] = i; } } parser = cmark_parser_new(options); for (i = 0; i < numfps; i++) { FILE *fp = fopen(argv[files[i]], "rb"); if (fp == NULL) { fprintf(stderr, "Error opening file %s: %s\n", argv[files[i]], strerror(errno)); exit(1); } while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { cmark_parser_feed(parser, buffer, bytes); if (bytes < sizeof(buffer)) { break; } } fclose(fp); } if (numfps == 0) { while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0) { cmark_parser_feed(parser, buffer, bytes); if (bytes < sizeof(buffer)) { break; } } } document = cmark_parser_finish(parser); cmark_parser_free(parser); print_document(document, writer, options, width); cmark_node_free(document); free(files); return 0; }
int main(int argc, char *argv[]) { int i, numfps = 0; bool ast = false; int *files; char buffer[4096]; cmark_parser *parser; size_t bytes; cmark_node *document; parser = cmark_parser_new(); files = (int *)malloc(argc * sizeof(*files)); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--version") == 0) { printf("cmark %s", CMARK_VERSION); printf(" - CommonMark converter (c) 2014 John MacFarlane\n"); exit(0); } else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) { print_usage(); exit(0); } else if (strcmp(argv[i], "--ast") == 0) { ast = true; } else if (*argv[i] == '-') { print_usage(); exit(1); } else { // treat as file argument files[numfps++] = i; } } for (i = 0; i < numfps; i++) { FILE *fp = fopen(argv[files[i]], "r"); if (fp == NULL) { fprintf(stderr, "Error opening file %s: %s\n", argv[files[i]], strerror(errno)); exit(1); } start_timer(); while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { cmark_parser_feed(parser, buffer, bytes); } end_timer("processing lines"); fclose(fp); } if (numfps == 0) { /* document = cmark_parse_file(stdin); print_document(document, ast); exit(0); */ while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0) { cmark_parser_feed(parser, buffer, bytes); } } start_timer(); document = cmark_parser_finish(parser); end_timer("finishing document"); cmark_parser_free(parser); start_timer(); print_document(document, ast); end_timer("print_document"); start_timer(); cmark_node_free(document); end_timer("free_blocks"); free(files); return 0; }