/* parse_blockquote • hanldes parsing of a regular paragraph */ static size_t parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size) { size_t i = 0, end = 0; int level = 0; struct buf work = { data, 0, 0, 0, 0 }; /* volatile working buffer */ while (i < size) { for (end = i + 1; end < size && data[end - 1] != '\n'; end += 1); if (is_empty(data + i, size - i) || (level = is_headerline(data + i, size - i)) != 0) break; if ((i && data[i] == '#') || is_hrule(data + i, size - i)) { end = i; break; } i = end; } work.size = i; while (work.size && data[work.size - 1] == '\n') work.size -= 1; if (!level) { struct buf *tmp = new_work_buffer(rndr); parse_inline(tmp, rndr, work.data, work.size); if (rndr->make.paragraph) rndr->make.paragraph(ob, tmp, rndr->make.opaque); release_work_buffer(rndr, tmp); } else { if (work.size) { size_t beg; i = work.size; work.size -= 1; while (work.size && data[work.size] != '\n') work.size -= 1; beg = work.size + 1; while (work.size && data[work.size - 1] == '\n') work.size -= 1; if (work.size) { struct buf *tmp = new_work_buffer(rndr); parse_inline(tmp, rndr, work.data, work.size); if (rndr->make.paragraph) rndr->make.paragraph(ob, tmp, rndr->make.opaque); release_work_buffer(rndr, tmp); work.data += beg; work.size = i - beg; } else work.size = i; } if (rndr->make.header) { struct buf *span = new_work_buffer(rndr); parse_inline(span, rndr, work.data, work.size); rndr->make.header(ob, span, level,rndr->make.opaque); release_work_buffer(rndr, span); } } return end; }
/* parse_blockquote • hanldes parsing of a blockquote fragment */ static size_t parse_blockquote(struct buf *ob, struct render *rndr, char *data, size_t size) { size_t beg, end = 0, pre, work_size = 0; char *work_data = 0; struct buf *out = new_work_buffer(rndr); beg = 0; while (beg < size) { for (end = beg + 1; end < size && data[end - 1] != '\n'; end += 1); pre = prefix_quote(data + beg, end - beg); if (pre) beg += pre; /* skipping prefix */ else if (is_empty(data + beg, end - beg) && (end >= size || (prefix_quote(data + end, size - end) == 0 && !is_empty(data + end, size - end)))) /* empty line followed by non-quote line */ break; if (beg < end) { /* copy into the in-place working buffer */ /* bufput(work, data + beg, end - beg); */ if (!work_data) work_data = data + beg; else if (data + beg != work_data + work_size) memmove(work_data + work_size, data + beg, end - beg); work_size += end - beg; } beg = end; } parse_block(out, rndr, work_data, work_size); if (rndr->make.blockquote) rndr->make.blockquote(ob, out, rndr->make.opaque); release_work_buffer(rndr, out); return end; }
/* closed by a symbol not preceded by whitespace and not followed by symbol */ static size_t parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c) { size_t i = 0, len; struct buf *work = 0; int r; if (!rndr->make.emphasis) return 0; /* skipping one symbol if coming from emph3 */ if (size > 1 && data[0] == c && data[1] == c) i = 1; while (i < size) { len = find_emph_char(data + i, size - i, c); if (!len) return 0; i += len; if (i >= size) return 0; if (i + 1 < size && data[i + 1] == c) { i += 1; continue; } if (data[i] == c && data[i - 1] != ' ' && data[i - 1] != '\t' && data[i - 1] != '\n') { work = new_work_buffer(rndr); parse_inline(work, rndr, data, i); r = rndr->make.emphasis(ob, work, c, rndr->make.opaque); release_work_buffer(rndr, work); return r ? i + 1 : 0; } } return 0; }
/* parse_atxheader • parsing of atx-style headers */ static size_t parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size) { int level = 0; size_t i, end, skip, span_beg, span_size; if (!size || data[0] != '#') return 0; while (level < size && level < 6 && data[level] == '#') level += 1; for (i = level; i < size && (data[i] == ' ' || data[i] == '\t'); i += 1); span_beg = i; for (end = i; end < size && data[end] != '\n'; end += 1); skip = end; if (end <= i) return parse_paragraph(ob, rndr, data, size); while (end && data[end - 1] == '#') end -= 1; while (end && (data[end - 1] == ' ' || data[end - 1] == '\t')) end -= 1; if (end <= i) return parse_paragraph(ob, rndr, data, size); span_size = end - span_beg; if (rndr->make.header) { struct buf *span = new_work_buffer(rndr); parse_inline(span, rndr, data + span_beg, span_size); rndr->make.header(ob, span, level, rndr->make.opaque); release_work_buffer(rndr, span); } return skip; }
/* parse_table_cell • parse a cell inside a table */ static void parse_table_cell(struct buf *ob, struct render *rndr, char *data, size_t size, int flags) { struct buf *span = new_work_buffer(rndr); parse_inline(span, rndr, data, size); rndr->make.table_cell(ob, span, flags, rndr->make.opaque); release_work_buffer(rndr, span); }
/* parse_blockquote • hanldes parsing of a block-level code fragment */ static size_t parse_blockcode(struct buf *ob, struct render *rndr, char *data, size_t size) { size_t beg, end, pre; struct buf *work = new_work_buffer(rndr); beg = 0; while (beg < size) { for (end = beg + 1; end < size && data[end - 1] != '\n'; end += 1); pre = prefix_code(data + beg, end - beg); if (pre) beg += pre; /* skipping prefix */ else if (!is_empty(data + beg, end - beg)) /* non-empty non-prefixed line breaks the pre */ break; if (beg < end) { /* verbatim copy to the working buffer, escaping entities */ if (is_empty(data + beg, end - beg)) bufputc(work, '\n'); else bufput(work, data + beg, end - beg); } beg = end; } while (work->size && work->data[work->size - 1] == '\n') work->size -= 1; bufputc(work, '\n'); if (rndr->make.blockcode) rndr->make.blockcode(ob, work, rndr->make.opaque); release_work_buffer(rndr, work); return beg; }
int32_t skiplist_delete(SkipList_t *ptrSl, SlKey_t slKey) { SkipListNode_t **rgPtrSlnBuf = obtain_work_buffer(ptrSl); SkipListNode_t *ptrSlnCurr,*ptrSlnNext; int32_t cMaxLevel = ptrSl->cMaxLevel; int32_t i; int32_t fFound = 0; ptrSlnCurr = ptrSl->ptrSlnHeader; for (i = cMaxLevel-1; i >= 0; i--) { while ((ptrSlnNext = ptrSlnCurr->rgPtrSlnForward[i]) && ptrSlnNext->slKey < slKey) { ptrSlnCurr = ptrSlnNext; } rgPtrSlnBuf[i] = ptrSlnCurr; fFound |= ptrSlnNext && ptrSlnNext->slKey == slKey; } if (!fFound) { return SKIPLIST_ERROR; } SkipListNode_t *ptrSlnToDelete = rgPtrSlnBuf[0]->rgPtrSlnForward[0]; for (i = 0; i <= ptrSlnToDelete->cLevel; i++) { ptrSlnCurr = rgPtrSlnBuf[i]; ptrSlnCurr->rgPtrSlnForward[i] = ptrSlnToDelete->rgPtrSlnForward[i]; ptrSlnCurr->rgCLinkWidth[i] = (ptrSlnCurr->rgCLinkWidth[i] + ptrSlnToDelete->rgCLinkWidth[i] - 1); } for (i = ptrSlnToDelete->cLevel+1; i < cMaxLevel; i++) { rgPtrSlnBuf[i]->rgCLinkWidth[i]--; } ptrSl->cSize--; return SKIPLIST_OK; release_work_buffer(rgPtrSlnBuf); }
/* parse_list • parsing ordered or unordered list block */ static size_t parse_list(struct buf *ob, struct render *rndr, char *data, size_t size, int flags) { struct buf *work = new_work_buffer(rndr); size_t i = 0, j; while (i < size) { j = parse_listitem(work, rndr, data + i, size - i, &flags); i += j; if (!j || (flags & MKD_LI_END)) break; } if (rndr->make.list) rndr->make.list(ob, work, flags, rndr->make.opaque); release_work_buffer(rndr, work); return i; }
void skiplist_insert(SkipList_t *ptrSl, SlKey_t slKey, void *ptrValue) { int32_t cMaxLevel = ptrSl->cMaxLevel; int32_t ixLevel = generate_level(ptrSl); SkipListNode_t *ptrSlnNew = skiplist_alloc_node(cMaxLevel); ptrSlnNew->cLevel = (int16_t)ixLevel; ptrSlnNew->slKey = slKey; ptrSlnNew->ptrValue = ptrValue; SkipListNode_t **rgPtrSlnBuf = obtain_work_buffer(ptrSl); SkipListNode_t *ptrSlnCurr = ptrSl->ptrSlnHeader; int32_t i; for (i = ptrSl->cMaxLevel-1; i >= 0; i--) { SkipListNode_t *ptrSlnNext; int32_t cLinkWidth = 0; while ((ptrSlnNext = ptrSlnCurr->rgPtrSlnForward[i]) && ptrSlnNext->slKey < slKey) { cLinkWidth += ptrSlnCurr->rgCLinkWidth[i]; ptrSlnCurr = ptrSlnNext; } rgPtrSlnBuf[i] = ptrSlnCurr; if (i < cMaxLevel - 1) { ptrSlnNew->rgCLinkWidth[i+1] = cLinkWidth; } } ptrSlnNew->rgCLinkWidth[0] = 1; int32_t cLinkWidth = 0; for (i = 0; i <= ixLevel; i++) { ptrSlnCurr = rgPtrSlnBuf[i]; ptrSlnNew->rgPtrSlnForward[i] = ptrSlnCurr->rgPtrSlnForward[i]; ptrSlnCurr->rgPtrSlnForward[i] = ptrSlnNew; int32_t cOldWidth = ptrSlnCurr->rgCLinkWidth[i]; int32_t cTraveled = ptrSlnNew->rgCLinkWidth[i]; cLinkWidth += cTraveled; int32_t cNewWidth = cOldWidth - cLinkWidth; ptrSlnCurr->rgCLinkWidth[i] = cLinkWidth; ptrSlnNew->rgCLinkWidth[i] = cNewWidth < 0 ? 0 : cNewWidth; } for (i = ixLevel + 1; i < cMaxLevel; i++) { rgPtrSlnBuf[i]->rgCLinkWidth[i]++; } ptrSl->cSize++; release_work_buffer(rgPtrSlnBuf); }
/* finds the first closing tag, and delegates to the other emph */ static size_t parse_emph3(struct buf *ob, struct render *rndr, char *data, size_t size, char c) { size_t i = 0, len; int r; while (i < size) { len = find_emph_char(data + i, size - i, c); if (!len) return 0; i += len; /* skip whitespace preceded symbols */ if (data[i] != c || data[i - 1] == ' ' || data[i - 1] == '\t' || data[i - 1] == '\n') continue; if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->make.triple_emphasis) { /* triple symbol found */ struct buf *work = new_work_buffer(rndr); parse_inline(work, rndr, data, i); r = rndr->make.triple_emphasis(ob, work, c, rndr->make.opaque); release_work_buffer(rndr, work); return r ? i + 3 : 0; } else if (i + 1 < size && data[i + 1] == c) { /* double symbol found, handing over to emph1 */ len = parse_emph1(ob, rndr, data - 2, size + 2, c); if (!len) return 0; else return len - 2; } else { /* single symbol found, handing over to emph2 */ len = parse_emph2(ob, rndr, data - 1, size + 1, c); if (!len) return 0; else return len - 1; } } return 0; }
/* parse_emph2 • parsing single emphase */ static size_t parse_emph2(struct buf *ob, struct render *rndr, char *data, size_t size, char c) { size_t i = 0, len; struct buf *work = 0; int r; if (!rndr->make.double_emphasis) return 0; while (i < size) { len = find_emph_char(data + i, size - i, c); if (!len) return 0; i += len; if (i + 1 < size && data[i] == c && data[i + 1] == c && i && data[i - 1] != ' ' && data[i - 1] != '\t' && data[i - 1] != '\n') { work = new_work_buffer(rndr); parse_inline(work, rndr, data, i); r = rndr->make.double_emphasis(ob, work, c, rndr->make.opaque); release_work_buffer(rndr, work); return r ? i + 2 : 0; } i += 1; } return 0; }
/* char_link • '[': parsing a link or an image */ static size_t char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size) { int is_img = (offset && data[-1] == '!'), level; size_t i = 1, txt_e; struct buf *content = 0; struct buf *link = 0; struct buf *title = 0; int text_has_nl = 0, ret; /* checking whether the correct renderer exists */ if ((is_img && !rndr->make.image) || (!is_img && !rndr->make.link)) return 0; /* looking for the matching closing bracket */ for (level = 1; i < size; i += 1) if (data[i] == '\n') text_has_nl = 1; else if (data[i - 1] == '\\') continue; else if (data[i] == '[') level += 1; else if (data[i] == ']') { level -= 1; if (level <= 0) break; } if (i >= size) return 0; txt_e = i; i += 1; /* skip any amount of whitespace or newline */ /* (this is much more laxist than original markdown syntax) */ while (i < size && (data[i] == ' ' || data[i] == '\t' || data[i] == '\n')) i += 1; /* allocate temporary buffers to store content, link and title */ content = new_work_buffer(rndr); link = new_work_buffer(rndr); title = new_work_buffer(rndr); ret = 0; /* error if we don't get to the callback */ /* inline style link */ if (i < size && data[i] == '(') { size_t span_end = i; while (span_end < size && !(data[span_end] == ')' && (span_end == i || data[span_end - 1] != '\\'))) span_end += 1; if (span_end >= size || get_link_inline(link, title, data + i+1, span_end - (i+1)) < 0) goto char_link_cleanup; i = span_end + 1; } /* reference style link */ else if (i < size && data[i] == '[') { char *id_data; size_t id_size, id_end = i; while (id_end < size && data[id_end] != ']') id_end += 1; if (id_end >= size) goto char_link_cleanup; if (i + 1 == id_end) { /* implicit id - use the contents */ id_data = data + 1; id_size = txt_e - 1; } else { /* explici id - between brackets */ id_data = data + i + 1; id_size = id_end - (i + 1); } if (get_link_ref(rndr, link, title, id_data, id_size) < 0) goto char_link_cleanup; i = id_end + 1; } /* shortcut reference style link */ else { if (get_link_ref(rndr, link, title, data + 1, txt_e - 1) < 0) goto char_link_cleanup; /* rewinding the whitespace */ i = txt_e + 1; } /* building content: img alt is escaped, link content is parsed */ if (txt_e > 1) { if (is_img) bufput(content, data + 1, txt_e - 1); else parse_inline(content, rndr, data + 1, txt_e - 1); } /* calling the relevant rendering function */ if (is_img) { if (ob->size && ob->data[ob->size - 1] == '!') ob->size -= 1; ret = rndr->make.image(ob, link, title, content, rndr->make.opaque); } else ret = rndr->make.link(ob, link, title, content, rndr->make.opaque); /* cleanup */ char_link_cleanup: release_work_buffer(rndr, title); release_work_buffer(rndr, link); release_work_buffer(rndr, content); return ret ? i : 0; }
/* parse_table • parsing of a whole table */ static size_t parse_table(struct buf *ob, struct render *rndr, char *data, size_t size) { size_t i = 0, head_end, col; size_t align_size = 0; int *aligns = 0; struct buf *head = 0; struct buf *rows = new_work_buffer(rndr); /* skip the first (presumably header) line */ while (i < size && data[i] != '\n') i += 1; head_end = i; /* fallback on end of input */ if (i >= size) { parse_table_row(rows, rndr, data, size, 0, 0, 0); rndr->make.table(ob, 0, rows, rndr->make.opaque); release_work_buffer(rndr, rows); return i; } /* attempt to parse a table rule, i.e. blanks, dash, colons and sep */ i += 1; col = 0; while (i < size && (data[i] == ' ' || data[i] == '\t' || data[i] == '-' || data[i] == ':' || data[i] == '|')) { if (data[i] == '|') align_size += 1; if (data[i] == ':') col = 1; i += 1; } if (i < size && data[i] == '\n') { align_size += 1; /* render the header row */ head = new_work_buffer(rndr); parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD); /* parse alignments if provided */ if (col && (aligns = malloc(align_size * sizeof *aligns)) != 0){ for (i = 0; i < align_size; i += 1) aligns[i] = 0; col = 0; i = head_end + 1; /* skip initial white space and optional separator */ while (i < size && (data[i] == ' ' || data[i] == '\t')) i += 1; if (data[i] == '|') i += 1; /* compute default alignment for each column */ while (i < size && data[i] != '\n') { if (data[i] == ':') aligns[col] |= MKD_CELL_ALIGN_LEFT; while (i < size && data[i] != '|' && data[i] != '\n') i += 1; if (data[i - 1] == ':') aligns[col] |= MKD_CELL_ALIGN_RIGHT; if (i < size && data[i] == '|') i += 1; col += 1; } } /* point i to the beginning of next line/row */ i += 1; } else { /* there is no valid ruler, continuing without header */ i = 0; } /* render the table body lines */ while (i < size && is_tableline(data + i, size - i)) i += parse_table_row(rows, rndr, data + i, size - i, aligns, align_size, 0); /* render the full table */ rndr->make.table(ob, head, rows, rndr->make.opaque); /* cleanup */ if (head) release_work_buffer(rndr, head); release_work_buffer(rndr, rows); free(aligns); return i; }
/* parse_table_row • parse an input line into a table row */ static size_t parse_table_row(struct buf *ob, struct render *rndr, char *data, size_t size, int *aligns, size_t align_size, int flags) { size_t i = 0, col = 0; size_t beg, end, total = 0; struct buf *cells = new_work_buffer(rndr); int align; /* skip leading blanks and sperator */ while (i < size && (data[i] == ' ' || data[i] == '\t')) i += 1; if (i < size && data[i] == '|') i += 1; /* go over all the cells */ while (i < size && total == 0) { /* check optional left/center align marker */ align = 0; if (data[i] == ':') { align |= MKD_CELL_ALIGN_LEFT; i += 1; } /* skip blanks */ while (i < size && (data[i] == ' ' || data[i] == '\t')) i += 1; beg = i; /* forward to the next separator or EOL */ while (i < size && !is_table_sep(data, i) && data[i] != '\n') i += 1; end = i; if (i < size) { i += 1; if (data[i - 1] == '\n') total = i; } /* check optional right/center align marker */ if (i > beg && data[end - 1] == ':') { align |= MKD_CELL_ALIGN_RIGHT; end -= 1; } /* remove trailing blanks */ while (end > beg && (data[end - 1] == ' ' || data[end - 1] == '\t')) end -= 1; /* skip the last cell if it was only blanks */ /* (because it is only the optional end separator) */ if (total && end <= beg) continue; /* fallback on default alignment if not explicit */ if (align == 0 && aligns && col < align_size) align = aligns[col]; /* render cells */ parse_table_cell(cells, rndr, data + beg, end - beg, align | flags); col += 1; } /* render the whole row and clean up */ rndr->make.table_row(ob, cells, flags, rndr->make.opaque); release_work_buffer(rndr, cells); return total ? total : size; }
/* assuming initial prefix is already removed */ static size_t parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int *flags) { struct buf *work = 0, *inter = 0; size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; int in_empty = 0, has_inside_empty = 0; /* keeping book of the first indentation prefix */ if (size > 1 && data[0] == ' ') { orgpre = 1; if (size > 2 && data[1] == ' ') { orgpre = 2; if (size > 3 && data[2] == ' ') { orgpre = 3; } } } beg = prefix_uli(data, size); if (!beg) beg = prefix_oli(data, size); if (!beg) return 0; /* skipping to the beginning of the following line */ end = beg; while (end < size && data[end - 1] != '\n') end += 1; /* getting working buffers */ work = new_work_buffer(rndr); inter = new_work_buffer(rndr); /* putting the first line into the working buffer */ bufput(work, data + beg, end - beg); beg = end; /* process the following lines */ while (beg < size) { end += 1; while (end < size && data[end - 1] != '\n') end += 1; /* process an empty line */ if (is_empty(data + beg, end - beg)) { in_empty = 1; beg = end; continue; } /* calculating the indentation */ i = 0; if (end - beg > 1 && data[beg] == ' ') { i = 1; if (end - beg > 2 && data[beg + 1] == ' ') { i = 2; if (end - beg > 3 && data[beg + 2] == ' ') { i = 3; if (end - beg > 3 && data[beg + 3] == ' ') { i = 4; } } } } pre = i; if (data[beg] == '\t') { i = 1; pre = 8; } /* checking for a new item */ if ((prefix_uli(data + beg + i, end - beg - i) && !is_hrule(data + beg + i, end - beg - i)) || prefix_oli(data + beg + i, end - beg - i)) { if (in_empty) has_inside_empty = 1; if (pre == orgpre) /* the following item must have */ break; /* the same indentation */ if (!sublist) sublist = work->size; } /* joining only indented stuff after empty lines */ else if (in_empty && i < 4 && data[beg] != '\t') { *flags |= MKD_LI_END; break; } else if (in_empty) { bufputc(work, '\n'); has_inside_empty = 1; } in_empty = 0; /* adding the line without prefix into the working buffer */ bufput(work, data + beg + i, end - beg - i); beg = end; } /* render of li contents */ if (has_inside_empty) *flags |= MKD_LI_BLOCK; if (*flags & MKD_LI_BLOCK) { /* intermediate render of block li */ if (sublist && sublist < work->size) { parse_block(inter, rndr, work->data, sublist); parse_block(inter, rndr, work->data + sublist, work->size - sublist); } else parse_block(inter, rndr, work->data, work->size); } else { /* intermediate render of inline li */ if (sublist && sublist < work->size) { parse_inline(inter, rndr, work->data, sublist); parse_block(inter, rndr, work->data + sublist, work->size - sublist); } else parse_inline(inter, rndr, work->data, work->size); } /* render of li itself */ if (rndr->make.listitem) rndr->make.listitem(ob, inter, *flags, rndr->make.opaque); release_work_buffer(rndr, inter); release_work_buffer(rndr, work); return beg; }