/* markdown • parses the input buffer and renders it into the output buffer */ void markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer) { struct link_ref *lr; struct buf *text = bufnew(TEXT_UNIT); size_t i, beg, end; struct render rndr; /* filling the render structure */ if (!rndrer) return; rndr.make = *rndrer; if (rndr.make.max_work_stack < 1) rndr.make.max_work_stack = 1; arr_init(&rndr.refs, sizeof (struct link_ref)); parr_init(&rndr.work); for (i = 0; i < 256; i += 1) rndr.active_char[i] = 0; if ((rndr.make.emphasis || rndr.make.double_emphasis || rndr.make.triple_emphasis) && rndr.make.emph_chars) for (i = 0; rndr.make.emph_chars[i]; i += 1) rndr.active_char[(unsigned char)rndr.make.emph_chars[i]] = char_emphasis; if (rndr.make.codespan) rndr.active_char['`'] = char_codespan; if (rndr.make.linebreak) rndr.active_char['\n'] = char_linebreak; if (rndr.make.image || rndr.make.link) rndr.active_char['['] = char_link; rndr.active_char['<'] = char_langle_tag; rndr.active_char['\\'] = char_escape; rndr.active_char['&'] = char_entity; /* first pass: looking for references, copying everything else */ beg = 0; while (beg < ib->size) /* iterating over lines */ if (is_ref(ib->data, beg, ib->size, &end, &rndr.refs)) beg = end; else { /* skipping to the next line */ end = beg; while (end < ib->size && ib->data[end] != '\n' && ib->data[end] != '\r') end += 1; /* adding the line body if present */ if (end > beg) bufput(text, ib->data + beg, end - beg); while (end < ib->size && (ib->data[end] == '\n' || ib->data[end] == '\r')) { /* add one \n per newline */ if (ib->data[end] == '\n' || (end + 1 < ib->size && ib->data[end + 1] != '\n')) bufputc(text, '\n'); end += 1; } beg = end; } /* sorting the reference array */ if (rndr.refs.size) qsort(rndr.refs.base, rndr.refs.size, rndr.refs.unit, cmp_link_ref_sort); /* adding a final newline if not already present */ if (text->size && text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') bufputc(text, '\n'); /* second pass: actual rendering */ if (rndr.make.prolog) rndr.make.prolog(ob, rndr.make.opaque); parse_block(ob, &rndr, text->data, text->size); if (rndr.make.epilog) rndr.make.epilog(ob, rndr.make.opaque); /* clean-up */ bufrelease(text); lr = rndr.refs.base; for (i = 0; i < rndr.refs.size; i += 1) { bufrelease(lr[i].id); bufrelease(lr[i].link); bufrelease(lr[i].title); } arr_free(&rndr.refs); assert(rndr.work.size == 0); for (i = 0; i < rndr.work.asize; i += 1) bufrelease(rndr.work.item[i]); parr_free(&rndr.work); }
/* markdown • parses the input buffer and renders it into the output buffer */ void ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer, unsigned int extensions) { struct link_ref *lr; struct buf *text; size_t i, beg, end; struct render rndr; /* filling the render structure */ if (!rndrer) return; text = bufnew(TEXT_UNIT); if (!text) return; rndr.make = *rndrer; arr_init(&rndr.refs, sizeof (struct link_ref)); parr_init(&rndr.work); for (i = 0; i < 256; i += 1) rndr.active_char[i] = 0; if (rndr.make.emphasis || rndr.make.double_emphasis || rndr.make.triple_emphasis) { rndr.active_char['*'] = char_emphasis; rndr.active_char['_'] = char_emphasis; if (extensions & MKDEXT_STRIKETHROUGH) rndr.active_char['~'] = char_emphasis; } if (rndr.make.codespan) rndr.active_char['`'] = char_codespan; if (rndr.make.linebreak) rndr.active_char['\n'] = char_linebreak; if (rndr.make.image || rndr.make.link) rndr.active_char['['] = char_link; rndr.active_char['<'] = char_langle_tag; rndr.active_char['\\'] = char_escape; rndr.active_char['&'] = char_entity; if (extensions & MKDEXT_AUTOLINK) { rndr.active_char['h'] = char_autolink; // http, https rndr.active_char['H'] = char_autolink; rndr.active_char['f'] = char_autolink; // ftp rndr.active_char['F'] = char_autolink; rndr.active_char['m'] = char_autolink; // mailto rndr.active_char['M'] = char_autolink; } /* Extension data */ rndr.ext_flags = extensions; rndr.max_nesting = 16; /* first pass: looking for references, copying everything else */ beg = 0; while (beg < ib->size) /* iterating over lines */ if (is_ref(ib->data, beg, ib->size, &end, &rndr.refs)) beg = end; else { /* skipping to the next line */ end = beg; while (end < ib->size && ib->data[end] != '\n' && ib->data[end] != '\r') end += 1; /* adding the line body if present */ if (end > beg) expand_tabs(text, ib->data + beg, end - beg); while (end < ib->size && (ib->data[end] == '\n' || ib->data[end] == '\r')) { /* add one \n per newline */ if (ib->data[end] == '\n' || (end + 1 < ib->size && ib->data[end + 1] != '\n')) bufputc(text, '\n'); end += 1; } beg = end; } /* sorting the reference array */ if (rndr.refs.size) qsort(rndr.refs.base, rndr.refs.size, rndr.refs.unit, cmp_link_ref_sort); /* adding a final newline if not already present */ if (!text->size) goto cleanup; if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') bufputc(text, '\n'); /* second pass: actual rendering */ if (rndr.make.doc_header) rndr.make.doc_header(ob, rndr.make.opaque); parse_block(ob, &rndr, text->data, text->size); if (rndr.make.doc_footer) rndr.make.doc_footer(ob, rndr.make.opaque); /* clean-up */ cleanup: bufrelease(text); lr = rndr.refs.base; for (i = 0; i < (size_t)rndr.refs.size; i += 1) { bufrelease(lr[i].id); bufrelease(lr[i].link); bufrelease(lr[i].title); } arr_free(&rndr.refs); assert(rndr.work.size == 0); for (i = 0; i < (size_t)rndr.work.asize; i += 1) bufrelease(rndr.work.item[i]); parr_free(&rndr.work); }