Initial commit
This commit is contained in:
commit
e1e5ea96ff
11 changed files with 4163 additions and 0 deletions
57
Makefile
Normal file
57
Makefile
Normal 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
97
README.markdown
Normal 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
75
examples/upskirt.c
Normal 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
728
render/xhtml.c
Executable 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, "<"); return 1;
|
||||
case '>': BUFPUTSL(ob, ">"); return 1;
|
||||
case '&': BUFPUTSL(ob, "&"); return 1;
|
||||
case '"': BUFPUTSL(ob, """); 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>", "’", 0 },
|
||||
{ '\'', "'t>", "’", 0 },
|
||||
{ '\'', "'re>", "’", 0 },
|
||||
{ '\'', "'ll>", "’", 0 },
|
||||
{ '\'', "'ve>", "’", 0 },
|
||||
{ '\'', "'m>", "’", 0 },
|
||||
{ '\'', "'d>", "’", 0 },
|
||||
{ '-', "--", "—", 1 },
|
||||
{ '-', "<->", "–", 0 },
|
||||
{ '.', "...", "…", 2 },
|
||||
{ '.', ". . .", "…", 4 },
|
||||
{ '(', "(c)", "©", 2 },
|
||||
{ '(', "(r)", "®", 2 },
|
||||
{ '(', "(tm)", "™", 3 },
|
||||
{ '3', "<3/4>", "¾", 2 },
|
||||
{ '3', "<3/4ths>", "¾", 2 },
|
||||
{ '1', "<1/2>", "½", 2 },
|
||||
{ '1', "<1/4>", "¼", 2 },
|
||||
{ '1', "<1/4th>", "¼", 2 },
|
||||
{ '&', "�", 0, 3 },
|
||||
};
|
||||
|
||||
#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>“result = ”.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, "“"); 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, "”"); 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
41
render/xhtml.h
Normal 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
300
src/array.c
Executable 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
148
src/array.h
Executable 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
319
src/buffer.c
Executable 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
145
src/buffer.h
Executable 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
2139
src/markdown.c
Executable file
File diff suppressed because it is too large
Load diff
114
src/markdown.h
Executable file
114
src/markdown.h
Executable 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: */
|
Loading…
Reference in a new issue