Initial commit

This commit is contained in:
Vicent Marti 2011-04-15 11:36:46 +03:00
commit e1e5ea96ff
11 changed files with 4163 additions and 0 deletions

57
Makefile Normal file
View file

@ -0,0 +1,57 @@
# Makefile
# Copyright (c) 2009, Natacha Porté
#
# 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.
DEPDIR=depends
CFLAGS=-c -g -O3 -Wall -Werror -Isrc -Irender
LDFLAGS=-g -O3 -Wall -Werror
CC=gcc
all: libupskirt.so upskirt
.PHONY: all clean
# libraries
libupskirt.so: libupskirt.so.1
ln -s $^ $@
libupskirt.so.1: src/markdown.o src/array.o src/buffer.o
$(CC) $(LDFLAGS) -shared -Wl $^ -o $@
# executables
upskirt: examples/upskirt.o libupskirt.so render/xhtml.o
$(CC) $(LDFLAGS) $^ -o $@
# housekeeping
clean:
rm -f src/*.o render/*.o examples/*.o
rm -f libupskirt.so libupskirt.so.1 upskirt
rm -rf $(DEPDIR)
# dependencies
include $(wildcard $(DEPDIR)/*.d)
# generic object compilations
%.o: src/%.c examples/%.c render/%.c
@mkdir -p $(DEPDIR)
@$(CC) -MM $< > $(DEPDIR)/$*.d
$(CC) $(CFLAGS) -o $@ $<

97
README.markdown Normal file
View file

@ -0,0 +1,97 @@
Upskirt
=======
> The Markdown library that sucks less than your Markdown library
* If your Markdown library is not standards compliant, Markdown is.
Upskirt passes out of the box the official Markdown v1.0.0 and v1.0.3
test suites, and has been extensively tested with additional corner cases
to make sure its output is as sane as possible at all times.
* If your Markdown library doesn't support Markdown extensions, Upskirt does.
Upskirt has optional support for several (unofficial) Markdown extensions,
such as non-strict emphasis, fenced code blocks, tables, autolinks,
strikethrough and more.
* If your Markdown library is ready to be used in production... No, nevermind,
your Markdown library is not ready to be used in production.
Upskirt is the only Markdown library ready to be used in a production environment
where users can submit arbritrary and potentially malicious contents to render.
Upskirt has been extensively security audited, and includes protection against
all possible DOS attacks (stack overflows, out of memory situations, malformed
Markdown syntax...) and against client attacks through malicious embedded HTML.
We've worked very hard to make Upskirt never crash or run out of memory
under *any* input. Upskirt renders all the Markdown content in GitHub and so
far hasn't crashed a single time.
* If your Markdown library only does HTML, Upskirt does much more.
Upskirt is not stuck with XHTML output: the Markdown parser of the library
is decoupled from the renderer, so it's trivial to extend the library with
custom renderers. A fully functional XHTML renderer is included.
* If your Markdown library is slow, Upskirt is not.
Upskirt is written in C, with a special emphasis on performance. When wrapped
on a dynamic language such as Python or Ruby, it has shown to be up to 40
times faster than other native alternatives.
* If your Markdown library has retarded dependencies, Upskirt doesn't.
Upskirt is a zero-dependency library composed of 3 `.c` files and their headers.
No dependencies, no bullshit. Only standard C99 that builds everywhere.
This is a fork
--------------
The original `libupskirt` is the amazing work of Natacha Porté and may be found
on its Fossil repository at:
We've created this fork of the library to focus on implementing extensive security fixes and
additional Markdown extensions which we believe are indispensable on a Markdown library
for the web. The original `libupskirt` backports some of these fixes, and we strongly
encourage you to check it out if you require a more UNIX-y Markdown library instead of one
aimed at the web.
None of this would be possible without Natacha's invaluable contributions, both when
writing the original library and when kindly reviewing and auditing the many changes
committed in this fork.
Henceforward she shall be known as the Lady of Markdown.
Help us
-------
Upskirt is all about security. If you find a (potential) security vulnerability in the library, or a way to make it crash
through malicious input, please report it to us, either directly via email or by opening an Issue on GitHub, and help
make the web safer for everybody.
Install
-------
There is nothing to install. Upskirt is composed of 3 `.c` files (`markdown.c`, `buffer.c` and `array.c`),
so just throw them in your project. Zero-dependency means zero-dependency. You might want to include `xhtml.c`
if you want to use the included XHTML renderer, or write your own renderer. Either way, it's all fun and joy.
If you are hardcore, you can use the included `Makefile` to build Upskirt into a static library, or to build
the sample `markdown` executable, which is just a commandline Markdown to XHTML parser.
License
-------
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.

75
examples/upskirt.c Normal file
View file

@ -0,0 +1,75 @@
/*
* 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 "xhtml.h"
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define READ_UNIT 1024
#define OUTPUT_UNIT 64
/* main • main function, interfacing STDIO with the parser */
int
main(int argc, char **argv)
{
struct buf *ib, *ob;
size_t ret;
FILE *in = stdin;
struct mkd_renderer renderer;
/* opening the file if given from the command line */
if (argc > 1) {
in = fopen(argv[1], "r");
if (!in) {
fprintf(stderr,"Unable to open input file \"%s\": %s\n", argv[0], strerror(errno));
return 1;
}
}
/* reading everything */
ib = bufnew(READ_UNIT);
bufgrow(ib, READ_UNIT);
while ((ret = fread(ib->data + ib->size, 1, ib->asize - ib->size, in)) > 0) {
ib->size += ret;
bufgrow(ib, ib->size + READ_UNIT);
}
if (in != stdin)
fclose(in);
/* performing markdown parsing */
ob = bufnew(OUTPUT_UNIT);
ups_xhtml_renderer(&renderer, 0);
ups_markdown(ob, ib, &renderer, 0);
ups_free_renderer(&renderer);
/* writing the result to stdout */
fwrite(ob->data, 1, ob->size, stdout);
/* cleanup */
bufrelease(ib);
bufrelease(ob);
return 0;
}
/* vim: set filetype=c: */

728
render/xhtml.c Executable file
View file

@ -0,0 +1,728 @@
/*
* 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 "xhtml.h"
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
struct xhtml_renderopt {
struct {
int header_count;
int current_level;
} toc_data;
unsigned int flags;
};
static inline int
put_scaped_char(struct buf *ob, char c)
{
switch (c) {
case '<': BUFPUTSL(ob, "&lt;"); return 1;
case '>': BUFPUTSL(ob, "&gt;"); return 1;
case '&': BUFPUTSL(ob, "&amp;"); return 1;
case '"': BUFPUTSL(ob, "&quot;"); return 1;
default: return 0;
}
}
/* lus_attr_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
static void
lus_attr_escape(struct buf *ob, const char *src, size_t size)
{
size_t i = 0, org;
while (i < size) {
/* copying directly unescaped characters */
org = i;
while (i < size && src[i] != '<' && src[i] != '>'
&& src[i] != '&' && src[i] != '"')
i += 1;
if (i > org) bufput(ob, src + org, i - org);
/* escaping */
if (i >= size) break;
put_scaped_char(ob, src[i]);
i++;
}
}
static int
is_html_tag(struct buf *tag, const char *tagname)
{
size_t i;
for (i = 0; i < tag->size; ++i) {
if (tagname[i] == '>')
break;
if (tag->data[i] != tagname[i])
return 0;
}
return (i == tag->size || isspace(tag->data[i]) || tag->data[i] == '>');
}
/********************
* GENERIC RENDERER *
********************/
static int
rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *opaque)
{
struct xhtml_renderopt *options = opaque;
if (!link || !link->size)
return 0;
if ((options->flags & XHTML_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
return 0;
BUFPUTSL(ob, "<a href=\"");
if (type == MKDA_IMPLICIT_EMAIL)
BUFPUTSL(ob, "mailto:");
bufput(ob, link->data, link->size);
BUFPUTSL(ob, "\">");
if (type == MKDA_EXPLICIT_EMAIL && link->size > 7)
lus_attr_escape(ob, link->data + 7, link->size - 7);
else
lus_attr_escape(ob, link->data, link->size);
BUFPUTSL(ob, "</a>");
return 1;
}
static void
rndr_blockcode(struct buf *ob, struct buf *text, void *opaque)
{
if (ob->size) bufputc(ob, '\n');
BUFPUTSL(ob, "<pre><code>");
if (text) lus_attr_escape(ob, text->data, text->size);
BUFPUTSL(ob, "</code></pre>\n");
}
static void
rndr_blockquote(struct buf *ob, struct buf *text, void *opaque)
{
BUFPUTSL(ob, "<blockquote>\n");
if (text) bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</blockquote>");
}
static int
rndr_codespan(struct buf *ob, struct buf *text, void *opaque)
{
BUFPUTSL(ob, "<code>");
if (text) lus_attr_escape(ob, text->data, text->size);
BUFPUTSL(ob, "</code>");
return 1;
}
static int
rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque)
{
if (!text || !text->size)
return 0;
if (c == '~') {
BUFPUTSL(ob, "<span style=\"text-decoration:line-through;\">");
bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</span>");
} else {
BUFPUTSL(ob, "<strong>");
bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</strong>");
}
return 1;
}
static int
rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque)
{
if (!text || !text->size || c == '~') return 0;
BUFPUTSL(ob, "<em>");
if (text) bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</em>");
return 1;
}
static void
rndr_header(struct buf *ob, struct buf *text, int level, void *opaque)
{
struct xhtml_renderopt *options = opaque;
if (ob->size)
bufputc(ob, '\n');
if (options->flags & XHTML_TOC) {
bufprintf(ob, "<a name=\"toc_%d\"></a>", options->toc_data.header_count++);
}
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, struct buf *link, struct buf *title, struct buf *content, void *opaque)
{
struct xhtml_renderopt *options = opaque;
if ((options->flags & XHTML_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
return 0;
BUFPUTSL(ob, "<a href=\"");
if (link && link->size) lus_attr_escape(ob, link->data, link->size);
if (title && title->size) {
BUFPUTSL(ob, "\" title=\"");
lus_attr_escape(ob, title->data, title->size); }
BUFPUTSL(ob, "\">");
if (content && content->size) bufput(ob, content->data, content->size);
BUFPUTSL(ob, "</a>");
return 1;
}
static void
rndr_list(struct buf *ob, 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, struct buf *text, int flags, void *opaque)
{
BUFPUTSL(ob, "<li>");
if (text) {
while (text->size && text->data[text->size - 1] == '\n')
text->size -= 1;
bufput(ob, text->data, text->size); }
BUFPUTSL(ob, "</li>\n");
}
static void
rndr_paragraph(struct buf *ob, struct buf *text, void *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) {
BUFPUTSL(ob, "<p>");
bufput(ob, &text->data[i], text->size - i);
BUFPUTSL(ob, "</p>\n");
}
}
static void
rndr_raw_block(struct buf *ob, struct buf *text, void *opaque)
{
size_t org, sz;
if (!text) return;
sz = text->size;
while (sz > 0 && text->data[sz - 1] == '\n') sz -= 1;
org = 0;
while (org < sz && text->data[org] == '\n') org += 1;
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, struct buf *text, char c, void *opaque)
{
if (!text || !text->size) return 0;
BUFPUTSL(ob, "<strong><em>");
bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</em></strong>");
return 1;
}
/**********************
* XHTML 1.0 RENDERER *
**********************/
static void
rndr_hrule(struct buf *ob, void *opaque)
{
if (ob->size) bufputc(ob, '\n');
BUFPUTSL(ob, "<hr />\n");
}
static int
rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, void *opaque)
{
if (!link || !link->size) return 0;
BUFPUTSL(ob, "<img src=\"");
lus_attr_escape(ob, link->data, link->size);
BUFPUTSL(ob, "\" alt=\"");
if (alt && alt->size)
lus_attr_escape(ob, alt->data, alt->size);
if (title && title->size) {
BUFPUTSL(ob, "\" title=\"");
lus_attr_escape(ob, title->data, title->size); }
BUFPUTSL(ob, "\" />");
return 1;
}
static int
rndr_linebreak(struct buf *ob, void *opaque)
{
BUFPUTSL(ob, "<br />\n");
return 1;
}
static int
rndr_raw_html(struct buf *ob, struct buf *text, void *opaque)
{
struct xhtml_renderopt *options = opaque;
int escape_html = 0;
if (options->flags & XHTML_SKIP_HTML)
escape_html = 1;
else if ((options->flags & XHTML_SKIP_STYLE) != 0 && is_html_tag(text, "<style>"))
escape_html = 1;
else if ((options->flags & XHTML_SKIP_LINKS) != 0 && is_html_tag(text, "<a>"))
escape_html = 1;
else if ((options->flags & XHTML_SKIP_IMAGES) != 0 && is_html_tag(text, "<img>"))
escape_html = 1;
if (escape_html)
lus_attr_escape(ob, text->data, text->size);
else
bufput(ob, text->data, text->size);
return 1;
}
static void
rndr_table(struct buf *ob, struct buf *header, 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, "\n</thead><tbody>\n");
if (body)
bufput(ob, body->data, body->size);
BUFPUTSL(ob, "\n</tbody></table>");
}
static void
rndr_tablerow(struct buf *ob, struct buf *text, void *opaque)
{
if (ob->size) bufputc(ob, '\n');
BUFPUTSL(ob, "<tr>\n");
if (text)
bufput(ob, text->data, text->size);
BUFPUTSL(ob, "\n</tr>");
}
static void
rndr_tablecell(struct buf *ob, struct buf *text, int align, void *opaque)
{
if (ob->size) bufputc(ob, '\n');
switch (align) {
case MKD_TABLE_ALIGN_L:
BUFPUTSL(ob, "<td align=\"left\">");
break;
case MKD_TABLE_ALIGN_R:
BUFPUTSL(ob, "<td align=\"right\">");
break;
case MKD_TABLE_ALIGN_CENTER:
BUFPUTSL(ob, "<td align=\"center\">");
break;
default:
BUFPUTSL(ob, "<td>");
break;
}
if (text)
bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</td>");
}
static struct {
char c0;
const char *pattern;
const char *entity;
int skip;
} smartypants_subs[] = {
{ '\'', "'s>", "&rsquo;", 0 },
{ '\'', "'t>", "&rsquo;", 0 },
{ '\'', "'re>", "&rsquo;", 0 },
{ '\'', "'ll>", "&rsquo;", 0 },
{ '\'', "'ve>", "&rsquo;", 0 },
{ '\'', "'m>", "&rsquo;", 0 },
{ '\'', "'d>", "&rsquo;", 0 },
{ '-', "--", "&mdash;", 1 },
{ '-', "<->", "&ndash;", 0 },
{ '.', "...", "&hellip;", 2 },
{ '.', ". . .", "&hellip;", 4 },
{ '(', "(c)", "&copy;", 2 },
{ '(', "(r)", "&reg;", 2 },
{ '(', "(tm)", "&trade;", 3 },
{ '3', "<3/4>", "&frac34;", 2 },
{ '3', "<3/4ths>", "&frac34;", 2 },
{ '1', "<1/2>", "&frac12;", 2 },
{ '1', "<1/4>", "&frac14;", 2 },
{ '1', "<1/4th>", "&frac14;", 2 },
{ '&', "&#0;", 0, 3 },
};
#define SUBS_COUNT (sizeof(smartypants_subs) / sizeof(smartypants_subs[0]))
static inline int
word_boundary(char c)
{
return isspace(c) || ispunct(c);
}
static int
smartypants_cmpsub(const struct buf *buf, size_t start, const char *prefix)
{
size_t i;
if (prefix[0] == '<') {
if (start == 0 || !word_boundary(buf->data[start - 1]))
return 0;
prefix++;
}
for (i = start; i < buf->size; ++i) {
char c, p;
c = tolower(buf->data[i]);
p = *prefix++;
if (p == 0)
return 1;
if (p == '>')
return word_boundary(c);
if (c != p)
return 0;
}
return (*prefix == '>');
}
static int
smartypants_quotes(struct buf *ob, struct buf *text, size_t i, int is_open)
{
char ent[8];
if (is_open && i + 1 < text->size && !word_boundary(text->data[i + 1]))
return 0;
if (!is_open && i > 0 && !word_boundary(text->data[i - 1]))
return 0;
snprintf(ent, sizeof(ent), "&%c%cquo;",
is_open ? 'r' : 'l',
text->data[i] == '\'' ? 's' : 'd');
bufputs(ob, ent);
return 1;
}
static void
rndr_normal_text(struct buf *ob, struct buf *text, void *opaque)
{
if (text)
lus_attr_escape(ob, text->data, text->size);
}
static void
rndr_smartypants(struct buf *ob, struct buf *text, void *opaque)
{
size_t i;
int open_single = 0, open_double = 0, open_tag = 0;
if (!text)
return;
for (i = 0; i < text->size; ++i) {
size_t sub;
char c = text->data[i];
for (sub = 0; sub < SUBS_COUNT; ++sub) {
if (c == smartypants_subs[sub].c0 &&
smartypants_cmpsub(text, i, smartypants_subs[sub].pattern)) {
if (smartypants_subs[sub].entity)
bufputs(ob, smartypants_subs[sub].entity);
i += smartypants_subs[sub].skip;
break;
}
}
if (sub < SUBS_COUNT)
continue;
switch (c) {
case '<':
open_tag = 1;
break;
case '>':
open_tag = 0;
break;
#if 0
/*
* FIXME: this is bongos.
*
* The markdown spec defines that code blocks can be delimited
* by more than one backtick, e.g.
*
* ``There is a literal backtick (`) here.``
* <p><code>There is a literal backtick (`) here.</code></p>
*
* Obviously, there's no way to differentiate between the start
* of a code block and the start of a quoted string for smartypants
*
* Look at this piece of Python code:
*
* ``result = ''.join(['this', 'is', 'bongos'])``
*
* This MD expression is clearly ambiguous since it can be parsed as:
*
* <p>&ldquo;result = &rdquo;.join ...</p>
*
* Or also as:
*
* <p><code>result = ''.join(['this', 'is', 'bongos'])</code></p>
*
* Fuck everything about this. This is temporarily disabled, because at GitHub
* it's probably smarter to prioritize code blocks than pretty cutesy punctuation.
*
* The equivalent closing tag for the (``), ('') has also been disabled, because
* it makes no sense to have closing tags without opening tags.
*/
case '`':
if (open_tag == 0) {
if (i + 1 < text->size && text->data[i + 1] == '`') {
BUFPUTSL(ob, "&ldquo;"); i++;
continue;
}
}
break;
#endif
case '\"':
if (open_tag == 0) {
if (smartypants_quotes(ob, text, i, open_double)) {
open_double = !open_double;
continue;
}
}
break;
case '\'':
if (open_tag == 0) {
#if 0 /* temporarily disabled, see previous comment */
if (i + 1 < text->size && text->data[i + 1] == '\'') {
BUFPUTSL(ob, "&rdquo;"); i++;
continue;
}
#endif
if (smartypants_quotes(ob, text, i, open_single)) {
open_single = !open_single;
continue;
}
}
break;
}
/*
* Copy raw character
*/
if (!put_scaped_char(ob, c))
bufputc(ob, c);
}
}
static void
toc_header(struct buf *ob, struct buf *text, int level, void *opaque)
{
struct xhtml_renderopt *options = opaque;
if (level > options->toc_data.current_level) {
if (level > 1)
BUFPUTSL(ob, "<li>");
BUFPUTSL(ob, "<ul>\n");
}
if (level < options->toc_data.current_level) {
BUFPUTSL(ob, "</ul>");
if (options->toc_data.current_level > 1)
BUFPUTSL(ob, "</li>\n");
}
options->toc_data.current_level = level;
bufprintf(ob, "<li><a href=\"#toc_%d\">", options->toc_data.header_count++);
if (text)
bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</a></li>\n");
}
static void
toc_finalize(struct buf *ob, void *opaque)
{
struct xhtml_renderopt *options = opaque;
while (options->toc_data.current_level > 1) {
BUFPUTSL(ob, "</ul></li>\n");
options->toc_data.current_level--;
}
if (options->toc_data.current_level)
BUFPUTSL(ob, "</ul>\n");
}
void
ups_toc_renderer(struct mkd_renderer *renderer)
{
static const struct mkd_renderer toc_render = {
NULL,
NULL,
NULL,
toc_header,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
rndr_autolink,
rndr_codespan,
rndr_double_emphasis,
rndr_emphasis,
rndr_image,
rndr_linebreak,
rndr_link,
rndr_raw_html,
rndr_triple_emphasis,
NULL,
NULL,
NULL,
toc_finalize,
NULL
};
struct xhtml_renderopt *options;
options = calloc(1, sizeof(struct xhtml_renderopt));
options->flags = XHTML_TOC;
memcpy(renderer, &toc_render, sizeof(struct mkd_renderer));
renderer->opaque = options;
}
void
ups_xhtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
{
static const struct mkd_renderer renderer_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,
NULL,
rndr_normal_text,
NULL,
NULL,
NULL
};
struct xhtml_renderopt *options;
options = calloc(1, sizeof(struct xhtml_renderopt));
options->flags = render_flags;
memcpy(renderer, &renderer_default, sizeof(struct mkd_renderer));
renderer->opaque = options;
if (render_flags & XHTML_SKIP_IMAGES)
renderer->image = NULL;
if (render_flags & XHTML_SKIP_LINKS) {
renderer->link = NULL;
renderer->autolink = NULL;
}
if (render_flags & XHTML_SMARTYPANTS)
renderer->normal_text = rndr_smartypants;
}
void
ups_free_renderer(struct mkd_renderer *renderer)
{
free(renderer->opaque);
}

41
render/xhtml.h Normal file
View file

@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef UPSKIRT_XHTML_H
#define UPSKIRT_XHTML_H
typedef enum {
XHTML_SKIP_HTML = (1 << 0),
XHTML_SKIP_STYLE = (1 << 1),
XHTML_SKIP_IMAGES = (1 << 2),
XHTML_SKIP_LINKS = (1 << 3),
XHTML_SMARTYPANTS = (1 << 4),
XHTML_EXPAND_TABS = (1 << 5),
XHTML_SAFELINK = (1 << 7),
XHTML_TOC = (1 << 8),
} render_mode;
extern void
ups_xhtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags);
extern void
ups_toc_renderer(struct mkd_renderer *renderer);
extern void
ups_free_renderer(struct mkd_renderer *renderer);
#endif

300
src/array.c Executable file
View file

@ -0,0 +1,300 @@
/* array.c - automatic dynamic array for pointers */
/*
* Copyright (c) 2008, Natacha Porté
*
* 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 "array.h"
#include <string.h>
/***************************
* STATIC HELPER FUNCTIONS *
***************************/
/* arr_realloc • realloc memory of a struct array */
static int
arr_realloc(struct array* arr, int neosz) {
void* neo;
neo = realloc(arr->base, neosz * arr->unit);
if (neo == 0) return 0;
arr->base = neo;
arr->asize = neosz;
if (arr->size > neosz) arr->size = neosz;
return 1; }
/* parr_realloc • realloc memory of a struct parray */
static int
parr_realloc(struct parray* arr, int neosz) {
void* neo;
neo = realloc(arr->item, neosz * sizeof (void*));
if (neo == 0) return 0;
arr->item = neo;
arr->asize = neosz;
if (arr->size > neosz) arr->size = neosz;
return 1; }
/***************************
* GENERIC ARRAY FUNCTIONS *
***************************/
/* arr_adjust • shrink the allocated memory to fit exactly the needs */
int
arr_adjust(struct array *arr) {
return arr_realloc(arr, arr->size); }
/* arr_free • frees the structure contents (buf NOT the struct itself) */
void
arr_free(struct array *arr) {
if (!arr) return;
free(arr->base);
arr->base = 0;
arr->size = arr->asize = 0; }
/* arr_grow • increases the array size to fit the given number of elements */
int
arr_grow(struct array *arr, int need) {
if (arr->asize >= need) return 1;
else return arr_realloc(arr, need); }
/* arr_init • initialization of the contents of the struct */
void
arr_init(struct array *arr, size_t unit) {
arr->base = 0;
arr->size = arr->asize = 0;
arr->unit = unit; }
/* arr_insert • inserting nb elements before the nth one */
int
arr_insert(struct array *arr, int nb, int n) {
char *src, *dst;
size_t len;
if (!arr || nb <= 0 || n < 0
|| !arr_grow(arr, arr->size + nb))
return 0;
if (n < arr->size) {
src = arr->base;
src += n * arr->unit;
dst = src + nb * arr->unit;
len = (arr->size - n) * arr->unit;
memmove(dst, src, len); }
arr->size += nb;
return 1; }
/* arr_item • returns a pointer to the n-th element */
void *
arr_item(struct array *arr, int no) {
char *ptr;
if (!arr || no < 0 || no >= arr->size) return 0;
ptr = arr->base;
ptr += no * arr->unit;
return ptr; }
/* arr_newitem • returns the index of a new element appended to the array */
int
arr_newitem(struct array *arr) {
if (!arr_grow(arr, arr->size + 1)) return -1;
arr->size += 1;
return arr->size - 1; }
/* arr_remove • removes the n-th elements of the array */
void
arr_remove(struct array *arr, int idx) {
if (!arr || idx < 0 || idx >= arr->size) return;
arr->size -= 1;
if (idx < arr->size) {
char *dst = arr->base;
char *src;
dst += idx * arr->unit;
src = dst + arr->unit;
memmove(dst, src, (arr->size - idx) * arr->unit); } }
/* arr_sorted_find • O(log n) search in a sorted array, returning entry */
void *
arr_sorted_find(struct array *arr, void *key, array_cmp_fn cmp) {
int mi, ma, cu, ret;
char *ptr = arr->base;
mi = -1;
ma = arr->size;
while (mi < ma - 1) {
cu = mi + (ma - mi) / 2;
ret = cmp(key, ptr + cu * arr->unit);
if (ret == 0) return ptr + cu * arr->unit;
else if (ret < 0) ma = cu;
else /* if (ret > 0) */ mi = cu; }
return 0; }
/* arr_sorted_find_i • O(log n) search in a sorted array,
* returning index of the smallest element larger than the key */
int
arr_sorted_find_i(struct array *arr, void *key, array_cmp_fn cmp) {
int mi, ma, cu, ret;
char *ptr = arr->base;
mi = -1;
ma = arr->size;
while (mi < ma - 1) {
cu = mi + (ma - mi) / 2;
ret = cmp(key, ptr + cu * arr->unit);
if (ret == 0) {
while (cu < arr->size && ret == 0) {
cu += 1;
ret = cmp(key, ptr + cu * arr->unit); }
return cu; }
else if (ret < 0) ma = cu;
else /* if (ret > 0) */ mi = cu; }
return ma; }
/***************************
* POINTER ARRAY FUNCTIONS *
***************************/
/* parr_adjust • shrinks the allocated memory to fit exactly the needs */
int
parr_adjust(struct parray* arr) {
return parr_realloc (arr, arr->size); }
/* parr_free • frees the structure contents (buf NOT the struct itself) */
void
parr_free(struct parray *arr) {
if (!arr) return;
free (arr->item);
arr->item = 0;
arr->size = 0;
arr->asize = 0; }
/* parr_grow • increases the array size to fit the given number of elements */
int
parr_grow(struct parray *arr, int need) {
if (arr->asize >= need) return 1;
else return parr_realloc (arr, need); }
/* parr_init • initialization of the struct (which is equivalent to zero) */
void
parr_init(struct parray *arr) {
arr->item = 0;
arr->size = 0;
arr->asize = 0; }
/* parr_insert • inserting nb elements before the nth one */
int
parr_insert(struct parray *parr, int nb, int n) {
char *src, *dst;
size_t len, i;
if (!parr || nb <= 0 || n < 0
|| !parr_grow(parr, parr->size + nb))
return 0;
if (n < parr->size) {
src = (void *)parr->item;
src += n * sizeof (void *);
dst = src + nb * sizeof (void *);
len = (parr->size - n) * sizeof (void *);
memmove(dst, src, len);
for (i = 0; i < (size_t)nb; ++i)
parr->item[n + i] = 0; }
parr->size += nb;
return 1; }
/* parr_pop • pops the last item of the array and returns it */
void *
parr_pop(struct parray *arr) {
if (arr->size <= 0) return 0;
arr->size -= 1;
return arr->item[arr->size]; }
/* parr_push • pushes a pointer at the end of the array (= append) */
int
parr_push(struct parray *arr, void *i) {
if (!parr_grow(arr, arr->size + 1)) return 0;
arr->item[arr->size] = i;
arr->size += 1;
return 1; }
/* parr_remove • removes the n-th element of the array and returns it */
void *
parr_remove(struct parray *arr, int idx) {
void* ret;
int i;
if (!arr || idx < 0 || idx >= arr->size) return 0;
ret = arr->item[idx];
for (i = idx+1; i < arr->size; ++i)
arr->item[i - 1] = arr->item[i];
arr->size -= 1;
return ret; }
/* parr_sorted_find • O(log n) search in a sorted array, returning entry */
void *
parr_sorted_find(struct parray *arr, void *key, array_cmp_fn cmp) {
int mi, ma, cu, ret;
mi = -1;
ma = arr->size;
while (mi < ma - 1) {
cu = mi + (ma - mi) / 2;
ret = cmp(key, arr->item[cu]);
if (ret == 0) return arr->item[cu];
else if (ret < 0) ma = cu;
else /* if (ret > 0) */ mi = cu; }
return 0; }
/* parr_sorted_find_i • O(log n) search in a sorted array,
* returning index of the smallest element larger than the key */
int
parr_sorted_find_i(struct parray *arr, void *key, array_cmp_fn cmp) {
int mi, ma, cu, ret;
mi = -1;
ma = arr->size;
while (mi < ma - 1) {
cu = mi + (ma - mi) / 2;
ret = cmp(key, arr->item[cu]);
if (ret == 0) {
while (cu < arr->size && ret == 0) {
cu += 1;
ret = cmp(key, arr->item[cu]); }
return cu; }
else if (ret < 0) ma = cu;
else /* if (ret > 0) */ mi = cu; }
return ma; }
/* parr_top • returns the top the stack (i.e. the last element of the array) */
void *
parr_top(struct parray *arr) {
if (arr == 0 || arr->size <= 0) return 0;
else return arr->item[arr->size - 1]; }
/* vim: set filetype=c: */

148
src/array.h Executable file
View file

@ -0,0 +1,148 @@
/* array.h - automatic dynamic array for pointers */
/*
* Copyright (c) 2008, Natacha Porté
*
* 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 LITHIUM_ARRAY_H
#define LITHIUM_ARRAY_H
#include <stdlib.h>
/********************
* TYPE DEFINITIONS *
********************/
/* struct array • generic linear array */
struct array {
void* base;
int size;
int asize;
size_t unit; };
/* struct parray • array of pointers */
struct parray {
void ** item;
int size;
int asize; };
/* array_cmp_fn • comparison functions for sorted arrays */
typedef int (*array_cmp_fn)(void *key, void *array_entry);
/***************************
* GENERIC ARRAY FUNCTIONS *
***************************/
/* arr_adjust • shrink the allocated memory to fit exactly the needs */
int
arr_adjust(struct array *);
/* arr_free • frees the structure contents (buf NOT the struct itself) */
void
arr_free(struct array *);
/* arr_grow • increases the array size to fit the given number of elements */
int
arr_grow(struct array *, int);
/* arr_init • initialization of the contents of the struct */
void
arr_init(struct array *, size_t);
/* arr_insert • inserting elements nb before the nth one */
int
arr_insert(struct array *, int nb, int n);
/* arr_item • returns a pointer to the n-th element */
void *
arr_item(struct array *, int);
/* arr_newitem • returns the index of a new element appended to the array */
int
arr_newitem(struct array *);
/* arr_remove • removes the n-th elements of the array */
void
arr_remove(struct array *, int);
/* arr_sorted_find • O(log n) search in a sorted array, returning entry */
/* equivalent to bsearch(key, arr->base, arr->size, arr->unit, cmp) */
void *
arr_sorted_find(struct array *, void *key, array_cmp_fn cmp);
/* arr_sorted_find_i • O(log n) search in a sorted array,
* returning index of the smallest element larger than the key */
int
arr_sorted_find_i(struct array *, void *key, array_cmp_fn cmp);
/***************************
* POINTER ARRAY FUNCTIONS *
***************************/
/* parr_adjust • shrinks the allocated memory to fit exactly the needs */
int
parr_adjust(struct parray *);
/* parr_free • frees the structure contents (buf NOT the struct itself) */
void
parr_free(struct parray *);
/* parr_grow • increases the array size to fit the given number of elements */
int
parr_grow(struct parray *, int);
/* parr_init • initialization of the struct (which is equivalent to zero) */
void
parr_init(struct parray *);
/* parr_insert • inserting nb elements before the nth one */
int
parr_insert(struct parray *, int nb, int n);
/* parr_pop • pops the last item of the array and returns it */
void *
parr_pop(struct parray *);
/* parr_push • pushes a pointer at the end of the array (= append) */
int
parr_push(struct parray *, void *);
/* parr_remove • removes the n-th element of the array and returns it */
void *
parr_remove(struct parray *, int);
/* parr_sorted_find • O(log n) search in a sorted array, returning entry */
void *
parr_sorted_find(struct parray *, void *key, array_cmp_fn cmp);
/* parr_sorted_find_i • O(log n) search in a sorted array,
* returning index of the smallest element larger than the key */
int
parr_sorted_find_i(struct parray *, void *key, array_cmp_fn cmp);
/* parr_top • returns the top the stack (i.e. the last element of the array) */
void *
parr_top(struct parray *);
#endif /* ndef LITHIUM_ARRAY_H */
/* vim: set filetype=c: */

319
src/buffer.c Executable file
View file

@ -0,0 +1,319 @@
/* buffer.c - automatic buffer structure */
/*
* Copyright (c) 2008, Natacha Porté
*
* 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.
*/
/*
* COMPILE TIME OPTIONS
*
* BUFFER_STATS if defined, stats are kept about memory usage
*/
#define BUFFER_STDARG
#define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb
#include "buffer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/********************
* GLOBAL VARIABLES *
********************/
#ifdef BUFFER_STATS
long buffer_stat_nb = 0;
size_t buffer_stat_alloc_bytes = 0;
#endif
/***************************
* STATIC HELPER FUNCTIONS *
***************************/
/* lower • retruns the lower-case variant of the input char */
static char
lower(char c) {
return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; }
/********************
* BUFFER FUNCTIONS *
********************/
/* bufcasecmp • case-insensitive buffer comparison */
int
bufcasecmp(const struct buf *a, const struct buf *b) {
size_t i = 0;
size_t cmplen;
if (a == b) return 0;
if (!a) return -1; else if (!b) return 1;
cmplen = (a->size < b->size) ? a->size : b->size;
while (i < cmplen && lower(a->data[i]) == lower(b->data[i])) ++i;
if (i < a->size) {
if (i < b->size) return lower(a->data[i]) - lower(b->data[i]);
else return 1; }
else { if (i < b->size) return -1;
else return 0; } }
/* bufcmp • case-sensitive buffer comparison */
int
bufcmp(const struct buf *a, const struct buf *b) {
size_t i = 0;
size_t cmplen;
if (a == b) return 0;
if (!a) return -1; else if (!b) return 1;
cmplen = (a->size < b->size) ? a->size : b->size;
while (i < cmplen && a->data[i] == b->data[i]) ++i;
if (i < a->size) {
if (i < b->size) return a->data[i] - b->data[i];
else return 1; }
else { if (i < b->size) return -1;
else return 0; } }
/* bufcmps • case-sensitive comparison of a string to a buffer */
int
bufcmps(const struct buf *a, const char *b) {
const size_t len = strlen(b);
size_t cmplen = len;
int r;
if (!a || !a->size) return b ? 0 : -1;
if (len < a->size) cmplen = a->size;
r = strncmp(a->data, b, cmplen);
if (r) return r;
else if (a->size == len) return 0;
else if (a->size < len) return -1;
else return 1; }
int
bufprefix(const struct buf *buf, const char *prefix)
{
size_t i;
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;
}
/* bufdup • buffer duplication */
struct buf *
bufdup(const struct buf *src, size_t dupunit) {
size_t blocks;
struct buf *ret;
if (src == 0) return 0;
ret = malloc(sizeof (struct buf));
if (ret == 0) return 0;
ret->unit = dupunit;
ret->size = src->size;
ret->ref = 1;
if (!src->size) {
ret->asize = 0;
ret->data = 0;
return ret; }
blocks = (src->size + dupunit - 1) / dupunit;
ret->asize = blocks * dupunit;
ret->data = malloc(ret->asize);
if (ret->data == 0) {
free(ret);
return 0; }
memcpy(ret->data, src->data, src->size);
#ifdef BUFFER_STATS
buffer_stat_nb += 1;
buffer_stat_alloc_bytes += ret->asize;
#endif
return ret; }
/* bufgrow • increasing the allocated size to the given value */
int
bufgrow(struct buf *buf, size_t neosz) {
size_t neoasz;
void *neodata;
if (!buf || !buf->unit || neosz > BUFFER_MAX_ALLOC_SIZE) return 0;
if (buf->asize >= neosz) return 1;
neoasz = buf->asize + buf->unit;
while (neoasz < neosz) neoasz += buf->unit;
neodata = realloc(buf->data, neoasz);
if (!neodata) return 0;
#ifdef BUFFER_STATS
buffer_stat_alloc_bytes += (neoasz - buf->asize);
#endif
buf->data = neodata;
buf->asize = neoasz;
return 1; }
/* bufnew • allocation of a new buffer */
struct buf *
bufnew(size_t unit) {
struct buf *ret;
ret = malloc(sizeof (struct buf));
if (ret) {
#ifdef BUFFER_STATS
buffer_stat_nb += 1;
#endif
ret->data = 0;
ret->size = ret->asize = 0;
ret->ref = 1;
ret->unit = unit; }
return ret; }
/* bufnullterm • NUL-termination of the string array (making a C-string) */
void
bufnullterm(struct buf *buf) {
if (!buf || !buf->unit) return;
if (buf->size < buf->asize && buf->data[buf->size] == 0) return;
if (bufgrow(buf, buf->size + 1))
buf->data[buf->size] = 0; }
/* bufprintf • formatted printing to a buffer */
void
bufprintf(struct buf *buf, const char *fmt, ...) {
va_list ap;
if (!buf || !buf->unit) return;
va_start(ap, fmt);
vbufprintf(buf, fmt, ap);
va_end(ap); }
/* bufput • appends raw data to a buffer */
void
bufput(struct buf *buf, const void *data, size_t len) {
if (!buf || !bufgrow(buf, buf->size + len)) return;
memcpy(buf->data + buf->size, data, len);
buf->size += len; }
/* bufputs • appends a NUL-terminated string to a buffer */
void
bufputs(struct buf *buf, const char *str) {
bufput(buf, str, strlen (str)); }
/* bufputc • appends a single char to a buffer */
void
bufputc(struct buf *buf, char c) {
if (!buf || !bufgrow(buf, buf->size + 1)) return;
buf->data[buf->size] = c;
buf->size += 1; }
/* bufrelease • decrease the reference count and free the buffer if needed */
void
bufrelease(struct buf *buf) {
if (!buf || !buf->unit || !buf->asize) return;
buf->ref -= 1;
if (buf->ref == 0) {
#ifdef BUFFER_STATS
buffer_stat_nb -= 1;
buffer_stat_alloc_bytes -= buf->asize;
#endif
free(buf->data);
free(buf); } }
/* bufreset • frees internal data of the buffer */
void
bufreset(struct buf *buf) {
if (!buf || !buf->unit || !buf->asize) return;
#ifdef BUFFER_STATS
buffer_stat_alloc_bytes -= buf->asize;
#endif
free(buf->data);
buf->data = 0;
buf->size = buf->asize = 0; }
/* bufset • safely assigns a buffer to another */
void
bufset(struct buf **dest, struct buf *src) {
if (src) {
if (!src->asize) src = bufdup(src, 1);
else src->ref += 1; }
bufrelease(*dest);
*dest = src; }
/* bufslurp • removes a given number of bytes from the head of the array */
void
bufslurp(struct buf *buf, size_t len) {
if (!buf || !buf->unit || len <= 0) return;
if (len >= buf->size) {
buf->size = 0;
return; }
buf->size -= len;
memmove(buf->data, buf->data + len, buf->size); }
/* buftoi • converts the numbers at the beginning of the buf into an int */
int
buftoi(struct buf *buf, size_t offset_i, size_t *offset_o) {
int r = 0, neg = 0;
size_t i = offset_i;
if (!buf || !buf->size) return 0;
if (buf->data[i] == '+') i += 1;
else if (buf->data[i] == '-') {
neg = 1;
i += 1; }
while (i < buf->size && buf->data[i] >= '0' && buf->data[i] <= '9') {
r = (r * 10) + buf->data[i] - '0';
i += 1; }
if (offset_o) *offset_o = i;
return neg ? -r : r; }
/* vbufprintf • stdarg variant of formatted printing into a buffer */
void
vbufprintf(struct buf *buf, const char *fmt, va_list ap) {
int n;
va_list ap_save;
if (buf == 0
|| (buf->size >= buf->asize && !bufgrow (buf, buf->size + 1)))
return;
va_copy(ap_save, ap);
n = vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap);
if (n < 0 || (size_t)n >= buf->asize - buf->size) {
size_t new_size = (n > 0) ? n : buf->size;
if (!bufgrow (buf, buf->size + new_size + 1))
return;
n = vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap_save);
}
va_end(ap_save);
if (n < 0)
return;
buf->size += n;
}
/* vim: set filetype=c: */

145
src/buffer.h Executable file
View file

@ -0,0 +1,145 @@
/* buffer.h - automatic buffer structure */
/*
* Copyright (c) 2008, Natacha Porté
*
* 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 LITHIUM_BUFFER_H
#define LITHIUM_BUFFER_H
#include <stddef.h>
/********************
* TYPE DEFINITIONS *
********************/
/* struct buf • character array buffer */
struct buf {
char * 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) */
int ref; }; /* reference count */
/**********
* MACROS *
**********/
/* CONST_BUF • global buffer from a string litteral */
#define CONST_BUF(name, string) \
static struct buf name = { string, sizeof string -1, sizeof string }
/* VOLATILE_BUF • macro for creating a volatile buffer on the stack */
#define VOLATILE_BUF(name, strname) \
struct buf name = { strname, strlen(strname) }
/* BUFPUTSL • optimized bufputs of a string litteral */
#define BUFPUTSL(output, litteral) \
bufput(output, litteral, sizeof litteral - 1)
/********************
* BUFFER FUNCTIONS *
********************/
/* bufcasecmp • case-insensitive buffer comparison */
int
bufcasecmp(const struct buf *, const struct buf *);
/* bufcmp • case-sensitive buffer comparison */
int
bufcmp(const struct buf *, const struct buf *);
/* bufcmps • case-sensitive comparison of a string to a buffer */
int
bufcmps(const struct buf *, const char *);
/* bufprefix * compare the beggining of a buffer with a string */
int
bufprefix(const struct buf *buf, const char *prefix);
/* bufdup • buffer duplication */
struct buf *
bufdup(const struct buf *, size_t)
__attribute__ ((malloc));
/* bufgrow • increasing the allocated size to the given value */
int
bufgrow(struct buf *, size_t);
/* bufnew • allocation of a new buffer */
struct buf *
bufnew(size_t)
__attribute__ ((malloc));
/* bufnullterm • NUL-termination of the string array (making a C-string) */
void
bufnullterm(struct buf *);
/* bufprintf • formatted printing to a buffer */
void
bufprintf(struct buf *, const char *, ...)
__attribute__ ((format (printf, 2, 3)));
/* bufput • appends raw data to a buffer */
void
bufput(struct buf *, const void*, size_t);
/* bufputs • appends a NUL-terminated string to a buffer */
void
bufputs(struct buf *, const char*);
/* bufputc • appends a single char to a buffer */
void
bufputc(struct buf *, char);
/* bufrelease • decrease the reference count and free the buffer if needed */
void
bufrelease(struct buf *);
/* bufreset • frees internal data of the buffer */
void
bufreset(struct buf *);
/* bufset • safely assigns a buffer to another */
void
bufset(struct buf **, struct buf *);
/* bufslurp • removes a given number of bytes from the head of the array */
void
bufslurp(struct buf *, size_t);
/* buftoi • converts the numbers at the beginning of the buf into an int */
int
buftoi(struct buf *, size_t, size_t *);
#ifdef BUFFER_STDARG
#include <stdarg.h>
/* vbufprintf • stdarg variant of formatted printing into a buffer */
void
vbufprintf(struct buf *, const char*, va_list);
#endif /* def BUFFER_STDARG */
#endif /* ndef LITHIUM_BUFFER_H */
/* vim: set filetype=c: */

2139
src/markdown.c Executable file

File diff suppressed because it is too large Load diff

114
src/markdown.h Executable file
View file

@ -0,0 +1,114 @@
/* markdown.h - generic markdown parser */
/*
* Copyright (c) 2009, Natacha Porté
*
* 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 UPSKIRT_MARKDOWN_H
#define UPSKIRT_MARKDOWN_H
#include "buffer.h"
/* Require a blank newline after HTML tags */
#define UPSKIRT_NEWLINE_AFTER_TAGS
/********************
* TYPE DEFINITIONS *
********************/
/* mkd_autolink • type of autolink */
enum mkd_autolink {
MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/
MKDA_NORMAL, /* normal http/http/ftp/etc link */
MKDA_EXPLICIT_EMAIL, /* e-mail link with explit mailto: */
MKDA_IMPLICIT_EMAIL /* e-mail link without mailto: */
};
enum mkd_extensions {
MKDEXT_LAX_EMPHASIS = (1 << 0),
MKDEXT_TABLES = (1 << 1),
MKDEXT_FENCED_CODE = (1 << 2),
MKDEXT_AUTOLINK = (1 << 3),
MKDEXT_STRIKETHROUGH = (1 << 4),
};
/* mkd_renderer • functions for rendering parsed data */
struct mkd_renderer {
/* block level callbacks - NULL skips the block */
void (*blockcode)(struct buf *ob, struct buf *text, void *opaque);
void (*blockquote)(struct buf *ob, struct buf *text, void *opaque);
void (*blockhtml)(struct buf *ob, struct buf *text, void *opaque);
void (*header)(struct buf *ob, struct buf *text, int level, void *opaque);
void (*hrule)(struct buf *ob, void *opaque);
void (*list)(struct buf *ob, struct buf *text, int flags, void *opaque);
void (*listitem)(struct buf *ob, struct buf *text, int flags, void *opaque);
void (*paragraph)(struct buf *ob, struct buf *text, void *opaque);
void (*table)(struct buf *ob, struct buf *header, struct buf *body, void *opaque);
void (*table_row)(struct buf *ob, struct buf *text, void *opaque);
void (*table_cell)(struct buf *ob, struct buf *text, int flags, void *opaque);
/* span level callbacks - NULL or return 0 prints the span verbatim */
int (*autolink)(struct buf *ob, struct buf *link, enum mkd_autolink type, void *opaque);
int (*codespan)(struct buf *ob, struct buf *text, void *opaque);
int (*double_emphasis)(struct buf *ob, struct buf *text, char c, void *opaque);
int (*emphasis)(struct buf *ob, struct buf *text, char c,void *opaque);
int (*image)(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, void *opaque);
int (*linebreak)(struct buf *ob, void *opaque);
int (*link)(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque);
int (*raw_html_tag)(struct buf *ob, struct buf *tag, void *opaque);
int (*triple_emphasis)(struct buf *ob, struct buf *text, char c, void *opaque);
/* low level callbacks - NULL copies input directly into the output */
void (*entity)(struct buf *ob, struct buf *entity, void *opaque);
void (*normal_text)(struct buf *ob, struct buf *text, void *opaque);
/* header and footer */
void (*doc_header)(struct buf *ob, void *opaque);
void (*doc_footer)(struct buf *ob, void *opaque);
/* user data */
void *opaque;
};
/*********
* FLAGS *
*********/
/* list/listitem flags */
#define MKD_LIST_ORDERED 1
#define MKD_LI_BLOCK 2 /* <li> containing block data */
#define MKD_TABLE_ALIGN_L (1 << 0)
#define MKD_TABLE_ALIGN_R (1 << 1)
#define MKD_TABLE_ALIGN_CENTER (MKD_TABLE_ALIGN_L | MKD_TABLE_ALIGN_R)
/*******************
* Auxiliar methods
*******************/
int
is_safe_link(const char *link, size_t link_len);
/**********************
* EXPORTED FUNCTIONS *
**********************/
/* markdown • parses the input buffer and renders it into the output buffer */
extern void
ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndr, unsigned int extensions);
#endif
/* vim: set filetype=c: */