Exemplo n.º 1
0
// Free a cmark_node list and any children.
static
void S_free_nodes(cmark_node *e)
{
	cmark_node *next;
	while (e != NULL) {
		cmark_strbuf_free(&e->string_content);
		switch (e->type) {
		case NODE_CODE_BLOCK:
			cmark_chunk_free(&e->as.code.info);
			cmark_chunk_free(&e->as.code.literal);
			break;
		case NODE_TEXT:
		case NODE_INLINE_HTML:
		case NODE_CODE:
		case NODE_HTML:
			cmark_chunk_free(&e->as.literal);
			break;
		case NODE_LINK:
		case NODE_IMAGE:
			free(e->as.link.url);
			free(e->as.link.title);
			break;
		default:
			break;
		}
		if (e->last_child) {
			// Splice children into list
			e->last_child->next = e->next;
			e->next = e->first_child;
		}
		next = e->next;
		free(e);
		e = next;
	}
}
Exemplo n.º 2
0
static void process_emphasis(subject *subj, delimiter *start_delim)
{
	delimiter *closer = subj->last_delim;
	delimiter *opener;

	// move back to first relevant delim.
	while (closer != NULL && closer->previous != start_delim) {
		closer = closer->previous;
	}

	// now move forward, looking for closers, and handling each
	while (closer != NULL) {
		if (closer->can_close &&
		    (closer->delim_char == '*' || closer->delim_char == '_' ||
		     closer->delim_char == '"' || closer->delim_char == '\'')) {
			// Now look backwards for first matching opener:
			opener = closer->previous;
			while (opener != NULL && opener != start_delim) {
				if (opener->delim_char == closer->delim_char &&
				    opener->can_open) {
					break;
				}
				opener = opener->previous;
			}
			if (closer->delim_char == '*' || closer->delim_char == '_') {
				if (opener != NULL && opener != start_delim) {
					closer = S_insert_emph(subj, opener, closer);
				} else {
					closer = closer->next;
				}
			} else if (closer->delim_char == '\'') {
				cmark_chunk_free(&closer->inl_text->as.literal);
				closer->inl_text->as.literal =
					cmark_chunk_literal(RIGHTSINGLEQUOTE);
				if (opener != NULL && opener != start_delim) {
					cmark_chunk_free(&opener->inl_text->as.literal);
					opener->inl_text->as.literal =
						cmark_chunk_literal(LEFTSINGLEQUOTE);
				}
				closer = closer->next;
			} else if (closer->delim_char == '"') {
				cmark_chunk_free(&closer->inl_text->as.literal);
				closer->inl_text->as.literal =
					cmark_chunk_literal(RIGHTDOUBLEQUOTE);
				if (opener != NULL && opener != start_delim) {
					cmark_chunk_free(&opener->inl_text->as.literal);
					opener->inl_text->as.literal =
						cmark_chunk_literal(LEFTDOUBLEQUOTE);
				}
				closer = closer->next;
			}
		} else {
			closer = closer->next;
		}
	}
	// free all delimiters in list until start_delim:
	while (subj->last_delim != start_delim) {
		remove_delimiter(subj, subj->last_delim);
	}
}
Exemplo n.º 3
0
static void reference_free(cmark_reference_map *map, cmark_reference *ref) {
  cmark_mem *mem = map->mem;
  if (ref != NULL) {
    mem->free(ref->label);
    cmark_chunk_free(mem, &ref->url);
    cmark_chunk_free(mem, &ref->title);
    mem->free(ref);
  }
}
Exemplo n.º 4
0
void cmark_consolidate_text_nodes(cmark_node *root) {
  cmark_iter *iter = cmark_iter_new(root);
  cmark_strbuf buf = GH_BUF_INIT;
  cmark_event_type ev_type;
  cmark_node *cur, *tmp, *next;

  while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
    cur = cmark_iter_get_node(iter);
    if (ev_type == CMARK_EVENT_ENTER && cur->type == CMARK_NODE_TEXT &&
        cur->next && cur->next->type == CMARK_NODE_TEXT) {
      cmark_strbuf_clear(&buf);
      cmark_strbuf_put(&buf, cur->as.literal.data, cur->as.literal.len);
      tmp = cur->next;
      while (tmp && tmp->type == CMARK_NODE_TEXT) {
        cmark_iter_next(iter); // advance pointer
        cmark_strbuf_put(&buf, tmp->as.literal.data, tmp->as.literal.len);
        next = tmp->next;
        cmark_node_free(tmp);
        tmp = next;
      }
      cmark_chunk_free(&cur->as.literal);
      cur->as.literal = cmark_chunk_buf_detach(&buf);
    }
  }

  cmark_strbuf_free(&buf);
  cmark_iter_free(iter);
}
Exemplo n.º 5
0
// Free an inline list.  Avoid recursion to prevent stack overflows
// on deeply nested structures.
void cmark_free_inlines(cmark_node_inl* e)
{
	node_inl * next;

	while (e != NULL) {
		switch (e->tag){
		case CMARK_INL_STRING:
		case CMARK_INL_RAW_HTML:
		case CMARK_INL_CODE:
			cmark_chunk_free(&e->content.literal);
			break;
		case CMARK_INL_LINEBREAK:
		case CMARK_INL_SOFTBREAK:
			break;
		case CMARK_INL_LINK:
		case CMARK_INL_IMAGE:
			free(e->content.linkable.url);
			free(e->content.linkable.title);
			splice_into_list(e, e->content.linkable.label);
			break;
		case CMARK_INL_EMPH:
		case CMARK_INL_STRONG:
		        splice_into_list(e, e->content.inlines);
			break;
		default:
		        fprintf(stderr, "[WARN] (%s:%d) Unknown inline tag %d",
				__FILE__, __LINE__, e->tag);
			break;
		}
		next = e->next;
		free(e);
		e = next;
	}
}
Exemplo n.º 6
0
Arquivo: node.c Projeto: tin-pot/cmark
// Free a cmark_node list and any children.
static void S_free_nodes(cmark_node *e) {
  cmark_node *next;
  while (e != NULL) {
    cmark_strbuf_free(&e->content);
    switch (e->type) {
    case CMARK_NODE_CODE_BLOCK:
      cmark_chunk_free(NODE_MEM(e), &e->as.code.info);
      cmark_chunk_free(NODE_MEM(e), &e->as.code.literal);
      break;
    case CMARK_NODE_TEXT:
    case CMARK_NODE_HTML_INLINE:
    case CMARK_NODE_CODE:
    case CMARK_NODE_HTML_BLOCK:
      cmark_chunk_free(NODE_MEM(e), &e->as.literal);
      break;
    case CMARK_NODE_LINK:
    case CMARK_NODE_IMAGE:
      cmark_chunk_free(NODE_MEM(e), &e->as.link.url);
      cmark_chunk_free(NODE_MEM(e), &e->as.link.title);
      break;
    case CMARK_NODE_CUSTOM_BLOCK:
    case CMARK_NODE_CUSTOM_INLINE:
      cmark_chunk_free(NODE_MEM(e), &e->as.custom.on_enter);
      cmark_chunk_free(NODE_MEM(e), &e->as.custom.on_exit);
      break;
    default:
      break;
    }
    if (e->last_child) {
      // Splice children into list
      e->last_child->next = e->next;
      e->next = e->first_child;
    }
    next = e->next;
    NODE_MEM(e)->free(e);
    e = next;
  }
}
Exemplo n.º 7
0
// Return a link, an image, or a literal close bracket.
static cmark_node* handle_close_bracket(subject* subj, cmark_node *parent)
{
	int initial_pos;
	int starturl, endurl, starttitle, endtitle, endall;
	int n;
	int sps;
	cmark_reference *ref;
	bool is_image = false;
	cmark_chunk url_chunk, title_chunk;
	unsigned char *url, *title;
	delimiter *opener;
	cmark_node *link_text;
	cmark_node *inl;
	cmark_chunk raw_label;
	int found_label;

	advance(subj);  // advance past ]
	initial_pos = subj->pos;

	// look through list of delimiters for a [ or !
	opener = subj->last_delim;
	while (opener) {
		if (opener->delim_char == '[' || opener->delim_char == '!') {
			break;
		}
		opener = opener->previous;
	}

	if (opener == NULL) {
		return make_str(cmark_chunk_literal("]"));
	}

	if (!opener->active) {
		// take delimiter off stack
		remove_delimiter(subj, opener);
		return make_str(cmark_chunk_literal("]"));
	}

	// If we got here, we matched a potential link/image text.
	is_image = opener->delim_char == '!';
	link_text = opener->inl_text->next;

	// Now we check to see if it's a link/image.

	// First, look for an inline link.
	if (peek_char(subj) == '(' &&
	    ((sps = scan_spacechars(&subj->input, subj->pos + 1)) > -1) &&
	    ((n = scan_link_url(&subj->input, subj->pos + 1 + sps)) > -1)) {

		// try to parse an explicit link:
		starturl = subj->pos + 1 + sps; // after (
		endurl = starturl + 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;

			url_chunk = cmark_chunk_dup(&subj->input, starturl, endurl - starturl);
			title_chunk = cmark_chunk_dup(&subj->input, starttitle, endtitle - starttitle);
			url = cmark_clean_url(&url_chunk);
			title = cmark_clean_title(&title_chunk);
			cmark_chunk_free(&url_chunk);
			cmark_chunk_free(&title_chunk);
			goto match;

		} else {
			goto noMatch;
		}
	}

	// Next, look for a following [link label] that matches in refmap.
	// skip spaces
	subj->pos = subj->pos + scan_spacechars(&subj->input, subj->pos);
	raw_label = cmark_chunk_literal("");
	found_label = link_label(subj, &raw_label);
	if (!found_label || raw_label.len == 0) {
		cmark_chunk_free(&raw_label);
		raw_label = cmark_chunk_dup(&subj->input, opener->position,
		                            initial_pos - opener->position - 1);
	}

	if (!found_label) {
		// If we have a shortcut reference link, back up
		// to before the spacse we skipped.
		subj->pos = initial_pos;
	}

	ref = cmark_reference_lookup(subj->refmap, &raw_label);
	cmark_chunk_free(&raw_label);

	if (ref != NULL) { // found
		url = bufdup(ref->url);
		title = bufdup(ref->title);
		goto match;
	} else {
		goto noMatch;
	}

