Last active
August 29, 2015 14:17
-
-
Save kjdev/2ecc58afe95e9ffbf105 to your computer and use it in GitHub Desktop.
Phalcon 2.0.0 patch: Phalcon\Mvc\View\Engine\Markdown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, "&"); | |
+ break; | |
+ | |
+ /* the single quote is a valid URL character | |
+ * according to the standard; it needs HTML | |
+ * entity escaping too */ | |
+ case '\'': | |
+ HOEDOWN_BUFPUTSL(ob, "'"); | |
+ 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: | |
+ * | |
+ * & --> & | |
+ * < --> < | |
+ * > --> > | |
+ * " --> " | |
+ * ' --> ' ' is not recommended | |
+ * / --> / 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[] = { | |
+ "", | |
+ """, | |
+ "&", | |
+ "'", | |
+ "/", | |
+ "<", | |
+ ">" | |
+}; | |
+ | |
+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, " <a href=\"#fnref%d\" rev=\"footnote\">↩</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 "'" 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[] = { "'", "'", "'", "'", 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. ' or ' or '. | |
+ '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 “ or ” */ | |
+ 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, "’"); | |
+ 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, "’"); | |
+ 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, "©"); | |
+ return 2; | |
+ } | |
+ | |
+ if (t1 == 'r' && t2 == ')') { | |
+ HOEDOWN_BUFPUTSL(ob, "®"); | |
+ return 2; | |
+ } | |
+ | |
+ if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') { | |
+ HOEDOWN_BUFPUTSL(ob, "™"); | |
+ 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, "—"); | |
+ return 2; | |
+ } | |
+ | |
+ if (size >= 2 && text[1] == '-') { | |
+ HOEDOWN_BUFPUTSL(ob, "–"); | |
+ return 1; | |
+ } | |
+ | |
+ hoedown_buffer_putc(ob, text[0]); | |
+ return 0; | |
+} | |
+ | |
+/* Converts " 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, """, 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, "�", 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, "…"); | |
+ return 2; | |
+ } | |
+ | |
+ if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') { | |
+ HOEDOWN_BUFPUTSL(ob, "…"); | |
+ 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, "½"); | |
+ 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, "¼"); | |
+ 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, "¾"); | |
+ 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, """); | |
+ | |
+ 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>", "’", 0 }, | |
+ { '\'', "'t>", "’", 0 }, | |
+ { '\'', "'re>", "’", 0 }, | |
+ { '\'', "'ll>", "’", 0 }, | |
+ { '\'', "'ve>", "’", 0 }, | |
+ { '\'', "'m>", "’", 0 }, | |
+ { '\'', "'d>", "’", 0 }, | |
+ { '-', "--", "—", 1 }, | |
+ { '-', "<->", "–", 0 }, | |
+ { '.', "...", "…", 2 }, | |
+ { '.', ". . .", "…", 4 }, | |
+ { '(', "(c)", "©", 2 }, | |
+ { '(', "(r)", "®", 2 }, | |
+ { '(', "(tm)", "™", 3 }, | |
+ { '3', "<3/4>", "¾", 2 }, | |
+ { '3', "<3/4ths>", "¾", 2 }, | |
+ { '1', "<1/2>", "½", 2 }, | |
+ { '1', "<1/4>", "¼", 2 }, | |
+ { '1', "<1/4th>", "¼", 2 }, | |
+ { '&', "�", 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] = ¶meters; | |
+ | |
+ 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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+ | |
+ 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(¶meters); | |
+} | |
+ | |
+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(¶meters); | |
+ | |
+ 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(¶meters); | |
+} | |
+ | |
+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