459 lines
12 KiB
C
459 lines
12 KiB
C
/* $Id$ */
|
|
/*
|
|
* Copyright (c) 2017--2021 Kristaps Dzonsons <kristaps@bsd.lv>
|
|
*
|
|
* 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 "config.h"
|
|
|
|
#if HAVE_SYS_QUEUE
|
|
# include <sys/queue.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "lowdown.h"
|
|
#include "extern.h"
|
|
|
|
static const char *const names[LOWDOWN__MAX] = {
|
|
"LOWDOWN_ROOT", /* LOWDOWN_ROOT */
|
|
"LOWDOWN_BLOCKCODE", /* LOWDOWN_BLOCKCODE */
|
|
"LOWDOWN_BLOCKQUOTE", /* LOWDOWN_BLOCKQUOTE */
|
|
"LOWDOWN_DEFINITION", /* LOWDOWN_DEFINITION */
|
|
"LOWDOWN_DEFINITION_TITLE", /* LOWDOWN_DEFINITION_TITLE */
|
|
"LOWDOWN_DEFINITION_DATA", /* LOWDOWN_DEFINITION_DATA */
|
|
"LOWDOWN_HEADER", /* LOWDOWN_HEADER */
|
|
"LOWDOWN_HRULE", /* LOWDOWN_HRULE */
|
|
"LOWDOWN_LIST", /* LOWDOWN_LIST */
|
|
"LOWDOWN_LISTITEM", /* LOWDOWN_LISTITEM */
|
|
"LOWDOWN_PARAGRAPH", /* LOWDOWN_PARAGRAPH */
|
|
"LOWDOWN_TABLE_BLOCK", /* LOWDOWN_TABLE_BLOCK */
|
|
"LOWDOWN_TABLE_HEADER", /* LOWDOWN_TABLE_HEADER */
|
|
"LOWDOWN_TABLE_BODY", /* LOWDOWN_TABLE_BODY */
|
|
"LOWDOWN_TABLE_ROW", /* LOWDOWN_TABLE_ROW */
|
|
"LOWDOWN_TABLE_CELL", /* LOWDOWN_TABLE_CELL */
|
|
"LOWDOWN_BLOCKHTML", /* LOWDOWN_BLOCKHTML */
|
|
"LOWDOWN_LINK_AUTO", /* LOWDOWN_LINK_AUTO */
|
|
"LOWDOWN_CODESPAN", /* LOWDOWN_CODESPAN */
|
|
"LOWDOWN_DOUBLE_EMPHASIS", /* LOWDOWN_DOUBLE_EMPHASIS */
|
|
"LOWDOWN_EMPHASIS", /* LOWDOWN_EMPHASIS */
|
|
"LOWDOWN_HIGHLIGHT", /* LOWDOWN_HIGHLIGHT */
|
|
"LOWDOWN_IMAGE", /* LOWDOWN_IMAGE */
|
|
"LOWDOWN_LINEBREAK", /* LOWDOWN_LINEBREAK */
|
|
"LOWDOWN_LINK", /* LOWDOWN_LINK */
|
|
"LOWDOWN_TRIPLE_EMPHASIS", /* LOWDOWN_TRIPLE_EMPHASIS */
|
|
"LOWDOWN_STRIKETHROUGH", /* LOWDOWN_STRIKETHROUGH */
|
|
"LOWDOWN_SUPERSCRIPT", /* LOWDOWN_SUPERSCRIPT */
|
|
"LOWDOWN_FOOTNOTE", /* LOWDOWN_FOOTNOTE */
|
|
"LOWDOWN_MATH_BLOCK", /* LOWDOWN_MATH_BLOCK */
|
|
"LOWDOWN_RAW_HTML", /* LOWDOWN_RAW_HTML */
|
|
"LOWDOWN_ENTITY", /* LOWDOWN_ENTITY */
|
|
"LOWDOWN_NORMAL_TEXT", /* LOWDOWN_NORMAL_TEXT */
|
|
"LOWDOWN_DOC_HEADER", /* LOWDOWN_DOC_HEADER */
|
|
"LOWDOWN_META", /* LOWDOWN_META */
|
|
};
|
|
|
|
static int
|
|
rndr_indent(struct lowdown_buf *ob, size_t indent)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < indent; i++)
|
|
if (!HBUF_PUTSL(ob, " "))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
rndr_short(struct lowdown_buf *ob, const struct lowdown_buf *b)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < 20 && i < b->size; i++)
|
|
if (b->data[i] == '\n') {
|
|
if (!HBUF_PUTSL(ob, "\\n"))
|
|
return 0;
|
|
} else if (b->data[i] == '\t') {
|
|
if (!HBUF_PUTSL(ob, "\\t"))
|
|
return 0;
|
|
} else if (iscntrl((unsigned char)b->data[i])) {
|
|
if (!hbuf_putc(ob, '?'))
|
|
return 0;
|
|
} else {
|
|
if (!hbuf_putc(ob, b->data[i]))
|
|
return 0;
|
|
}
|
|
|
|
if (i < b->size && !HBUF_PUTSL(ob, "..."))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
rndr(struct lowdown_buf *ob,
|
|
const struct lowdown_node *root, size_t indent)
|
|
{
|
|
const struct lowdown_node *n;
|
|
struct lowdown_buf *tmp;
|
|
|
|
if (!rndr_indent(ob, indent))
|
|
return 0;
|
|
if (root->chng == LOWDOWN_CHNG_INSERT &&
|
|
!HBUF_PUTSL(ob, "INSERT: "))
|
|
return 0;
|
|
if (root->chng == LOWDOWN_CHNG_DELETE &&
|
|
!HBUF_PUTSL(ob, "DELETE: "))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "%s (%zu)", names[root->type], root->id))
|
|
return 0;
|
|
if (!hbuf_putc(ob, '\n'))
|
|
return 0;
|
|
|
|
switch (root->type) {
|
|
case LOWDOWN_PARAGRAPH:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "lines: %zu, blank-after: %d\n",
|
|
root->rndr_paragraph.lines,
|
|
root->rndr_paragraph.beoln))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_IMAGE:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "source: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_image.link))
|
|
return 0;
|
|
if (root->rndr_image.dims.size) {
|
|
if (!HBUF_PUTSL(ob, "("))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_image.dims))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, ")"))
|
|
return 0;
|
|
}
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
if (root->rndr_image.title.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "title: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_image.title))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_image.alt.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "alt: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_image.alt))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_image.dims.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "dims: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_image.dims))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_image.attr_width.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "width (extended): "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_image.attr_width))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_image.attr_height.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "height (extended): "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_image.attr_height))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_image.attr_cls.size > 0) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "class: "))
|
|
return 0;
|
|
if (!hbuf_putb(ob, &root->rndr_image.attr_cls))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_image.attr_id.size > 0) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "id: "))
|
|
return 0;
|
|
if (!hbuf_putb(ob, &root->rndr_image.attr_id))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
break;
|
|
case LOWDOWN_HEADER:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "level: %zu\n",
|
|
root->rndr_header.level))
|
|
return 0;
|
|
if (root->rndr_header.attr_cls.size > 0) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "class: "))
|
|
return 0;
|
|
if (!hbuf_putb(ob, &root->rndr_header.attr_cls))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_header.attr_id.size > 0) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "id: "))
|
|
return 0;
|
|
if (!hbuf_putb(ob, &root->rndr_header.attr_id))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
break;
|
|
case LOWDOWN_RAW_HTML:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "data: %zu Bytes: ",
|
|
root->rndr_raw_html.text.size))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_raw_html.text))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_BLOCKHTML:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "data: %zu Bytes: ",
|
|
root->rndr_blockhtml.text.size))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_blockhtml.text))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_BLOCKCODE:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "data: %zu Bytes: ",
|
|
root->rndr_blockcode.text.size))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_blockcode.text))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_DEFINITION:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "scope: %s\n",
|
|
HLIST_FL_BLOCK & root->rndr_definition.flags ?
|
|
"block" : "span"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_TABLE_BLOCK:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "columns: %zu\n",
|
|
root->rndr_table.columns))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_TABLE_CELL:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "current: %zu\n",
|
|
root->rndr_table_cell.col))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_LISTITEM:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "scope: %s\n",
|
|
(root->rndr_listitem.flags & HLIST_FL_BLOCK) ?
|
|
"block" : "span"))
|
|
return 0;
|
|
if (!(root->rndr_listitem.flags &
|
|
(HLIST_FL_CHECKED | HLIST_FL_UNCHECKED)))
|
|
break;
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "check status: %s\n",
|
|
(root->rndr_listitem.flags & HLIST_FL_CHECKED) ?
|
|
"checked" : "unchecked"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_LIST:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "list type: %s\n",
|
|
HLIST_FL_ORDERED & root->rndr_list.flags ?
|
|
"ordered" : "unordered"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_META:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "key: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_meta.key))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_MATH_BLOCK:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "blockmode: %s\n",
|
|
root->rndr_math.blockmode ?
|
|
"block" : "inline"))
|
|
return 0;
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "data: %zu Bytes: ",
|
|
root->rndr_math.text.size))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_math.text))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_ENTITY:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "value: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_entity.text))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
break;
|
|
case LOWDOWN_LINK_AUTO:
|
|
if (root->rndr_autolink.link.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "link: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_autolink.link))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
break;
|
|
case LOWDOWN_LINK:
|
|
if (root->rndr_link.title.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "title: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_link.title))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_link.link.size) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "link: "))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_link.link))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_link.attr_cls.size > 0) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "class: "))
|
|
return 0;
|
|
if (!hbuf_putb(ob, &root->rndr_link.attr_cls))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
if (root->rndr_link.attr_id.size > 0) {
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "id: "))
|
|
return 0;
|
|
if (!hbuf_putb(ob, &root->rndr_link.attr_id))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
}
|
|
break;
|
|
case LOWDOWN_NORMAL_TEXT:
|
|
if (!rndr_indent(ob, indent + 1))
|
|
return 0;
|
|
if (!hbuf_printf(ob, "data: %zu Bytes: ",
|
|
root->rndr_normal_text.text.size))
|
|
return 0;
|
|
if (!rndr_short(ob, &root->rndr_normal_text.text))
|
|
return 0;
|
|
if (!HBUF_PUTSL(ob, "\n"))
|
|
return 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((tmp = hbuf_new(64)) == NULL)
|
|
return 0;
|
|
|
|
TAILQ_FOREACH(n, &root->children, entries)
|
|
if (!rndr(tmp, n, indent + 1)) {
|
|
hbuf_free(tmp);
|
|
return 0;
|
|
}
|
|
|
|
hbuf_putb(ob, tmp);
|
|
hbuf_free(tmp);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
lowdown_tree_rndr(struct lowdown_buf *ob,
|
|
const struct lowdown_node *root)
|
|
{
|
|
|
|
return rndr(ob, root, 0);
|
|
}
|
|
|