778 lines
17 KiB
C
778 lines
17 KiB
C
|
/*
|
||
|
* Copyright (c) 2009, Natacha Porté
|
||
|
* Copyright (c) 2011, Vicent Marti
|
||
|
*
|
||
|
* 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 "markdown.h"
|
||
|
#include "html.h"
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <ctype.h>
|
||
|
|
||
|
#include "houdini.h"
|
||
|
|
||
|
#define USE_XHTML( opt ) (opt->flags & HTML_USE_XHTML)
|
||
|
|
||
|
int sdhtml_is_tag( const uint8_t* tag_data, size_t tag_size, const char* tagname )
|
||
|
{
|
||
|
size_t i;
|
||
|
int closed = 0;
|
||
|
|
||
|
if( tag_size < 3 || tag_data[0] != '<' )
|
||
|
return HTML_TAG_NONE;
|
||
|
|
||
|
i = 1;
|
||
|
|
||
|
if( tag_data[i] == '/' )
|
||
|
{
|
||
|
closed = 1;
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
for( ; i < tag_size; ++i, ++tagname )
|
||
|
{
|
||
|
if( *tagname == 0 )
|
||
|
break;
|
||
|
|
||
|
if( tag_data[i] != *tagname )
|
||
|
return HTML_TAG_NONE;
|
||
|
}
|
||
|
|
||
|
if( i == tag_size )
|
||
|
return HTML_TAG_NONE;
|
||
|
|
||
|
if( isspace( tag_data[i] ) || tag_data[i] == '>' )
|
||
|
return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
|
||
|
|
||
|
return HTML_TAG_NONE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static inline void escape_html( struct buf* ob, const uint8_t* source, size_t length )
|
||
|
{
|
||
|
houdini_escape_html0( ob, source, length, 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
static inline void escape_href( struct buf* ob, const uint8_t* source, size_t length )
|
||
|
{
|
||
|
houdini_escape_href( ob, source, length );
|
||
|
}
|
||
|
|
||
|
|
||
|
/********************
|
||
|
* GENERIC RENDERER *
|
||
|
********************/
|
||
|
static int rndr_autolink( struct buf* ob,
|
||
|
const struct buf* link,
|
||
|
enum mkd_autolink type,
|
||
|
void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
if( !link || !link->size )
|
||
|
return 0;
|
||
|
|
||
|
if( (options->flags & HTML_SAFELINK) != 0
|
||
|
&& !sd_autolink_issafe( link->data, link->size )
|
||
|
&& type != MKDA_EMAIL )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<a href=\"" );
|
||
|
|
||
|
if( type == MKDA_EMAIL )
|
||
|
BUFPUTSL( ob, "mailto:" );
|
||
|
|
||
|
escape_href( ob, link->data, link->size );
|
||
|
|
||
|
if( options->link_attributes )
|
||
|
{
|
||
|
bufputc( ob, '\"' );
|
||
|
options->link_attributes( ob, link, opaque );
|
||
|
bufputc( ob, '>' );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
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( bufprefix( link, "mailto:" ) == 0 )
|
||
|
{
|
||
|
escape_html( ob, link->data + 7, link->size - 7 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
escape_html( ob, link->data, link->size );
|
||
|
}
|
||
|
|
||
|
BUFPUTSL( ob, "</a>" );
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_blockcode( struct buf* ob,
|
||
|
const struct buf* text,
|
||
|
const struct buf* lang,
|
||
|
void* opaque )
|
||
|
{
|
||
|
if( ob->size )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
if( lang && lang->size )
|
||
|
{
|
||
|
size_t i, cls;
|
||
|
BUFPUTSL( ob, "<pre><code class=\"" );
|
||
|
|
||
|
for( i = 0, cls = 0; i < lang->size; ++i, ++cls )
|
||
|
{
|
||
|
while( i < lang->size && isspace( lang->data[i] ) )
|
||
|
i++;
|
||
|
|
||
|
if( i < lang->size )
|
||
|
{
|
||
|
size_t org = i;
|
||
|
|
||
|
while( i < lang->size && !isspace( lang->data[i] ) )
|
||
|
i++;
|
||
|
|
||
|
if( lang->data[org] == '.' )
|
||
|
org++;
|
||
|
|
||
|
if( cls )
|
||
|
bufputc( ob, ' ' );
|
||
|
|
||
|
escape_html( ob, lang->data + org, i - org );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BUFPUTSL( ob, "\">" );
|
||
|
}
|
||
|
else
|
||
|
BUFPUTSL( ob, "<pre><code>" );
|
||
|
|
||
|
if( text )
|
||
|
escape_html( ob, text->data, text->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</code></pre>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_blockquote( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
if( ob->size )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
BUFPUTSL( ob, "<blockquote>\n" );
|
||
|
|
||
|
if( text )
|
||
|
bufput( ob, text->data, text->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</blockquote>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_codespan( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
BUFPUTSL( ob, "<code>" );
|
||
|
|
||
|
if( text )
|
||
|
escape_html( ob, text->data, text->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</code>" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_strikethrough( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
if( !text || !text->size )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<del>" );
|
||
|
bufput( ob, text->data, text->size );
|
||
|
BUFPUTSL( ob, "</del>" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_double_emphasis( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
if( !text || !text->size )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<strong>" );
|
||
|
bufput( ob, text->data, text->size );
|
||
|
BUFPUTSL( ob, "</strong>" );
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_emphasis( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
if( !text || !text->size )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<em>" );
|
||
|
|
||
|
if( text )
|
||
|
bufput( ob, text->data, text->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</em>" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_linebreak( struct buf* ob, void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
bufputs( ob, USE_XHTML( options ) ? "<br/>\n" : "<br>\n" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_header( struct buf* ob, const struct buf* text, int level, void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
if( ob->size )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
if( options->flags & HTML_TOC )
|
||
|
bufprintf( ob, "<h%d id=\"toc_%d\">", level, options->toc_data.header_count++ );
|
||
|
else
|
||
|
bufprintf( ob, "<h%d>", level );
|
||
|
|
||
|
if( text )
|
||
|
bufput( ob, text->data, text->size );
|
||
|
|
||
|
bufprintf( ob, "</h%d>\n", level );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_link( struct buf* ob,
|
||
|
const struct buf* link,
|
||
|
const struct buf* title,
|
||
|
const struct buf* content,
|
||
|
void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
if( link != NULL && (options->flags & HTML_SAFELINK) != 0
|
||
|
&& !sd_autolink_issafe( link->data, link->size ) )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<a href=\"" );
|
||
|
|
||
|
if( link && link->size )
|
||
|
escape_href( ob, link->data, link->size );
|
||
|
|
||
|
if( title && title->size )
|
||
|
{
|
||
|
BUFPUTSL( ob, "\" title=\"" );
|
||
|
escape_html( ob, title->data, title->size );
|
||
|
}
|
||
|
|
||
|
if( options->link_attributes )
|
||
|
{
|
||
|
bufputc( ob, '\"' );
|
||
|
options->link_attributes( ob, link, opaque );
|
||
|
bufputc( ob, '>' );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BUFPUTSL( ob, "\">" );
|
||
|
}
|
||
|
|
||
|
if( content && content->size )
|
||
|
bufput( ob, content->data, content->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</a>" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_list( struct buf* ob, const struct buf* text, int flags, void* opaque )
|
||
|
{
|
||
|
if( ob->size )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
bufput( ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5 );
|
||
|
|
||
|
if( text )
|
||
|
bufput( ob, text->data, text->size );
|
||
|
|
||
|
bufput( ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6 );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_listitem( struct buf* ob, const struct buf* text, int flags, void* opaque )
|
||
|
{
|
||
|
BUFPUTSL( ob, "<li>" );
|
||
|
|
||
|
if( text )
|
||
|
{
|
||
|
size_t size = text->size;
|
||
|
|
||
|
while( size && text->data[size - 1] == '\n' )
|
||
|
size--;
|
||
|
|
||
|
bufput( ob, text->data, size );
|
||
|
}
|
||
|
|
||
|
BUFPUTSL( ob, "</li>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_paragraph( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
size_t i = 0;
|
||
|
|
||
|
if( ob->size )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
if( !text || !text->size )
|
||
|
return;
|
||
|
|
||
|
while( i < text->size && isspace( text->data[i] ) )
|
||
|
i++;
|
||
|
|
||
|
if( i == text->size )
|
||
|
return;
|
||
|
|
||
|
BUFPUTSL( ob, "<p>" );
|
||
|
|
||
|
if( options->flags & HTML_HARD_WRAP )
|
||
|
{
|
||
|
size_t org;
|
||
|
|
||
|
while( i < text->size )
|
||
|
{
|
||
|
org = i;
|
||
|
|
||
|
while( i < text->size && text->data[i] != '\n' )
|
||
|
i++;
|
||
|
|
||
|
if( i > org )
|
||
|
bufput( ob, text->data + org, i - org );
|
||
|
|
||
|
/*
|
||
|
* do not insert a line break if this newline
|
||
|
* is the last character on the paragraph
|
||
|
*/
|
||
|
if( i >= text->size - 1 )
|
||
|
break;
|
||
|
|
||
|
rndr_linebreak( ob, opaque );
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bufput( ob, &text->data[i], text->size - i );
|
||
|
}
|
||
|
|
||
|
BUFPUTSL( ob, "</p>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_raw_block( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
size_t org, sz;
|
||
|
|
||
|
if( !text )
|
||
|
return;
|
||
|
|
||
|
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 )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
bufput( ob, text->data + org, sz - org );
|
||
|
bufputc( ob, '\n' );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_triple_emphasis( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
if( !text || !text->size )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<strong><em>" );
|
||
|
bufput( ob, text->data, text->size );
|
||
|
BUFPUTSL( ob, "</em></strong>" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_hrule( struct buf* ob, void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
if( ob->size )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
bufputs( ob, USE_XHTML( options ) ? "<hr/>\n" : "<hr>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_image( struct buf* ob,
|
||
|
const struct buf* link,
|
||
|
const struct buf* title,
|
||
|
const struct buf* alt,
|
||
|
void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
if( !link || !link->size )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<img src=\"" );
|
||
|
escape_href( ob, link->data, link->size );
|
||
|
BUFPUTSL( ob, "\" alt=\"" );
|
||
|
|
||
|
if( alt && alt->size )
|
||
|
escape_html( ob, alt->data, alt->size );
|
||
|
|
||
|
if( title && title->size )
|
||
|
{
|
||
|
BUFPUTSL( ob, "\" title=\"" );
|
||
|
escape_html( ob, title->data, title->size );
|
||
|
}
|
||
|
|
||
|
bufputs( ob, USE_XHTML( options ) ? "\"/>" : "\">" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_raw_html( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
/* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
|
||
|
* It doens't see if there are any valid tags, just escape all of them. */
|
||
|
if( (options->flags & HTML_ESCAPE) != 0 )
|
||
|
{
|
||
|
escape_html( ob, text->data, text->size );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if( (options->flags & HTML_SKIP_HTML) != 0 )
|
||
|
return 1;
|
||
|
|
||
|
if( (options->flags & HTML_SKIP_STYLE) != 0
|
||
|
&& sdhtml_is_tag( text->data, text->size, "style" ) )
|
||
|
return 1;
|
||
|
|
||
|
if( (options->flags & HTML_SKIP_LINKS) != 0
|
||
|
&& sdhtml_is_tag( text->data, text->size, "a" ) )
|
||
|
return 1;
|
||
|
|
||
|
if( (options->flags & HTML_SKIP_IMAGES) != 0
|
||
|
&& sdhtml_is_tag( text->data, text->size, "img" ) )
|
||
|
return 1;
|
||
|
|
||
|
bufput( ob, text->data, text->size );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_table( struct buf* ob,
|
||
|
const struct buf* header,
|
||
|
const struct buf* body,
|
||
|
void* opaque )
|
||
|
{
|
||
|
if( ob->size )
|
||
|
bufputc( ob, '\n' );
|
||
|
|
||
|
BUFPUTSL( ob, "<table><thead>\n" );
|
||
|
|
||
|
if( header )
|
||
|
bufput( ob, header->data, header->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</thead><tbody>\n" );
|
||
|
|
||
|
if( body )
|
||
|
bufput( ob, body->data, body->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</tbody></table>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_tablerow( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
BUFPUTSL( ob, "<tr>\n" );
|
||
|
|
||
|
if( text )
|
||
|
bufput( ob, text->data, text->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</tr>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_tablecell( struct buf* ob, const struct buf* text, int flags, void* opaque )
|
||
|
{
|
||
|
if( flags & MKD_TABLE_HEADER )
|
||
|
{
|
||
|
BUFPUTSL( ob, "<th" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BUFPUTSL( ob, "<td" );
|
||
|
}
|
||
|
|
||
|
switch( flags & MKD_TABLE_ALIGNMASK )
|
||
|
{
|
||
|
case MKD_TABLE_ALIGN_CENTER:
|
||
|
BUFPUTSL( ob, " align=\"center\">" );
|
||
|
break;
|
||
|
|
||
|
case MKD_TABLE_ALIGN_L:
|
||
|
BUFPUTSL( ob, " align=\"left\">" );
|
||
|
break;
|
||
|
|
||
|
case MKD_TABLE_ALIGN_R:
|
||
|
BUFPUTSL( ob, " align=\"right\">" );
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
BUFPUTSL( ob, ">" );
|
||
|
}
|
||
|
|
||
|
if( text )
|
||
|
bufput( ob, text->data, text->size );
|
||
|
|
||
|
if( flags & MKD_TABLE_HEADER )
|
||
|
{
|
||
|
BUFPUTSL( ob, "</th>\n" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BUFPUTSL( ob, "</td>\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static int rndr_superscript( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
if( !text || !text->size )
|
||
|
return 0;
|
||
|
|
||
|
BUFPUTSL( ob, "<sup>" );
|
||
|
bufput( ob, text->data, text->size );
|
||
|
BUFPUTSL( ob, "</sup>" );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rndr_normal_text( struct buf* ob, const struct buf* text, void* opaque )
|
||
|
{
|
||
|
if( text )
|
||
|
escape_html( ob, text->data, text->size );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void toc_header( struct buf* ob, const struct buf* text, int level, void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
/* set the level offset if this is the first header
|
||
|
* we're parsing for the document */
|
||
|
if( options->toc_data.current_level == 0 )
|
||
|
{
|
||
|
options->toc_data.level_offset = level - 1;
|
||
|
}
|
||
|
|
||
|
level -= options->toc_data.level_offset;
|
||
|
|
||
|
if( level > options->toc_data.current_level )
|
||
|
{
|
||
|
while( level > options->toc_data.current_level )
|
||
|
{
|
||
|
BUFPUTSL( ob, "<ul>\n<li>\n" );
|
||
|
options->toc_data.current_level++;
|
||
|
}
|
||
|
}
|
||
|
else if( level < options->toc_data.current_level )
|
||
|
{
|
||
|
BUFPUTSL( ob, "</li>\n" );
|
||
|
|
||
|
while( level < options->toc_data.current_level )
|
||
|
{
|
||
|
BUFPUTSL( ob, "</ul>\n</li>\n" );
|
||
|
options->toc_data.current_level--;
|
||
|
}
|
||
|
|
||
|
BUFPUTSL( ob, "<li>\n" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BUFPUTSL( ob, "</li>\n<li>\n" );
|
||
|
}
|
||
|
|
||
|
bufprintf( ob, "<a href=\"#toc_%d\">", options->toc_data.header_count++ );
|
||
|
|
||
|
if( text )
|
||
|
escape_html( ob, text->data, text->size );
|
||
|
|
||
|
BUFPUTSL( ob, "</a>\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int toc_link( struct buf* ob,
|
||
|
const struct buf* link,
|
||
|
const struct buf* title,
|
||
|
const struct buf* content,
|
||
|
void* opaque )
|
||
|
{
|
||
|
if( content && content->size )
|
||
|
bufput( ob, content->data, content->size );
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void toc_finalize( struct buf* ob, void* opaque )
|
||
|
{
|
||
|
struct html_renderopt* options = opaque;
|
||
|
|
||
|
while( options->toc_data.current_level > 0 )
|
||
|
{
|
||
|
BUFPUTSL( ob, "</li>\n</ul>\n" );
|
||
|
options->toc_data.current_level--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void sdhtml_toc_renderer( struct sd_callbacks* callbacks, struct html_renderopt* options )
|
||
|
{
|
||
|
static const struct sd_callbacks cb_default =
|
||
|
{
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
toc_header,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
|
||
|
NULL,
|
||
|
rndr_codespan,
|
||
|
rndr_double_emphasis,
|
||
|
rndr_emphasis,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
toc_link,
|
||
|
NULL,
|
||
|
rndr_triple_emphasis,
|
||
|
rndr_strikethrough,
|
||
|
rndr_superscript,
|
||
|
|
||
|
NULL,
|
||
|
NULL,
|
||
|
|
||
|
NULL,
|
||
|
toc_finalize,
|
||
|
};
|
||
|
|
||
|
memset( options, 0x0, sizeof(struct html_renderopt) );
|
||
|
options->flags = HTML_TOC;
|
||
|
|
||
|
memcpy( callbacks, &cb_default, sizeof(struct sd_callbacks) );
|
||
|
}
|
||
|
|
||
|
|
||
|
void sdhtml_renderer( struct sd_callbacks* callbacks,
|
||
|
struct html_renderopt* options,
|
||
|
unsigned int render_flags )
|
||
|
{
|
||
|
static const struct sd_callbacks cb_default =
|
||
|
{
|
||
|
rndr_blockcode,
|
||
|
rndr_blockquote,
|
||
|
rndr_raw_block,
|
||
|
rndr_header,
|
||
|
rndr_hrule,
|
||
|
rndr_list,
|
||
|
rndr_listitem,
|
||
|
rndr_paragraph,
|
||
|
rndr_table,
|
||
|
rndr_tablerow,
|
||
|
rndr_tablecell,
|
||
|
|
||
|
rndr_autolink,
|
||
|
rndr_codespan,
|
||
|
rndr_double_emphasis,
|
||
|
rndr_emphasis,
|
||
|
rndr_image,
|
||
|
rndr_linebreak,
|
||
|
rndr_link,
|
||
|
rndr_raw_html,
|
||
|
rndr_triple_emphasis,
|
||
|
rndr_strikethrough,
|
||
|
rndr_superscript,
|
||
|
|
||
|
NULL,
|
||
|
rndr_normal_text,
|
||
|
|
||
|
NULL,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
/* Prepare the options pointer */
|
||
|
memset( options, 0x0, sizeof(struct html_renderopt) );
|
||
|
options->flags = render_flags;
|
||
|
|
||
|
/* Prepare the callbacks */
|
||
|
memcpy( callbacks, &cb_default, sizeof(struct sd_callbacks) );
|
||
|
|
||
|
if( render_flags & HTML_SKIP_IMAGES )
|
||
|
callbacks->image = NULL;
|
||
|
|
||
|
if( render_flags & HTML_SKIP_LINKS )
|
||
|
{
|
||
|
callbacks->link = NULL;
|
||
|
callbacks->autolink = NULL;
|
||
|
}
|
||
|
|
||
|
if( render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE )
|
||
|
callbacks->blockhtml = NULL;
|
||
|
}
|