Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
lowdown/tree.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
458 lines (440 sloc)
12 KB
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
/* $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); | |
} | |