noMatch:
	// If we fall through to here, it means we didn't match a link:
	remove_delimiter(subj, opener);  // remove this opener from delimiter list
	subj->pos = initial_pos;
	return make_str(cmark_chunk_literal("]"));

match:
	inl = opener->inl_text;
	inl->type = is_image ? NODE_IMAGE : NODE_LINK;
	cmark_chunk_free(&inl->as.literal);
	inl->first_child = link_text;
	process_emphasis(subj, opener->previous);
	inl->as.link.url   = url;
	inl->as.link.title = title;
	inl->next = NULL;
	if (link_text) {
		cmark_node *tmp;
		link_text->prev = NULL;
		for (tmp = link_text; tmp->next != NULL; tmp = tmp->next) {
			tmp->parent = inl;
		}
		tmp->parent = inl;
		inl->last_child = tmp;
	}
	parent->last_child = inl;

	// process_emphasis will remove this delimiter and all later ones.
	// 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_delim;
		while (opener != NULL) {
			if (opener->delim_char == '[') {
				if (!opener->active) {
					break;
				} else {
					opener->active = false;
				}
			}
			opener = opener->previous;
		}
	}

	return NULL;
}
Exemplo n.º 8
0
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;
}
Exemplo n.º 9
0
static void S_out(cmark_renderer *renderer, cmark_node *node,
                  const char *source, bool wrap,
                  cmark_escaping escape) {
  int length = (int)strlen(source);
  unsigned char nextc;
  int32_t c;
  int i = 0;
  int last_nonspace;
  int len;
  cmark_chunk remainder = cmark_chunk_literal("");
  int k = renderer->buffer->size - 1;

  cmark_syntax_extension *ext = NULL;
  cmark_node *n = node;
  while (n && !ext) {
    ext = n->extension;
    if (!ext)
      n = n->parent;
  }
  if (ext && !ext->commonmark_escape_func)
    ext = NULL;

  wrap = wrap && !renderer->no_linebreaks;

  if (renderer->in_tight_list_item && renderer->need_cr > 1) {
    renderer->need_cr = 1;
  }
  while (renderer->need_cr) {
    if (k < 0 || renderer->buffer->ptr[k] == '\n') {
      k -= 1;
    } else {
      cmark_strbuf_putc(renderer->buffer, '\n');
      if (renderer->need_cr > 1) {
        cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
                         renderer->prefix->size);
      }
    }
    renderer->column = 0;
    renderer->begin_line = true;
    renderer->begin_content = true;
    renderer->need_cr -= 1;
  }

  while (i < length) {
    if (renderer->begin_line) {
      cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
                       renderer->prefix->size);
      // note: this assumes prefix is ascii:
      renderer->column = renderer->prefix->size;
    }

    len = cmark_utf8proc_iterate((const uint8_t *)source + i, length - i, &c);
    if (len == -1) { // error condition
      return;        // return without rendering rest of string
    }

    if (ext && ext->commonmark_escape_func(ext, node, c))
      cmark_strbuf_putc(renderer->buffer, '\\');

    nextc = source[i + len];
    if (c == 32 && wrap) {
      if (!renderer->begin_line) {
        last_nonspace = renderer->buffer->size;
        cmark_strbuf_putc(renderer->buffer, ' ');
        renderer->column += 1;
        renderer->begin_line = false;
        renderer->begin_content = false;
        // skip following spaces
        while (source[i + 1] == ' ') {
          i++;
        }
        // We don't allow breaks that make a digit the first character
        // because this causes problems with commonmark output.
        if (!cmark_isdigit(source[i + 1])) {
          renderer->last_breakable = last_nonspace;
        }
      }

    } else if (c == 10) {
      cmark_strbuf_putc(renderer->buffer, '\n');
      renderer->column = 0;
      renderer->begin_line = true;
      renderer->begin_content = true;
      renderer->last_breakable = 0;
    } else if (escape == LITERAL) {
      cmark_render_code_point(renderer, c);
      renderer->begin_line = false;
      // we don't set 'begin_content' to false til we've
      // finished parsing a digit.  Reason:  in commonmark
      // we need to escape a potential list marker after
      // a digit:
      renderer->begin_content =
          renderer->begin_content && cmark_isdigit((char)c) == 1;
    } else {
      (renderer->outc)(renderer, node, escape, c, nextc);
      renderer->begin_line = false;
      renderer->begin_content =
          renderer->begin_content && cmark_isdigit((char)c) == 1;
    }

    // If adding the character went beyond width, look for an
    // earlier place where the line could be broken:
    if (renderer->width > 0 && renderer->column > renderer->width &&
        !renderer->begin_line && renderer->last_breakable > 0) {

      // copy from last_breakable to remainder
      cmark_chunk_set_cstr(renderer->mem, &remainder,
                           (char *)renderer->buffer->ptr +
                               renderer->last_breakable + 1);
      // truncate at last_breakable
      cmark_strbuf_truncate(renderer->buffer, renderer->last_breakable);
      // add newline, prefix, and remainder
      cmark_strbuf_putc(renderer->buffer, '\n');
      cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
                       renderer->prefix->size);
      cmark_strbuf_put(renderer->buffer, remainder.data, remainder.len);
      renderer->column = renderer->prefix->size + remainder.len;
      cmark_chunk_free(renderer->mem, &remainder);
      renderer->last_breakable = 0;
      renderer->begin_line = false;
      renderer->begin_content = false;
    }

    i += len;
  }
}
Exemplo n.º 10
0
static
void S_out(cmark_renderer *renderer,
           const char *source,
           bool wrap,
           cmark_escaping escape)
{
	int length = cmark_strbuf_safe_strlen(source);
	unsigned char nextc;
	int32_t c;
	int i = 0;
	int len;
	cmark_chunk remainder = cmark_chunk_literal("");
	int k = renderer->buffer->size - 1;

	wrap = wrap && !renderer->no_wrap;

	if (renderer->in_tight_list_item && renderer->need_cr > 1) {
		renderer->need_cr = 1;
	}
	while (renderer->need_cr) {
		if (k < 0 || renderer->buffer->ptr[k] == '\n') {
			k -= 1;
		} else {
			cmark_strbuf_putc(renderer->buffer, '\n');
			if (renderer->need_cr > 1) {
				cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
				                 renderer->prefix->size);
			}
		}
		renderer->column = 0;
		renderer->begin_line = true;
		renderer->need_cr -= 1;
	}

	while (i < length) {
		if (renderer->begin_line) {
			cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
			                 renderer->prefix->size);
			// note: this assumes prefix is ascii:
			renderer->column = renderer->prefix->size;
		}

		len = utf8proc_iterate((const uint8_t *)source + i, length - i, &c);
		if (len == -1) { // error condition
			return;  // return without rendering rest of string
		}
		nextc = source[i + len];
		if (c == 32 && wrap) {
			if (!renderer->begin_line) {
				cmark_strbuf_putc(renderer->buffer, ' ');
				renderer->column += 1;
				renderer->begin_line = false;
				renderer->last_breakable = renderer->buffer->size -
				                           1;
				// skip following spaces
				while (source[i + 1] == ' ') {
					i++;
				}
			}

		} else if (c == 10) {
			cmark_strbuf_putc(renderer->buffer, '\n');
			renderer->column = 0;
			renderer->begin_line = true;
			renderer->last_breakable = 0;
		} else if (escape == LITERAL) {
			cmark_render_code_point(renderer, c);
			renderer->begin_line = false;
		} else {
			(renderer->outc)(renderer, escape, c, nextc);
			renderer->begin_line = false;
		}

		// If adding the character went beyond width, look for an
		// earlier place where the line could be broken:
		if (renderer->width > 0 &&
		    renderer->column > renderer->width &&
		    !renderer->begin_line &&
		    renderer->last_breakable > 0) {

			// copy from last_breakable to remainder
			cmark_chunk_set_cstr(&remainder, (char *) renderer->buffer->ptr + renderer->last_breakable + 1);
			// truncate at last_breakable
			cmark_strbuf_truncate(renderer->buffer, renderer->last_breakable);
			// add newline, prefix, and remainder
			cmark_strbuf_putc(renderer->buffer, '\n');
			cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
			                 renderer->prefix->size);
			cmark_strbuf_put(renderer->buffer, remainder.data, remainder.len);
			renderer->column = renderer->prefix->size + remainder.len;
			cmark_chunk_free(&remainder);
			renderer->last_breakable = 0;
			renderer->begin_line = false;
		}

		i += len;
	}
}
Exemplo n.º 11
0
// 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;
}
Exemplo n.º 12
0
static void process_emphasis(subject *subj, delimiter *stack_bottom) {
  delimiter *closer = subj->last_delim;
  delimiter *opener;
  delimiter *old_closer;
  bool opener_found;
  int openers_bottom_index;
  delimiter *openers_bottom[6] = {stack_bottom, stack_bottom, stack_bottom,
                                  stack_bottom, stack_bottom, stack_bottom};

  // move back to first relevant delim.
  while (closer != NULL && closer->previous != stack_bottom) {
    closer = closer->previous;
  }

  // now move forward, looking for closers, and handling each
  while (closer != NULL) {
    if (closer->can_close) {
      switch (closer->delim_char) {
      case '"':
        openers_bottom_index = 0;
        break;
      case '\'':
        openers_bottom_index = 1;
        break;
      case '_':
        openers_bottom_index = 2;
        break;
      case '*':
        openers_bottom_index = 3 + (closer->length % 3);
        break;
      default:
        assert(false);
      }

      // Now look backwards for first matching opener:
      opener = closer->previous;
      opener_found = false;
      while (opener != NULL && opener != openers_bottom[openers_bottom_index]) {
        if (opener->can_open && opener->delim_char == closer->delim_char) {
          // interior closer of size 2 can't match opener of size 1
          // or of size 1 can't match 2
          if (!(closer->can_open || opener->can_close) ||
              ((opener->length + closer->length) % 3) != 0) {
            opener_found = true;
            break;
          }
        }
        opener = opener->previous;
      }
      old_closer = closer;
      if (closer->delim_char == '*' || closer->delim_char == '_') {
        if (opener_found) {
          closer = S_insert_emph(subj, opener, closer);
        } else {
          closer = closer->next;
        }
      } else if (closer->delim_char == '\'') {
        cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
        closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE);
        if (opener_found) {
          cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
          opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE);
        }
        closer = closer->next;
      } else if (closer->delim_char == '"') {
        cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
        closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE);
        if (opener_found) {
          cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
          opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE);
        }
        closer = closer->next;
      }
      if (!opener_found) {
        // set lower bound for future searches for openers
        openers_bottom[openers_bottom_index] = old_closer->previous;
        if (!old_closer->can_open) {
          // we can remove a closer that can't be an
          // opener, once we've seen there's no
          // matching opener:
          remove_delimiter(subj, old_closer);
        }
      }
    } else {
      closer = closer->next;
    }
  }
  // free all delimiters in list until stack_bottom:
  while (subj->last_delim != NULL && subj->last_delim != stack_bottom) {
    remove_delimiter(subj, subj->last_delim);
  }
}
Exemplo n.º 13
0
static void process_emphasis(subject *subj, delimiter *stack_bottom) {
  delimiter *closer = subj->last_delim;
  delimiter *opener;
  delimiter *old_closer;
  bool opener_found;
  bool odd_match;
  delimiter *openers_bottom[128];

  // initialize openers_bottom:
  openers_bottom['*'] = stack_bottom;
  openers_bottom['_'] = stack_bottom;
  openers_bottom['\''] = stack_bottom;
  openers_bottom['"'] = stack_bottom;

  // move back to first relevant delim.
  while (closer != NULL && closer->previous != stack_bottom) {
    closer = closer->previous;
  }

  // now move forward, looking for closers, and handling each
  while (closer != NULL) {
    if (closer->can_close) {
      // Now look backwards for first matching opener:
      opener = closer->previous;
      opener_found = false;
      odd_match = false;
      while (opener != NULL && opener != stack_bottom &&
             opener != openers_bottom[closer->delim_char]) {
        // interior closer of size 2 can't match opener of size 1
        // or of size 1 can't match 2
        odd_match = (closer->can_open || opener->can_close) &&
                    ((opener->inl_text->as.literal.len +
                      closer->inl_text->as.literal.len) %
                         3 ==
                     0);
        if (opener->delim_char == closer->delim_char && opener->can_open &&
            !odd_match) {
          opener_found = true;
          break;
        }
        opener = opener->previous;
      }
      old_closer = closer;
      if (closer->delim_char == '*' || closer->delim_char == '_') {
        if (opener_found) {
          closer = S_insert_emph(subj, opener, closer);
        } else {
          closer = closer->next;
        }
      } else if (closer->delim_char == '\'') {
        cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
        closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE);
        if (opener_found) {
          cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
          opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE);
        }
        closer = closer->next;
      } else if (closer->delim_char == '"') {
        cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
        closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE);
        if (opener_found) {
          cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
          opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE);
        }
        closer = closer->next;
      }
      if (!opener_found && !odd_match) {
        // set lower bound for future searches for openers
        // (we don't do this with 'odd_match' set because
        // a ** that didn't match an earlier * might turn into
        // an opener, and the * might be matched by something
        // else.
        openers_bottom[old_closer->delim_char] = old_closer->previous;
        if (!old_closer->can_open) {
          // we can remove a closer that can't be an
          // opener, once we've seen there's no
          // matching opener:
          remove_delimiter(subj, old_closer);
        }
      }
    } else {
      closer = closer->next;
    }
  }
  // free all delimiters in list until stack_bottom:
  while (subj->last_delim != stack_bottom) {
    remove_delimiter(subj, subj->last_delim);
  }
}
Exemplo n.º 14
0
static void process_emphasis(subject *subj, delimiter *stack_bottom)
{
	delimiter *closer = subj->last_delim;
	delimiter *opener;
	delimiter *old_closer;
	bool opener_found;
	delimiter *openers_bottom[128];

	// initialize openers_bottom:
	openers_bottom['*'] = stack_bottom;
	openers_bottom['_'] = stack_bottom;
	openers_bottom['\''] = stack_bottom;
	openers_bottom['"'] = stack_bottom;

	// move back to first relevant delim.
	while (closer != NULL && closer->previous != stack_bottom) {
		closer = closer->previous;
	}

	// now move forward, looking for closers, and handling each
	while (closer != NULL) {
		if (closer->can_close &&
		    (closer->delim_char == '*' || closer->delim_char == '_' ||
		     closer->delim_char == '"' || closer->delim_char == '\'')) {
			// Now look backwards for first matching opener:
			opener = closer->previous;
			opener_found = false;
			while (opener != NULL && opener != stack_bottom &&
			       opener != openers_bottom[closer->delim_char]) {
				if (opener->delim_char == closer->delim_char &&
				    opener->can_open) {
					opener_found = true;
					break;
				}
				opener = opener->previous;
			}
			old_closer = closer;
			if (closer->delim_char == '*' || closer->delim_char == '_') {
				if (opener_found) {
					closer = S_insert_emph(subj, opener, closer);
				} else {
					closer = closer->next;
				}
			} else if (closer->delim_char == '\'') {
				cmark_chunk_free(&closer->inl_text->as.literal);
				closer->inl_text->as.literal =
				    cmark_chunk_literal(RIGHTSINGLEQUOTE);
				if (opener_found) {
					cmark_chunk_free(&opener->inl_text->as.literal);
					opener->inl_text->as.literal =
					    cmark_chunk_literal(LEFTSINGLEQUOTE);
				}
				closer = closer->next;
			} else if (closer->delim_char == '"') {
				cmark_chunk_free(&closer->inl_text->as.literal);
				closer->inl_text->as.literal =
				    cmark_chunk_literal(RIGHTDOUBLEQUOTE);
				if (opener_found) {
					cmark_chunk_free(&opener->inl_text->as.literal);
					opener->inl_text->as.literal =
					    cmark_chunk_literal(LEFTDOUBLEQUOTE);
				}
				closer = closer->next;
			}
			if (!opener_found) {
				// set lower bound for future searches for openers:
				openers_bottom[old_closer->delim_char] = old_closer->previous;
				if (!old_closer->can_open) {
					// we can remove a closer that can't be an
					// opener, once we've seen there's no
					// matching opener:
					remove_delimiter(subj, old_closer);
				}
			}
		} else {
			closer = closer->next;
		}
	}
	// free all delimiters in list until stack_bottom:
	while (subj->last_delim != stack_bottom) {
		remove_delimiter(subj, subj->last_delim);
	}
}