Skip to content

Instantly share code, notes, and snippets.

@kjdev
Last active August 29, 2015 14:17
Show Gist options
  • Save kjdev/2ecc58afe95e9ffbf105 to your computer and use it in GitHub Desktop.
Save kjdev/2ecc58afe95e9ffbf105 to your computer and use it in GitHub Desktop.
Phalcon 2.0.0 patch: Phalcon\Mvc\View\Engine\Markdown
diff --git a/config.json b/config.json
index 8183eec..b5cf118 100644
--- a/config.json
+++ b/config.json
@@ -28,6 +28,16 @@
"phalcon/mvc/model/query/parser.c",
"phalcon/mvc/view/engine/volt/parser.c",
"phalcon/mvc/view/engine/volt/scanner.c",
+ "phalcon/mvc/view/engine/markdown/parser.c",
+ "phalcon/mvc/view/engine/markdown/autolink.c",
+ "phalcon/mvc/view/engine/markdown/escape.c",
+ "phalcon/mvc/view/engine/markdown/html_smartypants.c",
+ "phalcon/mvc/view/engine/markdown/buffer.c",
+ "phalcon/mvc/view/engine/markdown/html.c",
+ "phalcon/mvc/view/engine/markdown/stack.c",
+ "phalcon/mvc/view/engine/markdown/document.c",
+ "phalcon/mvc/view/engine/markdown/html_blocks.c",
+ "phalcon/mvc/view/engine/markdown/hash.c",
"phalcon/assets/filters/jsminifier.c",
"phalcon/assets/filters/cssminifier.c",
"phalcon/mvc/url/utils.c"
diff --git a/ext/phalcon/mvc/view/engine/markdown/autolink.c b/ext/phalcon/mvc/view/engine/markdown/autolink.c
new file mode 100644
index 0000000..7142094
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/autolink.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "autolink.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#ifndef _MSC_VER
+#include <strings.h>
+#else
+#define strncasecmp _strnicmp
+#endif
+
+int
+hoedown_autolink_is_safe(const uint8_t *data, size_t size)
+{
+ static const size_t valid_uris_count = 6;
+ static const char *valid_uris[] = {
+ "http://", "https://", "/", "#", "ftp://", "mailto:"
+ };
+ static const size_t valid_uris_size[] = { 7, 8, 1, 1, 6, 7 };
+ size_t i;
+
+ for (i = 0; i < valid_uris_count; ++i) {
+ size_t len = valid_uris_size[i];
+
+ if (size > len &&
+ strncasecmp((char *)data, valid_uris[i], len) == 0 &&
+ isalnum(data[len]))
+ return 1;
+ }
+
+ return 0;
+}
+
+static size_t
+autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
+{
+ uint8_t cclose, copen = 0;
+ size_t i;
+
+ for (i = 0; i < link_end; ++i)
+ if (data[i] == '<') {
+ link_end = i;
+ break;
+ }
+
+ while (link_end > 0) {
+ if (strchr("?!.,:", data[link_end - 1]) != NULL)
+ link_end--;
+
+ else if (data[link_end - 1] == ';') {
+ size_t new_end = link_end - 2;
+
+ while (new_end > 0 && isalpha(data[new_end]))
+ new_end--;
+
+ if (new_end < link_end - 2 && data[new_end] == '&')
+ link_end = new_end;
+ else
+ link_end--;
+ }
+ else break;
+ }
+
+ if (link_end == 0)
+ return 0;
+
+ cclose = data[link_end - 1];
+
+ switch (cclose) {
+ case '"': copen = '"'; break;
+ case '\'': copen = '\''; break;
+ case ')': copen = '('; break;
+ case ']': copen = '['; break;
+ case '}': copen = '{'; break;
+ }
+
+ if (copen != 0) {
+ size_t closing = 0;
+ size_t opening = 0;
+ size_t i = 0;
+
+ /* Try to close the final punctuation sign in this same line;
+ * if we managed to close it outside of the URL, that means that it's
+ * not part of the URL. If it closes inside the URL, that means it
+ * is part of the URL.
+ *
+ * Examples:
+ *
+ * foo http://www.pokemon.com/Pikachu_(Electric) bar
+ * => http://www.pokemon.com/Pikachu_(Electric)
+ *
+ * foo (http://www.pokemon.com/Pikachu_(Electric)) bar
+ * => http://www.pokemon.com/Pikachu_(Electric)
+ *
+ * foo http://www.pokemon.com/Pikachu_(Electric)) bar
+ * => http://www.pokemon.com/Pikachu_(Electric))
+ *
+ * (foo http://www.pokemon.com/Pikachu_(Electric)) bar
+ * => foo http://www.pokemon.com/Pikachu_(Electric)
+ */
+
+ while (i < link_end) {
+ if (data[i] == copen)
+ opening++;
+ else if (data[i] == cclose)
+ closing++;
+
+ i++;
+ }
+
+ if (closing != opening)
+ link_end--;
+ }
+
+ return link_end;
+}
+
+static size_t
+check_domain(uint8_t *data, size_t size, int allow_short)
+{
+ size_t i, np = 0;
+
+ if (!isalnum(data[0]))
+ return 0;
+
+ for (i = 1; i < size - 1; ++i) {
+ if (strchr(".:", data[i]) != NULL) np++;
+ else if (!isalnum(data[i]) && data[i] != '-') break;
+ }
+
+ if (allow_short) {
+ /* We don't need a valid domain in the strict sense (with
+ * least one dot; so just make sure it's composed of valid
+ * domain characters and return the length of the the valid
+ * sequence. */
+ return i;
+ } else {
+ /* a valid domain needs to have at least a dot.
+ * that's as far as we get */
+ return np ? i : 0;
+ }
+}
+
+size_t
+hoedown_autolink__www(
+ size_t *rewind_p,
+ hoedown_buffer *link,
+ uint8_t *data,
+ size_t max_rewind,
+ size_t size,
+ unsigned int flags)
+{
+ size_t link_end;
+
+ if (max_rewind > 0 && !ispunct(data[-1]) && !isspace(data[-1]))
+ return 0;
+
+ if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)
+ return 0;
+
+ link_end = check_domain(data, size, 0);
+
+ if (link_end == 0)
+ return 0;
+
+ while (link_end < size && !isspace(data[link_end]))
+ link_end++;
+
+ link_end = autolink_delim(data, link_end, max_rewind, size);
+
+ if (link_end == 0)
+ return 0;
+
+ hoedown_buffer_put(link, data, link_end);
+ *rewind_p = 0;
+
+ return (int)link_end;
+}
+
+size_t
+hoedown_autolink__email(
+ size_t *rewind_p,
+ hoedown_buffer *link,
+ uint8_t *data,
+ size_t max_rewind,
+ size_t size,
+ unsigned int flags)
+{
+ size_t link_end, rewind;
+ int nb = 0, np = 0;
+
+ for (rewind = 0; rewind < max_rewind; ++rewind) {
+ uint8_t c = data[-1 - rewind];
+
+ if (isalnum(c))
+ continue;
+
+ if (strchr(".+-_", c) != NULL)
+ continue;
+
+ break;
+ }
+
+ if (rewind == 0)
+ return 0;
+
+ for (link_end = 0; link_end < size; ++link_end) {
+ uint8_t c = data[link_end];
+
+ if (isalnum(c))
+ continue;
+
+ if (c == '@')
+ nb++;
+ else if (c == '.' && link_end < size - 1)
+ np++;
+ else if (c != '-' && c != '_')
+ break;
+ }
+
+ if (link_end < 2 || nb != 1 || np == 0 ||
+ !isalpha(data[link_end - 1]))
+ return 0;
+
+ link_end = autolink_delim(data, link_end, max_rewind, size);
+
+ if (link_end == 0)
+ return 0;
+
+ hoedown_buffer_put(link, data - rewind, link_end + rewind);
+ *rewind_p = rewind;
+
+ return link_end;
+}
+
+size_t
+hoedown_autolink__url(
+ size_t *rewind_p,
+ hoedown_buffer *link,
+ uint8_t *data,
+ size_t max_rewind,
+ size_t size,
+ unsigned int flags)
+{
+ size_t link_end, rewind = 0, domain_len;
+
+ if (size < 4 || data[1] != '/' || data[2] != '/')
+ return 0;
+
+ while (rewind < max_rewind && isalpha(data[-1 - rewind]))
+ rewind++;
+
+ if (!hoedown_autolink_is_safe(data - rewind, size + rewind))
+ return 0;
+
+ link_end = strlen("://");
+
+ domain_len = check_domain(
+ data + link_end,
+ size - link_end,
+ flags & HOEDOWN_AUTOLINK_SHORT_DOMAINS);
+
+ if (domain_len == 0)
+ return 0;
+
+ link_end += domain_len;
+ while (link_end < size && !isspace(data[link_end]))
+ link_end++;
+
+ link_end = autolink_delim(data, link_end, max_rewind, size);
+
+ if (link_end == 0)
+ return 0;
+
+ hoedown_buffer_put(link, data - rewind, link_end + rewind);
+ *rewind_p = rewind;
+
+ return link_end;
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/autolink.h b/ext/phalcon/mvc/view/engine/markdown/autolink.h
new file mode 100644
index 0000000..55cb49b
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/autolink.h
@@ -0,0 +1,64 @@
+/* autolink.h - versatile autolinker */
+
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HOEDOWN_AUTOLINK_H
+#define HOEDOWN_AUTOLINK_H
+
+#include "buffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************
+ * CONSTANTS *
+ *************/
+
+typedef enum hoedown_autolink_flags {
+ HOEDOWN_AUTOLINK_SHORT_DOMAINS = (1 << 0)
+} hoedown_autolink_flags;
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_autolink_is_safe: verify that a URL has a safe protocol */
+int hoedown_autolink_is_safe(const uint8_t *data, size_t size);
+
+/* hoedown_autolink__www: search for the next www link in data */
+size_t hoedown_autolink__www(size_t *rewind_p, hoedown_buffer *link,
+ uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags);
+
+/* hoedown_autolink__email: search for the next email in data */
+size_t hoedown_autolink__email(size_t *rewind_p, hoedown_buffer *link,
+ uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags);
+
+/* hoedown_autolink__url: search for the next URL in data */
+size_t hoedown_autolink__url(size_t *rewind_p, hoedown_buffer *link,
+ uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** HOEDOWN_AUTOLINK_H **/
diff --git a/ext/phalcon/mvc/view/engine/markdown/buffer.c b/ext/phalcon/mvc/view/engine/markdown/buffer.c
new file mode 100644
index 0000000..ca1e516
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/buffer.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "buffer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+void *
+hoedown_malloc(size_t size)
+{
+ void *ret = malloc(size);
+
+ if (!ret) {
+ fprintf(stderr, "Allocation failed.\n");
+ abort();
+ }
+
+ return ret;
+}
+
+void *
+hoedown_calloc(size_t nmemb, size_t size)
+{
+ void *ret = calloc(nmemb, size);
+
+ if (!ret) {
+ fprintf(stderr, "Allocation failed.\n");
+ abort();
+ }
+
+ return ret;
+}
+
+void *
+hoedown_realloc(void *ptr, size_t size)
+{
+ void *ret = realloc(ptr, size);
+
+ if (!ret) {
+ fprintf(stderr, "Allocation failed.\n");
+ abort();
+ }
+
+ return ret;
+}
+
+void
+hoedown_buffer_init(
+ hoedown_buffer *buf,
+ size_t unit,
+ hoedown_realloc_callback data_realloc,
+ hoedown_free_callback data_free,
+ hoedown_free_callback buffer_free)
+{
+ assert(buf);
+
+ buf->data = NULL;
+ buf->size = buf->asize = 0;
+ buf->unit = unit;
+ buf->data_realloc = data_realloc;
+ buf->data_free = data_free;
+ buf->buffer_free = buffer_free;
+}
+
+hoedown_buffer *
+hoedown_buffer_new(size_t unit)
+{
+ hoedown_buffer *ret = hoedown_malloc(sizeof (hoedown_buffer));
+ hoedown_buffer_init(ret, unit, hoedown_realloc, free, free);
+ return ret;
+}
+
+void
+hoedown_buffer_free(hoedown_buffer *buf)
+{
+ if (!buf) return;
+
+ buf->data_free(buf->data);
+
+ if (buf->buffer_free)
+ buf->buffer_free(buf);
+}
+
+void
+hoedown_buffer_reset(hoedown_buffer *buf)
+{
+ assert(buf && buf->unit);
+
+ buf->data_free(buf->data);
+ buf->data = NULL;
+ buf->size = buf->asize = 0;
+}
+
+void
+hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz)
+{
+ size_t neoasz;
+ assert(buf && buf->unit);
+
+ if (buf->asize >= neosz)
+ return;
+
+ neoasz = buf->asize + buf->unit;
+ while (neoasz < neosz)
+ neoasz += buf->unit;
+
+ buf->data = buf->data_realloc(buf->data, neoasz);
+ buf->asize = neoasz;
+}
+
+void
+hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size)
+{
+ assert(buf && buf->unit);
+
+ if (buf->size + size > buf->asize)
+ hoedown_buffer_grow(buf, buf->size + size);
+
+ memcpy(buf->data + buf->size, data, size);
+ buf->size += size;
+}
+
+void
+hoedown_buffer_puts(hoedown_buffer *buf, const char *str)
+{
+ hoedown_buffer_put(buf, (const uint8_t *)str, strlen(str));
+}
+
+void
+hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c)
+{
+ assert(buf && buf->unit);
+
+ if (buf->size >= buf->asize)
+ hoedown_buffer_grow(buf, buf->size + 1);
+
+ buf->data[buf->size] = c;
+ buf->size += 1;
+}
+
+void
+hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size)
+{
+ assert(buf && buf->unit);
+
+ if (size > buf->asize)
+ hoedown_buffer_grow(buf, size);
+
+ memcpy(buf->data, data, size);
+ buf->size = size;
+}
+
+void
+hoedown_buffer_sets(hoedown_buffer *buf, const char *str)
+{
+ hoedown_buffer_set(buf, (const uint8_t *)str, strlen(str));
+}
+
+int
+hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size)
+{
+ if (buf->size != size) return 0;
+ return memcmp(buf->data, data, size) == 0;
+}
+
+int
+hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str)
+{
+ return hoedown_buffer_eq(buf, (const uint8_t *)str, strlen(str));
+}
+
+int
+hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix)
+{
+ size_t i;
+
+ assert(buf && buf->unit);
+
+ for (i = 0; i < buf->size; ++i) {
+ if (prefix[i] == 0)
+ return 0;
+
+ if (buf->data[i] != prefix[i])
+ return buf->data[i] - prefix[i];
+ }
+
+ return 0;
+}
+
+void
+hoedown_buffer_slurp(hoedown_buffer *buf, size_t size)
+{
+ assert(buf && buf->unit);
+
+ if (size >= buf->size) {
+ buf->size = 0;
+ return;
+ }
+
+ buf->size -= size;
+ memmove(buf->data, buf->data + size, buf->size);
+}
+
+const char *
+hoedown_buffer_cstr(hoedown_buffer *buf)
+{
+ assert(buf && buf->unit);
+
+ if (buf->size < buf->asize && buf->data[buf->size] == 0)
+ return (char *)buf->data;
+
+ hoedown_buffer_grow(buf, buf->size + 1);
+ buf->data[buf->size] = 0;
+
+ return (char *)buf->data;
+}
+
+void
+hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ assert(buf && buf->unit);
+
+ if (buf->size >= buf->asize)
+ hoedown_buffer_grow(buf, buf->size + 1);
+
+ va_start(ap, fmt);
+ n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
+ va_end(ap);
+
+ if (n < 0) {
+#ifndef _MSC_VER
+ return;
+#else
+ va_start(ap, fmt);
+ n = _vscprintf(fmt, ap);
+ va_end(ap);
+#endif
+ }
+
+ if ((size_t)n >= buf->asize - buf->size) {
+ hoedown_buffer_grow(buf, buf->size + n + 1);
+
+ va_start(ap, fmt);
+ n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
+ va_end(ap);
+ }
+
+ if (n < 0)
+ return;
+
+ buf->size += n;
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/buffer.h b/ext/phalcon/mvc/view/engine/markdown/buffer.h
new file mode 100644
index 0000000..162ac28
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/buffer.h
@@ -0,0 +1,143 @@
+/* buffer.h - simple, fast buffers */
+
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HOEDOWN_BUFFER_H
+#define HOEDOWN_BUFFER_H
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_MSC_VER)
+#define __attribute__(x)
+#define inline __inline
+#define __builtin_expect(x,n) x
+#endif
+
+
+/*********
+ * TYPES *
+ *********/
+
+typedef void *(*hoedown_realloc_callback)(void *, size_t);
+typedef void (*hoedown_free_callback)(void *);
+
+struct hoedown_buffer {
+ uint8_t *data; /* actual character data */
+ size_t size; /* size of the string */
+ size_t asize; /* allocated size (0 = volatile buffer) */
+ size_t unit; /* reallocation unit size (0 = read-only buffer) */
+
+ hoedown_realloc_callback data_realloc;
+ hoedown_free_callback data_free;
+ hoedown_free_callback buffer_free;
+};
+
+typedef struct hoedown_buffer hoedown_buffer;
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* allocation wrappers */
+void *hoedown_malloc(size_t size) __attribute__ ((malloc));
+void *hoedown_calloc(size_t nmemb, size_t size) __attribute__ ((malloc));
+void *hoedown_realloc(void *ptr, size_t size) __attribute__ ((malloc));
+
+/* hoedown_buffer_init: initialize a buffer with custom allocators */
+void hoedown_buffer_init(
+ hoedown_buffer *buffer,
+ size_t unit,
+ hoedown_realloc_callback data_realloc,
+ hoedown_free_callback data_free,
+ hoedown_free_callback buffer_free
+);
+
+/* hoedown_buffer_new: allocate a new buffer */
+hoedown_buffer *hoedown_buffer_new(size_t unit) __attribute__ ((malloc));
+
+/* hoedown_buffer_reset: free internal data of the buffer */
+void hoedown_buffer_reset(hoedown_buffer *buf);
+
+/* hoedown_buffer_grow: increase the allocated size to the given value */
+void hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz);
+
+/* hoedown_buffer_put: append raw data to a buffer */
+void hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size);
+
+/* hoedown_buffer_puts: append a NUL-terminated string to a buffer */
+void hoedown_buffer_puts(hoedown_buffer *buf, const char *str);
+
+/* hoedown_buffer_putc: append a single char to a buffer */
+void hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c);
+
+/* hoedown_buffer_set: replace the buffer's contents with raw data */
+void hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size);
+
+/* hoedown_buffer_sets: replace the buffer's contents with a NUL-terminated string */
+void hoedown_buffer_sets(hoedown_buffer *buf, const char *str);
+
+/* hoedown_buffer_eq: compare a buffer's data with other data for equality */
+int hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size);
+
+/* hoedown_buffer_eq: compare a buffer's data with NUL-terminated string for equality */
+int hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str);
+
+/* hoedown_buffer_prefix: compare the beginning of a buffer with a string */
+int hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix);
+
+/* hoedown_buffer_slurp: remove a given number of bytes from the head of the buffer */
+void hoedown_buffer_slurp(hoedown_buffer *buf, size_t size);
+
+/* hoedown_buffer_cstr: NUL-termination of the string array (making a C-string) */
+const char *hoedown_buffer_cstr(hoedown_buffer *buf);
+
+/* hoedown_buffer_printf: formatted printing to a buffer */
+void hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+/* hoedown_buffer_free: free the buffer */
+void hoedown_buffer_free(hoedown_buffer *buf);
+
+
+/* HOEDOWN_BUFPUTSL: optimized hoedown_buffer_puts of a string literal */
+#define HOEDOWN_BUFPUTSL(output, literal) \
+ hoedown_buffer_put(output, (const uint8_t *)literal, sizeof(literal) - 1)
+
+/* HOEDOWN_BUFSETSL: optimized hoedown_buffer_sets of a string literal */
+#define HOEDOWN_BUFSETSL(output, literal) \
+ hoedown_buffer_set(output, (const uint8_t *)literal, sizeof(literal) - 1)
+
+/* HOEDOWN_BUFEQSL: optimized hoedown_buffer_eqs of a string literal */
+#define HOEDOWN_BUFEQSL(output, literal) \
+ hoedown_buffer_eq(output, (const uint8_t *)literal, sizeof(literal) - 1)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** HOEDOWN_BUFFER_H **/
diff --git a/ext/phalcon/mvc/view/engine/markdown/document.c b/ext/phalcon/mvc/view/engine/markdown/document.c
new file mode 100644
index 0000000..f4cad0b
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/document.c
@@ -0,0 +1,3384 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "document.h"
+
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "stack.h"
+
+#ifndef _MSC_VER
+#include <strings.h>
+#else
+#define strncasecmp _strnicmp
+#endif
+
+#define REF_TABLE_SIZE 8
+
+#define BUFFER_BLOCK 0
+#define BUFFER_SPAN 1
+#define BUFFER_ATTRIBUTE 2
+
+const char *hoedown_find_block_tag(const char *str, unsigned int len);
+
+/***************
+ * LOCAL TYPES *
+ ***************/
+
+/* link_ref: reference to a link */
+struct link_ref {
+ unsigned int id;
+
+ hoedown_buffer *link;
+ hoedown_buffer *title;
+ hoedown_buffer *attr;
+
+ struct link_ref *next;
+};
+
+/* footnote_ref: reference to a footnote */
+struct footnote_ref {
+ unsigned int id;
+
+ int is_used;
+ unsigned int num;
+
+ hoedown_buffer *contents;
+};
+
+/* footnote_item: an item in a footnote_list */
+struct footnote_item {
+ struct footnote_ref *ref;
+ struct footnote_item *next;
+};
+
+/* footnote_list: linked list of footnote_item */
+struct footnote_list {
+ unsigned int count;
+ struct footnote_item *head;
+ struct footnote_item *tail;
+};
+
+/* char_trigger: function pointer to render active chars */
+/* returns the number of chars taken care of */
+/* data is the pointer of the beginning of the span */
+/* offset is the number of valid chars before data */
+typedef size_t
+(*char_trigger)(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+
+static size_t char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+static size_t char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size);
+
+enum markdown_char_t {
+ MD_CHAR_NONE = 0,
+ MD_CHAR_EMPHASIS,
+ MD_CHAR_CODESPAN,
+ MD_CHAR_LINEBREAK,
+ MD_CHAR_LINK,
+ MD_CHAR_LANGLE,
+ MD_CHAR_ESCAPE,
+ MD_CHAR_ENTITY,
+ MD_CHAR_AUTOLINK_URL,
+ MD_CHAR_AUTOLINK_EMAIL,
+ MD_CHAR_AUTOLINK_WWW,
+ MD_CHAR_SUPERSCRIPT,
+ MD_CHAR_QUOTE,
+ MD_CHAR_MATH
+};
+
+static char_trigger markdown_char_ptrs[] = {
+ NULL,
+ &char_emphasis,
+ &char_codespan,
+ &char_linebreak,
+ &char_link,
+ &char_langle_tag,
+ &char_escape,
+ &char_entity,
+ &char_autolink_url,
+ &char_autolink_email,
+ &char_autolink_www,
+ &char_superscript,
+ &char_quote,
+ &char_math
+};
+
+struct hoedown_document {
+ hoedown_renderer md;
+ hoedown_renderer_data data;
+
+ struct link_ref *refs[REF_TABLE_SIZE];
+ struct footnote_list footnotes_found;
+ struct footnote_list footnotes_used;
+ uint8_t active_char[256];
+ hoedown_stack work_bufs[3];
+ hoedown_extensions ext_flags;
+ size_t max_nesting;
+ int in_link_body;
+
+ hoedown_user_block user_block;
+ hoedown_buffer *meta;
+};
+
+/***************************
+ * HELPER FUNCTIONS *
+ ***************************/
+
+static hoedown_buffer *
+newbuf(hoedown_document *doc, int type)
+{
+ static const size_t buf_size[3] = {256, 64, 64};
+ hoedown_buffer *work = NULL;
+ hoedown_stack *pool = &doc->work_bufs[type];
+
+ if (pool->size < pool->asize &&
+ pool->item[pool->size] != NULL) {
+ work = pool->item[pool->size++];
+ work->size = 0;
+ } else {
+ work = hoedown_buffer_new(buf_size[type]);
+ hoedown_stack_push(pool, work);
+ }
+
+ return work;
+}
+
+static void
+popbuf(hoedown_document *doc, int type)
+{
+ doc->work_bufs[type].size--;
+}
+
+static void
+unscape_text(hoedown_buffer *ob, hoedown_buffer *src)
+{
+ size_t i = 0, org;
+ while (i < src->size) {
+ org = i;
+ while (i < src->size && src->data[i] != '\\')
+ i++;
+
+ if (i > org)
+ hoedown_buffer_put(ob, src->data + org, i - org);
+
+ if (i + 1 >= src->size)
+ break;
+
+ hoedown_buffer_putc(ob, src->data[i + 1]);
+ i += 2;
+ }
+}
+
+static unsigned int
+hash_link_ref(const uint8_t *link_ref, size_t length)
+{
+ size_t i;
+ unsigned int hash = 0;
+
+ for (i = 0; i < length; ++i)
+ hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;
+
+ return hash;
+}
+
+static struct link_ref *
+add_link_ref(
+ struct link_ref **references,
+ const uint8_t *name, size_t name_size)
+{
+ struct link_ref *ref = hoedown_calloc(1, sizeof(struct link_ref));
+
+ ref->id = hash_link_ref(name, name_size);
+ ref->next = references[ref->id % REF_TABLE_SIZE];
+
+ references[ref->id % REF_TABLE_SIZE] = ref;
+ return ref;
+}
+
+static struct link_ref *
+find_link_ref(struct link_ref **references, uint8_t *name, size_t length)
+{
+ unsigned int hash = hash_link_ref(name, length);
+ struct link_ref *ref = NULL;
+
+ ref = references[hash % REF_TABLE_SIZE];
+
+ while (ref != NULL) {
+ if (ref->id == hash)
+ return ref;
+
+ ref = ref->next;
+ }
+
+ return NULL;
+}
+
+static void
+free_link_refs(struct link_ref **references)
+{
+ size_t i;
+
+ for (i = 0; i < REF_TABLE_SIZE; ++i) {
+ struct link_ref *r = references[i];
+ struct link_ref *next;
+
+ while (r) {
+ next = r->next;
+ hoedown_buffer_free(r->link);
+ hoedown_buffer_free(r->title);
+ hoedown_buffer_free(r->attr);
+ free(r);
+ r = next;
+ }
+ }
+}
+
+static struct footnote_ref *
+create_footnote_ref(struct footnote_list *list, const uint8_t *name, size_t name_size)
+{
+ struct footnote_ref *ref = hoedown_calloc(1, sizeof(struct footnote_ref));
+
+ ref->id = hash_link_ref(name, name_size);
+
+ return ref;
+}
+
+static int
+add_footnote_ref(struct footnote_list *list, struct footnote_ref *ref)
+{
+ struct footnote_item *item = hoedown_calloc(1, sizeof(struct footnote_item));
+ if (!item)
+ return 0;
+ item->ref = ref;
+
+ if (list->head == NULL) {
+ list->head = list->tail = item;
+ } else {
+ list->tail->next = item;
+ list->tail = item;
+ }
+ list->count++;
+
+ return 1;
+}
+
+static struct footnote_ref *
+find_footnote_ref(struct footnote_list *list, uint8_t *name, size_t length)
+{
+ unsigned int hash = hash_link_ref(name, length);
+ struct footnote_item *item = NULL;
+
+ item = list->head;
+
+ while (item != NULL) {
+ if (item->ref->id == hash)
+ return item->ref;
+ item = item->next;
+ }
+
+ return NULL;
+}
+
+static void
+free_footnote_ref(struct footnote_ref *ref)
+{
+ hoedown_buffer_free(ref->contents);
+ free(ref);
+}
+
+static void
+free_footnote_list(struct footnote_list *list, int free_refs)
+{
+ struct footnote_item *item = list->head;
+ struct footnote_item *next;
+
+ while (item) {
+ next = item->next;
+ if (free_refs)
+ free_footnote_ref(item->ref);
+ free(item);
+ item = next;
+ }
+}
+
+
+/*
+ * Check whether a char is a Markdown spacing char.
+
+ * Right now we only consider spaces the actual
+ * space and a newline: tabs and carriage returns
+ * are filtered out during the preprocessing phase.
+ *
+ * If we wanted to actually be UTF-8 compliant, we
+ * should instead extract an Unicode codepoint from
+ * this character and check for space properties.
+ */
+static int
+_isspace(int c)
+{
+ return c == ' ' || c == '\n';
+}
+
+/* is_empty_all: verify that all the data is spacing */
+static int
+is_empty_all(const uint8_t *data, size_t size)
+{
+ size_t i = 0;
+ while (i < size && _isspace(data[i])) i++;
+ return i == size;
+}
+
+/*
+ * Replace all spacing characters in data with spaces. As a special
+ * case, this collapses a newline with the previous space, if possible.
+ */
+static void
+replace_spacing(hoedown_buffer *ob, const uint8_t *data, size_t size)
+{
+ size_t i = 0, mark;
+ hoedown_buffer_grow(ob, size);
+ while (1) {
+ mark = i;
+ while (i < size && data[i] != '\n') i++;
+ hoedown_buffer_put(ob, data + mark, i - mark);
+
+ if (i >= size) break;
+
+ if (!(i > 0 && data[i-1] == ' '))
+ hoedown_buffer_putc(ob, ' ');
+ i++;
+ }
+}
+
+/****************************
+ * INLINE PARSING FUNCTIONS *
+ ****************************/
+
+/* is_mail_autolink 窶「 looks for the address part of a mail autolink and '>' */
+/* this is less strict than the original markdown e-mail address matching */
+static size_t
+is_mail_autolink(uint8_t *data, size_t size)
+{
+ size_t i = 0, nb = 0;
+
+ /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */
+ for (i = 0; i < size; ++i) {
+ if (isalnum(data[i]))
+ continue;
+
+ switch (data[i]) {
+ case '@':
+ nb++;
+
+ case '-':
+ case '.':
+ case '_':
+ break;
+
+ case '>':
+ return (nb == 1) ? i + 1 : 0;
+
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static size_t
+script_tag_length(uint8_t *data, size_t size)
+{
+ size_t i = 2;
+ char comment = 0;
+
+ if (size < 3 || data[0] != '<' || data[1] != '?') {
+ return 0;
+ }
+
+ i = 2;
+
+ while (i < size) {
+ if (data[i - 1] == '?' && data[i] == '>' && comment == 0) {
+ break;
+ }
+
+ if (data[i] == '\'' || data[i] == '"') {
+ if (comment != 0) {
+ if (data[i] == comment && data[i - 1] != '\\') {
+ comment = 0;
+ }
+ } else {
+ comment = data[i];
+ }
+ }
+
+ ++i;
+ }
+
+ return i + 1;
+}
+
+/* tag_length 窶「 returns the length of the given tag, or 0 is it's not valid */
+static size_t
+tag_length(uint8_t *data, size_t size, hoedown_autolink_type *autolink, int script_tag)
+{
+ size_t i, j;
+
+ /* a valid tag can't be shorter than 3 chars */
+ if (size < 3) return 0;
+
+ /* begins with a '<' optionally followed by '/', followed by letter or number */
+ if (data[0] != '<') return 0;
+ i = (data[1] == '/') ? 2 : 1;
+
+ if (!isalnum(data[i])) {
+ if (script_tag) {
+ return script_tag_length(data, size);
+ }
+ return 0;
+ }
+
+ /* scheme test */
+ *autolink = HOEDOWN_AUTOLINK_NONE;
+
+ /* try to find the beginning of an URI */
+ while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-'))
+ i++;
+
+ if (i > 1 && data[i] == '@') {
+ if ((j = is_mail_autolink(data + i, size - i)) != 0) {
+ *autolink = HOEDOWN_AUTOLINK_EMAIL;
+ return i + j;
+ }
+ }
+
+ if (i > 2 && data[i] == ':') {
+ *autolink = HOEDOWN_AUTOLINK_NORMAL;
+ i++;
+ }
+
+ /* completing autolink test: no spacing or ' or " */
+ if (i >= size)
+ *autolink = HOEDOWN_AUTOLINK_NONE;
+
+ else if (*autolink) {
+ j = i;
+
+ while (i < size) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == '>' || data[i] == '\'' ||
+ data[i] == '"' || data[i] == ' ' || data[i] == '\n')
+ break;
+ else i++;
+ }
+
+ if (i >= size) return 0;
+ if (i > j && data[i] == '>') return i + 1;
+ /* one of the forbidden chars has been found */
+ *autolink = HOEDOWN_AUTOLINK_NONE;
+ }
+
+ /* looking for something looking like a tag end */
+ while (i < size && data[i] != '>') i++;
+ if (i >= size) return 0;
+ return i + 1;
+}
+
+/* parse_inline 窶「 parses inline markdown elements */
+static void
+parse_inline(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
+{
+ size_t i = 0, end = 0;
+ hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL };
+ uint8_t *active_char = doc->active_char;
+
+ if (doc->work_bufs[BUFFER_SPAN].size +
+ doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting)
+ return;
+
+ while (i < size) {
+ size_t user_block = 0;
+ while (end < size) {
+ if (doc->user_block) {
+ user_block = doc->user_block(data+end, size - end, &doc->data);
+ if (user_block) {
+ break;
+ }
+ }
+ /* copying inactive chars into the output */
+ if (active_char[data[end]] != 0) {
+ break;
+ }
+ end++;
+ }
+
+ if (doc->md.normal_text) {
+ work.data = data + i;
+ work.size = end - i;
+ doc->md.normal_text(ob, &work, &doc->data);
+ }
+ else
+ hoedown_buffer_put(ob, data + i, end - i);
+
+ if (end >= size) {
+ break;
+ }
+ i = end;
+
+ if (user_block) {
+ work.data = data + i;
+ work.size = user_block;
+ end = user_block;
+ if (doc->md.user_block) {
+ doc->md.user_block(ob, &work, &doc->data);
+ } else {
+ hoedown_buffer_put(ob, data + i, size - i);
+ }
+ if (!end) {
+ end = i + 1;
+ } else {
+ i += end;
+ end = i;
+ }
+ } else {
+ end = markdown_char_ptrs[ (int)active_char[data[end]] ](ob, doc, data + i, i, size - i);
+ if (!end) /* no action from the callback */
+ end = i + 1;
+ else {
+ i += end;
+ end = i;
+ }
+ }
+ }
+}
+
+static size_t parse_attributes(uint8_t *data, size_t size, struct hoedown_buffer *attr, struct hoedown_buffer *block_attr, int is_header)
+{
+ size_t i, len, begin = 0, end = 0;
+
+ i = size;
+ while (data[i-1] == '\n') {
+ i--;
+ }
+ len = i;
+
+ if (i && data[i-1] == '}') {
+ do {
+ i--;
+ } while (i && data[i] != '{');
+
+ begin = i + 1;
+ end = len - 1;
+ while (i && data[i-1] == ' ') {
+ i--;
+ }
+ }
+
+ if (is_header && i && data[i-1] == '#') {
+ while (i && data[i-1] == '#') {
+ i--;
+ }
+ while (i && data[i-1] == ' ') {
+ i--;
+ }
+ }
+
+ if (begin && end) {
+ if (block_attr && data[begin] == '@') {
+ while (data[begin] != ' ') {
+ begin++;
+ }
+ block_attr->data = data + begin;
+ block_attr->size = end - begin;
+ len = i;
+ if (attr) {
+ len = parse_attributes(data, len, attr, NULL, is_header);
+ }
+ } else if (attr) {
+ if (attr->size) {
+ hoedown_buffer_reset(attr);
+ }
+ hoedown_buffer_put(attr, data + begin, end - begin);
+ len = i;
+ }
+ }
+
+ return len;
+}
+
+/* is_escaped 窶「 returns whether special char at data[loc] is escaped by '\\' */
+static int
+is_escaped(uint8_t *data, size_t loc)
+{
+ size_t i = loc;
+ while (i >= 1 && data[i - 1] == '\\')
+ i--;
+
+ /* odd numbers of backslashes escapes data[loc] */
+ return (loc - i) % 2;
+}
+
+/* find_emph_char 窶「 looks for the next emph uint8_t, skipping other constructs */
+static size_t
+find_emph_char(uint8_t *data, size_t size, uint8_t c)
+{
+ size_t i = 0;
+
+ while (i < size) {
+ while (i < size && data[i] != c && data[i] != '[' && data[i] != '`')
+ i++;
+
+ if (i == size)
+ return 0;
+
+ /* not counting escaped chars */
+ if (is_escaped(data, i)) {
+ i++; continue;
+ }
+
+ if (data[i] == c)
+ return i;
+
+ /* skipping a codespan */
+ if (data[i] == '`') {
+ size_t span_nb = 0, bt;
+ size_t tmp_i = 0;
+
+ /* counting the number of opening backticks */
+ while (i < size && data[i] == '`') {
+ i++; span_nb++;
+ }
+
+ if (i >= size) return 0;
+
+ /* finding the matching closing sequence */
+ bt = 0;
+ while (i < size && bt < span_nb) {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ if (data[i] == '`') bt++;
+ else bt = 0;
+ i++;
+ }
+
+ /* not a well-formed codespan; use found matching emph char */
+ if (i >= size) return tmp_i;
+ }
+ /* skipping a link */
+ else if (data[i] == '[') {
+ size_t tmp_i = 0;
+ uint8_t cc;
+
+ i++;
+ while (i < size && data[i] != ']') {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ i++;
+ }
+
+ i++;
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ if (i >= size)
+ return tmp_i;
+
+ switch (data[i]) {
+ case '[':
+ cc = ']'; break;
+
+ case '(':
+ cc = ')'; break;
+
+ default:
+ if (tmp_i)
+ return tmp_i;
+ else
+ continue;
+ }
+
+ i++;
+ while (i < size && data[i] != cc) {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ i++;
+ }
+
+ if (i >= size)
+ return tmp_i;
+
+ i++;
+ }
+ }
+
+ return 0;
+}
+
+/* parse_emph1 窶「 parsing single emphase */
+/* closed by a symbol not preceded by spacing and not followed by symbol */
+static size_t
+parse_emph1(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c)
+{
+ size_t i = 0, len;
+ hoedown_buffer *work = 0;
+ int r;
+
+ /* 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 (data[i] == c && !_isspace(data[i - 1])) {
+
+ if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) {
+ if (i + 1 < size && isalnum(data[i + 1]))
+ continue;
+ }
+
+ work = newbuf(doc, BUFFER_SPAN);
+ parse_inline(work, doc, data, i);
+
+ if (doc->ext_flags & HOEDOWN_EXT_UNDERLINE && c == '_')
+ r = doc->md.underline(ob, work, &doc->data);
+ else
+ r = doc->md.emphasis(ob, work, &doc->data);
+
+ popbuf(doc, BUFFER_SPAN);
+ return r ? i + 1 : 0;
+ }
+ }
+
+ return 0;
+}
+
+/* parse_emph2 窶「 parsing single emphase */
+static size_t
+parse_emph2(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c)
+{
+ size_t i = 0, len;
+ hoedown_buffer *work = 0;
+ int r;
+
+ 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 && !_isspace(data[i - 1])) {
+ work = newbuf(doc, BUFFER_SPAN);
+ parse_inline(work, doc, data, i);
+
+ if (c == '~')
+ r = doc->md.strikethrough(ob, work, &doc->data);
+ else if (c == '=')
+ r = doc->md.highlight(ob, work, &doc->data);
+ else
+ r = doc->md.double_emphasis(ob, work, &doc->data);
+
+ popbuf(doc, BUFFER_SPAN);
+ return r ? i + 2 : 0;
+ }
+ i++;
+ }
+ return 0;
+}
+
+/* parse_emph3 窶「 parsing single emphase */
+/* finds the first closing tag, and delegates to the other emph */
+static size_t
+parse_emph3(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t 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 spacing preceded symbols */
+ if (data[i] != c || _isspace(data[i - 1]))
+ continue;
+
+ if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && doc->md.triple_emphasis) {
+ /* triple symbol found */
+ hoedown_buffer *work = newbuf(doc, BUFFER_SPAN);
+
+ parse_inline(work, doc, data, i);
+ r = doc->md.triple_emphasis(ob, work, &doc->data);
+ popbuf(doc, BUFFER_SPAN);
+ 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, doc, 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, doc, data - 1, size + 1, c);
+ if (!len) return 0;
+ else return len - 1;
+ }
+ }
+ return 0;
+}
+
+/* parse_math 窶「 parses a math span until the given ending delimiter */
+static size_t
+parse_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size, const char *end, size_t delimsz, int displaymode)
+{
+ hoedown_buffer text = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ size_t i = delimsz;
+
+ if (!doc->md.math)
+ return 0;
+
+ /* find ending delimiter */
+ while (1) {
+ while (i < size && data[i] != (uint8_t)end[0])
+ i++;
+
+ if (i >= size)
+ return 0;
+
+ if (!is_escaped(data, i) && !(i + delimsz > size)
+ && memcmp(data + i, end, delimsz) == 0)
+ break;
+
+ i++;
+ }
+
+ /* prepare buffers */
+ text.data = data + delimsz;
+ text.size = i - delimsz;
+
+ /* if this is a $$ and MATH_EXPLICIT is not active,
+ * guess whether displaymode should be enabled from the context */
+ i += delimsz;
+ if (delimsz == 2 && !(doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT))
+ displaymode = is_empty_all(data - offset, offset) && is_empty_all(data + i, size - i);
+
+ /* call callback */
+ if (doc->md.math(ob, &text, displaymode, &doc->data))
+ return i;
+
+ return 0;
+}
+
+/* char_emphasis 窶「 single and double emphasis parsing */
+static size_t
+char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ uint8_t c = data[0];
+ size_t ret;
+
+ if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) {
+ if (offset > 0 && !_isspace(data[-1]) && data[-1] != '>' && data[-1] != '(')
+ return 0;
+ }
+
+ if (size > 2 && data[1] != c) {
+ /* spacing cannot follow an opening emphasis;
+ * strikethrough and highlight only takes two characters '~~' */
+ if (c == '~' || c == '=' || _isspace(data[1]) || (ret = parse_emph1(ob, doc, data + 1, size - 1, c)) == 0)
+ return 0;
+
+ return ret + 1;
+ }
+
+ if (size > 3 && data[1] == c && data[2] != c) {
+ if (_isspace(data[2]) || (ret = parse_emph2(ob, doc, data + 2, size - 2, c)) == 0)
+ return 0;
+
+ return ret + 2;
+ }
+
+ if (size > 4 && data[1] == c && data[2] == c && data[3] != c) {
+ if (c == '~' || c == '=' || _isspace(data[3]) || (ret = parse_emph3(ob, doc, data + 3, size - 3, c)) == 0)
+ return 0;
+
+ return ret + 3;
+ }
+
+ return 0;
+}
+
+
+/* char_linebreak 窶「 '\n' preceded by two spaces (assuming linebreak != 0) */
+static size_t
+char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')
+ return 0;
+
+ /* removing the last space from ob and rendering */
+ while (ob->size && ob->data[ob->size - 1] == ' ')
+ ob->size--;
+
+ return doc->md.linebreak(ob, &doc->data) ? 1 : 0;
+}
+
+
+/* char_codespan 窶「 '`' parsing a code span (assuming codespan != 0) */
+static size_t
+char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ size_t end, nb = 0, i, f_begin, f_end;
+
+ /* counting the number of backticks in the delimiter */
+ while (nb < size && data[nb] == '`')
+ nb++;
+
+ /* finding the next delimiter */
+ i = 0;
+ for (end = nb; end < size && i < nb; end++) {
+ if (data[end] == '`') i++;
+ else i = 0;
+ }
+
+ if (i < nb && end >= size)
+ return 0; /* no matching delimiter */
+
+ /* trimming outside spaces */
+ f_begin = nb;
+ while (f_begin < end && data[f_begin] == ' ')
+ f_begin++;
+
+ f_end = end - nb;
+ while (f_end > nb && data[f_end-1] == ' ')
+ f_end--;
+
+ /* real code span */
+ if (f_begin < f_end) {
+ hoedown_buffer attr = { 0, 0, 0, 0, NULL, NULL, NULL };
+
+ work.data = data + f_begin;
+ work.size = f_end - f_begin;
+
+ if ((doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) &&
+ data[end] == '{') {
+ size_t a_begin = end+1, a_end = a_begin;
+ while (a_end < size && data[a_end] != '}') {
+ ++a_end;
+ }
+ if (a_end <= size) {
+ attr.data = data + a_begin;
+ attr.size = a_end - a_begin;
+ end += attr.size + 2;
+ }
+ }
+
+ if (!doc->md.codespan(ob, &work, &attr, &doc->data))
+ end = 0;
+ } else {
+ if (!doc->md.codespan(ob, 0, 0, &doc->data))
+ end = 0;
+ }
+
+ return end;
+}
+
+/* char_quote 窶「 '"' parsing a quote */
+static size_t
+char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ size_t end, nq = 0, i, f_begin, f_end;
+
+ /* counting the number of quotes in the delimiter */
+ while (nq < size && data[nq] == '"')
+ nq++;
+
+ /* finding the next delimiter */
+ end = nq;
+ while (1) {
+ i = end;
+ end += find_emph_char(data + end, size - end, '"');
+ if (end == i) return 0; /* no matching delimiter */
+ i = end;
+ while (end < size && data[end] == '"' && end - i < nq) end++;
+ if (end - i >= nq) break;
+ }
+
+ /* trimming outside spaces */
+ f_begin = nq;
+ while (f_begin < end && data[f_begin] == ' ')
+ f_begin++;
+
+ f_end = end - nq;
+ while (f_end > nq && data[f_end-1] == ' ')
+ f_end--;
+
+ /* real quote */
+ if (f_begin < f_end) {
+ hoedown_buffer *work = newbuf(doc, BUFFER_SPAN);
+ parse_inline(work, doc, data + f_begin, f_end - f_begin);
+
+ if (!doc->md.quote(ob, work, &doc->data))
+ end = 0;
+ popbuf(doc, BUFFER_SPAN);
+ } else {
+ if (!doc->md.quote(ob, 0, &doc->data))
+ end = 0;
+ }
+
+ return end;
+}
+
+
+/* char_escape 窶「 '\\' backslash escape */
+static size_t
+char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~=\"$";
+ hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL };
+ size_t w;
+
+ if (size > 1) {
+ if (data[1] == '\\' && (doc->ext_flags & HOEDOWN_EXT_MATH) &&
+ size > 2 && (data[2] == '(' || data[2] == '[')) {
+ const char *end = (data[2] == '[') ? "\\\\]" : "\\\\)";
+ w = parse_math(ob, doc, data, offset, size, end, 3, data[2] == '[');
+ if (w) return w;
+ }
+
+ if (strchr(escape_chars, data[1]) == NULL)
+ return 0;
+
+ if (doc->md.normal_text) {
+ work.data = data + 1;
+ work.size = 1;
+ doc->md.normal_text(ob, &work, &doc->data);
+ }
+ else hoedown_buffer_putc(ob, data[1]);
+ } else if (size == 1) {
+ hoedown_buffer_putc(ob, data[0]);
+ }
+
+ return 2;
+}
+
+/* char_entity 窶「 '&' escaped when it doesn't belong to an entity */
+/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */
+static size_t
+char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ size_t end = 1;
+ hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL };
+
+ if (end < size && data[end] == '#')
+ end++;
+
+ while (end < size && isalnum(data[end]))
+ end++;
+
+ if (end < size && data[end] == ';')
+ end++; /* real entity */
+ else
+ return 0; /* lone '&' */
+
+ if (doc->md.entity) {
+ work.data = data;
+ work.size = end;
+ doc->md.entity(ob, &work, &doc->data);
+ }
+ else hoedown_buffer_put(ob, data, end);
+
+ return end;
+}
+
+/* char_langle_tag 窶「 '<' when tags or autolinks are allowed */
+static size_t
+char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ hoedown_autolink_type altype = HOEDOWN_AUTOLINK_NONE;
+ size_t end = tag_length(data, size, &altype, doc->ext_flags & HOEDOWN_EXT_SCRIPT_TAGS);
+ int ret = 0;
+
+ work.data = data;
+ work.size = end;
+
+ if (end > 2) {
+ if (doc->md.autolink && altype != HOEDOWN_AUTOLINK_NONE) {
+ hoedown_buffer *u_link = newbuf(doc, BUFFER_SPAN);
+ work.data = data + 1;
+ work.size = end - 2;
+ unscape_text(u_link, &work);
+ ret = doc->md.autolink(ob, u_link, altype, &doc->data);
+ popbuf(doc, BUFFER_SPAN);
+ }
+ else if (doc->md.raw_html)
+ ret = doc->md.raw_html(ob, &work, &doc->data);
+ }
+
+ if (!ret) return 0;
+ else return end;
+}
+
+static size_t
+char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ hoedown_buffer *link, *link_url, *link_text;
+ size_t link_len, rewind;
+
+ if (!doc->md.link || doc->in_link_body)
+ return 0;
+
+ link = newbuf(doc, BUFFER_SPAN);
+
+ if ((link_len = hoedown_autolink__www(&rewind, link, data, offset, size, HOEDOWN_AUTOLINK_SHORT_DOMAINS)) > 0) {
+ link_url = newbuf(doc, BUFFER_SPAN);
+ HOEDOWN_BUFPUTSL(link_url, "http://");
+ hoedown_buffer_put(link_url, link->data, link->size);
+
+ ob->size -= rewind;
+ if (doc->md.normal_text) {
+ link_text = newbuf(doc, BUFFER_SPAN);
+ doc->md.normal_text(link_text, link, &doc->data);
+ doc->md.link(ob, link_text, link_url, NULL, NULL, &doc->data);
+ popbuf(doc, BUFFER_SPAN);
+ } else {
+ doc->md.link(ob, link, link_url, NULL, NULL, &doc->data);
+ }
+ popbuf(doc, BUFFER_SPAN);
+ }
+
+ popbuf(doc, BUFFER_SPAN);
+ return link_len;
+}
+
+static size_t
+char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ hoedown_buffer *link;
+ size_t link_len, rewind;
+
+ if (!doc->md.autolink || doc->in_link_body)
+ return 0;
+
+ link = newbuf(doc, BUFFER_SPAN);
+
+ if ((link_len = hoedown_autolink__email(&rewind, link, data, offset, size, 0)) > 0) {
+ ob->size -= rewind;
+ doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_EMAIL, &doc->data);
+ }
+
+ popbuf(doc, BUFFER_SPAN);
+ return link_len;
+}
+
+static size_t
+char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ hoedown_buffer *link;
+ size_t link_len, rewind;
+
+ if (!doc->md.autolink || doc->in_link_body)
+ return 0;
+
+ link = newbuf(doc, BUFFER_SPAN);
+
+ if ((link_len = hoedown_autolink__url(&rewind, link, data, offset, size, 0)) > 0) {
+ ob->size -= rewind;
+ doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_NORMAL, &doc->data);
+ }
+
+ popbuf(doc, BUFFER_SPAN);
+ return link_len;
+}
+
+/* char_link 窶「 '[': parsing a link, a footnote or an image */
+static size_t
+char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ int is_img = (offset && data[-1] == '!' && !is_escaped(data - offset, offset - 1));
+ int is_footnote = (doc->ext_flags & HOEDOWN_EXT_FOOTNOTES && data[1] == '^');
+ size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0;
+ size_t attr_b = 0, attr_e = 0;
+ hoedown_buffer *content = NULL;
+ hoedown_buffer *link = NULL;
+ hoedown_buffer *title = NULL;
+ hoedown_buffer *u_link = NULL;
+ hoedown_buffer *attr = NULL;
+ size_t org_work_size = doc->work_bufs[BUFFER_SPAN].size;
+ int ret = 0, in_title = 0, qtype = 0;
+
+ /* checking whether the correct renderer exists */
+ if ((is_footnote && !doc->md.footnote_ref) || (is_img && !doc->md.image)
+ || (!is_img && !is_footnote && !doc->md.link))
+ goto cleanup;
+
+ /* looking for the matching closing bracket */
+ i += find_emph_char(data + i, size - i, ']');
+ txt_e = i;
+
+ if (i < size && data[i] == ']') i++;
+ else goto cleanup;
+
+ /* footnote link */
+ if (is_footnote) {
+ hoedown_buffer id = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ struct footnote_ref *fr;
+
+ if (txt_e < 3)
+ goto cleanup;
+
+ id.data = data + 2;
+ id.size = txt_e - 2;
+
+ fr = find_footnote_ref(&doc->footnotes_found, id.data, id.size);
+
+ /* mark footnote used */
+ if (fr && !fr->is_used) {
+ if(!add_footnote_ref(&doc->footnotes_used, fr))
+ goto cleanup;
+ fr->is_used = 1;
+ fr->num = doc->footnotes_used.count;
+
+ /* render */
+ if (doc->md.footnote_ref)
+ ret = doc->md.footnote_ref(ob, fr->num, &doc->data);
+ }
+
+ goto cleanup;
+ }
+
+ /* skip any amount of spacing */
+ /* (this is much more laxist than original markdown syntax) */
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ /* inline style link */
+ if (i < size && data[i] == '(') {
+ size_t nb_p;
+
+ /* skipping initial spacing */
+ i++;
+
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ link_b = i;
+
+ /* looking for link end: ' " ) */
+ /* Count the number of open parenthesis */
+ nb_p = 0;
+
+ while (i < size) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == '(' && i != 0) {
+ nb_p++; i++;
+ }
+ else if (data[i] == ')') {
+ if (nb_p == 0) break;
+ else nb_p--; i++;
+ } else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;
+ else i++;
+ }
+
+ if (i >= size) goto cleanup;
+ link_e = i;
+
+ /* looking for title end if present */
+ if (data[i] == '\'' || data[i] == '"') {
+ qtype = data[i];
+ in_title = 1;
+ i++;
+ title_b = i;
+
+ while (i < size) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == qtype) {in_title = 0; i++;}
+ else if ((data[i] == ')') && !in_title) break;
+ else i++;
+ }
+
+ if (i >= size) goto cleanup;
+
+ /* skipping spacing 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 spacing 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 = newbuf(doc, BUFFER_SPAN);
+ hoedown_buffer_put(link, data + link_b, link_e - link_b);
+ }
+
+ if (title_e > title_b) {
+ title = newbuf(doc, BUFFER_SPAN);
+ hoedown_buffer_put(title, data + title_b, title_e - title_b);
+ }
+
+ i++;
+ }
+
+ /* reference style link */
+ else if (i < size && data[i] == '[') {
+ hoedown_buffer *id = newbuf(doc, BUFFER_SPAN);
+ struct link_ref *lr;
+
+ /* looking for the id */
+ i++;
+ 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)
+ replace_spacing(id, data + 1, txt_e - 1);
+ else
+ hoedown_buffer_put(id, data + link_b, link_e - link_b);
+
+ lr = find_link_ref(doc->refs, id->data, id->size);
+ if (!lr)
+ goto cleanup;
+
+ /* keeping link and title from link_ref */
+ link = lr->link;
+ title = lr->title;
+ attr = lr->attr;
+ i++;
+ }
+
+ /* shortcut reference style link */
+ else {
+ hoedown_buffer *id = newbuf(doc, BUFFER_SPAN);
+ struct link_ref *lr;
+
+ /* crafting the id */
+ replace_spacing(id, data + 1, txt_e - 1);
+
+ /* finding the link_ref */
+ lr = find_link_ref(doc->refs, id->data, id->size);
+ if (!lr)
+ goto cleanup;
+
+ /* keeping link and title from link_ref */
+ link = lr->link;
+ title = lr->title;
+ attr = lr->attr;
+
+ /* rewinding the spacing */
+ i = txt_e + 1;
+ }
+
+ /* building content: img alt is kept, only link content is parsed */
+ if (txt_e > 1) {
+ content = newbuf(doc, BUFFER_SPAN);
+ if (is_img) {
+ hoedown_buffer_put(content, data + 1, txt_e - 1);
+ } else {
+ /* disable autolinking when parsing inline the
+ * content of a link */
+ doc->in_link_body = 1;
+ parse_inline(content, doc, data + 1, txt_e - 1);
+ doc->in_link_body = 0;
+ }
+ }
+
+ if (link) {
+ u_link = newbuf(doc, BUFFER_SPAN);
+ unscape_text(u_link, link);
+ }
+
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ if (i < size && data[i] == '{') {
+ /* skipping initial whitespace */
+ i++;
+
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ attr_b = i;
+
+ while (i < size) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == '}') break;
+ else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;
+ else i++;
+ }
+
+ if (i >= size) goto cleanup;
+ attr_e = i;
+
+ /* remove whitespace at the end of the attributes */
+ while (attr_e > attr_b && _isspace(data[attr_e - 1]))
+ attr_e--;
+
+ /* remove optional angle brackets around the attributes */
+ if (data[attr_b] == '<') attr_b++;
+ if (data[attr_e - 1] == '>') attr_e--;
+
+ /* building escaped attributes */
+ if (attr_e > attr_b) {
+ if (attr) {
+ hoedown_buffer_putc(attr, ' ');
+ } else {
+ attr = newbuf(doc, BUFFER_SPAN);
+ }
+ hoedown_buffer_put(attr, data + attr_b, attr_e - attr_b);
+ }
+
+ i++;
+ }
+ }
+
+ /* calling the relevant rendering function */
+ if (is_img) {
+ if (ob->size && ob->data[ob->size - 1] == '!')
+ ob->size -= 1;
+
+ ret = doc->md.image(ob, u_link, title, content, attr, &doc->data);
+ } else {
+ ret = doc->md.link(ob, content, u_link, title, attr, &doc->data);
+ }
+
+ /* cleanup */
+cleanup:
+ doc->work_bufs[BUFFER_SPAN].size = (int)org_work_size;
+ return ret ? i : 0;
+}
+
+static size_t
+char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ size_t sup_start, sup_len;
+ hoedown_buffer *sup;
+
+ if (!doc->md.superscript)
+ return 0;
+
+ if (size < 2)
+ return 0;
+
+ if (data[1] == '(') {
+ sup_start = 2;
+ sup_len = find_emph_char(data + 2, size - 2, ')') + 2;
+
+ if (sup_len == size)
+ return 0;
+ } else {
+ sup_start = sup_len = 1;
+
+ while (sup_len < size && !_isspace(data[sup_len]))
+ sup_len++;
+ }
+
+ if (sup_len - sup_start == 0)
+ return (sup_start == 2) ? 3 : 0;
+
+ sup = newbuf(doc, BUFFER_SPAN);
+ parse_inline(sup, doc, data + sup_start, sup_len - sup_start);
+ doc->md.superscript(ob, sup, &doc->data);
+ popbuf(doc, BUFFER_SPAN);
+
+ return (sup_start == 2) ? sup_len + 1 : sup_len;
+}
+
+static size_t
+char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
+{
+ /* double dollar */
+ if (size > 1 && data[1] == '$')
+ return parse_math(ob, doc, data, offset, size, "$$", 2, 1);
+
+ /* single dollar allowed only with MATH_EXPLICIT flag */
+ if (doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT)
+ return parse_math(ob, doc, data, offset, size, "$", 1, 0);
+
+ return 0;
+}
+
+/*********************************
+ * BLOCK-LEVEL PARSING FUNCTIONS *
+ *********************************/
+
+/* is_empty 窶「 returns the line length when it is empty, 0 otherwise */
+static size_t
+is_empty(const uint8_t *data, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size && data[i] != '\n'; i++)
+ if (data[i] != ' ')
+ return 0;
+
+ return i + 1;
+}
+
+/* is_hrule 窶「 returns whether a line is a horizontal rule */
+static int
+is_hrule(uint8_t *data, size_t size)
+{
+ size_t i = 0, n = 0;
+ uint8_t c;
+
+ /* skipping initial spaces */
+ if (size < 3) return 0;
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
+
+ /* looking at the hrule uint8_t */
+ if (i + 2 >= size
+ || (data[i] != '*' && data[i] != '-' && data[i] != '_'))
+ return 0;
+ c = data[i];
+
+ /* the whole line must be the char or space */
+ while (i < size && data[i] != '\n') {
+ if (data[i] == c) n++;
+ else if (data[i] != ' ')
+ return 0;
+
+ i++;
+ }
+
+ return n >= 3;
+}
+
+/* check if a line is a code fence; return the
+ * end of the code fence. if passed, width of
+ * the fence rule and character will be returned */
+static size_t
+is_codefence(uint8_t *data, size_t size, size_t *width, uint8_t *chr)
+{
+ size_t i = 0, n = 1;
+ uint8_t c;
+
+ /* skipping initial spaces */
+ if (size < 3)
+ return 0;
+
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
+
+ /* looking at the hrule uint8_t */
+ c = data[i];
+ if (i + 2 >= size || !(c=='~' || c=='`'))
+ return 0;
+
+ /* the fence must be that same character */
+ while (++i < size && data[i] == c)
+ ++n;
+
+ if (n < 3)
+ return 0;
+
+ if (width) *width = n;
+ if (chr) *chr = c;
+ return i;
+}
+
+/* expects single line, checks if it's a codefence and extracts language */
+static int
+parse_codefence(uint8_t *data, size_t size, hoedown_buffer *lang, size_t *width, uint8_t *chr, unsigned int flags, hoedown_buffer *attr)
+{
+ size_t i, w, lang_start, attr_start = 0;
+
+ i = w = is_codefence(data, size, width, chr);
+ if (i == 0)
+ return 0;
+
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ lang_start = i;
+
+ if (flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ int e = 0;
+ while (i < size) {
+ if (!e) {
+ if (data[i] == '{') {
+ attr_start = i + 1;
+ e = '}';
+ } else if (_isspace(data[i])) {
+ if ((i+1 < size) && data[i+1] != '{') {
+ break;
+ }
+ }
+ } else if (data[i] == '}') {
+ attr->data = data + attr_start;
+ attr->size = i - attr_start;
+ break;
+ }
+ ++i;
+ }
+ } else {
+ while (i < size && !_isspace(data[i]))
+ i++;
+ }
+
+ lang->data = data + lang_start;
+ lang->size = i - lang_start;
+
+ /* Avoid parsing a codespan as a fence */
+ i = lang_start + 2;
+ while (i < size && !(data[i] == *chr && data[i-1] == *chr && data[i-2] == *chr)) i++;
+ if (i < size) return 0;
+
+ return w;
+}
+
+/* is_atxheader 窶「 returns whether the line is a hash-prefixed header */
+static int
+is_atxheader(hoedown_document *doc, uint8_t *data, size_t size)
+{
+ if (data[0] != '#')
+ return 0;
+
+ if (doc->ext_flags & HOEDOWN_EXT_SPACE_HEADERS) {
+ size_t level = 0;
+
+ while (level < size && level < 6 && data[level] == '#')
+ level++;
+
+ if (level < size && data[level] != ' ')
+ return 0;
+ }
+
+ return 1;
+}
+
+/* is_headerline 窶「 returns whether the line is a setext-style hdr underline */
+static int
+is_headerline(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ /* test of level 1 header */
+ if (data[i] == '=') {
+ for (i = 1; i < size && data[i] == '='; i++);
+ while (i < size && data[i] == ' ') i++;
+ return (i >= size || data[i] == '\n') ? 1 : 0; }
+
+ /* test of level 2 header */
+ if (data[i] == '-') {
+ for (i = 1; i < size && data[i] == '-'; i++);
+ while (i < size && data[i] == ' ') i++;
+ return (i >= size || data[i] == '\n') ? 2 : 0; }
+
+ return 0;
+}
+
+static int
+is_next_headerline(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ while (i < size && data[i] != '\n')
+ i++;
+
+ if (++i >= size)
+ return 0;
+
+ return is_headerline(data + i, size - i);
+}
+
+/* prefix_quote 窶「 returns blockquote prefix length */
+static size_t
+prefix_quote(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i < size && data[i] == '>') {
+ if (i + 1 < size && data[i + 1] == ' ')
+ return i + 2;
+
+ return i + 1;
+ }
+
+ return 0;
+}
+
+/* prefix_code 窶「 returns prefix length for block code*/
+static size_t
+prefix_code(uint8_t *data, size_t size)
+{
+ if (size > 3 && data[0] == ' ' && data[1] == ' '
+ && data[2] == ' ' && data[3] == ' ') return 4;
+
+ return 0;
+}
+
+/* prefix_oli 窶「 returns ordered list item prefix */
+static size_t
+prefix_oli(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i >= size || data[i] < '0' || data[i] > '9')
+ return 0;
+
+ while (i < size && data[i] >= '0' && data[i] <= '9')
+ i++;
+
+ if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ')
+ return 0;
+
+ if (is_next_headerline(data + i, size - i))
+ return 0;
+
+ return i + 2;
+}
+
+/* prefix_uli 窶「 returns ordered list item prefix */
+static size_t
+prefix_uli(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i + 1 >= size ||
+ (data[i] != '*' && data[i] != '+' && data[i] != '-') ||
+ data[i + 1] != ' ')
+ return 0;
+
+ if (is_next_headerline(data + i, size - i))
+ return 0;
+
+ return i + 2;
+}
+
+
+/* parse_block 窶「 parsing of one block, returning next uint8_t to parse */
+static void parse_block(hoedown_buffer *ob, hoedown_document *doc,
+ uint8_t *data, size_t size);
+
+
+/* parse_blockquote 窶「 handles parsing of a blockquote fragment */
+static size_t
+parse_blockquote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
+{
+ size_t beg, end = 0, pre, work_size = 0;
+ uint8_t *work_data = 0;
+ hoedown_buffer *out = 0;
+
+ out = newbuf(doc, BUFFER_BLOCK);
+ beg = 0;
+ while (beg < size) {
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
+
+ pre = prefix_quote(data + beg, end - beg);
+
+ if (pre)
+ beg += pre; /* skipping prefix */
+
+ /* empty line followed by non-quote line */
+ else if (is_empty(data + beg, end - beg) &&
+ (end >= size || (prefix_quote(data + end, size - end) == 0 &&
+ !is_empty(data + end, size - end))))
+ break;
+
+ if (beg < end) { /* copy into the in-place working buffer */
+ /* hoedown_buffer_put(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, doc, work_data, work_size);
+ if (doc->md.blockquote)
+ doc->md.blockquote(ob, out, &doc->data);
+ popbuf(doc, BUFFER_BLOCK);
+ return end;
+}
+
+static size_t
+parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render);
+
+/* parse_blockquote 窶「 handles parsing of a regular paragraph */
+static size_t
+parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
+{
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ size_t i = 0, end = 0;
+ int level = 0;
+
+ work.data = data;
+
+ while (i < size) {
+ for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;
+
+ if (is_empty(data + i, size - i))
+ break;
+
+ if ((level = is_headerline(data + i, size - i)) != 0)
+ break;
+
+ if (is_atxheader(doc, data + i, size - i) ||
+ is_hrule(data + i, size - i) ||
+ prefix_quote(data + i, size - i)) {
+ end = i;
+ break;
+ }
+
+ i = end;
+ }
+
+ work.size = i;
+ while (work.size && data[work.size - 1] == '\n')
+ work.size--;
+
+ if (!level) {
+ hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK);
+ parse_inline(tmp, doc, work.data, work.size);
+ if (doc->md.paragraph)
+ doc->md.paragraph(ob, tmp, &doc->data);
+ popbuf(doc, BUFFER_BLOCK);
+ } else {
+ hoedown_buffer *header_work;
+ hoedown_buffer *attr_work;
+
+ 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 > 0) {
+ hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK);
+ parse_inline(tmp, doc, work.data, work.size);
+
+ if (doc->md.paragraph)
+ doc->md.paragraph(ob, tmp, &doc->data);
+
+ popbuf(doc, BUFFER_BLOCK);
+ work.data += beg;
+ work.size = i - beg;
+ }
+ else work.size = i;
+ }
+
+ header_work = newbuf(doc, BUFFER_SPAN);
+ attr_work = newbuf(doc, BUFFER_ATTRIBUTE);
+ parse_inline(header_work, doc, work.data, work.size);
+
+ if (doc->md.header) {
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ header_work->size = parse_attributes(header_work->data, header_work->size, attr_work, NULL, 1);
+ }
+ doc->md.header(ob, header_work, attr_work, (int)level, &doc->data);
+ }
+
+ popbuf(doc, BUFFER_SPAN);
+ popbuf(doc, BUFFER_ATTRIBUTE);
+ }
+
+ return end;
+}
+
+/* parse_fencedcode 窶「 handles parsing of a block-level code fragment */
+static size_t
+parse_fencedcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, unsigned int flags)
+{
+ hoedown_buffer text = { 0, 0, 0, 0, NULL, NULL, NULL };
+ hoedown_buffer lang = { 0, 0, 0, 0, NULL, NULL, NULL };
+ size_t i = 0, text_start, line_start;
+ size_t w, w2;
+ size_t width, width2;
+ uint8_t chr, chr2;
+ hoedown_buffer attr = { 0, 0, 0, 0, NULL, NULL, NULL };
+
+
+ /* parse codefence line */
+ while (i < size && data[i] != '\n')
+ i++;
+
+ w = parse_codefence(data, i, &lang, &width, &chr, flags, &attr);
+ if (!w)
+ return 0;
+
+ /* search for end */
+ i++;
+ text_start = i;
+ while ((line_start = i) < size) {
+ while (i < size && data[i] != '\n')
+ i++;
+
+ w2 = is_codefence(data + line_start, i - line_start, &width2, &chr2);
+ if (w == w2 && width == width2 && chr == chr2 &&
+ is_empty(data + (line_start+w), i - (line_start+w)))
+ break;
+
+ i++;
+ }
+
+ text.data = data + text_start;
+ text.size = line_start - text_start;
+
+ if (doc->md.blockcode)
+ doc->md.blockcode(ob, text.size ? &text : NULL, lang.size ? &lang : NULL, attr.size ? &attr : NULL, &doc->data);
+
+ return i;
+}
+
+static size_t
+parse_blockcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
+{
+ size_t beg, end, pre;
+ hoedown_buffer *work = 0;
+ hoedown_buffer attr = { 0, 0, 0, 0, NULL, NULL, NULL };
+
+ work = newbuf(doc, BUFFER_BLOCK);
+
+ beg = 0;
+ while (beg < size) {
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {};
+ 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))
+ hoedown_buffer_putc(work, '\n');
+ else hoedown_buffer_put(work, data + beg, end - beg);
+ }
+ beg = end;
+ }
+
+ while (work->size && work->data[work->size - 1] == '\n')
+ work->size -= 1;
+
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ work->size = parse_attributes(work->data, work->size, NULL, &attr, 0);
+ }
+
+ hoedown_buffer_putc(work, '\n');
+
+ if (doc->md.blockcode)
+ doc->md.blockcode(ob, work, NULL, &attr, &doc->data);
+
+ popbuf(doc, BUFFER_BLOCK);
+ return beg;
+}
+
+/* parse_listitem 窶「 parsing of a single list item */
+/* assuming initial prefix is already removed */
+static size_t
+parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags *flags, hoedown_buffer *attribute)
+{
+ hoedown_buffer *work = 0, *inter = 0;
+ hoedown_buffer *attr = 0;
+ size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i, len;
+ int in_empty = 0, has_inside_empty = 0, in_fence = 0;
+
+ /* keeping track of the first indentation prefix */
+ while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')
+ orgpre++;
+
+ 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++;
+
+ /* getting working buffers */
+ work = newbuf(doc, BUFFER_SPAN);
+ inter = newbuf(doc, BUFFER_SPAN);
+
+ /* putting the first line into the working buffer */
+ hoedown_buffer_put(work, data + beg, end - beg);
+ beg = end;
+
+ attr = newbuf(doc, BUFFER_ATTRIBUTE);
+
+ /* process the following lines */
+ while (beg < size) {
+ size_t has_next_uli = 0, has_next_oli = 0;
+
+ end++;
+
+ while (end < size && data[end - 1] != '\n')
+ end++;
+
+ /* process an empty line */
+ if (is_empty(data + beg, end - beg)) {
+ in_empty = 1;
+ beg = end;
+ continue;
+ }
+
+ /* calculating the indentation */
+ i = 0;
+ while (i < 4 && beg + i < end && data[beg + i] == ' ')
+ i++;
+
+ pre = i;
+
+ if (doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) {
+ if (is_codefence(data + beg + i, end - beg - i, NULL, NULL))
+ in_fence = !in_fence;
+ }
+
+ /* Only check for new list items if we are **not** inside
+ * a fenced code block */
+ if (!in_fence) {
+ has_next_uli = prefix_uli(data + beg + i, end - beg - i);
+ has_next_oli = prefix_oli(data + beg + i, end - beg - i);
+ }
+
+ /* checking for a new item */
+ if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {
+ if (in_empty)
+ has_inside_empty = 1;
+
+ /* the following item must have the same (or less) indentation */
+ if (pre <= orgpre) {
+ /* if the following item has different list type, we end this list */
+ if (in_empty && (
+ ((*flags & HOEDOWN_LIST_ORDERED) && has_next_uli) ||
+ (!(*flags & HOEDOWN_LIST_ORDERED) && has_next_oli))) {
+ *flags |= HOEDOWN_LI_END;
+ has_inside_empty = 0;
+ }
+ break;
+ }
+
+ if (!sublist)
+ sublist = work->size;
+ }
+ /* joining only indented stuff after empty lines;
+ * note that now we only require 1 space of indentation
+ * to continue a list */
+ else if (in_empty && pre == 0) {
+ *flags |= HOEDOWN_LI_END;
+ break;
+ }
+
+ if (in_empty) {
+ hoedown_buffer_putc(work, '\n');
+ has_inside_empty = 1;
+ in_empty = 0;
+ }
+
+ /* adding the line without prefix into the working buffer */
+ hoedown_buffer_put(work, data + beg + i, end - beg - i);
+ beg = end;
+ }
+
+ /* render of li contents */
+ if (has_inside_empty)
+ *flags |= HOEDOWN_LI_BLOCK;
+
+ if (*flags & HOEDOWN_LI_BLOCK) {
+ /* intermediate render of block li */
+ if (sublist && sublist < work->size) {
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ len = parse_attributes(work->data, sublist, attr, attribute, 0);
+ } else {
+ len = sublist;
+ }
+ parse_block(inter, doc, work->data, len);
+ parse_block(inter, doc, work->data + sublist, work->size - sublist);
+ } else {
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ len = parse_attributes(work->data, work->size, attr, attribute, 0);
+ } else {
+ len = work->size;
+ }
+ parse_block(inter, doc, work->data, len);
+ }
+ } else {
+ /* intermediate render of inline li */
+ if (sublist && sublist < work->size) {
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ len = parse_attributes(work->data, sublist, attr, attribute, 0);
+ } else {
+ len = sublist;
+ }
+ parse_inline(inter, doc, work->data, len);
+ parse_block(inter, doc, work->data + sublist, work->size - sublist);
+ } else {
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ len = parse_attributes(work->data, work->size, attr, attribute, 0);
+ } else {
+ len = work->size;
+ }
+ parse_inline(inter, doc, work->data, len);
+ }
+ }
+
+ /* render of li itself */
+ if (doc->md.listitem)
+ doc->md.listitem(ob, inter, attr, flags, &doc->data);
+
+ popbuf(doc, BUFFER_SPAN);
+ popbuf(doc, BUFFER_SPAN);
+ popbuf(doc, BUFFER_ATTRIBUTE);
+ return beg;
+}
+
+
+/* parse_list 窶「 parsing ordered or unordered list block */
+static size_t
+parse_list(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags flags)
+{
+ hoedown_buffer *work = 0;
+ hoedown_buffer attr = { 0, 0, 0, 0, NULL, NULL, NULL };
+ size_t i = 0, j;
+
+ work = newbuf(doc, BUFFER_BLOCK);
+
+ while (i < size) {
+ j = parse_listitem(work, doc, data + i, size - i, &flags, &attr);
+ i += j;
+
+ if (!j || (flags & HOEDOWN_LI_END))
+ break;
+ }
+
+ if (doc->md.list)
+ doc->md.list(ob, work, &attr, flags, &doc->data);
+ popbuf(doc, BUFFER_BLOCK);
+ return i;
+}
+
+/* parse_atxheader 窶「 parsing of atx-style headers */
+static size_t
+parse_atxheader(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
+{
+ size_t level = 0;
+ size_t i, end, skip;
+
+ while (level < size && level < 6 && data[level] == '#')
+ level++;
+
+ for (i = level; i < size && data[i] == ' '; i++);
+
+ for (end = i; end < size && data[end] != '\n'; end++);
+ skip = end;
+
+ while (end && data[end - 1] == '#')
+ end--;
+
+ while (end && data[end - 1] == ' ')
+ end--;
+
+ if (end > i) {
+ hoedown_buffer *work = newbuf(doc, BUFFER_SPAN);
+ hoedown_buffer *attr = newbuf(doc, BUFFER_ATTRIBUTE);
+
+ parse_inline(work, doc, data + i, end - i);
+
+ if (doc->md.header) {
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ work->size = parse_attributes(work->data, work->size, attr, NULL, 1);
+ }
+ doc->md.header(ob, work, attr, (int)level, &doc->data);
+ }
+
+ popbuf(doc, BUFFER_SPAN);
+ popbuf(doc, BUFFER_ATTRIBUTE);
+ }
+
+ return skip;
+}
+
+/* parse_footnote_def 窶「 parse a single footnote definition */
+static void
+parse_footnote_def(hoedown_buffer *ob, hoedown_document *doc, unsigned int num, uint8_t *data, size_t size)
+{
+ hoedown_buffer *work = 0;
+ work = newbuf(doc, BUFFER_SPAN);
+
+ parse_block(work, doc, data, size);
+
+ if (doc->md.footnote_def)
+ doc->md.footnote_def(ob, work, num, &doc->data);
+ popbuf(doc, BUFFER_SPAN);
+}
+
+/* parse_footnote_list 窶「 render the contents of the footnotes */
+static void
+parse_footnote_list(hoedown_buffer *ob, hoedown_document *doc, struct footnote_list *footnotes)
+{
+ hoedown_buffer *work = 0;
+ struct footnote_item *item;
+ struct footnote_ref *ref;
+
+ if (footnotes->count == 0)
+ return;
+
+ work = newbuf(doc, BUFFER_BLOCK);
+
+ item = footnotes->head;
+ while (item) {
+ ref = item->ref;
+ parse_footnote_def(work, doc, ref->num, ref->contents->data, ref->contents->size);
+ item = item->next;
+ }
+
+ if (doc->md.footnotes)
+ doc->md.footnotes(ob, work, &doc->data);
+ popbuf(doc, BUFFER_BLOCK);
+}
+
+/* htmlblock_is_end 窶「 check for end of HTML block : </tag>( *)\n */
+/* returns tag length on match, 0 otherwise */
+/* assumes data starts with "<" */
+static size_t
+htmlblock_is_end(
+ const char *tag,
+ size_t tag_len,
+ hoedown_document *doc,
+ uint8_t *data,
+ size_t size)
+{
+ size_t i = tag_len + 3, w;
+
+ /* try to match the end tag */
+ /* note: we're not considering tags like "</tag >" which are still valid */
+ if (i > size ||
+ data[1] != '/' ||
+ strncasecmp((char *)data + 2, tag, tag_len) != 0 ||
+ data[tag_len + 2] != '>')
+ return 0;
+
+ /* rest of the line must be empty */
+ if ((w = is_empty(data + i, size - i)) == 0 && i < size)
+ return 0;
+
+ return i + w;
+}
+
+/* htmlblock_find_end 窶「 try to find HTML block ending tag */
+/* returns the length on match, 0 otherwise */
+static size_t
+htmlblock_find_end(
+ const char *tag,
+ size_t tag_len,
+ hoedown_document *doc,
+ uint8_t *data,
+ size_t size)
+{
+ size_t i = 0, w;
+
+ while (1) {
+ while (i < size && data[i] != '<') i++;
+ if (i >= size) return 0;
+
+ w = htmlblock_is_end(tag, tag_len, doc, data + i, size - i);
+ if (w) return i + w;
+ i++;
+ }
+}
+
+/* htmlblock_find_end_strict 窶「 try to find end of HTML block in strict mode */
+/* (it must be an unindented line, and have a blank line afterwads) */
+/* returns the length on match, 0 otherwise */
+static size_t
+htmlblock_find_end_strict(
+ const char *tag,
+ size_t tag_len,
+ hoedown_document *doc,
+ uint8_t *data,
+ size_t size)
+{
+ size_t i = 0, mark;
+
+ while (1) {
+ mark = i;
+ while (i < size && data[i] != '\n') i++;
+ if (i < size) i++;
+ if (i == mark) return 0;
+
+ if (data[mark] == ' ' && mark > 0) continue;
+ mark += htmlblock_find_end(tag, tag_len, doc, data + mark, i - mark);
+ if (mark == i && (is_empty(data + i, size - i) || i >= size)) break;
+ }
+
+ return i;
+}
+
+/* parse_htmlblock 窶「 parsing of inline HTML block */
+static size_t
+parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render)
+{
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ size_t i, j = 0, tag_len, tag_end;
+ const char *curtag = NULL;
+ int meta = 0;
+
+ work.data = data;
+
+ /* identification of the opening tag */
+ if (size < 2 || data[0] != '<')
+ return 0;
+
+ i = 1;
+ while (i < size && data[i] != '>' && data[i] != ' ')
+ i++;
+
+ if (i < size)
+ curtag = hoedown_find_block_tag((char *)data + 1, (int)i - 1);
+
+ /* handling of special cases */
+ if (!curtag) {
+
+ /* HTML comment, laxist form */
+ if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') {
+ i = 5;
+
+ if (data[4] == '*') {
+ meta++;
+ }
+
+ while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>'))
+ i++;
+
+ if (data[i - 3] == '*') {
+ meta++;
+ }
+
+ i++;
+
+ if (i < size)
+ j = is_empty(data + i, size - i);
+
+ if (j) {
+ work.size = i + j;
+
+ if (doc->ext_flags & HOEDOWN_EXT_META_BLOCK &&
+ meta == 2 && doc->meta) {
+ size_t org, sz;
+
+ sz = work.size - 5;
+ while (sz > 0 && work.data[sz - 1] == '\n') {
+ sz--;
+ }
+
+ org = 5;
+ while (org < sz && work.data[org] == '\n') {
+ org++;
+ }
+
+ if (org < sz) {
+ hoedown_buffer_put(doc->meta, work.data + org, sz - org);
+ hoedown_buffer_putc(doc->meta, '\n');
+ }
+ } else if (do_render && doc->md.blockhtml) {
+ doc->md.blockhtml(ob, &work, &doc->data);
+ }
+ return work.size;
+ }
+ }
+
+ /* HR, which is the only self-closing block tag considered */
+ if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {
+ i = 3;
+ while (i < size && data[i] != '>')
+ i++;
+
+ if (i + 1 < size) {
+ i++;
+ j = is_empty(data + i, size - i);
+ if (j) {
+ work.size = i + j;
+ if (do_render && doc->md.blockhtml)
+ doc->md.blockhtml(ob, &work, &doc->data);
+ return work.size;
+ }
+ }
+ }
+
+ /* Extension script tags */
+ if (doc->ext_flags & HOEDOWN_EXT_SCRIPT_TAGS) {
+ i = script_tag_length(data, size);
+ if (i) {
+ if (i < size) {
+ j = is_empty(data + i, size - i);
+ }
+
+ if (j) {
+ work.size = i + j;
+ if (do_render && doc->md.blockhtml) {
+ doc->md.blockhtml(ob, &work, &doc->data);
+ }
+ return work.size;
+ }
+ }
+
+ }
+
+ /* no special case recognised */
+ return 0;
+ }
+
+ /* looking for a matching closing tag in strict mode */
+ tag_len = strlen(curtag);
+ tag_end = htmlblock_find_end_strict(curtag, tag_len, doc, data, size);
+
+ /* if not found, trying a second pass looking for indented match */
+ /* but not if tag is "ins" or "del" (following original Markdown.pl) */
+ if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0)
+ tag_end = htmlblock_find_end(curtag, tag_len, doc, data, size);
+
+ if (!tag_end)
+ return 0;
+
+ /* the end of the block has been found */
+ work.size = tag_end;
+ if (do_render && doc->md.blockhtml)
+ doc->md.blockhtml(ob, &work, &doc->data);
+
+ return tag_end;
+}
+
+static void
+parse_table_row(
+ hoedown_buffer *ob,
+ hoedown_document *doc,
+ uint8_t *data,
+ size_t size,
+ size_t columns,
+ hoedown_table_flags *col_data,
+ hoedown_table_flags header_flag)
+{
+ size_t i = 0, col, len;
+ hoedown_buffer *row_work = 0;
+
+ if (!doc->md.table_cell || !doc->md.table_row)
+ return;
+
+ row_work = newbuf(doc, BUFFER_SPAN);
+
+ if (i < size && data[i] == '|')
+ i++;
+
+ for (col = 0; col < columns && i < size; ++col) {
+ size_t cell_start, cell_end;
+ hoedown_buffer *cell_work;
+
+ cell_work = newbuf(doc, BUFFER_SPAN);
+
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ cell_start = i;
+
+ len = find_emph_char(data + i, size - i, '|');
+ i += len ? len : size - i;
+
+ cell_end = i - 1;
+
+ while (cell_end > cell_start && _isspace(data[cell_end]))
+ cell_end--;
+
+ parse_inline(cell_work, doc, data + cell_start, 1 + cell_end - cell_start);
+ doc->md.table_cell(row_work, cell_work, col_data[col] | header_flag, &doc->data);
+
+ popbuf(doc, BUFFER_SPAN);
+ i++;
+ }
+
+ for (; col < columns; ++col) {
+ hoedown_buffer empty_cell = { 0, 0, 0, 0, NULL, NULL, NULL };
+ doc->md.table_cell(row_work, &empty_cell, col_data[col] | header_flag, &doc->data);
+ }
+
+ doc->md.table_row(ob, row_work, &doc->data);
+
+ popbuf(doc, BUFFER_SPAN);
+}
+
+static size_t
+parse_table_header(
+ hoedown_buffer *ob,
+ hoedown_buffer *attr,
+ hoedown_document *doc,
+ uint8_t *data,
+ size_t size,
+ size_t *columns,
+ hoedown_table_flags **column_data)
+{
+ int pipes;
+ size_t i = 0, col, header_end, under_end;
+
+ pipes = 0;
+ while (i < size && data[i] != '\n')
+ if (data[i++] == '|')
+ pipes++;
+
+ if (i == size || pipes == 0)
+ return 0;
+
+ header_end = i;
+
+ while (header_end > 0 && _isspace(data[header_end - 1]))
+ header_end--;
+
+ if (data[0] == '|')
+ pipes--;
+
+ if (header_end && data[header_end - 1] == '|')
+ pipes--;
+
+ if (doc->ext_flags & HOEDOWN_EXT_SPECIAL_ATTRIBUTE) {
+ if (header_end && data[header_end - 1] == '}') {
+ size_t n = header_end - 1;
+ while (n > 0 && data[n] != '{')
+ n--;
+
+ hoedown_buffer_put(attr, &data[n+1], header_end - n - 2);
+
+ while (n > 0 && _isspace(data[n-1]))
+ n--;
+
+ if (n && data[n - 1] == '|')
+ pipes--;
+
+ header_end = n + 1;
+ }
+ }
+
+ if (pipes < 0)
+ return 0;
+
+ *columns = pipes + 1;
+ *column_data = hoedown_calloc(*columns, sizeof(hoedown_table_flags));
+
+ /* Parse the header underline */
+ i++;
+ if (i < size && data[i] == '|')
+ i++;
+
+ under_end = i;
+ while (under_end < size && data[under_end] != '\n')
+ under_end++;
+
+ for (col = 0; col < *columns && i < under_end; ++col) {
+ size_t dashes = 0;
+
+ while (i < under_end && data[i] == ' ')
+ i++;
+
+ if (data[i] == ':') {
+ i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_LEFT;
+ dashes++;
+ }
+
+ while (i < under_end && data[i] == '-') {
+ i++; dashes++;
+ }
+
+ if (i < under_end && data[i] == ':') {
+ i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_RIGHT;
+ dashes++;
+ }
+
+ while (i < under_end && data[i] == ' ')
+ i++;
+
+ if (i < under_end && data[i] != '|' && data[i] != '+')
+ break;
+
+ if (dashes < 3)
+ break;
+
+ i++;
+ }
+
+ if (col < *columns)
+ return 0;
+
+ parse_table_row(
+ ob, doc, data,
+ header_end,
+ *columns,
+ *column_data,
+ HOEDOWN_TABLE_HEADER
+ );
+
+ return under_end + 1;
+}
+
+static size_t
+parse_table(
+ hoedown_buffer *ob,
+ hoedown_document *doc,
+ uint8_t *data,
+ size_t size)
+{
+ size_t i;
+
+ hoedown_buffer *work = 0;
+ hoedown_buffer *header_work = 0;
+ hoedown_buffer *body_work = 0;
+ hoedown_buffer *attr_work = 0;
+
+ size_t columns;
+ hoedown_table_flags *col_data = NULL;
+
+ work = newbuf(doc, BUFFER_BLOCK);
+ header_work = newbuf(doc, BUFFER_SPAN);
+ body_work = newbuf(doc, BUFFER_BLOCK);
+ attr_work = newbuf(doc, BUFFER_ATTRIBUTE);
+ i = parse_table_header(header_work, attr_work, doc, data, size, &columns, &col_data);
+ if (i > 0) {
+
+ while (i < size) {
+ size_t row_start;
+ int pipes = 0;
+
+ row_start = i;
+
+ while (i < size && data[i] != '\n')
+ if (data[i++] == '|')
+ pipes++;
+
+ if (pipes == 0 || i == size) {
+ i = row_start;
+ break;
+ }
+
+ parse_table_row(
+ body_work,
+ doc,
+ data + row_start,
+ i - row_start,
+ columns,
+ col_data, 0
+ );
+
+ i++;
+ }
+
+ if (doc->md.table_header)
+ doc->md.table_header(work, header_work, &doc->data);
+
+ if (doc->md.table_body)
+ doc->md.table_body(work, body_work, &doc->data);
+
+ if (doc->md.table)
+ doc->md.table(ob, work, attr_work, &doc->data);
+ }
+
+ free(col_data);
+ popbuf(doc, BUFFER_SPAN);
+ popbuf(doc, BUFFER_BLOCK);
+ popbuf(doc, BUFFER_BLOCK);
+ popbuf(doc, BUFFER_ATTRIBUTE);
+ return i;
+}
+
+/* parse_userblock 窶「 parsing of user block */
+static size_t
+parse_userblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
+{
+ hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL };
+ size_t len = doc->user_block(data, size, &doc->data);
+
+ if (!len) {
+ return 0;
+ }
+
+ work.data = data;
+ work.size = len;
+
+ if (doc->md.user_block) {
+ doc->md.user_block(ob, &work, &doc->data);
+ } else {
+ hoedown_buffer_put(ob, work.data, work.size);
+ }
+ return len;
+}
+
+/* parse_block 窶「 parsing of one block, returning next uint8_t to parse */
+static void
+parse_block(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
+{
+ size_t beg, end, i;
+ uint8_t *txt_data;
+ beg = 0;
+
+ if (doc->work_bufs[BUFFER_SPAN].size +
+ doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting)
+ return;
+
+ while (beg < size) {
+ txt_data = data + beg;
+ end = size - beg;
+
+ if (is_atxheader(doc, txt_data, end))
+ beg += parse_atxheader(ob, doc, txt_data, end);
+
+ else if (doc->user_block &&
+ (i = parse_userblock(ob, doc, txt_data, end)) != 0)
+ beg += i;
+
+ else if (data[beg] == '<' && doc->md.blockhtml &&
+ (i = parse_htmlblock(ob, doc, txt_data, end, 1)) != 0)
+ beg += i;
+
+ else if ((i = is_empty(txt_data, end)) != 0)
+ beg += i;
+
+ else if (is_hrule(txt_data, end)) {
+ if (doc->md.hrule)
+ doc->md.hrule(ob, &doc->data);
+
+ while (beg < size && data[beg] != '\n')
+ beg++;
+
+ beg++;
+ }
+
+ else if ((doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) != 0 &&
+ (i = parse_fencedcode(ob, doc, txt_data, end, doc->ext_flags)) != 0)
+ beg += i;
+
+ else if ((doc->ext_flags & HOEDOWN_EXT_TABLES) != 0 &&
+ (i = parse_table(ob, doc, txt_data, end)) != 0)
+ beg += i;
+
+ else if (prefix_quote(txt_data, end))
+ beg += parse_blockquote(ob, doc, txt_data, end);
+
+ else if (!(doc->ext_flags & HOEDOWN_EXT_DISABLE_INDENTED_CODE) && prefix_code(txt_data, end))
+ beg += parse_blockcode(ob, doc, txt_data, end);
+
+ else if (prefix_uli(txt_data, end))
+ beg += parse_list(ob, doc, txt_data, end, 0);
+
+ else if (prefix_oli(txt_data, end))
+ beg += parse_list(ob, doc, txt_data, end, HOEDOWN_LIST_ORDERED);
+
+ else
+ beg += parse_paragraph(ob, doc, txt_data, end);
+ }
+}
+
+
+
+/*********************
+ * REFERENCE PARSING *
+ *********************/
+
+/* is_footnote 窶「 returns whether a line is a footnote definition or not */
+static int
+is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, struct footnote_list *list)
+{
+ size_t i = 0;
+ hoedown_buffer *contents = 0;
+ size_t ind = 0;
+ int in_empty = 0;
+ size_t start = 0;
+
+ size_t id_offset, id_end;
+
+ /* up to 3 optional leading spaces */
+ if (beg + 3 >= end) return 0;
+ if (data[beg] == ' ') { i = 1;
+ if (data[beg + 1] == ' ') { i = 2;
+ if (data[beg + 2] == ' ') { i = 3;
+ if (data[beg + 3] == ' ') return 0; } } }
+ i += beg;
+
+ /* id part: caret followed by anything between brackets */
+ if (data[i] != '[') return 0;
+ i++;
+ if (i >= end || data[i] != '^') return 0;
+ i++;
+ id_offset = i;
+ while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
+ i++;
+ if (i >= end || data[i] != ']') return 0;
+ id_end = i;
+
+ /* spacer: colon (space | tab)* newline? (space | tab)* */
+ i++;
+ if (i >= end || data[i] != ':') return 0;
+ i++;
+
+ /* getting content buffer */
+ contents = hoedown_buffer_new(64);
+
+ start = i;
+
+ /* process lines similar to a list item */
+ while (i < end) {
+ while (i < end && data[i] != '\n' && data[i] != '\r') i++;
+
+ /* process an empty line */
+ if (is_empty(data + start, i - start)) {
+ in_empty = 1;
+ if (i < end && (data[i] == '\n' || data[i] == '\r')) {
+ i++;
+ if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++;
+ }
+ start = i;
+ continue;
+ }
+
+ /* calculating the indentation */
+ ind = 0;
+ while (ind < 4 && start + ind < end && data[start + ind] == ' ')
+ ind++;
+
+ /* joining only indented stuff after empty lines;
+ * note that now we only require 1 space of indentation
+ * to continue, just like lists */
+ if (ind == 0) {
+ if (start == id_end + 2 && data[start] == '\t') {}
+ else break;
+ }
+ else if (in_empty) {
+ hoedown_buffer_putc(contents, '\n');
+ }
+
+ in_empty = 0;
+
+ /* adding the line into the content buffer */
+ hoedown_buffer_put(contents, data + start + ind, i - start - ind);
+ /* add carriage return */
+ if (i < end) {
+ hoedown_buffer_putc(contents, '\n');
+ if (i < end && (data[i] == '\n' || data[i] == '\r')) {
+ i++;
+ if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++;
+ }
+ }
+ start = i;
+ }
+
+ if (last)
+ *last = start;
+
+ if (list) {
+ struct footnote_ref *ref;
+ ref = create_footnote_ref(list, data + id_offset, id_end - id_offset);
+ if (!ref)
+ return 0;
+ if (!add_footnote_ref(list, ref)) {
+ free_footnote_ref(ref);
+ return 0;
+ }
+ ref->contents = contents;
+ }
+
+ return 1;
+}
+
+/* is_ref 窶「 returns whether a line is a reference or not */
+static int
+is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)
+{
+/* int n; */
+ size_t i = 0;
+ size_t id_offset, id_end;
+ size_t link_offset, link_end;
+ size_t title_offset, title_end;
+ size_t line_end;
+ size_t attr_offset = 0, attr_end = 0;
+
+ /* up to 3 optional leading spaces */
+ if (beg + 3 >= end) return 0;
+ if (data[beg] == ' ') { i = 1;
+ if (data[beg + 1] == ' ') { i = 2;
+ if (data[beg + 2] == ' ') { i = 3;
+ if (data[beg + 3] == ' ') return 0; } } }
+ i += beg;
+
+ /* id part: anything but a newline between brackets */
+ if (data[i] != '[') return 0;
+ i++;
+ id_offset = i;
+ while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
+ i++;
+ if (i >= end || data[i] != ']') return 0;
+ id_end = i;
+
+ /* spacer: colon (space | tab)* newline? (space | tab)* */
+ i++;
+ if (i >= end || data[i] != ':') return 0;
+ i++;
+ while (i < end && data[i] == ' ') i++;
+ if (i < end && (data[i] == '\n' || data[i] == '\r')) {
+ i++;
+ if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
+ while (i < end && data[i] == ' ') i++;
+ if (i >= end) return 0;
+
+ /* link: spacing-free sequence, optionally between angle brackets */
+ if (data[i] == '<')
+ i++;
+
+ link_offset = i;
+
+ while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')
+ i++;
+
+ if (data[i - 1] == '>') link_end = i - 1;
+ else link_end = i;
+
+ /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
+ while (i < end && data[i] == ' ') i++;
+ if (i < end && data[i] != '\n' && data[i] != '\r'
+ && data[i] != '\'' && data[i] != '"' && data[i] != '(')
+ return 0;
+ line_end = 0;
+ /* computing end-of-line */
+ if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i;
+ if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
+ line_end = i + 1;
+
+ /* optional (space|tab)* spacer after a newline */
+ if (line_end) {
+ i = line_end + 1;
+ while (i < end && data[i] == ' ') i++; }
+
+ /* optional title: any non-newline sequence enclosed in '"()
+ alone on its line */
+ title_offset = title_end = 0;
+ if (i + 1 < end
+ && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {
+ char d = data[i++];
+ title_offset = i;
+
+ /* looking for end of tile */
+ while (i < end && data[i] != d && data[i] != '\n' && data[i] != '\r') {
+ ++i;
+ }
+
+ if (i + 1 < end && data[i] == d) {
+ title_end = i++;
+ attr_offset = i;
+
+ /* looking for EOL */
+ while (i < end && data[i] != '\n' && data[i] != '\r') {
+ i++;
+ }
+
+ /* looking for attribute */
+ if (data[i-1] == '}' &&
+ memchr(&data[attr_offset], '{', i - attr_offset)) {
+ while (attr_offset < i && data[attr_offset] != '{') {
+ ++attr_offset;
+ }
+ ++attr_offset;
+ attr_end = i - 1;
+ } else {
+ if (data[i-1] == d) {
+ title_end = i - 1;
+ } else {
+ title_end = i;
+ }
+ attr_offset = 0;
+ attr_end = 0;
+ }
+ if (i + 1 < end && data[i] == '\r' && data[i + 1] == '\n') {
+ ++i;
+ }
+
+ line_end = i;
+ } else {
+ /* looking for EOL */
+ while (i < end && data[i] != '\n' && data[i] != '\r') {
+ i++;
+ }
+ if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') {
+ title_end = i + 1;
+ } else {
+ title_end = i;
+ }
+ /* stepping back */
+ i -= 1;
+ while (i > title_offset && data[i] == ' ') {
+ i -= 1;
+ }
+ if (i > title_offset &&
+ (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
+ line_end = title_end;
+ title_end = i;
+ }
+ }
+ }
+
+ if (!line_end || link_end == link_offset)
+ return 0; /* garbage after the link empty link */
+
+ /* a valid ref has been found, filling-in return structures */
+ if (last)
+ *last = line_end;
+
+ if (refs) {
+ struct link_ref *ref;
+
+ ref = add_link_ref(refs, data + id_offset, id_end - id_offset);
+ if (!ref)
+ return 0;
+
+ ref->link = hoedown_buffer_new(link_end - link_offset);
+ hoedown_buffer_put(ref->link, data + link_offset, link_end - link_offset);
+
+ if (title_end > title_offset) {
+ ref->title = hoedown_buffer_new(title_end - title_offset);
+ hoedown_buffer_put(ref->title, data + title_offset, title_end - title_offset);
+ }
+ if (attr_end > attr_offset) {
+ ref->attr = hoedown_buffer_new(attr_end - attr_offset);
+ hoedown_buffer_put(ref->attr, data + attr_offset, attr_end - attr_offset);
+ }
+ }
+
+ return 1;
+}
+
+static void expand_tabs(hoedown_buffer *ob, const uint8_t *line, size_t size)
+{
+ /* This code makes two assumptions:
+ * - Input is valid UTF-8. (Any byte with top two bits 10 is skipped,
+ * whether or not it is a valid UTF-8 continuation byte.)
+ * - Input contains no combining characters. (Combining characters
+ * should be skipped but are not.)
+ */
+ size_t i = 0, tab = 0;
+
+ while (i < size) {
+ size_t org = i;
+
+ while (i < size && line[i] != '\t') {
+ /* ignore UTF-8 continuation bytes */
+ if ((line[i] & 0xc0) != 0x80)
+ tab++;
+ i++;
+ }
+
+ if (i > org)
+ hoedown_buffer_put(ob, line + org, i - org);
+
+ if (i >= size)
+ break;
+
+ do {
+ hoedown_buffer_putc(ob, ' '); tab++;
+ } while (tab % 4);
+
+ i++;
+ }
+}
+
+/**********************
+ * EXPORTED FUNCTIONS *
+ **********************/
+
+hoedown_document *
+hoedown_document_new(
+ const hoedown_renderer *renderer,
+ hoedown_extensions extensions,
+ size_t max_nesting,
+ hoedown_user_block user_block,
+ hoedown_buffer *meta)
+{
+ hoedown_document *doc = NULL;
+
+ assert(max_nesting > 0 && renderer);
+
+ doc = hoedown_malloc(sizeof(hoedown_document));
+ memcpy(&doc->md, renderer, sizeof(hoedown_renderer));
+
+ doc->data.opaque = renderer->opaque;
+
+ hoedown_stack_init(&doc->work_bufs[BUFFER_BLOCK], 4);
+ hoedown_stack_init(&doc->work_bufs[BUFFER_SPAN], 8);
+ hoedown_stack_init(&doc->work_bufs[BUFFER_ATTRIBUTE], 8);
+
+ memset(doc->active_char, 0x0, 256);
+
+ if (extensions & HOEDOWN_EXT_UNDERLINE && doc->md.underline) {
+ doc->active_char['_'] = MD_CHAR_EMPHASIS;
+ }
+
+ if (doc->md.emphasis || doc->md.double_emphasis || doc->md.triple_emphasis) {
+ doc->active_char['*'] = MD_CHAR_EMPHASIS;
+ doc->active_char['_'] = MD_CHAR_EMPHASIS;
+ if (extensions & HOEDOWN_EXT_STRIKETHROUGH)
+ doc->active_char['~'] = MD_CHAR_EMPHASIS;
+ if (extensions & HOEDOWN_EXT_HIGHLIGHT)
+ doc->active_char['='] = MD_CHAR_EMPHASIS;
+ }
+
+ if (doc->md.codespan)
+ doc->active_char['`'] = MD_CHAR_CODESPAN;
+
+ if (doc->md.linebreak)
+ doc->active_char['\n'] = MD_CHAR_LINEBREAK;
+
+ if (doc->md.image || doc->md.link || doc->md.footnotes || doc->md.footnote_ref)
+ doc->active_char['['] = MD_CHAR_LINK;
+
+ doc->active_char['<'] = MD_CHAR_LANGLE;
+ doc->active_char['\\'] = MD_CHAR_ESCAPE;
+ doc->active_char['&'] = MD_CHAR_ENTITY;
+
+ if (extensions & HOEDOWN_EXT_AUTOLINK) {
+ doc->active_char[':'] = MD_CHAR_AUTOLINK_URL;
+ doc->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
+ doc->active_char['w'] = MD_CHAR_AUTOLINK_WWW;
+ }
+
+ if (extensions & HOEDOWN_EXT_SUPERSCRIPT)
+ doc->active_char['^'] = MD_CHAR_SUPERSCRIPT;
+
+ if (extensions & HOEDOWN_EXT_QUOTE)
+ doc->active_char['"'] = MD_CHAR_QUOTE;
+
+ if (extensions & HOEDOWN_EXT_MATH)
+ doc->active_char['$'] = MD_CHAR_MATH;
+
+ /* Extension data */
+ doc->ext_flags = extensions;
+ doc->max_nesting = max_nesting;
+ doc->in_link_body = 0;
+ doc->user_block = user_block;
+ doc->meta = meta;
+
+ return doc;
+}
+
+void
+hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size)
+{
+ static const uint8_t UTF8_BOM[] = {0xEF, 0xBB, 0xBF};
+
+ hoedown_buffer *text;
+ size_t beg, end;
+
+ int footnotes_enabled;
+
+ text = hoedown_buffer_new(64);
+
+ /* Preallocate enough space for our buffer to avoid expanding while copying */
+ hoedown_buffer_grow(text, size);
+
+ /* reset the references table */
+ memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
+
+ footnotes_enabled = doc->ext_flags & HOEDOWN_EXT_FOOTNOTES;
+
+ /* reset the footnotes lists */
+ if (footnotes_enabled) {
+ memset(&doc->footnotes_found, 0x0, sizeof(doc->footnotes_found));
+ memset(&doc->footnotes_used, 0x0, sizeof(doc->footnotes_used));
+ }
+
+ /* first pass: looking for references, copying everything else */
+ beg = 0;
+
+ /* Skip a possible UTF-8 BOM, even though the Unicode standard
+ * discourages having these in UTF-8 documents */
+ if (size >= 3 && memcmp(data, UTF8_BOM, 3) == 0)
+ beg += 3;
+
+ while (beg < size) /* iterating over lines */
+ if (footnotes_enabled && is_footnote(data, beg, size, &end, &doc->footnotes_found))
+ beg = end;
+ else if (is_ref(data, beg, size, &end, doc->refs))
+ beg = end;
+ else { /* skipping to the next line */
+ end = beg;
+ while (end < size && data[end] != '\n' && data[end] != '\r')
+ end++;
+
+ /* adding the line body if present */
+ if (end > beg)
+ expand_tabs(text, data + beg, end - beg);
+
+ while (end < size && (data[end] == '\n' || data[end] == '\r')) {
+ /* add one \n per newline */
+ if (data[end] == '\n' || (end + 1 < size && data[end + 1] != '\n'))
+ hoedown_buffer_putc(text, '\n');
+ end++;
+ }
+
+ beg = end;
+ }
+
+ /* pre-grow the output buffer to minimize allocations */
+ hoedown_buffer_grow(ob, text->size + (text->size >> 1));
+
+ /* second pass: actual rendering */
+ if (doc->md.doc_header)
+ doc->md.doc_header(ob, 0, &doc->data);
+
+ if (text->size) {
+ /* adding a final newline if not already present */
+ if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r')
+ hoedown_buffer_putc(text, '\n');
+
+ parse_block(ob, doc, text->data, text->size);
+ }
+
+ /* footnotes */
+ if (footnotes_enabled)
+ parse_footnote_list(ob, doc, &doc->footnotes_used);
+
+ if (doc->md.doc_footer)
+ doc->md.doc_footer(ob, 0, &doc->data);
+
+ /* clean-up */
+ hoedown_buffer_free(text);
+ free_link_refs(doc->refs);
+ if (footnotes_enabled) {
+ free_footnote_list(&doc->footnotes_found, 1);
+ free_footnote_list(&doc->footnotes_used, 0);
+ }
+
+ assert(doc->work_bufs[BUFFER_SPAN].size == 0);
+ assert(doc->work_bufs[BUFFER_BLOCK].size == 0);
+ assert(doc->work_bufs[BUFFER_ATTRIBUTE].size == 0);
+}
+
+void
+hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size)
+{
+ size_t i = 0, mark;
+ hoedown_buffer *text = hoedown_buffer_new(64);
+
+ /* reset the references table */
+ memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
+
+ /* first pass: expand tabs and process newlines */
+ hoedown_buffer_grow(text, size);
+ while (1) {
+ mark = i;
+ while (i < size && data[i] != '\n' && data[i] != '\r')
+ i++;
+
+ expand_tabs(text, data + mark, i - mark);
+
+ if (i >= size)
+ break;
+
+ while (i < size && (data[i] == '\n' || data[i] == '\r')) {
+ /* add one \n per newline */
+ if (data[i] == '\n' || (i + 1 < size && data[i + 1] != '\n'))
+ hoedown_buffer_putc(text, '\n');
+ i++;
+ }
+ }
+
+ /* second pass: actual rendering */
+ hoedown_buffer_grow(ob, text->size + (text->size >> 1));
+
+ if (doc->md.doc_header)
+ doc->md.doc_header(ob, 1, &doc->data);
+
+ parse_inline(ob, doc, text->data, text->size);
+
+ if (doc->md.doc_footer)
+ doc->md.doc_footer(ob, 1, &doc->data);
+
+ /* clean-up */
+ hoedown_buffer_free(text);
+
+ assert(doc->work_bufs[BUFFER_SPAN].size == 0);
+ assert(doc->work_bufs[BUFFER_BLOCK].size == 0);
+}
+
+void
+hoedown_document_free(hoedown_document *doc)
+{
+ size_t i;
+
+ for (i = 0; i < (size_t)doc->work_bufs[BUFFER_SPAN].asize; ++i)
+ hoedown_buffer_free(doc->work_bufs[BUFFER_SPAN].item[i]);
+
+ for (i = 0; i < (size_t)doc->work_bufs[BUFFER_BLOCK].asize; ++i)
+ hoedown_buffer_free(doc->work_bufs[BUFFER_BLOCK].item[i]);
+
+ for (i = 0; i < (size_t)doc->work_bufs[BUFFER_ATTRIBUTE].asize; ++i)
+ hoedown_buffer_free(doc->work_bufs[BUFFER_ATTRIBUTE].item[i]);
+
+ hoedown_stack_uninit(&doc->work_bufs[BUFFER_SPAN]);
+ hoedown_stack_uninit(&doc->work_bufs[BUFFER_BLOCK]);
+ hoedown_stack_uninit(&doc->work_bufs[BUFFER_ATTRIBUTE]);
+
+ free(doc);
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/document.h b/ext/phalcon/mvc/view/engine/markdown/document.h
new file mode 100644
index 0000000..7f48149
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/document.h
@@ -0,0 +1,211 @@
+/* document.h - generic markdown parser */
+
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HOEDOWN_DOCUMENT_H
+#define HOEDOWN_DOCUMENT_H
+
+#include "buffer.h"
+#include "autolink.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************
+ * CONSTANTS *
+ *************/
+
+typedef enum hoedown_extensions {
+ /* block-level extensions */
+ HOEDOWN_EXT_TABLES = (1 << 0),
+ HOEDOWN_EXT_FENCED_CODE = (1 << 1),
+ HOEDOWN_EXT_FOOTNOTES = (1 << 2),
+
+ /* span-level extensions */
+ HOEDOWN_EXT_AUTOLINK = (1 << 3),
+ HOEDOWN_EXT_STRIKETHROUGH = (1 << 4),
+ HOEDOWN_EXT_UNDERLINE = (1 << 5),
+ HOEDOWN_EXT_HIGHLIGHT = (1 << 6),
+ HOEDOWN_EXT_QUOTE = (1 << 7),
+ HOEDOWN_EXT_SUPERSCRIPT = (1 << 8),
+ HOEDOWN_EXT_MATH = (1 << 9),
+
+ /* other flags */
+ HOEDOWN_EXT_NO_INTRA_EMPHASIS = (1 << 11),
+ HOEDOWN_EXT_SPACE_HEADERS = (1 << 12),
+ HOEDOWN_EXT_MATH_EXPLICIT = (1 << 13),
+
+ /* negative flags */
+ HOEDOWN_EXT_DISABLE_INDENTED_CODE = (1 << 14),
+
+ /* special attribute */
+ HOEDOWN_EXT_SPECIAL_ATTRIBUTE = (1 << 15),
+
+ /* script tags */
+ HOEDOWN_EXT_SCRIPT_TAGS = (1 << 16),
+
+ /* meta block */
+ HOEDOWN_EXT_META_BLOCK = (1 << 17)
+} hoedown_extensions;
+
+#define HOEDOWN_EXT_BLOCK (\
+ HOEDOWN_EXT_TABLES |\
+ HOEDOWN_EXT_FENCED_CODE |\
+ HOEDOWN_EXT_FOOTNOTES )
+
+#define HOEDOWN_EXT_SPAN (\
+ HOEDOWN_EXT_AUTOLINK |\
+ HOEDOWN_EXT_STRIKETHROUGH |\
+ HOEDOWN_EXT_UNDERLINE |\
+ HOEDOWN_EXT_HIGHLIGHT |\
+ HOEDOWN_EXT_QUOTE |\
+ HOEDOWN_EXT_SUPERSCRIPT |\
+ HOEDOWN_EXT_MATH )
+
+#define HOEDOWN_EXT_FLAGS (\
+ HOEDOWN_EXT_NO_INTRA_EMPHASIS |\
+ HOEDOWN_EXT_SPACE_HEADERS |\
+ HOEDOWN_EXT_MATH_EXPLICIT |\
+ HOEDOWN_EXT_SPECIAL_ATTRIBUTE |\
+ HOEDOWN_EXT_SCRIPT_TAGS |\
+ HOEDOWN_EXT_META_BLOCK )
+
+#define HOEDOWN_EXT_NEGATIVE (\
+ HOEDOWN_EXT_DISABLE_INDENTED_CODE )
+
+typedef enum hoedown_list_flags {
+ HOEDOWN_LIST_ORDERED = (1 << 0),
+ HOEDOWN_LI_BLOCK = (1 << 1), /* <li> containing block data */
+ HOEDOWN_LI_TASK = (1 << 2),
+ HOEDOWN_LI_END = (1 << 3) /* internal list flag */
+} hoedown_list_flags;
+
+typedef enum hoedown_table_flags {
+ HOEDOWN_TABLE_ALIGN_LEFT = 1,
+ HOEDOWN_TABLE_ALIGN_RIGHT = 2,
+ HOEDOWN_TABLE_ALIGN_CENTER = 3,
+ HOEDOWN_TABLE_ALIGNMASK = 3,
+ HOEDOWN_TABLE_HEADER = 4
+} hoedown_table_flags;
+
+typedef enum hoedown_autolink_type {
+ HOEDOWN_AUTOLINK_NONE, /* used internally when it is not an autolink*/
+ HOEDOWN_AUTOLINK_NORMAL, /* normal http/http/ftp/mailto/etc link */
+ HOEDOWN_AUTOLINK_EMAIL /* e-mail link without explit mailto: */
+} hoedown_autolink_type;
+
+
+/*********
+ * TYPES *
+ *********/
+
+struct hoedown_document;
+typedef struct hoedown_document hoedown_document;
+
+struct hoedown_renderer_data {
+ void *opaque;
+};
+typedef struct hoedown_renderer_data hoedown_renderer_data;
+
+/* hoedown_renderer - functions for rendering parsed data */
+struct hoedown_renderer {
+ /* state object */
+ void *opaque;
+
+ /* block level callbacks - NULL skips the block */
+ void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_buffer *attr, const hoedown_renderer_data *data);
+ void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*header)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, int level, const hoedown_renderer_data *data);
+ void (*hrule)(hoedown_buffer *ob, const hoedown_renderer_data *data);
+ void (*list)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, hoedown_list_flags flags, const hoedown_renderer_data *data);
+ void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, hoedown_list_flags *flags, const hoedown_renderer_data *data);
+ void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, const hoedown_renderer_data *data);
+ void (*table_header)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table_body)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data);
+ void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data);
+ void (*blockhtml)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
+
+ /* span level callbacks - NULL or return 0 prints the span verbatim */
+ int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data);
+ int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, const hoedown_renderer_data *data);
+ int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*underline)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*quote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_buffer *attr, const hoedown_renderer_data *data);
+ int (*linebreak)(hoedown_buffer *ob, const hoedown_renderer_data *data);
+ int (*link)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *attr, const hoedown_renderer_data *data);
+ int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data);
+ int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data);
+ int (*raw_html)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
+
+ /* low level callbacks - NULL copies input directly into the output */
+ void (*entity)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
+ void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
+
+ /* miscellaneous callbacks */
+ void (*doc_header)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data);
+ void (*doc_footer)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data);
+
+ /* user block */
+ void (*user_block)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
+};
+typedef struct hoedown_renderer hoedown_renderer;
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+typedef size_t (*hoedown_user_block)(uint8_t *context, size_t size, const hoedown_renderer_data *data);
+
+/* hoedown_document_new: allocate a new document processor instance */
+hoedown_document *hoedown_document_new(
+ const hoedown_renderer *renderer,
+ hoedown_extensions extensions,
+ size_t max_nesting,
+ hoedown_user_block user_block,
+ hoedown_buffer *meta
+) __attribute__ ((malloc));
+
+/* hoedown_document_render: render regular Markdown using the document processor */
+void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size);
+
+/* hoedown_document_render_inline: render inline Markdown using the document processor */
+void hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size);
+
+/* hoedown_document_free: deallocate a document processor instance */
+void hoedown_document_free(hoedown_document *doc);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** HOEDOWN_DOCUMENT_H **/
diff --git a/ext/phalcon/mvc/view/engine/markdown/escape.c b/ext/phalcon/mvc/view/engine/markdown/escape.c
new file mode 100644
index 0000000..be9cb78
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/escape.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "escape.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+
+
+/*
+ * The following characters will not be escaped:
+ *
+ * -_.+!*'(),%#@?=;:/,+&$ alphanum
+ *
+ * Note that this character set is the addition of:
+ *
+ * - The characters which are safe to be in an URL
+ * - The characters which are *not* safe to be in
+ * an URL because they are RESERVED characters.
+ *
+ * We assume (lazily) that any RESERVED char that
+ * appears inside an URL is actually meant to
+ * have its native function (i.e. as an URL
+ * component/separator) and hence needs no escaping.
+ *
+ * There are two exceptions: the chacters & (amp)
+ * and ' (single quote) do not appear in the table.
+ * They are meant to appear in the URL as components,
+ * yet they require special HTML-entity escaping
+ * to generate valid HTML markup.
+ *
+ * All other characters will be escaped to %XX.
+ *
+ */
+static const uint8_t HREF_SAFE[UINT8_MAX+1] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+void
+hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size)
+{
+ static const char hex_chars[] = "0123456789ABCDEF";
+ size_t i = 0, mark;
+ char hex_str[3];
+
+ hex_str[0] = '%';
+
+ while (i < size) {
+ mark = i;
+ while (i < size && HREF_SAFE[data[i]]) i++;
+
+ /* Optimization for cases where there's nothing to escape */
+ if (mark == 0 && i >= size) {
+ hoedown_buffer_put(ob, data, size);
+ return;
+ }
+
+ if (likely(i > mark)) {
+ hoedown_buffer_put(ob, data + mark, i - mark);
+ }
+
+ /* escaping */
+ if (i >= size)
+ break;
+
+ switch (data[i]) {
+ /* amp appears all the time in URLs, but needs
+ * HTML-entity escaping to be inside an href */
+ case '&':
+ HOEDOWN_BUFPUTSL(ob, "&amp;");
+ break;
+
+ /* the single quote is a valid URL character
+ * according to the standard; it needs HTML
+ * entity escaping too */
+ case '\'':
+ HOEDOWN_BUFPUTSL(ob, "&#x27;");
+ break;
+
+ /* the space can be escaped to %20 or a plus
+ * sign. we're going with the generic escape
+ * for now. the plus thing is more commonly seen
+ * when building GET strings */
+#if 0
+ case ' ':
+ hoedown_buffer_putc(ob, '+');
+ break;
+#endif
+
+ /* every other character goes with a %XX escaping */
+ default:
+ hex_str[1] = hex_chars[(data[i] >> 4) & 0xF];
+ hex_str[2] = hex_chars[data[i] & 0xF];
+ hoedown_buffer_put(ob, (uint8_t *)hex_str, 3);
+ }
+
+ i++;
+ }
+}
+
+
+/**
+ * According to the OWASP rules:
+ *
+ * & --> &amp;
+ * < --> &lt;
+ * > --> &gt;
+ * " --> &quot;
+ * ' --> &#x27; &apos; is not recommended
+ * / --> &#x2F; forward slash is included as it helps end an HTML entity
+ *
+ */
+static const uint8_t HTML_ESCAPE_TABLE[UINT8_MAX+1] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static const char *HTML_ESCAPES[] = {
+ "",
+ "&quot;",
+ "&amp;",
+ "&#39;",
+ "&#47;",
+ "&lt;",
+ "&gt;"
+};
+
+void
+hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure)
+{
+ size_t i = 0, mark;
+
+ while (1) {
+ mark = i;
+ while (i < size && HTML_ESCAPE_TABLE[data[i]] == 0) i++;
+
+ /* Optimization for cases where there's nothing to escape */
+ if (mark == 0 && i >= size) {
+ hoedown_buffer_put(ob, data, size);
+ return;
+ }
+
+ if (likely(i > mark))
+ hoedown_buffer_put(ob, data + mark, i - mark);
+
+ if (i >= size) break;
+
+ /* The forward slash is only escaped in secure mode */
+ if (!secure && data[i] == '/') {
+ hoedown_buffer_putc(ob, '/');
+ } else {
+ hoedown_buffer_puts(ob, HTML_ESCAPES[HTML_ESCAPE_TABLE[data[i]]]);
+ }
+
+ i++;
+ }
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/escape.h b/ext/phalcon/mvc/view/engine/markdown/escape.h
new file mode 100644
index 0000000..906b3fb
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/escape.h
@@ -0,0 +1,46 @@
+/* escape.h - escape utilities */
+
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HOEDOWN_ESCAPE_H
+#define HOEDOWN_ESCAPE_H
+
+#include "buffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_escape_href: escape (part of) a URL inside HTML */
+void hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size);
+
+/* hoedown_escape_html: escape HTML */
+void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** HOEDOWN_ESCAPE_H **/
diff --git a/ext/phalcon/mvc/view/engine/markdown/hash.c b/ext/phalcon/mvc/view/engine/markdown/hash.c
new file mode 100644
index 0000000..11a505e
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/hash.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "hash.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define HOEDOWN_HASH_ITEM_SIZE 255
+#define HOEDOWN_HASH_FNV_PRIME 0x01000193
+#define HOEDOWN_HASH_FNV_OFFSET_BASIS 0x811c9dc5
+
+static char *
+hoedown_hash_strndup(const char* str, size_t n)
+{
+ if (str) {
+ char *s = (char *)malloc(sizeof(char) * (n + 1));
+ if (s) {
+ memcpy(s, str, n);
+ s[n] = '\0';
+ }
+ return s;
+ }
+ return NULL;
+}
+
+static char *
+hoedown_hash_strdup(const char* str)
+{
+ if (str) {
+ return hoedown_hash_strndup(str, strlen(str));
+ }
+ return NULL;
+}
+
+static unsigned int
+hoedown_hash_fnv(const char *key, const char *max, size_t limit)
+{
+ unsigned int hash = HOEDOWN_HASH_FNV_OFFSET_BASIS;
+
+ if (max == NULL) {
+ if (key) {
+ max = key + strlen(key);
+ } else {
+ max = key;
+ }
+ }
+
+ while (key < max) {
+ hash *= HOEDOWN_HASH_FNV_PRIME;
+ hash ^= *key;
+ key++;
+ }
+
+ hash %= limit;
+
+ return hash;
+}
+
+static hoedown_hash_item *
+hoedown_hash_item_new(void)
+{
+ hoedown_hash_item *item;
+
+ item = (hoedown_hash_item *)malloc(sizeof(hoedown_hash_item));
+ if (!item) {
+ return NULL;
+ }
+
+ item->key = NULL;
+ item->value = NULL;
+ item->destruct = NULL;
+ item->next = NULL;
+ item->tail = NULL;
+
+ return item;
+}
+
+static void
+hoedown_hash_item_free(hoedown_hash_item *item)
+{
+ if (item) {
+ if (item->next) {
+ hoedown_hash_item_free(item->next);
+ }
+ if (item->key) {
+ free(item->key);
+ }
+ if (item->destruct) {
+ (item->destruct)(item->value);
+ }
+ free(item);
+ }
+}
+
+static int
+hoedown_hash_item_push(hoedown_hash_item *item, const char *key, size_t key_len,
+ void *value, hoedown_hash_value_destruct *destruct)
+{
+ hoedown_hash_item *entry;
+
+ if (!item || !key || !value) {
+ return 1;
+ }
+
+ if (item->key != NULL) {
+ entry = hoedown_hash_item_new();
+ if (!entry) {
+ return 1;
+ }
+ } else {
+ entry = item;
+ }
+
+ if (key_len > 0) {
+ entry->key = hoedown_hash_strndup(key, key_len);
+ } else {
+ entry->key = hoedown_hash_strdup(key);
+ }
+ entry->value = value;
+ entry->destruct = destruct;
+
+ if (item->tail) {
+ item->tail->next = entry;
+ } else if (item != entry) {
+ item->next = entry;
+ }
+ item->tail = entry;
+
+ return 0;
+}
+
+hoedown_hash *
+hoedown_hash_new(size_t size)
+{
+ hoedown_hash *hash;
+ size_t items_size;
+
+ hash = (hoedown_hash *)malloc(sizeof(hoedown_hash));
+ if (!hash) {
+ return NULL;
+ }
+
+ if (size == 0) {
+ size = HOEDOWN_HASH_ITEM_SIZE;
+ }
+
+ items_size = sizeof(hoedown_hash_item *) * size;
+
+ hash->items = (hoedown_hash_item **)malloc(items_size);
+ if (!hash->items) {
+ free(hash);
+ return NULL;
+ }
+
+ memset(hash->items, 0, items_size);
+
+ hash->asize = size;
+
+ return hash;
+}
+
+void
+hoedown_hash_free(hoedown_hash *hash)
+{
+ if (hash) {
+ if (hash->items) {
+ size_t i = 0;
+ while (i < hash->asize) {
+ if (hash->items[i]) {
+ hoedown_hash_item_free(hash->items[i]);
+ }
+ ++i;
+ }
+ free(hash->items);
+ }
+ free(hash);
+ }
+}
+
+int
+hoedown_hash_add(hoedown_hash *hash, const char *key, size_t key_len,
+ void *value, hoedown_hash_value_destruct *destruct)
+{
+ unsigned int h;
+
+ if (!hash || !key || !value) {
+ return 1;
+ }
+
+ h = hoedown_hash_fnv(key, key + key_len, hash->asize);
+
+ if (!hash->items[h]) {
+ hash->items[h] = hoedown_hash_item_new();
+ if (!hash->items[h]) {
+ return 1;
+ }
+ }
+
+ if (hoedown_hash_item_push(hash->items[h], key, key_len,
+ value, destruct) != 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void *
+hoedown_hash_find(hoedown_hash *hash, char *key, size_t key_len)
+{
+ unsigned int h;
+
+ if (!hash || !key) {
+ return NULL;
+ }
+
+ h = hoedown_hash_fnv(key, key + key_len, hash->asize);
+
+ if (hash->items[h]) {
+ hoedown_hash_item *item = hash->items[h];
+ while (item != NULL) {
+ if (item->key && strncmp(item->key, key, key_len) == 0) {
+ return item->value;
+ }
+ item = item->next;
+ }
+ }
+
+ return NULL;
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/hash.h b/ext/phalcon/mvc/view/engine/markdown/hash.h
new file mode 100644
index 0000000..de1e7c2
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/hash.h
@@ -0,0 +1,56 @@
+/* hash.h - generic markdown parser */
+
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HOEDOWN_HASH_H
+#define HOEDOWN_HASH_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct hoedown_hash_item hoedown_hash_item;
+typedef struct hoedown_hash hoedown_hash;
+typedef void (hoedown_hash_value_destruct) (void *data);
+
+struct hoedown_hash_item {
+ char *key;
+ void *value;
+ hoedown_hash_value_destruct *destruct;
+ hoedown_hash_item *next;
+ hoedown_hash_item *tail;
+};
+
+struct hoedown_hash {
+ hoedown_hash_item **items;
+ size_t asize;
+};
+
+hoedown_hash * hoedown_hash_new(size_t size);
+void hoedown_hash_free(hoedown_hash *hash);
+int hoedown_hash_add(hoedown_hash *hash, const char *key, size_t key_len, void *value, hoedown_hash_value_destruct *destruct);
+void * hoedown_hash_find(hoedown_hash *hash, char *key, size_t key_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** HOEDOWN_HASH_H **/
diff --git a/ext/phalcon/mvc/view/engine/markdown/html.c b/ext/phalcon/mvc/view/engine/markdown/html.c
new file mode 100644
index 0000000..9245637
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/html.c
@@ -0,0 +1,1095 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "html.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "escape.h"
+#include "hash.h"
+
+#ifdef _MSC_VER
+#define strncasecmp _strnicmp
+#endif
+
+#define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML)
+#define USE_TASK_LIST(opt) (opt->flags & HOEDOWN_HTML_USE_TASK_LIST)
+
+hoedown_html_tag
+hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname)
+{
+ size_t i;
+ int closed = 0;
+
+ if (size < 3 || data[0] != '<')
+ return HOEDOWN_HTML_TAG_NONE;
+
+ i = 1;
+
+ if (data[i] == '/') {
+ closed = 1;
+ i++;
+ }
+
+ for (; i < size; ++i, ++tagname) {
+ if (*tagname == 0)
+ break;
+
+ if (data[i] != *tagname)
+ return HOEDOWN_HTML_TAG_NONE;
+ }
+
+ if (i == size)
+ return HOEDOWN_HTML_TAG_NONE;
+
+ if (isspace(data[i]) || data[i] == '>')
+ return closed ? HOEDOWN_HTML_TAG_CLOSE : HOEDOWN_HTML_TAG_OPEN;
+
+ return HOEDOWN_HTML_TAG_NONE;
+}
+
+static void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length)
+{
+ hoedown_escape_html(ob, source, length, 0);
+}
+
+static void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length)
+{
+ hoedown_escape_href(ob, source, length);
+}
+
+/********************
+ * GENERIC RENDERER *
+ ********************/
+static int
+rndr_attributes(struct hoedown_buffer *ob, const uint8_t *buf, const size_t size, int *flags, const hoedown_renderer_data *data)
+{
+ size_t n, i = 0, len = 0;
+ int id = 0, type = 0;
+
+ hoedown_buffer *class = 0;
+
+ while (i < size) {
+ if (buf[i] == '#') {
+ type = 1;
+ } else if (buf[i] == '.') {
+ type = 2;
+ } else if (buf[i] != ' ') {
+ type = 3;
+ } else {
+ ++i;
+ continue;
+ }
+
+ n = i;
+
+ while (i < size && buf[i] != ' ') {
+ ++i;
+ }
+
+ len = i - n;
+ if (len == 0) {
+ ++i;
+ continue;
+ }
+
+ switch (type) {
+ case 1:
+ /* id */
+ if (!id) {
+ HOEDOWN_BUFPUTSL(ob, " id=\"");
+ escape_html(ob, buf+n+1, len-1);
+ hoedown_buffer_putc(ob, '"');
+ id = 1;
+ }
+ break;
+ case 2:
+ /* class */
+ if (!class) {
+ class = hoedown_buffer_new(size);
+ }
+ escape_html(class, buf+n+1, len-1);
+ hoedown_buffer_putc(class, ' ');
+ break;
+ case 3: {
+ /* attribute */
+ size_t j;
+ void *s =memchr(buf+n, '=', len);
+ if (s == NULL) {
+ break;
+ }
+ j = (char *)s - ((char *)buf + n) + 1;
+ if (buf[n+j] != buf[i-1]) {
+ while (i < size && buf[i-1] != buf[n+j]) {
+ ++i;
+ ++len;
+ }
+ }
+ if (len > 3 && strncasecmp((char *)buf+n, "id=", 3) == 0) {
+ if (id) {
+ break;
+ }
+ id = 1;
+ } else if (len > 6 &&
+ strncasecmp((char *)buf+n, "class=", 6) == 0) {
+ if (!class) {
+ class = hoedown_buffer_new(size);
+ }
+ escape_html(class, buf+n+7, len-8);
+ hoedown_buffer_putc(class, ' ');
+ break;
+ }
+ hoedown_buffer_putc(ob, ' ');
+ hoedown_buffer_put(ob, buf+n, len);
+ break;
+ }
+ }
+ }
+
+ if (class) {
+ if (class->size > 0) {
+ if (flags) {
+ *flags = 1;
+ }
+ HOEDOWN_BUFPUTSL(ob, " class=\"");
+ hoedown_buffer_put(ob, class->data, class->size-1);
+ hoedown_buffer_putc(ob, '"');
+ }
+ hoedown_buffer_free(class);
+ }
+
+ return 1;
+}
+
+static int
+rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+
+ if (!link || !link->size)
+ return 0;
+
+ HOEDOWN_BUFPUTSL(ob, "<a href=\"");
+ if (type == HOEDOWN_AUTOLINK_EMAIL)
+ HOEDOWN_BUFPUTSL(ob, "mailto:");
+ escape_href(ob, link->data, link->size);
+
+ if (state->link_attributes) {
+ hoedown_buffer_putc(ob, '\"');
+ state->link_attributes(ob, link, data);
+ hoedown_buffer_putc(ob, '>');
+ } else {
+ HOEDOWN_BUFPUTSL(ob, "\">");
+ }
+
+ /*
+ * Pretty printing: if we get an email address as
+ * an actual URI, e.g. `mailto:foo@bar.com`, we don't
+ * want to print the `mailto:` prefix
+ */
+ if (hoedown_buffer_prefix(link, "mailto:") == 0) {
+ escape_html(ob, link->data + 7, link->size - 7);
+ } else {
+ escape_html(ob, link->data, link->size);
+ }
+
+ HOEDOWN_BUFPUTSL(ob, "</a>");
+
+ return 1;
+}
+
+static void
+rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ int class = 0;
+
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+
+ if (lang) {
+ hoedown_html_renderer_state *state = data->opaque;
+ if ((state->flags & HOEDOWN_HTML_FENCED_CODE_SCRIPT) &&
+ lang->size > 7 && memcmp(lang->data, "script@", 7) == 0) {
+ HOEDOWN_BUFPUTSL(ob, "<script type=\"");
+ escape_html(ob, lang->data + 7, lang->size - 7);
+ HOEDOWN_BUFPUTSL(ob, "\">\n");
+ hoedown_buffer_put(ob, text->data, text->size);
+ HOEDOWN_BUFPUTSL(ob, "</script>\n");
+ return;
+ }
+ HOEDOWN_BUFPUTSL(ob, "<pre><code");
+ if (attr && attr->size) {
+ rndr_attributes(ob, attr->data, attr->size, &class, data);
+ }
+ if (!class) {
+ HOEDOWN_BUFPUTSL(ob, " class=\"language-");
+ escape_html(ob, lang->data, lang->size);
+ hoedown_buffer_putc(ob, '"');
+ }
+ hoedown_buffer_putc(ob, '>');
+ } else if (attr && attr->size) {
+ HOEDOWN_BUFPUTSL(ob, "<pre><code");
+ rndr_attributes(ob, attr->data, attr->size, &class, data);
+ hoedown_buffer_putc(ob, '>');
+ } else {
+ HOEDOWN_BUFPUTSL(ob, "<pre><code>");
+ }
+
+ if (text)
+ escape_html(ob, text->data, text->size);
+
+ HOEDOWN_BUFPUTSL(ob, "</code></pre>\n");
+}
+
+static void
+rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<blockquote>\n");
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</blockquote>\n");
+}
+
+static int
+rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ HOEDOWN_BUFPUTSL(ob, "<code");
+ if (attr && attr->size) {
+ rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ }
+ hoedown_buffer_putc(ob, '>');
+ if (text) escape_html(ob, text->data, text->size);
+ HOEDOWN_BUFPUTSL(ob, "</code>");
+ return 1;
+}
+
+static int
+rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size)
+ return 0;
+
+ HOEDOWN_BUFPUTSL(ob, "<del>");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</del>");
+ return 1;
+}
+
+static int
+rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size)
+ return 0;
+
+ HOEDOWN_BUFPUTSL(ob, "<strong>");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</strong>");
+
+ return 1;
+}
+
+static int
+rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size) return 0;
+ HOEDOWN_BUFPUTSL(ob, "<em>");
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</em>");
+ return 1;
+}
+
+static int
+rndr_underline(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size)
+ return 0;
+
+ HOEDOWN_BUFPUTSL(ob, "<u>");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</u>");
+
+ return 1;
+}
+
+static int
+rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size)
+ return 0;
+
+ HOEDOWN_BUFPUTSL(ob, "<mark>");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</mark>");
+
+ return 1;
+}
+
+static int
+rndr_quote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size)
+ return 0;
+
+ HOEDOWN_BUFPUTSL(ob, "<q>");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</q>");
+
+ return 1;
+}
+
+static int
+rndr_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+ hoedown_buffer_puts(ob, USE_XHTML(state) ? "<br/>\n" : "<br>\n");
+ return 1;
+}
+
+static void
+rndr_header_id(hoedown_buffer *ob, const uint8_t *source, size_t length, int escape, const hoedown_renderer_data *data)
+{
+ size_t i = 0, n = 0;
+ hoedown_html_renderer_state *state = data->opaque;
+ hoedown_hash *hash = state->hash.header_id;
+
+ while (i < length) {
+ if (isalnum(source[i])) {
+ hoedown_buffer_putc(ob, tolower(source[i]));
+ } else if (source[i] == ' ') {
+ hoedown_buffer_putc(ob, '-');
+ } else if (source[i] == '-' || source[i] == '_') {
+ hoedown_buffer_putc(ob, source[i]);
+ } else if (!isascii(source[i])) {
+ if (escape) {
+ hoedown_buffer_printf(ob, "%%%02X", source[i]);
+ } else {
+ hoedown_buffer_putc(ob, source[i]);
+ }
+ } else if (source[i] == '&') {
+ while (i < length && source[i] != ';') {
+ ++i;
+ }
+ } else if (source[i] == '<') {
+ while (i < length && source[i] != '>') {
+ ++i;
+ }
+ }
+ ++i;
+ }
+
+ if (hash) {
+ void *value = hoedown_hash_find(hash, (char *)source, length);
+ if (value) {
+ size_t *p = (size_t *)value;
+ ++(*p);
+ n = *p;
+ }
+ if (n > 0) {
+ hoedown_buffer_printf(ob, "-%ld", n);
+ } else if (hash) {
+ size_t *p = (size_t *)malloc(sizeof(size_t));
+ if (p) {
+ *p = 0;
+ hoedown_hash_add(hash, (char *)source, length, (void *)p, free);
+ }
+ }
+ }
+}
+
+static void
+rndr_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, int level, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+
+ if (ob->size)
+ hoedown_buffer_putc(ob, '\n');
+
+ if (attr && attr->size) {
+ hoedown_buffer_printf(ob, "<h%d", level);
+ rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ hoedown_buffer_putc(ob, '>');
+ } else if ((state->flags & HOEDOWN_HTML_HEADER_ID) || (level <= state->toc_data.nesting_level)) {
+ hoedown_buffer_printf(ob, "<h%d id=\"", level);
+ rndr_header_id(ob, content->data, content->size, 0, data);
+ hoedown_buffer_puts(ob, "\">");
+ } else {
+ hoedown_buffer_printf(ob, "<h%d>", level);
+ }
+
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+ hoedown_buffer_printf(ob, "</h%d>\n", level);
+}
+
+static int
+rndr_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+
+ HOEDOWN_BUFPUTSL(ob, "<a href=\"");
+
+ if (link && link->size) {
+ escape_href(ob, link->data, link->size);
+ }
+
+ if (title && title->size) {
+ HOEDOWN_BUFPUTSL(ob, "\" title=\"");
+ escape_html(ob, title->data, title->size);
+ }
+
+ if (state->link_attributes) {
+ hoedown_buffer_putc(ob, '\"');
+ state->link_attributes(ob, link, data);
+ hoedown_buffer_putc(ob, '>');
+ } else if (attr && attr->size) {
+ hoedown_buffer_putc(ob, '"');
+ rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ hoedown_buffer_putc(ob, '>');
+ } else {
+ HOEDOWN_BUFPUTSL(ob, "\">");
+ }
+
+ if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</a>");
+ return 1;
+}
+
+static void
+rndr_list(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, unsigned int flags, const hoedown_renderer_data *data)
+{
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+
+ if (flags & HOEDOWN_LIST_ORDERED) {
+ HOEDOWN_BUFPUTSL(ob, "<ol");
+ if (attr && attr->size) {
+ rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ }
+ HOEDOWN_BUFPUTSL(ob, ">\n");
+ } else {
+ HOEDOWN_BUFPUTSL(ob, "<ul");
+ if (attr && attr->size) {
+ rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ }
+ HOEDOWN_BUFPUTSL(ob, ">\n");
+ }
+
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+ hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "</ol>\n" : "</ul>\n"), 6);
+}
+
+static void
+rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, hoedown_list_flags *flags, const hoedown_renderer_data *data)
+{
+ if (content) {
+ hoedown_html_renderer_state *state = data->opaque;
+ size_t prefix = 0;
+ size_t size = content->size;
+ while (size && content->data[size - 1] == '\n')
+ size--;
+
+ HOEDOWN_BUFPUTSL(ob, "<li");
+ if (attr && attr->size) {
+ rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ }
+ hoedown_buffer_putc(ob, '>');
+
+ if (USE_TASK_LIST(state) && size >= 3) {
+ if (*flags & HOEDOWN_LI_BLOCK) {
+ prefix = 3;
+ }
+ if (strncmp((char *)content->data + prefix, "[ ]", 3) == 0) {
+ hoedown_buffer_put(ob, content->data, prefix);
+ HOEDOWN_BUFPUTSL(ob, "<input type=\"checkbox\"");
+ hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
+ prefix += 3;
+ *flags |= HOEDOWN_LI_TASK;
+ } else if (strncasecmp((char *)content->data + prefix, "[x]", 3) == 0) {
+ hoedown_buffer_put(ob, content->data, prefix);
+ HOEDOWN_BUFPUTSL(ob, "<input checked=\"\" type=\"checkbox\"");
+ hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
+ prefix += 3;
+ *flags |= HOEDOWN_LI_TASK;
+ } else {
+ prefix = 0;
+ }
+ }
+
+ hoedown_buffer_put(ob, content->data+prefix, size-prefix);
+ } else {
+ HOEDOWN_BUFPUTSL(ob, "<li>");
+ }
+ HOEDOWN_BUFPUTSL(ob, "</li>\n");
+}
+
+static void
+rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+ size_t i = 0;
+
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+
+ if (!content || !content->size)
+ return;
+
+ while (i < content->size && isspace(content->data[i])) i++;
+
+ if (i == content->size)
+ return;
+
+ HOEDOWN_BUFPUTSL(ob, "<p>");
+ if (state->flags & HOEDOWN_HTML_HARD_WRAP) {
+ size_t org;
+ while (i < content->size) {
+ org = i;
+ while (i < content->size && content->data[i] != '\n')
+ i++;
+
+ if (i > org)
+ hoedown_buffer_put(ob, content->data + org, i - org);
+
+ /*
+ * do not insert a line break if this newline
+ * is the last character on the paragraph
+ */
+ if (i >= content->size - 1)
+ break;
+
+ rndr_linebreak(ob, data);
+ i++;
+ }
+ } else if (state->flags & HOEDOWN_HTML_LINE_CONTINUE) {
+ size_t org;
+ while (i < content->size) {
+ org = i;
+ while (i < content->size && content->data[i] != '\n') {
+ ++i;
+ }
+ if (i > org) {
+ hoedown_buffer_put(ob, content->data + org, i - org);
+ }
+
+ if (i >= content->size - 1) {
+ break;
+ }
+
+ if (content->data[i] == '\n' &&
+ (isascii(content->data[i-1]) || isascii(content->data[i+1]))) {
+ if (i < 5 ||
+ strncmp((char *)content->data+i-5, "<br/>", 5) != 0 ||
+ strncmp((char *)content->data+i-4, "<br>", 4) != 0) {
+ HOEDOWN_BUFPUTSL(ob, " ");
+ }
+ }
+ ++i;
+ }
+ } else {
+ hoedown_buffer_put(ob, content->data + i, content->size - i);
+ }
+ HOEDOWN_BUFPUTSL(ob, "</p>\n");
+}
+
+static void
+rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ size_t org, sz;
+
+ if (!text)
+ return;
+
+ /* FIXME: Do we *really* need to trim the HTML? How does that make a difference? */
+ sz = text->size;
+ while (sz > 0 && text->data[sz - 1] == '\n')
+ sz--;
+
+ org = 0;
+ while (org < sz && text->data[org] == '\n')
+ org++;
+
+ if (org >= sz)
+ return;
+
+ if (ob->size)
+ hoedown_buffer_putc(ob, '\n');
+
+ hoedown_buffer_put(ob, text->data + org, sz - org);
+ hoedown_buffer_putc(ob, '\n');
+}
+
+static int
+rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size) return 0;
+ HOEDOWN_BUFPUTSL(ob, "<strong><em>");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</em></strong>");
+ return 1;
+}
+
+static void
+rndr_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
+}
+
+static int
+rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+ if (!link || !link->size) return 0;
+
+ HOEDOWN_BUFPUTSL(ob, "<img src=\"");
+ escape_href(ob, link->data, link->size);
+ HOEDOWN_BUFPUTSL(ob, "\" alt=\"");
+
+ if (alt && alt->size)
+ escape_html(ob, alt->data, alt->size);
+
+ if (title && title->size) {
+ HOEDOWN_BUFPUTSL(ob, "\" title=\"");
+ escape_html(ob, title->data, title->size); }
+
+ hoedown_buffer_putc(ob, '"');
+ if (attr && attr->size) {
+ rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ }
+
+ hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
+ return 1;
+}
+
+static int
+rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+
+ /* ESCAPE overrides SKIP_HTML. It doesn't look to see if
+ * there are any valid tags, just escapes all of them. */
+ if((state->flags & HOEDOWN_HTML_ESCAPE) != 0) {
+ escape_html(ob, text->data, text->size);
+ return 1;
+ }
+
+ if ((state->flags & HOEDOWN_HTML_SKIP_HTML) != 0)
+ return 1;
+
+ hoedown_buffer_put(ob, text->data, text->size);
+ return 1;
+}
+
+static void
+rndr_table(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<table");
+ if (attr) rndr_attributes(ob, attr->data, attr->size, NULL, data);
+ HOEDOWN_BUFPUTSL(ob, ">\n");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</table>\n");
+}
+
+static void
+rndr_table_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<thead>\n");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</thead>\n");
+}
+
+static void
+rndr_table_body(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<tbody>\n");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</tbody>\n");
+}
+
+static void
+rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ HOEDOWN_BUFPUTSL(ob, "<tr>\n");
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</tr>\n");
+}
+
+static void
+rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data)
+{
+ if (flags & HOEDOWN_TABLE_HEADER) {
+ HOEDOWN_BUFPUTSL(ob, "<th");
+ } else {
+ HOEDOWN_BUFPUTSL(ob, "<td");
+ }
+
+ switch (flags & HOEDOWN_TABLE_ALIGNMASK) {
+ case HOEDOWN_TABLE_ALIGN_CENTER:
+ HOEDOWN_BUFPUTSL(ob, " style=\"text-align: center\">");
+ break;
+
+ case HOEDOWN_TABLE_ALIGN_LEFT:
+ HOEDOWN_BUFPUTSL(ob, " style=\"text-align: left\">");
+ break;
+
+ case HOEDOWN_TABLE_ALIGN_RIGHT:
+ HOEDOWN_BUFPUTSL(ob, " style=\"text-align: right\">");
+ break;
+
+ default:
+ HOEDOWN_BUFPUTSL(ob, ">");
+ }
+
+ if (content)
+ hoedown_buffer_put(ob, content->data, content->size);
+
+ if (flags & HOEDOWN_TABLE_HEADER) {
+ HOEDOWN_BUFPUTSL(ob, "</th>\n");
+ } else {
+ HOEDOWN_BUFPUTSL(ob, "</td>\n");
+ }
+}
+
+static int
+rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (!content || !content->size) return 0;
+ HOEDOWN_BUFPUTSL(ob, "<sup>");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</sup>");
+ return 1;
+}
+
+static void
+rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (content)
+ escape_html(ob, content->data, content->size);
+}
+
+static void
+rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<div class=\"footnotes\">\n");
+ hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
+ HOEDOWN_BUFPUTSL(ob, "<ol>\n");
+
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+
+ HOEDOWN_BUFPUTSL(ob, "\n</ol>\n</div>\n");
+}
+
+static void
+rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data)
+{
+ size_t i = 0;
+ int pfound = 0;
+
+ /* insert anchor at the end of first paragraph block */
+ if (content) {
+ while ((i+3) < content->size) {
+ if (content->data[i++] != '<') continue;
+ if (content->data[i++] != '/') continue;
+ if (content->data[i++] != 'p' && content->data[i] != 'P') continue;
+ if (content->data[i] != '>') continue;
+ i -= 3;
+ pfound = 1;
+ break;
+ }
+ }
+
+ hoedown_buffer_printf(ob, "\n<li id=\"fn%d\">\n", num);
+ if (pfound) {
+ hoedown_buffer_put(ob, content->data, i);
+ hoedown_buffer_printf(ob, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
+ hoedown_buffer_put(ob, content->data + i, content->size - i);
+ } else if (content) {
+ hoedown_buffer_put(ob, content->data, content->size);
+ }
+ HOEDOWN_BUFPUTSL(ob, "</li>\n");
+}
+
+static int
+rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data)
+{
+ hoedown_buffer_printf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\" rel=\"footnote\">%d</a></sup>", num, num, num);
+ return 1;
+}
+
+static int
+rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data)
+{
+ hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\[" : "\\("), 2);
+ escape_html(ob, text->data, text->size);
+ hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\]" : "\\)"), 2);
+ return 1;
+}
+
+static void
+toc_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, int level, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+
+ if (level <= state->toc_data.nesting_level) {
+ if (level < state->toc_data.level_offset) {
+ state->toc_data.current_level++;
+ return;
+ }
+
+ if (level > state->toc_data.current_level) {
+ while (level > state->toc_data.current_level) {
+ HOEDOWN_BUFPUTSL(ob, "<ul>\n<li>\n");
+ state->toc_data.current_level++;
+ }
+ } else if (level < state->toc_data.current_level) {
+ HOEDOWN_BUFPUTSL(ob, "</li>\n");
+ while (level < state->toc_data.current_level) {
+ HOEDOWN_BUFPUTSL(ob, "</ul>\n</li>\n");
+ state->toc_data.current_level--;
+ }
+ HOEDOWN_BUFPUTSL(ob,"<li>\n");
+ } else {
+ HOEDOWN_BUFPUTSL(ob,"</li>\n<li>\n");
+ }
+
+ if (attr && attr->size) {
+ size_t n, i = 0;
+ do {
+ i++;
+ } while (i < attr->size && attr->data[i-1] != '#');
+ if (i < attr->size) {
+ n = i;
+ while (n < attr->size && attr->data[n] != '#' &&
+ attr->data[n] != '.' && attr->data[n] != ' ') {
+ n++;
+ }
+ HOEDOWN_BUFPUTSL(ob, "<a href=\"#");
+ escape_html(ob, attr->data + i, n - i);
+ HOEDOWN_BUFPUTSL(ob, "\">");
+ }
+ } else {
+ hoedown_buffer_puts(ob, "<a href=\"#");
+ rndr_header_id(ob, content->data, content->size, 1, data);
+ hoedown_buffer_puts(ob, "\">");
+ }
+
+ if (content) {
+ hoedown_buffer_put(ob, content->data, content->size);
+ }
+ HOEDOWN_BUFPUTSL(ob, "</a>\n");
+ }
+}
+
+static int
+toc_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
+ return 1;
+}
+
+static void
+toc_initialize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+ if (state->toc_data.header) {
+ hoedown_buffer_printf(ob, "%s\n", state->toc_data.header);
+ }
+}
+
+static void
+toc_finalize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data)
+{
+ hoedown_html_renderer_state *state;
+
+ if (inline_render)
+ return;
+
+ state = data->opaque;
+
+ while (state->toc_data.current_level > 0) {
+ HOEDOWN_BUFPUTSL(ob, "</li>\n</ul>\n");
+ state->toc_data.current_level--;
+ if (state->toc_data.current_level < state->toc_data.level_offset) {
+ break;
+ }
+ }
+
+ if (state->toc_data.footer) {
+ hoedown_buffer_printf(ob, "%s\n", state->toc_data.footer);
+ }
+
+ state->toc_data.header_count = 0;
+}
+
+hoedown_renderer *
+hoedown_html_toc_renderer_new(int nesting_level)
+{
+ static const hoedown_renderer cb_default = {
+ NULL,
+
+ NULL,
+ NULL,
+ toc_header,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ rndr_codespan,
+ rndr_double_emphasis,
+ rndr_emphasis,
+ rndr_underline,
+ rndr_highlight,
+ rndr_quote,
+ NULL,
+ NULL,
+ toc_link,
+ rndr_triple_emphasis,
+ rndr_strikethrough,
+ rndr_superscript,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ rndr_normal_text,
+
+ toc_initialize,
+ toc_finalize,
+
+ NULL,
+ };
+
+ hoedown_html_renderer_state *state;
+ hoedown_renderer *renderer;
+
+ /* Prepare the state pointer */
+ state = hoedown_malloc(sizeof(hoedown_html_renderer_state));
+ memset(state, 0x0, sizeof(hoedown_html_renderer_state));
+
+ state->toc_data.nesting_level = nesting_level;
+
+ /* Prepare the renderer */
+ renderer = hoedown_malloc(sizeof(hoedown_renderer));
+ memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
+
+ state->hash.header_id = hoedown_hash_new(0);
+
+ renderer->opaque = state;
+ return renderer;
+}
+
+hoedown_renderer *
+hoedown_html_renderer_new(hoedown_html_flags render_flags, int nesting_level)
+{
+ static const hoedown_renderer cb_default = {
+ NULL,
+
+ rndr_blockcode,
+ rndr_blockquote,
+ rndr_header,
+ rndr_hrule,
+ rndr_list,
+ rndr_listitem,
+ rndr_paragraph,
+ rndr_table,
+ rndr_table_header,
+ rndr_table_body,
+ rndr_tablerow,
+ rndr_tablecell,
+ rndr_footnotes,
+ rndr_footnote_def,
+ rndr_raw_block,
+
+ rndr_autolink,
+ rndr_codespan,
+ rndr_double_emphasis,
+ rndr_emphasis,
+ rndr_underline,
+ rndr_highlight,
+ rndr_quote,
+ rndr_image,
+ rndr_linebreak,
+ rndr_link,
+ rndr_triple_emphasis,
+ rndr_strikethrough,
+ rndr_superscript,
+ rndr_footnote_ref,
+ rndr_math,
+ rndr_raw_html,
+
+ NULL,
+ rndr_normal_text,
+
+ NULL,
+ NULL,
+
+ NULL,
+ };
+
+ hoedown_html_renderer_state *state;
+ hoedown_renderer *renderer;
+
+ /* Prepare the state pointer */
+ state = hoedown_malloc(sizeof(hoedown_html_renderer_state));
+ memset(state, 0x0, sizeof(hoedown_html_renderer_state));
+
+ state->flags = render_flags;
+ state->toc_data.nesting_level = nesting_level;
+
+ /* Prepare the renderer */
+ renderer = hoedown_malloc(sizeof(hoedown_renderer));
+ memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
+
+ if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE)
+ renderer->blockhtml = NULL;
+
+ state->hash.header_id = hoedown_hash_new(0);
+
+ renderer->opaque = state;
+
+ return renderer;
+}
+
+void
+hoedown_html_renderer_free(hoedown_renderer *renderer)
+{
+ if (renderer->opaque) {
+ hoedown_html_renderer_state *state = renderer->opaque;
+ if (state->hash.header_id) {
+ hoedown_hash_free(state->hash.header_id);
+ }
+ }
+ free(renderer->opaque);
+ free(renderer);
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/html.h b/ext/phalcon/mvc/view/engine/markdown/html.h
new file mode 100644
index 0000000..9b69768
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/html.h
@@ -0,0 +1,113 @@
+/* html.h - HTML renderer and utilities */
+
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HOEDOWN_HTML_H
+#define HOEDOWN_HTML_H
+
+#include "document.h"
+#include "buffer.h"
+#include "hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************
+ * CONSTANTS *
+ *************/
+
+typedef enum hoedown_html_flags {
+ HOEDOWN_HTML_SKIP_HTML = (1 << 0),
+ HOEDOWN_HTML_ESCAPE = (1 << 1),
+ HOEDOWN_HTML_HARD_WRAP = (1 << 2),
+ HOEDOWN_HTML_USE_XHTML = (1 << 3),
+ HOEDOWN_HTML_USE_TASK_LIST = (1 << 4),
+ HOEDOWN_HTML_LINE_CONTINUE = (1 << 5),
+ HOEDOWN_HTML_HEADER_ID = (1 << 6),
+ HOEDOWN_HTML_FENCED_CODE_SCRIPT = (1 << 7)
+} hoedown_html_flags;
+
+typedef enum hoedown_html_tag {
+ HOEDOWN_HTML_TAG_NONE = 0,
+ HOEDOWN_HTML_TAG_OPEN,
+ HOEDOWN_HTML_TAG_CLOSE
+} hoedown_html_tag;
+
+
+/*********
+ * TYPES *
+ *********/
+
+struct hoedown_html_renderer_state {
+ void *opaque;
+
+ struct {
+ int header_count;
+ int current_level;
+ int level_offset;
+ int nesting_level;
+ char *header;
+ char *footer;
+ } toc_data;
+
+ struct {
+ hoedown_hash *header_id;
+ } hash;
+
+ hoedown_html_flags flags;
+
+ /* extra callbacks */
+ void (*link_attributes)(hoedown_buffer *ob, const hoedown_buffer *url, const hoedown_renderer_data *data);
+};
+typedef struct hoedown_html_renderer_state hoedown_html_renderer_state;
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_html_smartypants: process an HTML snippet using SmartyPants for smart punctuation */
+void hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *data, size_t size);
+
+/* hoedown_html_is_tag: checks if data starts with a specific tag, returns the tag type or NONE */
+hoedown_html_tag hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname);
+
+
+/* hoedown_html_renderer_new: allocates a regular HTML renderer */
+hoedown_renderer *hoedown_html_renderer_new(
+ hoedown_html_flags render_flags,
+ int nesting_level
+) __attribute__ ((malloc));
+
+/* hoedown_html_toc_renderer_new: like hoedown_html_renderer_new, but the returned renderer produces the Table of Contents */
+hoedown_renderer *hoedown_html_toc_renderer_new(
+ int nesting_level
+) __attribute__ ((malloc));
+
+/* hoedown_html_renderer_free: deallocate an HTML renderer */
+void hoedown_html_renderer_free(hoedown_renderer *renderer);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** HOEDOWN_HTML_H **/
diff --git a/ext/phalcon/mvc/view/engine/markdown/html_blocks.c b/ext/phalcon/mvc/view/engine/markdown/html_blocks.c
new file mode 100644
index 0000000..e9ff137
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/html_blocks.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ANSI-C code produced by gperf version 3.0.3 */
+/* Command-line: gperf -L ANSI-C -N hoedown_find_block_tag -c -C -E -S 1 --ignore-case -m100 html_block_names.gperf */
+/* Computed positions: -k'1-2' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+/* maximum key range = 24, duplicates = 0 */
+
+#ifndef GPERF_DOWNCASE
+#define GPERF_DOWNCASE 1
+static unsigned char gperf_downcase[256] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255
+ };
+#endif
+
+#ifndef GPERF_CASE_STRNCMP
+#define GPERF_CASE_STRNCMP 1
+static int
+gperf_case_strncmp (register const char *s1, register const char *s2, register unsigned int n)
+{
+ for (; n > 0;)
+ {
+ unsigned char c1 = gperf_downcase[(unsigned char)*s1++];
+ unsigned char c2 = gperf_downcase[(unsigned char)*s2++];
+ if (c1 != 0 && c1 == c2)
+ {
+ n--;
+ continue;
+ }
+ return (int)c1 - (int)c2;
+ }
+ return 0;
+}
+#endif
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+hash (register const char *str, register unsigned int len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 22, 21, 19, 18, 16, 0, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 1, 25, 0, 25,
+ 1, 0, 0, 13, 0, 25, 25, 11, 2, 1,
+ 0, 25, 25, 5, 0, 2, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 1, 25,
+ 0, 25, 1, 0, 0, 13, 0, 25, 25, 11,
+ 2, 1, 0, 25, 25, 5, 0, 2, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25
+ };
+ register int hval = (int)len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[1]+1];
+ /*FALLTHROUGH*/
+ case 1:
+ hval += asso_values[(unsigned char)str[0]];
+ break;
+ }
+ return hval;
+}
+
+#ifdef __GNUC__
+__inline
+#ifdef __GNUC_STDC_INLINE__
+__attribute__ ((__gnu_inline__))
+#endif
+#endif
+const char *
+hoedown_find_block_tag (register const char *str, register unsigned int len)
+{
+ enum
+ {
+ TOTAL_KEYWORDS = 24,
+ MIN_WORD_LENGTH = 1,
+ MAX_WORD_LENGTH = 10,
+ MIN_HASH_VALUE = 1,
+ MAX_HASH_VALUE = 24
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)
+ {
+ register const char *resword;
+
+ switch (key - 1)
+ {
+ case 0:
+ resword = "p";
+ goto compare;
+ case 1:
+ resword = "h6";
+ goto compare;
+ case 2:
+ resword = "div";
+ goto compare;
+ case 3:
+ resword = "del";
+ goto compare;
+ case 4:
+ resword = "form";
+ goto compare;
+ case 5:
+ resword = "table";
+ goto compare;
+ case 6:
+ resword = "figure";
+ goto compare;
+ case 7:
+ resword = "pre";
+ goto compare;
+ case 8:
+ resword = "fieldset";
+ goto compare;
+ case 9:
+ resword = "noscript";
+ goto compare;
+ case 10:
+ resword = "script";
+ goto compare;
+ case 11:
+ resword = "style";
+ goto compare;
+ case 12:
+ resword = "dl";
+ goto compare;
+ case 13:
+ resword = "ol";
+ goto compare;
+ case 14:
+ resword = "ul";
+ goto compare;
+ case 15:
+ resword = "math";
+ goto compare;
+ case 16:
+ resword = "ins";
+ goto compare;
+ case 17:
+ resword = "h5";
+ goto compare;
+ case 18:
+ resword = "iframe";
+ goto compare;
+ case 19:
+ resword = "h4";
+ goto compare;
+ case 20:
+ resword = "h3";
+ goto compare;
+ case 21:
+ resword = "blockquote";
+ goto compare;
+ case 22:
+ resword = "h2";
+ goto compare;
+ case 23:
+ resword = "h1";
+ goto compare;
+ }
+ return 0;
+ compare:
+ if ((((unsigned char)*str ^ (unsigned char)*resword) & ~32) == 0 && !gperf_case_strncmp (str, resword, len) && resword[len] == '\0')
+ return resword;
+ }
+ }
+ return 0;
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/html_smartypants.c b/ext/phalcon/mvc/view/engine/markdown/html_smartypants.c
new file mode 100644
index 0000000..ef2ce2c
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/html_smartypants.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "html.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#endif
+
+struct smartypants_data {
+ int in_squote;
+ int in_dquote;
+};
+
+static size_t smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+
+static size_t (*smartypants_cb_ptrs[])
+ (hoedown_buffer *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) =
+{
+ NULL, /* 0 */
+ smartypants_cb__dash, /* 1 */
+ smartypants_cb__parens, /* 2 */
+ smartypants_cb__squote, /* 3 */
+ smartypants_cb__dquote, /* 4 */
+ smartypants_cb__amp, /* 5 */
+ smartypants_cb__period, /* 6 */
+ smartypants_cb__number, /* 7 */
+ smartypants_cb__ltag, /* 8 */
+ smartypants_cb__backtick, /* 9 */
+ smartypants_cb__escape, /* 10 */
+};
+
+static const uint8_t smartypants_cb_chars[UINT8_MAX+1] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0,
+ 0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0,
+ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static int
+word_boundary(uint8_t c)
+{
+ return c == 0 || isspace(c) || ispunct(c);
+}
+
+/*
+ If 'text' begins with any kind of single quote (e.g. "'" or "&apos;" etc.),
+ returns the length of the sequence of characters that makes up the single-
+ quote. Otherwise, returns zero.
+*/
+static size_t
+squote_len(const uint8_t *text, size_t size)
+{
+ static char* single_quote_list[] = { "'", "&#39;", "&#x27;", "&apos;", NULL };
+ char** p;
+
+ for (p = single_quote_list; *p; ++p) {
+ size_t len = strlen(*p);
+ if (size >= len && memcmp(text, *p, len) == 0) {
+ return len;
+ }
+ }
+
+ return 0;
+}
+
+/* Converts " or ' at very beginning or end of a word to left or right quote */
+static int
+smartypants_quotes(hoedown_buffer *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open)
+{
+ char ent[8];
+
+ if (*is_open && !word_boundary(next_char))
+ return 0;
+
+ if (!(*is_open) && !word_boundary(previous_char))
+ return 0;
+
+ snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote);
+ *is_open = !(*is_open);
+ hoedown_buffer_puts(ob, ent);
+ return 1;
+}
+
+/*
+ Converts ' to left or right single quote; but the initial ' might be in
+ different forms, e.g. &apos; or &#39; or &#x27;.
+ 'squote_text' points to the original single quote, and 'squote_size' is its length.
+ 'text' points at the last character of the single-quote, e.g. ' or ;
+*/
+static size_t
+smartypants_squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size,
+ const uint8_t *squote_text, size_t squote_size)
+{
+ if (size >= 2) {
+ uint8_t t1 = tolower(text[1]);
+ size_t next_squote_len = squote_len(text+1, size-1);
+
+ /* convert '' to &ldquo; or &rdquo; */
+ if (next_squote_len > 0) {
+ uint8_t next_char = (size > 1+next_squote_len) ? text[1+next_squote_len] : 0;
+ if (smartypants_quotes(ob, previous_char, next_char, 'd', &smrt->in_dquote))
+ return next_squote_len;
+ }
+
+ /* Tom's, isn't, I'm, I'd */
+ if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') &&
+ (size == 3 || word_boundary(text[2]))) {
+ HOEDOWN_BUFPUTSL(ob, "&rsquo;");
+ return 0;
+ }
+
+ /* you're, you'll, you've */
+ if (size >= 3) {
+ uint8_t t2 = tolower(text[2]);
+
+ if (((t1 == 'r' && t2 == 'e') ||
+ (t1 == 'l' && t2 == 'l') ||
+ (t1 == 'v' && t2 == 'e')) &&
+ (size == 4 || word_boundary(text[3]))) {
+ HOEDOWN_BUFPUTSL(ob, "&rsquo;");
+ return 0;
+ }
+ }
+ }
+
+ if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote))
+ return 0;
+
+ hoedown_buffer_put(ob, squote_text, squote_size);
+ return 0;
+}
+
+/* Converts ' to left or right single quote. */
+static size_t
+smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ return smartypants_squote(ob, smrt, previous_char, text, size, text, 1);
+}
+
+/* Converts (c), (r), (tm) */
+static size_t
+smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ if (size >= 3) {
+ uint8_t t1 = tolower(text[1]);
+ uint8_t t2 = tolower(text[2]);
+
+ if (t1 == 'c' && t2 == ')') {
+ HOEDOWN_BUFPUTSL(ob, "&copy;");
+ return 2;
+ }
+
+ if (t1 == 'r' && t2 == ')') {
+ HOEDOWN_BUFPUTSL(ob, "&reg;");
+ return 2;
+ }
+
+ if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') {
+ HOEDOWN_BUFPUTSL(ob, "&trade;");
+ return 3;
+ }
+ }
+
+ hoedown_buffer_putc(ob, text[0]);
+ return 0;
+}
+
+/* Converts "--" to em-dash, etc. */
+static size_t
+smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ if (size >= 3 && text[1] == '-' && text[2] == '-') {
+ HOEDOWN_BUFPUTSL(ob, "&mdash;");
+ return 2;
+ }
+
+ if (size >= 2 && text[1] == '-') {
+ HOEDOWN_BUFPUTSL(ob, "&ndash;");
+ return 1;
+ }
+
+ hoedown_buffer_putc(ob, text[0]);
+ return 0;
+}
+
+/* Converts &quot; etc. */
+static size_t
+smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ size_t len;
+ if (size >= 6 && memcmp(text, "&quot;", 6) == 0) {
+ if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote))
+ return 5;
+ }
+
+ len = squote_len(text, size);
+ if (len > 0) {
+ return (len-1) + smartypants_squote(ob, smrt, previous_char, text+(len-1), size-(len-1), text, len);
+ }
+
+ if (size >= 4 && memcmp(text, "&#0;", 4) == 0)
+ return 3;
+
+ hoedown_buffer_putc(ob, '&');
+ return 0;
+}
+
+/* Converts "..." to ellipsis */
+static size_t
+smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ if (size >= 3 && text[1] == '.' && text[2] == '.') {
+ HOEDOWN_BUFPUTSL(ob, "&hellip;");
+ return 2;
+ }
+
+ if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') {
+ HOEDOWN_BUFPUTSL(ob, "&hellip;");
+ return 4;
+ }
+
+ hoedown_buffer_putc(ob, text[0]);
+ return 0;
+}
+
+/* Converts `` to opening double quote */
+static size_t
+smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ if (size >= 2 && text[1] == '`') {
+ if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))
+ return 1;
+ }
+
+ hoedown_buffer_putc(ob, text[0]);
+ return 0;
+}
+
+/* Converts 1/2, 1/4, 3/4 */
+static size_t
+smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ if (word_boundary(previous_char) && size >= 3) {
+ if (text[0] == '1' && text[1] == '/' && text[2] == '2') {
+ if (size == 3 || word_boundary(text[3])) {
+ HOEDOWN_BUFPUTSL(ob, "&frac12;");
+ return 2;
+ }
+ }
+
+ if (text[0] == '1' && text[1] == '/' && text[2] == '4') {
+ if (size == 3 || word_boundary(text[3]) ||
+ (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) {
+ HOEDOWN_BUFPUTSL(ob, "&frac14;");
+ return 2;
+ }
+ }
+
+ if (text[0] == '3' && text[1] == '/' && text[2] == '4') {
+ if (size == 3 || word_boundary(text[3]) ||
+ (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) {
+ HOEDOWN_BUFPUTSL(ob, "&frac34;");
+ return 2;
+ }
+ }
+ }
+
+ hoedown_buffer_putc(ob, text[0]);
+ return 0;
+}
+
+/* Converts " to left or right double quote */
+static size_t
+smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote))
+ HOEDOWN_BUFPUTSL(ob, "&quot;");
+
+ return 0;
+}
+
+static size_t
+smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ static const char *skip_tags[] = {
+ "pre", "code", "var", "samp", "kbd", "math", "script", "style"
+ };
+ static const size_t skip_tags_count = 8;
+
+ size_t tag, i = 0;
+
+ /* This is a comment. Copy everything verbatim until --> or EOF is seen. */
+ if (i + 4 < size && memcmp(text, "<!--", 4) == 0) {
+ i += 4;
+ while (i + 3 < size && memcmp(text + i, "-->", 3) != 0)
+ i++;
+ i += 3;
+ hoedown_buffer_put(ob, text, i + 1);
+ return i;
+ }
+
+ while (i < size && text[i] != '>')
+ i++;
+
+ for (tag = 0; tag < skip_tags_count; ++tag) {
+ if (hoedown_html_is_tag(text, size, skip_tags[tag]) == HOEDOWN_HTML_TAG_OPEN)
+ break;
+ }
+
+ if (tag < skip_tags_count) {
+ for (;;) {
+ while (i < size && text[i] != '<')
+ i++;
+
+ if (i == size)
+ break;
+
+ if (hoedown_html_is_tag(text + i, size - i, skip_tags[tag]) == HOEDOWN_HTML_TAG_CLOSE)
+ break;
+
+ i++;
+ }
+
+ while (i < size && text[i] != '>')
+ i++;
+ }
+
+ hoedown_buffer_put(ob, text, i + 1);
+ return i;
+}
+
+static size_t
+smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+ if (size < 2)
+ return 0;
+
+ switch (text[1]) {
+ case '\\':
+ case '"':
+ case '\'':
+ case '.':
+ case '-':
+ case '`':
+ hoedown_buffer_putc(ob, text[1]);
+ return 1;
+
+ default:
+ hoedown_buffer_putc(ob, '\\');
+ return 0;
+ }
+}
+
+#if 0
+static struct {
+ uint8_t c0;
+ const uint8_t *pattern;
+ const uint8_t *entity;
+ int skip;
+} smartypants_subs[] = {
+ { '\'', "'s>", "&rsquo;", 0 },
+ { '\'', "'t>", "&rsquo;", 0 },
+ { '\'', "'re>", "&rsquo;", 0 },
+ { '\'', "'ll>", "&rsquo;", 0 },
+ { '\'', "'ve>", "&rsquo;", 0 },
+ { '\'', "'m>", "&rsquo;", 0 },
+ { '\'', "'d>", "&rsquo;", 0 },
+ { '-', "--", "&mdash;", 1 },
+ { '-', "<->", "&ndash;", 0 },
+ { '.', "...", "&hellip;", 2 },
+ { '.', ". . .", "&hellip;", 4 },
+ { '(', "(c)", "&copy;", 2 },
+ { '(', "(r)", "&reg;", 2 },
+ { '(', "(tm)", "&trade;", 3 },
+ { '3', "<3/4>", "&frac34;", 2 },
+ { '3', "<3/4ths>", "&frac34;", 2 },
+ { '1', "<1/2>", "&frac12;", 2 },
+ { '1', "<1/4>", "&frac14;", 2 },
+ { '1', "<1/4th>", "&frac14;", 2 },
+ { '&', "&#0;", 0, 3 },
+};
+#endif
+
+void
+hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *text, size_t size)
+{
+ size_t i;
+ struct smartypants_data smrt = {0, 0};
+
+ if (!text)
+ return;
+
+ hoedown_buffer_grow(ob, size);
+
+ for (i = 0; i < size; ++i) {
+ size_t org;
+ uint8_t action = 0;
+
+ org = i;
+ while (i < size && (action = smartypants_cb_chars[text[i]]) == 0)
+ i++;
+
+ if (i > org)
+ hoedown_buffer_put(ob, text + org, i - org);
+
+ if (i < size) {
+ i += smartypants_cb_ptrs[(int)action]
+ (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i);
+ }
+ }
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/markdown.h b/ext/phalcon/mvc/view/engine/markdown/markdown.h
new file mode 100644
index 0000000..e64d1cd
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/markdown.h
@@ -0,0 +1,6 @@
+#ifndef PHMARKDOWN_MARKDOWN_H
+#define PHMARKDOWN_MARKDOWN_H
+
+extern int phmarkdown_parse_view(zval *result, zval *view_code, zval *options, zval *render TSRMLS_DC);
+
+#endif /** PHMARKDOWN_MARKDOWN_H **/
diff --git a/ext/phalcon/mvc/view/engine/markdown/parser.c b/ext/phalcon/mvc/view/engine/markdown/parser.c
new file mode 100644
index 0000000..8065785
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/parser.c
@@ -0,0 +1,1311 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_phalcon.h"
+#include "phalcon.h"
+
+#include "markdown.h"
+#include "document.h"
+#include "html.h"
+#include "buffer.h"
+
+#include "kernel/main.h"
+#include "kernel/memory.h"
+#include "kernel/fcall.h"
+#include "kernel/exception.h"
+
+typedef struct {
+ int renderer;
+ int html;
+ int extension;
+ struct {
+ int begin;
+ int end;
+ } toc;
+ zval *renders;
+} phmarkdown_options_t;
+
+typedef struct {
+ unsigned int option;
+ const char *name;
+} phmarkdown_flag_t;
+
+#define PHMARKDOWN_RENDERER_HTML 0
+#define PHMARKDOWN_RENDERER_TOC 1
+#define PHMARKDOWN_BUFFER_UNIT 64
+#define PHMARKDOWN_DEFAULT_TOC_LEVEL 6
+
+static void phmarkdown_set_option(phmarkdown_options_t *options, zval *name, int opt TSRMLS_DC)
+{
+ phmarkdown_flag_t extension_flags[] = {
+ { HOEDOWN_EXT_TABLES, "tables" },
+ { HOEDOWN_EXT_FENCED_CODE, "fenced-code" },
+ { HOEDOWN_EXT_AUTOLINK, "autolink" },
+ { HOEDOWN_EXT_STRIKETHROUGH, "strikethrough" },
+ { HOEDOWN_EXT_NO_INTRA_EMPHASIS, "no-intra-emphasis" },
+ { HOEDOWN_EXT_FOOTNOTES, "footnotes" },
+ { HOEDOWN_EXT_UNDERLINE, "underline" },
+ { HOEDOWN_EXT_HIGHLIGHT, "highlight" },
+ { HOEDOWN_EXT_QUOTE, "quote" },
+ { HOEDOWN_EXT_SUPERSCRIPT, "superscript" },
+ { HOEDOWN_EXT_MATH, "math" },
+ { HOEDOWN_EXT_SPACE_HEADERS, "space-headers" },
+ { HOEDOWN_EXT_MATH_EXPLICIT, "math-explicit" },
+ { HOEDOWN_EXT_DISABLE_INDENTED_CODE, "disable-indented-code" },
+ { HOEDOWN_EXT_SPECIAL_ATTRIBUTE, "special-attribute" },
+ { HOEDOWN_EXT_SCRIPT_TAGS, "script-tags" },
+ { HOEDOWN_EXT_META_BLOCK, "meta-block" },
+ };
+ phmarkdown_flag_t html_flags[] = {
+ { HOEDOWN_HTML_SKIP_HTML, "skip-html" },
+ { HOEDOWN_HTML_HARD_WRAP, "hard-wrap" },
+ { HOEDOWN_HTML_USE_XHTML, "xhtml" },
+ { HOEDOWN_HTML_ESCAPE, "escape" },
+ { HOEDOWN_HTML_USE_TASK_LIST, "task" },
+ { HOEDOWN_HTML_LINE_CONTINUE, "line-continue" },
+ { HOEDOWN_HTML_HEADER_ID, "header-id" },
+ { HOEDOWN_HTML_FENCED_CODE_SCRIPT, "fenced-code-script" },
+ };
+ phmarkdown_flag_t *flag;
+ int toc_len, n, i = 0, j = 0;
+ char *toc;
+
+ // extension
+ i = 0;
+ n = sizeof(extension_flags)/sizeof(phmarkdown_flag_t);
+ do {
+ flag = extension_flags + i;
+ if (strncasecmp(Z_STRVAL_P(name), flag->name, Z_STRLEN_P(name)) == 0) {
+ if (opt) {
+ options->extension |= flag->option;
+ } else if (options->extension & flag->option) {
+ options->extension ^= flag->option;
+ }
+ return;
+ }
+ ++i;
+ } while (i < n);
+
+ // html
+ i = 0;
+ n = sizeof(html_flags)/sizeof(phmarkdown_flag_t);
+ do {
+ flag = html_flags + i;
+ if (strncasecmp(Z_STRVAL_P(name), flag->name, Z_STRLEN_P(name)) == 0) {
+ if (opt) {
+ options->html |= flag->option;
+ if (flag->option == HOEDOWN_HTML_FENCED_CODE_SCRIPT) {
+ options->extension |= HOEDOWN_EXT_FENCED_CODE;
+ }
+ } else if (options->html & flag->option) {
+ options->html ^= flag->option;
+ }
+ return;
+ }
+ ++i;
+ } while (i < n);
+
+ // toc
+ if (strncasecmp(Z_STRVAL_P(name), "toc", 3) == 0) {
+ toc = (char *)Z_STRVAL_P(name);
+ toc_len = Z_STRLEN_P(name);
+ options->renderer = PHMARKDOWN_RENDERER_TOC;
+ options->toc.end = PHMARKDOWN_DEFAULT_TOC_LEVEL;
+ if (toc_len > 3 && toc[3] == ':') {
+ n = 0;
+ for (i = 4; i < toc_len; i++) {
+ if (isdigit(toc[i])) {
+ if (n) {
+ n = (n * 10) + (toc[i] - '0');
+ } else {
+ n = (toc[i] - '0');
+ }
+ } else if (toc[i] == ':') {
+ if (n) {
+ options->toc.begin = n;
+ }
+ n = 0;
+ j++;
+ }
+ }
+ if (n) {
+ if (j) {
+ options->toc.end = n;
+ } else {
+ options->toc.begin = n;
+ }
+ }
+ }
+ }
+}
+
+static void phmarkdown_set_options(phmarkdown_options_t *options, zval *values, int flag TSRMLS_DC)
+{
+ zval **value;
+ for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(values)); zend_hash_get_current_data(Z_ARRVAL_P(values), (void *)&value) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(values))) {
+ if (Z_TYPE_PP(value) == IS_STRING) {
+ phmarkdown_set_option(options, *value, flag TSRMLS_CC);
+ }
+ }
+}
+
+static void phmarkdown_init_options(phmarkdown_options_t *options, zval *enable, zval *disable, zval *renders TSRMLS_DC)
+{
+ memset(options, 0, sizeof(phmarkdown_options_t));
+
+ options->renderer = PHMARKDOWN_RENDERER_HTML;
+ options->toc.end = 0;
+ options->extension = 0;
+ options->html = 0;
+ options->renders = renders;
+
+ if (enable) {
+ phmarkdown_set_options(options, enable, 1 TSRMLS_CC);
+ }
+ if (disable) {
+ phmarkdown_set_options(options, disable, 0 TSRMLS_CC);
+ }
+}
+
+static void phmarkdown_run_renderer(hoedown_buffer *ob, zval *definition, zval *parameters TSRMLS_DC)
+{
+ zval fname;
+ zval *result = NULL, **args[2];
+
+ ZVAL_STRINGL(&fname, "call_user_func_array", sizeof("call_user_func_array")-1, 0);
+
+ args[0] = &definition;
+ args[1] = &parameters;
+
+ call_user_function_ex(EG(function_table), NULL, &fname, &result, 2, args, 0, NULL TSRMLS_CC);
+ if (result) {
+ if (Z_TYPE_P(result) == IS_STRING) {
+ hoedown_buffer_put(ob, Z_STRVAL_P(result), Z_STRLEN_P(result));
+ }
+ zval_ptr_dtor(&result);
+ }
+}
+
+static zval * phmarkdown_is_renderer(char *name, const hoedown_renderer_data *data TSRMLS_DC)
+{
+ hoedown_html_renderer_state *state = data->opaque;
+ zval *renders = NULL;
+ zval **definition = NULL;
+
+ if (state) {
+ renders = ((phmarkdown_options_t *)state->opaque)->renders;
+ }
+
+ if (!name || !renders || Z_TYPE_P(renders) != IS_ARRAY) {
+ return NULL;
+ }
+
+ if (zend_hash_find(Z_ARRVAL_P(renders), name, strlen(name)+1, (void **)&definition) == FAILURE || !definition) {
+ return NULL;
+ }
+
+ return *definition;
+}
+
+static void phmarkdown_renderer_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("blockcode", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 3);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (lang) {
+ add_next_index_stringl(parameters, (char *)lang->data, lang->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_blockquote(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("blockquote", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_blockhtml(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("blockhtml", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_header(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, int level, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("header", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 3);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ add_next_index_long(parameters, level);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("hrule", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 0);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_list(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, unsigned int flags, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("list", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 3);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ add_next_index_long(parameters, flags);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_listitem(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, unsigned int *flags, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("listitem", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 3);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ add_next_index_long(parameters, *flags);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_paragraph(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("paragraph", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_table(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("table", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 2);
+ if (content) {
+ add_next_index_stringl(parameters, (char *)content->data, content->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_table_header(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("tableheader", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_table_body(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("tablebody", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_table_row(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("tablerow", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_table_cell(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flags, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("tablecell", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 2);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ add_next_index_long(parameters, flags);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_footnotes(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("footnotes", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static void phmarkdown_renderer_footnote_def(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int num, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("footnotedef", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 2);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ add_next_index_long(parameters, num);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static int phmarkdown_renderer_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("autolink", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 2);
+ if (link) {
+ add_next_index_stringl(parameters, (char *)link->data, link->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ add_next_index_long(parameters, type);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("codespan", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 2);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("doubleemphasis", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("emphasis", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_underline(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("underline", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_highlight(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("highlight", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_quote(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("quote", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("image", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 4);
+ if (link) {
+ add_next_index_stringl(parameters, (char *)link->data, link->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (title) {
+ add_next_index_stringl(parameters, (char *)title->data, title->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (alt) {
+ add_next_index_stringl(parameters, (char *)alt->data, alt->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("linebreak", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 0);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *attr, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("link", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 4);
+ if (content) {
+ add_next_index_stringl(parameters, (char *)content->data, content->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (link) {
+ add_next_index_stringl(parameters, (char *)link->data, link->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (title) {
+ add_next_index_stringl(parameters, (char *)title->data, title->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ if (attr) {
+ add_next_index_stringl(parameters, (char *)attr->data, attr->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_raw_html(hoedown_buffer *ob, const hoedown_buffer *tag, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("rawhtml", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (tag) {
+ add_next_index_stringl(parameters, (char *)tag->data, tag->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("tripleemphasis", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_strikethrough(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("strikethrough", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_superscript(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("superscript", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static int phmarkdown_renderer_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("footnoteref", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ add_next_index_long(parameters, num);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static void phmarkdown_renderer_entity(hoedown_buffer *ob, const hoedown_buffer *entity, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("entity", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (entity) {
+ add_next_index_stringl(parameters, (char *)entity->data, entity->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static int phmarkdown_renderer_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("math", data TSRMLS_CC);
+ if (!definition) {
+ return 0;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 2);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+ add_next_index_long(parameters, displaymode);
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+
+ return 1;
+}
+
+static void phmarkdown_renderer_normal_text(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
+{
+ zval *definition, *parameters;
+ TSRMLS_FETCH();
+
+ definition = phmarkdown_is_renderer("normaltext", data TSRMLS_CC);
+ if (!definition) {
+ return;
+ }
+
+ MAKE_STD_ZVAL(parameters);
+ array_init_size(parameters, 1);
+ if (text) {
+ add_next_index_stringl(parameters, (char *)text->data, text->size, 1);
+ } else {
+ add_next_index_stringl(parameters, "", 0, 1);
+ }
+
+ phmarkdown_run_renderer(ob, definition, parameters TSRMLS_CC);
+
+ zval_ptr_dtor(&parameters);
+}
+
+static int phmarkdown_set_renderer(zval *renders, hoedown_renderer *renderer, char *name, zval **error_msg TSRMLS_DC)
+{
+ zval **definition = NULL;
+ char *error = NULL;
+ zend_bool retval;
+
+ if (!name || !renders || Z_TYPE_P(renders) != IS_ARRAY) {
+ MAKE_STD_ZVAL(*error_msg);
+ ZVAL_STRING(*error_msg, "Undefined renders", 1);
+ return FAILURE;
+ }
+
+ if (zend_hash_find(Z_ARRVAL_P(renders), name, strlen(name)+1, (void **)&definition) == FAILURE || !definition) {
+ MAKE_STD_ZVAL(*error_msg);
+ ZVAL_STRING(*error_msg, "Undefined render", 1);
+ return FAILURE;
+ }
+
+ retval = zend_is_callable_ex(*definition, NULL, 0, NULL, NULL, NULL, &error TSRMLS_CC);
+ if (error) {
+ efree(error);
+ }
+ if (!retval) {
+ ZVAL_STRING(*error_msg, "Call to undefined render", 1);
+ return FAILURE;
+ }
+
+ if (strcmp(name, "blockcode") == 0) {
+ renderer->blockcode = phmarkdown_renderer_blockcode;
+ } else if (strcmp(name, "blockquote") == 0) {
+ renderer->blockquote = phmarkdown_renderer_blockquote;
+ } else if (strcmp(name, "blockhtml") == 0) {
+ renderer->blockhtml = phmarkdown_renderer_blockhtml;
+ } else if (strcmp(name, "header") == 0) {
+ renderer->header = phmarkdown_renderer_header;
+ } else if (strcmp(name, "hrule") == 0) {
+ renderer->hrule = phmarkdown_renderer_hrule;
+ } else if (strcmp(name, "list") == 0) {
+ renderer->list = phmarkdown_renderer_list;
+ } else if (strcmp(name, "listitem") == 0) {
+ renderer->listitem = phmarkdown_renderer_listitem;
+ } else if (strcmp(name, "paragraph") == 0) {
+ renderer->paragraph = phmarkdown_renderer_paragraph;
+ } else if (strcmp(name, "table") == 0) {
+ renderer->table = phmarkdown_renderer_table;
+ } else if (strcmp(name, "tableheader") == 0) {
+ renderer->table_header = phmarkdown_renderer_table_header;
+ } else if (strcmp(name, "tablebody") == 0) {
+ renderer->table_body = phmarkdown_renderer_table_body;
+ } else if (strcmp(name, "tablerow") == 0) {
+ renderer->table_row = phmarkdown_renderer_table_row;
+ } else if (strcmp(name, "tablecell") == 0) {
+ renderer->table_cell = phmarkdown_renderer_table_cell;
+ } else if (strcmp(name, "footnotes") == 0) {
+ renderer->footnotes = phmarkdown_renderer_footnotes;
+ } else if (strcmp(name, "footnotedef") == 0) {
+ renderer->footnote_def = phmarkdown_renderer_footnote_def;
+ } else if (strcmp(name, "autolink") == 0) {
+ renderer->autolink = phmarkdown_renderer_autolink;
+ } else if (strcmp(name, "codespan") == 0) {
+ renderer->codespan = phmarkdown_renderer_codespan;
+ } else if (strcmp(name, "doubleemphasis") == 0) {
+ renderer->double_emphasis = phmarkdown_renderer_double_emphasis;
+ } else if (strcmp(name, "emphasis") == 0) {
+ renderer->emphasis = phmarkdown_renderer_emphasis;
+ } else if (strcmp(name, "underline") == 0) {
+ renderer->underline = phmarkdown_renderer_underline;
+ } else if (strcmp(name, "highlight") == 0) {
+ renderer->highlight = phmarkdown_renderer_highlight;
+ } else if (strcmp(name, "quote") == 0) {
+ renderer->quote = phmarkdown_renderer_quote;
+ } else if (strcmp(name, "image") == 0) {
+ renderer->image = phmarkdown_renderer_image;
+ } else if (strcmp(name, "linebreak") == 0) {
+ renderer->linebreak = phmarkdown_renderer_linebreak;
+ } else if (strcmp(name, "link") == 0) {
+ renderer->link = phmarkdown_renderer_link;
+ } else if (strcmp(name, "rawhtml") == 0) {
+ renderer->raw_html = phmarkdown_renderer_raw_html;
+ } else if (strcmp(name, "tripleemphasis") == 0) {
+ renderer->triple_emphasis = phmarkdown_renderer_triple_emphasis;
+ } else if (strcmp(name, "strikethrough") == 0) {
+ renderer->strikethrough = phmarkdown_renderer_strikethrough;
+ } else if (strcmp(name, "superscript") == 0) {
+ renderer->superscript = phmarkdown_renderer_superscript;
+ } else if (strcmp(name, "footnoteref") == 0) {
+ renderer->footnote_ref = phmarkdown_renderer_footnote_ref;
+ } else if (strcmp(name, "math") == 0) {
+ renderer->math = phmarkdown_renderer_math;
+ } else if (strcmp(name, "entity") == 0) {
+ renderer->entity = phmarkdown_renderer_entity;
+ } else if (strcmp(name, "normaltext") == 0) {
+ renderer->normal_text = phmarkdown_renderer_normal_text;
+ } else {
+ ZVAL_STRING(*error_msg, "Undefined render function", 1);
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+static int phmarkdown_internal_parse_view(zval **result, zval *view_code, phmarkdown_options_t *options, zval **error_msg TSRMLS_DC)
+{
+ hoedown_buffer *buf, *meta;
+ hoedown_renderer *renderer;
+ hoedown_document *document;
+ hoedown_html_renderer_state *state;
+ int toc_level = 0, html = 0, extension = 0;
+
+ /** Check if the view has code */
+ if (!Z_STRVAL_P(view_code)) {
+ MAKE_STD_ZVAL(*error_msg);
+ ZVAL_STRING(*error_msg, "View code cannot be null", 1);
+ return FAILURE;
+ }
+
+ if (!Z_STRLEN_P(view_code)) {
+ array_init(*result);
+ return SUCCESS;
+ }
+
+ /** Initilize buffer */
+ buf = hoedown_buffer_new(PHMARKDOWN_BUFFER_UNIT);
+ if (!buf) {
+ MAKE_STD_ZVAL(*error_msg);
+ ZVAL_STRING(*error_msg, "Couldn't allocate output buffer", 1);
+ return FAILURE;
+ }
+
+ meta = hoedown_buffer_new(PHMARKDOWN_BUFFER_UNIT);
+ if (!meta) {
+ hoedown_buffer_free(buf);
+ MAKE_STD_ZVAL(*error_msg);
+ ZVAL_STRING(*error_msg, "Couldn't allocate meta block buffer", 1);
+ return FAILURE;
+ }
+
+ /** Parse toc */
+ if (options->toc.end > 0) {
+ toc_level = options->toc.end;
+ if (options->renderer == PHMARKDOWN_RENDERER_TOC) {
+ zval *zv;
+
+ renderer = hoedown_html_toc_renderer_new(0);
+
+ state = (hoedown_html_renderer_state *)renderer->opaque;
+
+ state->toc_data.current_level = 0;
+ state->toc_data.level_offset = options->toc.begin;
+ state->toc_data.nesting_level = toc_level;
+
+ document = hoedown_document_new(renderer, options->extension, 16, NULL, NULL);
+
+ hoedown_document_render(document, buf, (const uint8_t *)Z_STRVAL_P(view_code), Z_STRLEN_P(view_code));
+
+ hoedown_document_free(document);
+ hoedown_html_renderer_free(renderer);
+
+ MAKE_STD_ZVAL(zv);
+ ZVAL_STRINGL(zv, (char *)buf->data, buf->size, 1);
+
+ if (Z_TYPE_PP(result) != IS_ARRAY) {
+ array_init(*result);
+ }
+
+ zend_symtable_update(Z_ARRVAL_PP(result), "toc", sizeof("toc"), &zv, sizeof(zv), NULL);
+
+ hoedown_buffer_reset(buf);
+ }
+ }
+
+ /** Initilize renderer */
+ renderer = hoedown_html_renderer_new(options->html, toc_level);
+
+ /** Override render function */
+ if (options->renders && Z_TYPE_P(options->renders) == IS_ARRAY) {
+ HashTable *h = Z_ARRVAL_P(options->renders);
+ HashPosition pos;
+ ulong int_key;
+ char *str_key = NULL;
+ uint str_key_len;
+
+ zend_hash_internal_pointer_reset_ex(h, &pos);
+ do {
+ if (zend_hash_get_current_key_ex(h, &str_key, &str_key_len, &int_key, 0, &pos) != HASH_KEY_IS_STRING || !str_key) {
+ break;
+ }
+ if (phmarkdown_set_renderer(options->renders, renderer, str_key, error_msg TSRMLS_CC) != SUCCESS) {
+ hoedown_buffer_free(buf);
+ hoedown_buffer_free(meta);
+ return FAILURE;
+ }
+ } while (!zend_hash_move_forward_ex(h, &pos));
+ }
+
+ state = (hoedown_html_renderer_state *)renderer->opaque;
+ state->opaque = (void *)options;
+
+ document = hoedown_document_new(renderer, options->extension, 16, NULL, meta);
+
+ /** Execute parse */
+ hoedown_document_render(document, buf, (const uint8_t *)Z_STRVAL_P(view_code), Z_STRLEN_P(view_code));
+
+ hoedown_document_free(document);
+ hoedown_html_renderer_free(renderer);
+
+ /** Setting return value */
+ if (Z_TYPE_PP(result) != IS_ARRAY) {
+ array_init(*result);
+ }
+
+ add_assoc_stringl(*result, "result", (char *)buf->data, buf->size, 1);
+
+ /** Setting meta block */
+ if ((options->extension & HOEDOWN_EXT_META_BLOCK) && meta->size > 0) {
+ zval *zv;
+
+ MAKE_STD_ZVAL(zv);
+ ZVAL_STRINGL(zv, (char *)meta->data, meta->size, 1);
+
+ zend_symtable_update(Z_ARRVAL_PP(result), "meta", sizeof("meta"), &zv, sizeof(zv), NULL);
+ }
+
+ hoedown_buffer_free(buf);
+ hoedown_buffer_free(meta);
+
+ return SUCCESS;
+}
+
+int phmarkdown_parse_view(zval *result, zval *view_code, zval *options, zval *render TSRMLS_DC)
+{
+ phmarkdown_options_t opts;
+ zval *error_msg = NULL;
+
+ ZVAL_NULL(result);
+
+ if (Z_TYPE_P(view_code) != IS_STRING) {
+ ZEPHIR_THROW_EXCEPTION_STRW(phalcon_mvc_view_exception_ce, "View code must be a string");
+ return FAILURE;
+ }
+
+ phmarkdown_init_options(&opts, options, NULL, render TSRMLS_CC);
+
+ if (phmarkdown_internal_parse_view(&result, view_code, &opts, &error_msg TSRMLS_CC) == FAILURE) {
+ ZEPHIR_THROW_EXCEPTION_STRW(phalcon_mvc_view_exception_ce, Z_STRVAL_P(error_msg));
+ zval_ptr_dtor(&error_msg);
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/stack.c b/ext/phalcon/mvc/view/engine/markdown/stack.c
new file mode 100644
index 0000000..16736f7
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/stack.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "stack.h"
+
+#include "buffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+void
+hoedown_stack_init(hoedown_stack *st, size_t initial_size)
+{
+ assert(st);
+
+ st->item = NULL;
+ st->size = st->asize = 0;
+
+ if (!initial_size)
+ initial_size = 8;
+
+ hoedown_stack_grow(st, initial_size);
+}
+
+void
+hoedown_stack_uninit(hoedown_stack *st)
+{
+ assert(st);
+
+ free(st->item);
+}
+
+void
+hoedown_stack_grow(hoedown_stack *st, size_t neosz)
+{
+ assert(st);
+
+ if (st->asize >= neosz)
+ return;
+
+ st->item = hoedown_realloc(st->item, neosz * sizeof(void *));
+ memset(st->item + st->asize, 0x0, (neosz - st->asize) * sizeof(void *));
+
+ st->asize = neosz;
+
+ if (st->size > neosz)
+ st->size = neosz;
+}
+
+void
+hoedown_stack_push(hoedown_stack *st, void *item)
+{
+ assert(st);
+
+ if (st->size >= st->asize)
+ hoedown_stack_grow(st, st->size * 2);
+
+ st->item[st->size++] = item;
+}
+
+void *
+hoedown_stack_pop(hoedown_stack *st)
+{
+ assert(st);
+
+ if (!st->size)
+ return NULL;
+
+ return st->item[--st->size];
+}
+
+void *
+hoedown_stack_top(const hoedown_stack *st)
+{
+ assert(st);
+
+ if (!st->size)
+ return NULL;
+
+ return st->item[st->size - 1];
+}
diff --git a/ext/phalcon/mvc/view/engine/markdown/stack.h b/ext/phalcon/mvc/view/engine/markdown/stack.h
new file mode 100644
index 0000000..90ccc4e
--- /dev/null
+++ b/ext/phalcon/mvc/view/engine/markdown/stack.h
@@ -0,0 +1,70 @@
+/* stack.h - simple stacking */
+
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ * Copyright (c) 2013, Devin Torres and the Hoedown authors
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HOEDOWN_STACK_H
+#define HOEDOWN_STACK_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*********
+ * TYPES *
+ *********/
+
+struct hoedown_stack {
+ void **item;
+ size_t size;
+ size_t asize;
+};
+typedef struct hoedown_stack hoedown_stack;
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_stack_init: initialize a stack */
+void hoedown_stack_init(hoedown_stack *st, size_t initial_size);
+
+/* hoedown_stack_uninit: free internal data of the stack */
+void hoedown_stack_uninit(hoedown_stack *st);
+
+/* hoedown_stack_grow: increase the allocated size to the given value */
+void hoedown_stack_grow(hoedown_stack *st, size_t neosz);
+
+/* hoedown_stack_push: push an item to the top of the stack */
+void hoedown_stack_push(hoedown_stack *st, void *item);
+
+/* hoedown_stack_pop: retrieve and remove the item at the top of the stack */
+void *hoedown_stack_pop(hoedown_stack *st);
+
+/* hoedown_stack_top: retrieve the item at the top of the stack */
+void *hoedown_stack_top(const hoedown_stack *st);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** HOEDOWN_STACK_H **/
diff --git a/optimizers/PhmarkdownParseViewOptimizer.php b/optimizers/PhmarkdownParseViewOptimizer.php
new file mode 100644
index 0000000..d87e0c8
--- /dev/null
+++ b/optimizers/PhmarkdownParseViewOptimizer.php
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ +------------------------------------------------------------------------+
+ | Phalcon Framework |
+ +------------------------------------------------------------------------+
+ | Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
+ +------------------------------------------------------------------------+
+ | This source file is subject to the New BSD License that is bundled |
+ | with this package in the file docs/LICENSE.txt. |
+ | |
+ | If you did not receive a copy of the license and are unable to |
+ | obtain it through the world-wide-web, please send an email |
+ | to license@phalconphp.com so we can send you a copy immediately. |
+ +------------------------------------------------------------------------+
+ | Authors: Andres Gutierrez <andres@phalconphp.com> |
+ | Eduar Carvajal <eduar@phalconphp.com> |
+ +------------------------------------------------------------------------+
+ */
+
+namespace Zephir\Optimizers\FunctionCall;
+
+use Zephir\Call;
+use Zephir\CompilationContext;
+use Zephir\CompilerException;
+use Zephir\CompiledExpression;
+use Zephir\Optimizers\OptimizerAbstract;
+use Zephir\HeadersManager;
+
+class PhmarkdownParseViewOptimizer extends OptimizerAbstract
+{
+
+ /**
+ *
+ * @param array $expression
+ * @param Call $call
+ * @param CompilationContext $context
+ */
+ public function optimize(array $expression, Call $call, CompilationContext $context)
+ {
+ if (!isset($expression['parameters'])) {
+ return false;
+ }
+
+ if (count($expression['parameters']) != 3) {
+ throw new CompilerException("'phmarkdown_parse_view' only accepts two parameters", $expression);
+ }
+
+ /**
+ * Process the expected symbol to be returned
+ */
+ $call->processExpectedReturn($context);
+
+ $symbolVariable = $call->getSymbolVariable();
+ if ($symbolVariable->getType() != 'variable') {
+ throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression);
+ }
+
+ if ($call->mustInitSymbolVariable()) {
+ $symbolVariable->initVariant($context);
+ }
+
+ $symbolVariable->setDynamicTypes('array');
+
+ $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);
+
+ $context->headersManager->add('phalcon/mvc/view/engine/markdown/markdown');
+
+ $context->codePrinter->output('if (phmarkdown_parse_view(' . $symbolVariable->getName() . ', ' . $resolvedParams[0] . ', ' . $resolvedParams[1] . ', ' . $resolvedParams[2] . ' TSRMLS_CC) == FAILURE) {');
+ $context->codePrinter->output("\t" . 'RETURN_MM();');
+ $context->codePrinter->output('}');
+
+ return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
+ }
+
+}
diff --git a/phalcon/mvc/view/engine/markdown.zep b/phalcon/mvc/view/engine/markdown.zep
new file mode 100644
index 0000000..040f03b
--- /dev/null
+++ b/phalcon/mvc/view/engine/markdown.zep
@@ -0,0 +1,161 @@
+
+/*
+ +------------------------------------------------------------------------+
+ | Phalcon Framework |
+ +------------------------------------------------------------------------+
+ | Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
+ +------------------------------------------------------------------------+
+ | This source file is subject to the New BSD License that is bundled |
+ | with this package in the file docs/LICENSE.txt. |
+ | |
+ | If you did not receive a copy of the license and are unable to |
+ | obtain it through the world-wide-web, please send an email |
+ | to license@phalconphp.com so we can send you a copy immediately. |
+ +------------------------------------------------------------------------+
+ | Authors: Andres Gutierrez <andres@phalconphp.com> |
+ | Eduar Carvajal <eduar@phalconphp.com> |
+ +------------------------------------------------------------------------+
+*/
+
+namespace Phalcon\Mvc\View\Engine;
+
+use Phalcon\Mvc\View\Engine;
+use Phalcon\Mvc\View\EngineInterface;
+use Phalcon\Mvc\View\Engine\Markdown\Compiler;
+use Phalcon\Mvc\View\Exception;
+
+/**
+ * Phalcon\Mvc\View\Engine\Markdown
+ *
+ * Designer friendly and fast template engine for PHP written in C
+ */
+class Markdown extends Engine implements EngineInterface
+{
+ const SKIP_HTML = "skip-html";
+ const HARD_WRAP = "hard-wrap";
+ const USE_XHTML = "xhtml";
+ const ESCAPE = "escape";
+ const TABLES = "tables";
+ const FENCED_CODE = "fenced-code";
+ const FENCED_CODE_SCRIPT = "fenced-code-script";
+ const AUTOLINK = "autolink";
+ const STRIKETHROUGH = "strikethrough";
+ const NO_INTRA_EMPHASIS = "no-intra-emphasis";
+ const FOOTNOTES = "footnotes";
+ const UNDERLINE = "underline";
+ const HIGHLIGHT = "highlight";
+ const QUOTE = "quote";
+ const SUPERSCRIPT = "superscript";
+ const MATH = "math";
+ const MATH_EXPLICIT = "math-explicit";
+ const SPACE_HEADERS = "space-headers";
+ const DISABLE_INDENTED_CODE = "disable-indented-code";
+ const HEADER_ID = "header-id";
+ const LINE_CONTINUE = "line-continue";
+ const TASK = "task";
+ const SPECIAL_ATTRIBUTE = "special-attribute";
+ const SCRIPT_TAGS = "script-tags";
+ const META_BLOCK = "meta-block";
+ const TOC = "toc";
+
+ protected _options;
+
+ protected _compiler;
+
+ /**
+ * Set Markdown's options
+ *
+ * @param array options
+ */
+ public function setOptions(array! options)
+ {
+ let this->_options = options;
+ }
+
+ /**
+ * Return Markdown's options
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return this->_options;
+ }
+
+ /**
+ * Returns the Markdown's compiler
+ *
+ * @return Phalcon\Mvc\View\Engine\Markdown\Compiler
+ */
+ public function getCompiler() -> <Compiler>
+ {
+ var compiler, dependencyInjector, options;
+
+ let compiler = this->_compiler;
+ if typeof compiler != "object" {
+
+ let compiler = new Compiler(this->_view);
+
+ /**
+ * Pass the IoC to the compiler only of it's an object
+ */
+ let dependencyInjector = <\Phalcon\Di> this->_dependencyInjector;
+ if typeof dependencyInjector == "object" {
+ compiler->setDi(dependencyInjector);
+ }
+
+ /**
+ * Pass the options to the compiler only if they're an array
+ */
+ let options = this->_options;
+ if typeof options == "array" {
+ compiler->setOptions(options);
+ }
+
+ let this->_compiler = compiler;
+ }
+ return compiler;
+ }
+
+ /**
+ * Renders a view using the template engine
+ *
+ * @param string $templatePath
+ * @param array $params
+ * @param boolean $mustClean
+ */
+ public function render(string! templatePath, var params, boolean mustClean = false)
+ {
+ var compiler, compiledTemplatePath, key, value;
+
+ if mustClean {
+ ob_clean();
+ }
+
+ /**
+ * The compilation process is done by Phalcon\Mvc\View\Engine\Markdown\Compiler
+ */
+ let compiler = this->getCompiler();
+
+ compiler->compile(templatePath);
+
+ let compiledTemplatePath = compiler->getCompiledTemplatePath();
+
+ /**
+ * Export the variables the current symbol table
+ */
+ if typeof params == "array" {
+ for key, value in params {
+ let {key} = value;
+ }
+ }
+
+ require compiledTemplatePath;
+
+ if mustClean {
+ this->_view->setContent(ob_get_contents());
+ //ob_clean();
+ }
+ }
+
+}
diff --git a/phalcon/mvc/view/engine/markdown/compiler.zep b/phalcon/mvc/view/engine/markdown/compiler.zep
new file mode 100644
index 0000000..1554ce6
--- /dev/null
+++ b/phalcon/mvc/view/engine/markdown/compiler.zep
@@ -0,0 +1,517 @@
+
+/*
+ +------------------------------------------------------------------------+
+ | Phalcon Framework |
+ +------------------------------------------------------------------------+
+ | Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
+ +------------------------------------------------------------------------+
+ | This source file is subject to the New BSD License that is bundled |
+ | with this package in the file docs/LICENSE.txt. |
+ | |
+ | If you did not receive a copy of the license and are unable to |
+ | obtain it through the world-wide-web, please send an email |
+ | to license@phalconphp.com so we can send you a copy immediately. |
+ +------------------------------------------------------------------------+
+ | Authors: Andres Gutierrez <andres@phalconphp.com> |
+ | Eduar Carvajal <eduar@phalconphp.com> |
+ +------------------------------------------------------------------------+
+*/
+
+namespace Phalcon\Mvc\View\Engine\Markdown;
+
+use Phalcon\Di\InjectionAwareInterface;
+use Phalcon\Mvc\View\Exception;
+use Phalcon\Mvc\View\Engine\Markdown as Markdown;
+
+/**
+ * Phalcon\Mvc\View\Engine\Markdown\Compiler
+ *
+ * This class reads and compiles Markdown templates into PHP plain code
+ *
+ *<code>
+ * $compiler = new \Phalcon\Mvc\View\Engine\Markdown\Compiler();
+ *
+ * $compiler->compile('views/partials/header.md');
+ *
+ * require $compiler->getCompiledTemplatePath();
+ *</code>
+ */
+class Compiler implements InjectionAwareInterface
+{
+ protected _dependencyInjector;
+
+ protected _view;
+
+ protected _options;
+
+ protected _currentPath;
+
+ protected _compiledTemplatePath;
+
+ protected _compilation = "";
+
+ protected _toc = "";
+
+ protected _meta = "";
+
+ protected _defaultCompileOption = [
+ Markdown::TABLES, Markdown::FENCED_CODE, Markdown::AUTOLINK,
+ Markdown::STRIKETHROUGH, Markdown::NO_INTRA_EMPHASIS,
+ Markdown::TASK, Markdown::SCRIPT_TAGS
+ ];
+
+ /**
+ * Phalcon\Mvc\View\Engine\Markdown\Compiler
+ *
+ * @param Phalcon\Mvc\ViewInterface view
+ */
+ public function __construct(view=null)
+ {
+ if typeof view == "object" {
+ let this->_view = view;
+ }
+ }
+
+ /**
+ * Sets the dependency injector
+ *
+ * @param Phalcon\DiInterface dependencyInjector
+ */
+ public function setDI(<\Phalcon\DiInterface> dependencyInjector)
+ {
+ if typeof dependencyInjector != "object" {
+ throw new Exception("Dependency Injector is invalid");
+ }
+ let this->_dependencyInjector = dependencyInjector;
+ }
+
+ /**
+ * Returns the internal dependency injector
+ *
+ * @return Phalcon\DiInterface
+ */
+ public function getDI() -> <\Phalcon\DiInterface>
+ {
+ return this->_dependencyInjector;
+ }
+
+ /**
+ * Sets the compiler options
+ *
+ * @param array options
+ */
+ public function setOptions(array! options)
+ {
+ let this->_options = options;
+ }
+
+ /**
+ * Sets a single compiler option
+ *
+ * @param string option
+ * @param mixed value
+ */
+ public function setOption(string! option, value)
+ {
+ let this->_options[option] = value;
+ }
+
+ /**
+ * Returns a compiler's option
+ *
+ * @param string option
+ * @return string
+ */
+ public function getOption(string! option)
+ {
+ var value;
+ if fetch value, this->_options[option] {
+ return value;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the compiler options
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return this->_options;
+ }
+
+ /**
+ * Compiles a Markdown source code returning a PHP plain version
+ *
+ * @param string viewCode
+ * @return string
+ */
+ protected function _compileSource(string! viewCode) -> string
+ {
+ var intermediate, options, code,
+ compileOption, compileRender, afterCompile;
+
+ let compileOption = this->_defaultCompileOption,
+ compileRender = [], afterCompile = null;
+
+ let options = this->_options;
+ if typeof options == "array" {
+
+ if isset options["compileOption"] && typeof options["compileOption"] == "array" {
+ let compileOption = options["compileOption"];
+ }
+
+ if isset options["compileRender"] && typeof options["compileRender"] == "array" {
+ let compileRender = options["compileRender"];
+ }
+
+ if isset options["beforeCompile"] && typeof options["beforeCompile"] == "object" && options["beforeCompile"] instanceof \Closure {
+ let code = call_user_func_array(options["beforeCompile"], [viewCode]);
+ let viewCode = (string)code;
+ }
+
+ if isset options["afterCompile"] && typeof options["afterCompile"] == "object" && options["afterCompile"] instanceof \Closure {
+ let afterCompile = options["afterCompile"];
+ }
+
+ }
+
+ let intermediate = phmarkdown_parse_view(viewCode, compileOption, compileRender);
+
+ if typeof intermediate == "array" {
+ if isset intermediate["result"] {
+ let this->_compilation = intermediate["result"];
+ }
+
+ if isset intermediate["toc"] {
+ let this->_toc = intermediate["toc"];
+ }
+
+ if isset intermediate["meta"] {
+ let this->_meta = intermediate["meta"];
+ }
+
+ if is_callable(afterCompile) {
+ let code = call_user_func_array(afterCompile, [this]);
+ if code != null {
+ let this->_compilation = code;
+ }
+ }
+
+ return this->_compilation;
+ }
+
+ throw new Exception("Invalid intermediate representation");
+ }
+
+ /**
+ * Compiles a template into a string
+ *
+ *<code>
+ * echo $compiler->compileString('hello world');
+ *</code>
+ *
+ * @param string viewCode
+ * @return string
+ */
+ public function compileString(string! viewCode) -> string
+ {
+ let this->_currentPath = "eval code";
+ return this->_compileSource(viewCode);
+ }
+
+ /**
+ * Compiles a template into a file forcing the destination path
+ *
+ *<code>
+ * $compiler->compile('views/layouts/main.md', 'views/layouts/main.markdown.php');
+ *</code>
+ *
+ * @param string path
+ * @param string compiledPath
+ * @return string|array
+ */
+ public function compileFile(string! path, string! compiledPath)
+ {
+ var viewCode, compilation;
+
+ if path == compiledPath {
+ throw new Exception("Template path and compilation template path cannot be the same");
+ }
+
+ /**
+ * Check if the template does exist
+ */
+ if !file_exists(path) {
+ throw new Exception("Template file " . path . " does not exist");
+ }
+
+ /**
+ * Always use file_get_contents instead of read the file directly, this respect the open_basedir directive
+ */
+ let viewCode = file_get_contents(path);
+ if viewCode === false {
+ throw new Exception("Template file " . path . " could not be opened");
+ }
+
+ let this->_currentPath = path;
+ let compilation = this->_compileSource(viewCode);
+
+ /**
+ * Always use file_put_contents to write files instead of write the file directly, this respect the open_basedir directive
+ */
+ if file_put_contents(compiledPath, compilation) === false {
+ throw new Exception("Markdown directory can't be written");
+ }
+
+ return compilation;
+ }
+
+ /**
+ * Compiles a template into a file applying the compiler options
+ * This method does not return the compiled path if the template was not compiled
+ *
+ *<code>
+ * $compiler->compile('views/layouts/main.md');
+ * require $compiler->getCompiledTemplatePath();
+ *</code>
+ *
+ * @param string templatePath
+ * @return string|array
+ */
+ public function compile(string! templatePath)
+ {
+ var stat, compileAlways, prefix, compiledPath, compiledSeparator,
+ compiledExtension, compilation, options, realCompiledPath,
+ compiledTemplatePath, templateSepPath;
+
+ /**
+ * Re-initialize some properties already initialized when the object is cloned
+ */
+ let stat = true;
+ let compileAlways = false;
+ let compiledPath = "";
+ let prefix = null;
+ let compiledSeparator = "%%";
+ let compiledExtension = ".php";
+ let compilation = null;
+
+ let options = this->_options;
+ if typeof options == "array" {
+
+ /**
+ * This makes that templates will be compiled always
+ */
+ if isset options["compileAlways"] {
+ let compileAlways = options["compileAlways"];
+ if typeof compileAlways != "boolean" {
+ throw new Exception("compileAlways must be a bool value");
+ }
+ }
+
+ /**
+ * Prefix is prepended to the template name
+ */
+ if isset options["prefix"] {
+ let prefix = options["prefix"];
+ if typeof prefix != "string" {
+ throw new Exception("prefix must be a string");
+ }
+ }
+
+ /**
+ * Compiled path is a directory where the compiled templates will be located
+ */
+ if isset options["compiledPath"] {
+ let compiledPath = options["compiledPath"];
+ if typeof compiledPath != "string" {
+ if typeof compiledPath != "object" {
+ throw new Exception("compiledPath must be a string or a closure");
+ }
+ }
+ }
+
+ /**
+ * There is no compiled separator by default
+ */
+ if isset options["compiledSeparator"] {
+ let compiledSeparator = options["compiledSeparator"];
+ if typeof compiledSeparator != "string" {
+ throw new Exception("compiledSeparator must be a string");
+ }
+ }
+
+ /**
+ * By default the compile extension is .php
+ */
+ if isset options["compiledExtension"] {
+ let compiledExtension = options["compiledExtension"];
+ if typeof compiledExtension != "string" {
+ throw new Exception("compiledExtension must be a string");
+ }
+ }
+
+ /**
+ * Stat option assumes the compilation of the file
+ */
+ if isset options["stat"] {
+ let stat = options["stat"];
+ }
+ }
+
+ /**
+ * Check if there is a compiled path
+ */
+ if typeof compiledPath == "string" {
+
+ /**
+ * Calculate the template realpath's
+ */
+ if !empty compiledPath {
+ /**
+ * Create the virtual path replacing the directory separator by the compiled separator
+ */
+ let templateSepPath = prepare_virtual_path(realpath(templatePath), compiledSeparator);
+ } else {
+ let templateSepPath = templatePath;
+ }
+
+ /**
+ * In extends mode we add an additional 'e' suffix to the file
+ */
+ let compiledTemplatePath = compiledPath . prefix . templateSepPath . compiledExtension;
+
+ } else {
+
+ /**
+ * A closure can dynamically compile the path
+ */
+ if typeof compiledPath == "object" {
+
+ if compiledPath instanceof \Closure {
+
+ let compiledTemplatePath = call_user_func_array(compiledPath, [templatePath, options]);
+
+ /**
+ * The closure must return a valid path
+ */
+ if typeof compiledTemplatePath != "string" {
+ throw new Exception("compiledPath closure didn't return a valid string");
+ }
+ } else {
+ throw new Exception("compiledPath must be a string or a closure");
+ }
+ }
+ }
+
+ /**
+ * Use the real path to avoid collisions
+ */
+ let realCompiledPath = compiledTemplatePath;
+
+ if compileAlways {
+
+ /**
+ * Compile always must be used only in the development stage
+ */
+ let compilation = this->compileFile(templatePath, realCompiledPath);
+ } else {
+ if stat === true {
+ if file_exists(compiledTemplatePath) {
+
+ /**
+ * Compare modification timestamps to check if the file needs to be recompiled
+ */
+ if compare_mtime(templatePath, realCompiledPath) {
+ let compilation = this->compileFile(templatePath, realCompiledPath);
+ }
+ } else {
+
+ /**
+ * The file doesn't exist so we compile the php version for the first time
+ */
+ let compilation = this->compileFile(templatePath, realCompiledPath);
+ }
+ } else {
+
+ /**
+ * Stat is off but the compiled file doesn't exist
+ */
+ if !file_exists(realCompiledPath) {
+ throw new Exception("Compiled template file " . realCompiledPath . " does not exist");
+ }
+
+ }
+ }
+
+ let this->_compiledTemplatePath = realCompiledPath;
+
+ return compilation;
+ }
+
+ /**
+ * Returns the path that is currently being compiled
+ *
+ * @return string
+ */
+ public function getTemplatePath()
+ {
+ return this->_currentPath;
+ }
+
+ /**
+ * Returns the path to the last compiled template
+ *
+ * @return string
+ */
+ public function getCompiledTemplatePath()
+ {
+ return this->_compiledTemplatePath;
+ }
+
+ /**
+ * Parses a Markdown template returning its intermediate representation
+ *
+ *<code>
+ * print_r($compiler->parse('# example'));
+ *</code>
+ *
+ * @param string viewCode
+ * @return array
+ */
+ public function parse(string! viewCode)
+ {
+ return this->compileString(viewCode);
+ }
+
+ /**
+ * Returns the compiled content to the last compiled template
+ *
+ * @return string
+ */
+ public function getCompiled()
+ {
+ return this->_compilation;
+ }
+
+ /**
+ * Returns the toc content to the last compiled template
+ *
+ * @return string
+ */
+ public function getToc()
+ {
+ return this->_toc;
+ }
+
+ /**
+ * Returns the meta content to the last compiled template
+ *
+ * @return string
+ */
+ public function getMeta()
+ {
+ return this->_meta;
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment