예제 #1
0
/* 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; }
예제 #2
0
/* 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; }
예제 #3
0
/* 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; }
예제 #4
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; }
예제 #5
0
/* 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); }
예제 #6
0
/* 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; }
예제 #7
0
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);
}
예제 #8
0
/* 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; }
예제 #9
0
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);
}
예제 #10
0
/* 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; }
예제 #11
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; }
예제 #12
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; }
예제 #13
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; }
예제 #14
0
/* 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; }
예제 #15
0
/*	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; }