/* get_link_ref • extract referenced link and title from id */ static int get_link_ref(struct render *rndr, struct buf *link, struct buf *title, char * data, size_t size) { struct link_ref *lr; /* find the link from its id (stored temporarily in link) */ link->size = 0; if (build_ref_id(link, data, size) < 0) return -1; lr = arr_sorted_find(&rndr->refs, link, cmp_link_ref); if (!lr) return -1; /* fill the output buffers */ link->size = 0; if (lr->link) bufput(link, lr->link->data, lr->link->size); title->size = 0; if (lr->title) bufput(title, lr->title->data, lr->title->size); 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, link_b = 0, link_e = 0, title_b = 0, title_e = 0; struct buf *content = 0; struct buf *link = 0; struct buf *title = 0; struct buf *u_link = 0; size_t org_work_size = rndr->work.size; int text_has_nl = 0, ret = 0; /* checking whether the correct renderer exists */ if ((is_img && !rndr->make.image) || (!is_img && !rndr->make.link)) goto cleanup; /* 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++; else if (data[i] == ']') { level--; if (level <= 0) break; } } if (i >= size) goto cleanup; txt_e = i; i += 1; /* skip any amount of whitespace or newline */ /* (this is much more laxist than original markdown syntax) */ while (i < size && isspace(data[i])) i++; /* inline style link */ if (i < size && data[i] == '(') { /* skipping initial whitespace */ i += 1; while (i < size && isspace(data[i])) i++; link_b = i; /* looking for link end: ' " ) */ while (i < size) { if (data[i] == '\\') i += 2; else if (data[i] == ')' || data[i] == '\'' || data[i] == '"') break; else i += 1; } if (i >= size) goto cleanup; link_e = i; /* looking for title end if present */ if (data[i] == '\'' || data[i] == '"') { i++; title_b = i; while (i < size) { if (data[i] == '\\') i += 2; else if (data[i] == ')') break; else i += 1; } if (i >= size) goto cleanup; /* skipping whitespaces after title */ title_e = i - 1; while (title_e > title_b && isspace(data[title_e])) title_e--; /* checking for closing quote presence */ if (data[title_e] != '\'' && data[title_e] != '"') { title_b = title_e = 0; link_e = i; } } /* remove whitespace at the end of the link */ while (link_e > link_b && isspace(data[link_e - 1])) link_e--; /* remove optional angle brackets around the link */ if (data[link_b] == '<') link_b++; if (data[link_e - 1] == '>') link_e--; /* building escaped link and title */ if (link_e > link_b) { link = rndr_newbuf(rndr); bufput(link, data + link_b, link_e - link_b); } if (title_e > title_b) { title = rndr_newbuf(rndr); bufput(title, data + title_b, title_e - title_b); } i++; } /* reference style link */ else if (i < size && data[i] == '[') { struct buf id = { 0, 0, 0, 0, 0 }; struct link_ref *lr; /* looking for the id */ i += 1; link_b = i; while (i < size && data[i] != ']') i++; if (i >= size) goto cleanup; link_e = i; /* finding the link_ref */ if (link_b == link_e) { if (text_has_nl) { struct buf *b = rndr_newbuf(rndr); size_t j; for (j = 1; j < txt_e; j++) { if (data[j] != '\n') bufputc(b, data[j]); else if (data[j - 1] != ' ') bufputc(b, ' '); } id.data = b->data; id.size = b->size; } else { id.data = data + 1; id.size = txt_e - 1; } } else { id.data = data + link_b; id.size = link_e - link_b; } lr = arr_sorted_find(&rndr->refs, &id, cmp_link_ref); if (!lr) goto cleanup; /* keeping link and title from link_ref */ link = lr->link; title = lr->title; i += 1; } /* shortcut reference style link */ else { struct buf id = { 0, 0, 0, 0, 0 }; struct link_ref *lr; /* crafting the id */ if (text_has_nl) { struct buf *b = rndr_newbuf(rndr); size_t j; for (j = 1; j < txt_e; j++) { if (data[j] != '\n') bufputc(b, data[j]); else if (data[j - 1] != ' ') bufputc(b, ' '); } id.data = b->data; id.size = b->size; } else { id.data = data + 1; id.size = txt_e - 1; } /* finding the link_ref */ lr = arr_sorted_find(&rndr->refs, &id, cmp_link_ref); if (!lr) goto cleanup; /* keeping link and title from link_ref */ link = lr->link; title = lr->title; /* rewinding the whitespace */ i = txt_e + 1; } /* building content: img alt is escaped, link content is parsed */ if (txt_e > 1) { content = rndr_newbuf(rndr); if (is_img) bufput(content, data + 1, txt_e - 1); else parse_inline(content, rndr, data + 1, txt_e - 1); } if (link) { u_link = rndr_newbuf(rndr); unscape_text(u_link, link); } /* calling the relevant rendering function */ if (is_img) { if (ob->size && ob->data[ob->size - 1] == '!') ob->size -= 1; ret = rndr->make.image(ob, u_link, title, content, rndr->make.opaque); } else { ret = rndr->make.link(ob, u_link, title, content, rndr->make.opaque); } /* cleanup */ cleanup: rndr->work.size = (int)org_work_size; return ret ? i : 0; }