[Reproducible-commits] [discount] 01/121: Imported Upstream version 2.0.4
Jérémy Bobbio
lunar at moszumanska.debian.org
Tue Sep 23 20:56:11 UTC 2014
This is an automated email from the git hooks/post-receive script.
lunar pushed a commit to branch pu/reproducible_builds
in repository discount.
commit 57c981d8c922b13c3991b1e4e722bc9a19844f62
Author: Alessandro Ghedini <al3xbio at gmail.com>
Date: Fri Jan 14 17:20:48 2011 +0100
Imported Upstream version 2.0.4
---
COPYRIGHT | 47 ++
CREDITS | 33 +
Csio.c | 61 ++
INSTALL | 41 ++
Makefile.in | 116 ++++
Plan9/README | 40 ++
Plan9/markdown.1 | 169 +++++
Plan9/markdown.2 | 332 ++++++++++
Plan9/markdown.6 | 543 ++++++++++++++++
Plan9/mkfile | 37 ++
README | 16 +
VERSION | 1 +
amalloc.c | 111 ++++
amalloc.h | 29 +
basename.c | 43 ++
configure.inc | 1660 +++++++++++++++++++++++++++++++++++++++++++++++++
configure.sh | 141 +++++
css.c | 85 +++
cstring.h | 77 +++
docheader.c | 49 ++
dumptree.c | 152 +++++
emmatch.c | 188 ++++++
flags.c | 83 +++
generate.c | 1643 ++++++++++++++++++++++++++++++++++++++++++++++++
html5.c | 24 +
main.c | 235 +++++++
makepage.1 | 34 +
makepage.c | 27 +
markdown.1 | 164 +++++
markdown.3 | 136 ++++
markdown.7 | 1020 ++++++++++++++++++++++++++++++
markdown.c | 1215 ++++++++++++++++++++++++++++++++++++
markdown.h | 169 +++++
mkd-callbacks.3 | 71 +++
mkd-extensions.7 | 190 ++++++
mkd-functions.3 | 182 ++++++
mkd-line.3 | 41 ++
mkd2html.1 | 52 ++
mkd2html.c | 185 ++++++
mkdio.c | 344 ++++++++++
mkdio.h.in | 100 +++
resource.c | 157 +++++
setup.c | 47 ++
tags.c | 123 ++++
tags.h | 19 +
tests/autolink.t | 27 +
tests/automatic.t | 27 +
tests/backslash.t | 16 +
tests/callbacks.t | 17 +
tests/chrome.text | 13 +
tests/code.t | 35 ++
tests/compat.t | 29 +
tests/crash.t | 31 +
tests/div.t | 45 ++
tests/dl.t | 96 +++
tests/embedlinks.text | 9 +
tests/emphasis.t | 19 +
tests/flow.t | 33 +
tests/footnotes.t | 16 +
tests/functions.sh | 77 +++
tests/header.t | 26 +
tests/html.t | 141 +++++
tests/html5.t | 17 +
tests/links.text | 14 +
tests/linkylinky.t | 130 ++++
tests/linkypix.t | 21 +
tests/list.t | 155 +++++
tests/list3deep.t | 38 ++
tests/misc.t | 12 +
tests/pandoc.t | 44 ++
tests/para.t | 19 +
tests/paranoia.t | 12 +
tests/peculiarities.t | 77 +++
tests/pseudo.t | 20 +
tests/reddit.t | 27 +
tests/reparse.t | 14 +
tests/schiraldi.t | 91 +++
tests/smarty.t | 24 +
tests/snakepit.t | 29 +
tests/strikethrough.t | 15 +
tests/style.t | 34 +
tests/superscript.t | 17 +
tests/syntax.text | 897 ++++++++++++++++++++++++++
tests/tables.t | 167 +++++
tests/tabstop.t | 48 ++
tests/toc.t | 35 ++
tests/xml.t | 18 +
theme.1 | 142 +++++
theme.c | 609 ++++++++++++++++++
toc.c | 101 +++
tools/checkbits.sh | 11 +
tools/cols.c | 38 ++
tools/echo.c | 23 +
version.c.in | 22 +
xml.c | 82 +++
xmlpage.c | 48 ++
96 files changed, 13940 insertions(+)
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..0cf9820
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,47 @@
+->Copyright (C) 2007 David Loren Parsons.
+All rights reserved.<-
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicence, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution, and in the same place and form as other
+ copyright, license and disclaimer information.
+
+ 3. The end-user documentation included with the redistribution, if
+ any, must include the following acknowledgment:
+
+ This product includes software developed by
+ David Loren Parsons <http://www.pell.portland.or.us/~orc>
+
+ in the same place and form as other third-party acknowledgments.
+ Alternately, this acknowledgment may appear in the software
+ itself, in the same form and location as other such third-party
+ acknowledgments.
+
+ 4. Except as contained in this notice, the name of David Loren
+ Parsons shall not be used in advertising or otherwise to promote
+ the sale, use or other dealings in this Software without prior
+ written authorization from David Loren Parsons.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL DAVID LOREN PARSONS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..3ec9d8c
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,33 @@
+Discount is primarily my work, but it has only reached the point
+where it is via contributions, critiques, and bug reports from a
+host of other people, some of which are listed before. If your
+name isn't on this list, please remind me
+ -david parsons (orc at pell.chi.il.us)
+
+
+Josh Wood -- Plan9 support.
+Mike Schiraldi -- Reddit style automatic links, MANY MANY MANY
+ bug reports about boundary conditions and
+ places where I didn't get it right.
+Jjgod Jiang -- Table of contents support.
+Petite Abeille -- Many bug reports about places where I didn't
+ get it right.
+Tim Channon -- inspiration for the `mkd_xhtmlpage()` function
+Christian Herenz-- Many bug reports regarding my implementation of
+ `[]()` and `![]()`
+A.S.Bradbury -- Portability bug reports for 64 bit systems.
+Joyent -- Loan of a solaris box so I could get discount
+ working under solaris.
+Ryan Tomayko -- Portability requests (and the rdiscount ruby
+ binding.)
+yidabu -- feedback on the documentation, bug reports
+ against utf-8 support.
+Pierre Joye -- bug reports, php discount binding.
+Masayoshi Sekimura- perl discount binding.
+Jeremy Hinegardner- bug reports about list handling.
+Andrew White -- bug reports about the format of generated urls.
+Steve Huff -- bug reports about Makefile portability (for Fink)
+Ignacio Burgue?o-- bug reports about `>%class%`
+Henrik Nyh -- bug reports about embedded html handling.
+
+
diff --git a/Csio.c b/Csio.c
new file mode 100644
index 0000000..4358b33
--- /dev/null
+++ b/Csio.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+
+/* putc() into a cstring
+ */
+void
+Csputc(int c, Cstring *iot)
+{
+ EXPAND(*iot) = c;
+}
+
+
+/* printf() into a cstring
+ */
+int
+Csprintf(Cstring *iot, char *fmt, ...)
+{
+ va_list ptr;
+ int siz=100;
+
+ do {
+ RESERVE(*iot, siz);
+ va_start(ptr, fmt);
+ siz = vsnprintf(T(*iot)+S(*iot), ALLOCATED(*iot)-S(*iot), fmt, ptr);
+ va_end(ptr);
+ } while ( siz > (ALLOCATED(*iot)-S(*iot)) );
+
+ S(*iot) += siz;
+ return siz;
+}
+
+
+/* write() into a cstring
+ */
+int
+Cswrite(Cstring *iot, char *bfr, int size)
+{
+ RESERVE(*iot, size);
+ memcpy(T(*iot)+S(*iot), bfr, size);
+ S(*iot) += size;
+ return size;
+}
+
+
+/* reparse() into a cstring
+ */
+void
+Csreparse(Cstring *iot, char *buf, int size, int flags)
+{
+ MMIOT f;
+ ___mkd_initmmiot(&f, 0);
+ ___mkd_reparse(buf, size, 0, &f);
+ ___mkd_emblock(&f);
+ SUFFIX(*iot, T(f.out), S(f.out));
+ ___mkd_freemmiot(&f, 0);
+}
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..c591387
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,41 @@
+
+ HOW TO BUILD AND INSTALL DISCOUNT
+
+1) Unpacking the distribution
+
+The DISCOUNT sources are distributed in tarballs. After extracting from
+the tarball, you should end up with all the source and build files in the
+directory
+ discount-(version)
+
+2) Installing the distribution
+
+DISCOUNT uses configure.sh to set itself up for compilation. To run
+configure, just do ``./configure.sh'' and it will check your system for
+build dependencies and build makefiles for you. If configure.sh finishes
+without complaint, you can then do a ``make'' to compile everything and a
+``make install'' to install the binaries.
+
+Configure.sh has a few options that can be set:
+
+--src=DIR where the source lives (.)
+--prefix=DIR where to install the final product (/usr/local)
+--execdir=DIR where to put executables (prefix/bin)
+--sbindir=DIR where to put static executables (prefix/sbin)
+--confdir=DIR where to put configuration information (/etc)
+--libdir=DIR where to put libraries (prefix/lib)
+--libexecdir=DIR where to put private executables
+--mandir=DIR where to put manpages
+--enable-dl-tag Use the DL tag extension
+--enable-pandoc-header Use pandoc-style header blocks
+--enable-superscript A^B expands to A<sup>B</sup>
+--enable-amalloc Use a debugging memory allocator (to detect leaks)
+--relaxed-emphasis Don't treat _ in the middle of a word as emphasis
+--with-tabstops=N Set tabstops to N characters (default is 4)
+
+3) Installing sample programs and manpages
+
+The standard ``make install'' rule just installs the binaries. If you
+want to install the sample programs, they are installed with
+``make install.samples''; to install manpages, ``make install.man''.
+A shortcut to install everything is ``make install.everything''
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..6c38a5f
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,116 @@
+CC=@CC@ -I. -L.
+CFLAGS=@CFLAGS@
+AR=@AR@
+RANLIB=@RANLIB@
+
+BINDIR=@exedir@
+MANDIR=@mandir@
+LIBDIR=@libdir@
+INCDIR=@prefix@/include
+
+PGMS=markdown
+SAMPLE_PGMS=mkd2html makepage
+ at THEME@SAMPLE_PGMS+= theme
+MKDLIB=libmarkdown
+OBJS=mkdio.o markdown.o dumptree.o generate.o \
+ resource.o docheader.o version.o toc.o css.o \
+ xml.o Csio.o xmlpage.o basename.o emmatch.o \
+ setup.o tags.o html5.o flags.o @AMALLOC@
+
+MAN3PAGES=mkd-callbacks.3 mkd-functions.3 markdown.3 mkd-line.3
+
+all: $(PGMS) $(SAMPLE_PGMS)
+
+install: $(PGMS)
+ @INSTALL_PROGRAM@ $(PGMS) $(DESTDIR)/$(BINDIR)
+ ./librarian.sh install libmarkdown VERSION $(DESTDIR)/$(LIBDIR)
+ @INSTALL_DATA@ mkdio.h $(DESTDIR)/$(INCDIR)
+
+install.everything: install install.samples install.man
+
+install.samples: $(SAMPLE_PGMS) install
+ @INSTALL_PROGRAM@ $(SAMPLE_PGMS) $(DESTDIR)/$(BINDIR)
+ @INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man1
+ @INSTALL_DATA@ theme.1 makepage.1 mkd2html.1 $(DESTDIR)/$(MANDIR)/man1
+
+install.man:
+ @INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man3
+ @INSTALL_DATA@ $(MAN3PAGES) $(DESTDIR)/$(MANDIR)/man3
+ for x in mkd_line mkd_generateline; do \
+ ( echo '.\"' ; echo ".so man3/mkd-line.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3;\
+ done
+ for x in mkd_in mkd_string; do \
+ ( echo '.\"' ; echo ".so man3/markdown.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3;\
+ done
+ for x in mkd_compile mkd_css mkd_generatecss mkd_generatehtml mkd_cleanup mkd_doc_title mkd_doc_author mkd_doc_date; do \
+ ( echo '.\"' ; echo ".so man3/mkd-functions.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3; \
+ done
+ @INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man7
+ @INSTALL_DATA@ markdown.7 mkd-extensions.7 $(DESTDIR)/$(MANDIR)/man7
+ @INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man1
+ @INSTALL_DATA@ markdown.1 $(DESTDIR)/$(MANDIR)/man1
+
+install.everything: install install.man
+
+version.o: version.c VERSION
+ $(CC) -DVERSION=\"`cat VERSION`\" -c version.c
+
+markdown: main.o $(MKDLIB)
+ $(CC) $(CFLAGS) -o markdown main.o -lmarkdown @LIBS@
+
+# example programs
+ at THEME@theme: theme.o $(MKDLIB) mkdio.h
+ at THEME@ $(CC) -o theme theme.o -lmarkdown @LIBS@
+
+
+mkd2html: mkd2html.o $(MKDLIB) mkdio.h
+ $(CC) -o mkd2html mkd2html.o -lmarkdown @LIBS@
+
+makepage: makepage.c $(MKDLIB) mkdio.h
+ $(CC) -o makepage makepage.c -lmarkdown @LIBS@
+
+main.o: main.c mkdio.h config.h
+ $(CC) -I. -c main.c
+
+$(MKDLIB): $(OBJS)
+ ./librarian.sh make $(MKDLIB) VERSION $(OBJS)
+
+verify: echo tools/checkbits.sh
+ @./echo -n "headers ... "; tools/checkbits.sh && echo "GOOD"
+
+test: $(PGMS) echo cols verify
+ @for x in tests/*.t; do \
+ @LD_LIBRARY_PATH@=`pwd` sh $$x || exit 1; \
+ done
+
+cols: tools/cols.c config.h
+ $(CC) -o cols tools/cols.c
+echo: tools/echo.c config.h
+ $(CC) -o echo tools/echo.c
+
+clean:
+ rm -f $(PGMS) $(SAMPLE_PGMS) *.o
+ rm -f $(MKDLIB) `./librarian.sh files $(MKDLIB) VERSION`
+
+distclean spotless: clean
+ rm -f @GENERATED_FILES@ @CONFIGURE_FILES@
+
+Csio.o: Csio.c cstring.h amalloc.h config.h markdown.h
+amalloc.o: amalloc.c
+basename.o: basename.c config.h cstring.h amalloc.h markdown.h
+css.o: css.c config.h cstring.h amalloc.h markdown.h
+docheader.o: docheader.c config.h cstring.h amalloc.h markdown.h
+dumptree.o: dumptree.c markdown.h cstring.h amalloc.h config.h
+emmatch.o: emmatch.c config.h cstring.h amalloc.h markdown.h
+generate.o: generate.c config.h cstring.h amalloc.h markdown.h
+main.o: main.c config.h amalloc.h
+makepage.o: makepage.c
+markdown.o: markdown.c config.h cstring.h amalloc.h markdown.h
+mkd2html.o: mkd2html.c config.h mkdio.h cstring.h amalloc.h
+mkdio.o: mkdio.c config.h cstring.h amalloc.h markdown.h
+resource.o: resource.c config.h cstring.h amalloc.h markdown.h
+theme.o: theme.c config.h mkdio.h cstring.h amalloc.h
+toc.o: toc.c config.h cstring.h amalloc.h markdown.h
+version.o: version.c config.h
+xml.o: xml.c config.h cstring.h amalloc.h markdown.h
+xmlpage.o: xmlpage.c config.h cstring.h amalloc.h markdown.h
diff --git a/Plan9/README b/Plan9/README
new file mode 100644
index 0000000..255b212
--- /dev/null
+++ b/Plan9/README
@@ -0,0 +1,40 @@
+% Discount on Plan 9
+% Josh Wood
+% 2009-06-12
+
+# *Discount* Markdown compiler on Plan 9
+
+## Build
+
+ % CONFIG='--enable-all-features' mk config
+ % mk install
+ % markdown -V
+ markdown: discount X.Y.Z DL_TAG HEADER DEBUG SUPERSCRIPT RELAXED DIV
+
+`--enable-all-features` may be replaced by zero or more of:
+
+ --enable-dl-tag Use the DL tag extension
+ --enable-pandoc-header Use pandoc-style header blocks
+ --enable-superscript A^B becomes A<sup>B</sup>
+ --enable-amalloc Enable memory allocation debugging
+ --relaxed-emphasis underscores aren't special in the middle of words
+ --with-tabstops=N Set tabstops to N characters (default is 4)
+ --enable-div Enable >%id% divisions
+ --enable-alpha-list Enable (a)/(b)/(c) lists
+ --enable-all-features Turn on all stable optional features
+
+## Notes
+
+The supplied mkfile merely drives Discount's own configure script and
+then APE's *psh* environment to build the Discount source, then copies
+the result(s) to locations appropriate for system-wide use on Plan 9.
+There are a few other *mk*(1) targets:
+
+`install.libs`: Discount includes a C library and header.
+Installation is optional. Plan 9 binaries are statically linked.
+
+`install.man`: Add manual pages for markdown(1) and (6).
+
+`install.progs`: Extra programs. *makepage* writes complete XHTML
+documents, rather than fragments. *mkd2html* is similar, but produces
+HTML.
diff --git a/Plan9/markdown.1 b/Plan9/markdown.1
new file mode 100644
index 0000000..b38947f
--- /dev/null
+++ b/Plan9/markdown.1
@@ -0,0 +1,169 @@
+.TH MARKDOWN 1
+.SH NAME
+markdown \- convert Markdown text to HTML
+.SH SYNOPSIS
+.B markdown
+[
+.B -dTV
+]
+[
+.BI -b " url-base
+]
+[
+.BI -F " bitmap
+]
+[
+.BI -f " flags
+]
+[
+.BI -o " ofile
+]
+[
+.BI -s " text
+]
+[
+.BI -t " text
+]
+[
+.I file
+]
+.SH DESCRIPTION
+The
+.I markdown
+utility reads the
+.IR Markdown (6)-formatted
+.I file
+(or standard input) and writes its
+.SM HTML
+fragment representation on standard output.
+.PP
+The options are:
+.TF dfdoptions
+.TP
+.BI -b " url-base
+Links in source begining with
+.B /
+will be prefixed with
+.I url-base
+in the output.
+.TP
+.B -d
+Instead of printing an
+.SM HTML
+fragment, print a parse tree.
+.TP
+.BI -F " bitmap
+Set translation flags.
+.I Bitmap
+is a bit map of the various configuration options described in
+.IR markdown (2).
+.TP
+.BI -f " flags
+Set or clear various translation
+.IR flags ,
+described below.
+.I Flags
+are in a comma-delimited list, with an optional
+.B +
+(set) prefix on each flag.
+.TP
+.BI -o " ofile
+Write the generated
+.SM HTML
+to
+.IR ofile .
+.TP
+.BI -s " text
+Use the
+.IR markdown (2)
+function to format the
+.I text
+on standard input.
+.TP
+.B -T
+Under
+.B -f
+.BR toc ,
+print the table of contents as an unordered list before the usual
+.SM HTML
+output.
+.TP
+.BI -t " text
+Use
+.IR mkd_text
+(in
+.IR markdown (2))
+to format
+.I text
+instead of processing standard input with
+.IR markdown .
+.TP
+.B -V
+Show version number and configuration. If the version includes the string
+.BR DL_TAG ,
+.I markdown
+was configured with definition list support. If the version includes the string
+.BR HEADER ,
+.I markdown
+was configured to support pandoc header blocks.
+.PD
+.SS TRANSLATION FLAGS
+The translation flags understood by
+.B -f
+are:
+.TF \ noheader
+.TP
+.B noimage
+Don't allow image tags.
+.TP
+.B nolinks
+Don't allow links.
+.TP
+.B nohtml
+Don't allow any embedded HTML.
+.TP
+.B cdata
+Generate valid XML output.
+.TP
+.B noheader
+Do not process pandoc headers.
+.TP
+.B notables
+Do not process the syntax extension for tables.
+.TP
+.B tabstops
+Use Markdown-standard 4-space tabstops.
+.TP
+.B strict
+Disable superscript and relaxed emphasis.
+.TP
+.B relax
+Enable superscript and relaxed emphasis (the default).
+.TP
+.B toc
+Enable table of contents support, generated from headings (in
+.IR markdown (6))
+in the source.
+.TP
+.B 1.0
+Revert to Markdown 1.0 compatibility.
+.PD
+.PP
+For example,
+.B -f nolinks,quot
+tells
+.I markdown
+not to allow
+.B <a>
+tags, and to expand double quotes.
+.SH SOURCE
+.B /sys/src/cmd/discount
+.SH SEE ALSO
+.IR markdown (2),
+.IR markdown (6)
+.PP
+http://daringfireball.net/projects/markdown/,
+``Markdown''.
+.SH DIAGNOSTICS
+.I Markdown
+exits 0 on success and >0 if an error occurs.
diff --git a/Plan9/markdown.2 b/Plan9/markdown.2
new file mode 100644
index 0000000..9cb1c9d
--- /dev/null
+++ b/Plan9/markdown.2
@@ -0,0 +1,332 @@
+.TH MARKDOWN 2
+.SH NAME
+mkd_in, mkd_string, markdown, mkd_compile, mkd_css, mkd_generatecss,
+mkd_document, mkd_generatehtml, mkd_xhtmlpage, mkd_toc, mkd_generatetoc,
+mkd_cleanup, mkd_doc_title, mkd_doc_author, mkd_doc_date, mkd_line,
+mkd_generateline \- convert Markdown text to HTML
+.SH SYNOPSIS
+.ta \w'MMIOT* 'u
+.B #include <mkdio.h>
+.PP
+.B
+MMIOT* mkd_in(FILE *input, int flags)
+.PP
+.B
+MMIOT* mkd_string(char *buf, int size, int flags)
+.PP
+.B
+int markdown(MMIOT *doc, FILE *output, int flags)
+.PP
+.B
+int mkd_compile(MMIOT *document, int flags)
+.PP
+.B
+int mkd_css(MMIOT *document, char **doc)
+.PP
+.B
+int mkd_generatecss(MMIOT *document, FILE *output)
+.PP
+.B
+int mkd_document(MMIOT *document, char **doc)
+.PP
+.B
+int mkd_generatehtml(MMIOT *document, FILE *output)
+.PP
+.B
+int mkd_xhtmlpage(MMIOT *document, int flags, FILE *output)
+.PP
+.B
+int mkd_toc(MMIOT *document, char **doc)
+.PP
+.B
+int mkd_generatetoc(MMIOT *document, FILE *output)
+.PP
+.B
+void mkd_cleanup(MMIOT*);
+.PP
+.B
+char* mkd_doc_title(MMIOT*)
+.PP
+.B
+char* mkd_doc_author(MMIOT*)
+.PP
+.B
+char* mkd_doc_date(MMIOT*)
+.PP
+.B
+int mkd_line(char *string, int size, char **doc, int flags)
+.PP
+.B
+int mkd_generateline(char *string, int size, FILE *output, int flags)
+.PD
+.PP
+.SH DESCRIPTION
+These functions convert
+.IR Markdown (6)
+text into
+.SM HTML
+markup.
+.PP
+.I Mkd_in
+reads the text referenced by pointer to
+.B FILE
+.I input
+and returns a pointer to an
+.B MMIOT
+structure of the form expected by
+.I markdown
+and the other converters.
+.I Mkd_string
+accepts one
+.I string
+and returns a pointer to
+.BR MMIOT .
+.PP
+After such preparation,
+.I markdown
+converts
+.I doc
+and writes the result to
+.IR output ,
+while
+.I mkd_compile
+transforms
+.I document
+in-place.
+.PP
+One or more of the following
+.I flags
+(combined with
+.BR OR )
+control
+.IR markdown 's
+processing of
+.IR doc :
+.TF MKD_NOIMAGE
+.TP
+.B MKD_NOIMAGE
+Do not process
+.B ![]
+and remove
+.B <img>
+tags from the output.
+.TP
+.B MKD_NOLINKS
+Do not process
+.B []
+and remove
+.B <a>
+tags from the output.
+.TP
+.B MKD_NOPANTS
+Suppress Smartypants-style replacement of quotes, dashes, or ellipses.
+.TP
+.B MKD_STRICT
+Disable superscript and relaxed emphasis processing if configured; otherwise a no-op.
+.TP
+.B MKD_TAGTEXT
+Process as inside an
+.SM HTML
+tag: no
+.BR <em> ,
+no
+.BR <bold> ,
+no
+.SM HTML
+or
+.B []
+expansion.
+.TP
+.B MKD_NO_EXT
+Don't process pseudo-protocols (in
+.IR markdown (6)).
+.TP
+.B MKD_CDATA
+Generate code for
+.SM XML
+.B ![CDATA[...]]
+element.
+.TP
+.B MKD_NOHEADER
+Don't process Pandoc-style headers.
+.TP
+.B MKD_TABSTOP
+When reading documents, expand tabs to 4 spaces, overriding any compile-time configuration.
+.TP
+.B MKD_TOC
+Label headings for use with the
+.I mkd_generatetoc
+and
+.I mkd_toc
+functions.
+.TP
+.B MKD_1_COMPAT
+MarkdownTest_1.0 compatibility. Trim trailing spaces from first line of code blocks and disable implicit reference links (in
+.IR markdown (6)).
+.TP
+.B MKD_AUTOLINK
+Greedy
+.SM URL
+generation. When set, any
+.SM URL
+is converted to a hyperlink, even those not encased in
+.BR <> .
+.TP
+.B MKD_SAFELINK
+Don't make hyperlinks from
+.B [][]
+links that have unknown
+.SM URL
+protocol types.
+.TP
+.B MKD_NOTABLES
+Do not process the syntax extension for tables (in
+.IR markdown (6)).
+.TP
+.B MKD_EMBED
+All of
+.BR MKD_NOLINKS ,
+.BR MKD_NOIMAGE ,
+and
+.BR MKD_TAGTEXT .
+.PD
+.PP
+This implementation supports
+Pandoc-style
+headers and inline
+.SM CSS
+.B <style>
+blocks, but
+.I markdown
+does not access the data provided by these extensions.
+The following functions do, and allow other manipulations.
+.PP
+Given a pointer to
+.B MMIOT
+prepared by
+.I mkd_in
+or
+.IR mkd_string ,
+.I mkd_compile
+compiles the
+.I document
+into
+.BR <style> ,
+Pandoc, and
+.SM HTML
+sections. It accepts the
+.I flags
+described for
+.IR markdown ,
+above.
+.PP
+Once compiled, the document particulars can be read and written:
+.PP
+.I Mkd_css
+allocates a string and populates it with any
+.B <style>
+sections from the document.
+.I Mkd_generatecss
+writes any
+.B <style>
+sections to
+.IR output .
+.PP
+.I Mkd_document
+points
+.I doc
+to the
+.B MMIOT
+.IR document ,
+returning
+.IR document 's
+size.
+.PP
+.I Mkd_generatehtml
+writes the rest of the
+.I document
+to the
+.IR output .
+.PP
+.IR Mkd_doc_title ,
+.IR mkd_doc_author ,
+and
+.I mkd_doc_date
+read the contents of any Pandoc header.
+.PP
+.I Mkd_xhtmlpage
+writes an
+.SM XHTML
+page representation of the document.
+It accepts the
+.I flags
+described for
+.IR markdown ,
+above.
+.PP
+.I Mkd_toc
+.IR malloc s
+a buffer into which it writes an outline, in the form of a
+.B <ul>
+element populated with
+.BR <li> s
+each containing a link to successive headings in the
+.IR document .
+It returns the size of that string.
+.I Mkd_generatetoc
+is similar,
+but writes the outline to the
+.I output
+referenced by a pointer to
+.BR FILE .
+.PP
+.I Mkd_cleanup
+deletes a processed
+.BR MMIOT .
+.PP
+The last two functions convert a single line of markdown source, for example a page title or a signature.
+.I Mkd_line
+allocates a buffer into which it writes an
+.SM HTML
+fragment representation of the
+.IR string .
+.I Mkd_generateline
+writes the result to
+.IR output .
+.SH SOURCE
+.B /sys/src/cmd/discount
+.SH SEE ALSO
+.IR markdown (1),
+.IR markdown (6)
+.SH DIAGNOSTICS
+The
+.I mkd_in
+and
+.I mkd_string
+functions return a pointer to
+.B MMIOT
+on success, null on failure.
+.IR Markdown ,
+.IR mkd_compile ,
+.IR mkd_style ,
+and
+.I mkd_generatehtml
+return
+.B 0
+on success,
+.B -1
+otherwise.
+.SH BUGS
+Error handling is minimal at best.
+.PP
+The
+.B MMIOT
+created by
+.I mkd_string
+is deleted by the
+.I markdown
+function.
+.PP
+This is an
+.SM APE
+library.
diff --git a/Plan9/markdown.6 b/Plan9/markdown.6
new file mode 100644
index 0000000..fdefcd4
--- /dev/null
+++ b/Plan9/markdown.6
@@ -0,0 +1,543 @@
+.TH MARKDOWN 6
+.SH NAME
+Markdown \- text formatting syntax
+.SH DESCRIPTION
+Markdown
+is a text markup syntax for machine conversion to
+the more complex
+.SM HTML
+or
+.SM XHTML
+markup languages.
+It is intended to be easy to read and to write, with
+emphasis on readability.
+A Markdown-formatted document should be publishable as-is,
+in plain text, without the formatting distracting the reader.
+.PP
+The biggest source of inspiration for Markdown's
+syntax is the format of plain text email. The markup is comprised entirely
+of punctuation characters, chosen so as to look like what they mean.
+Asterisks around a word look like
+.IR *emphasis* .
+Markdown lists look like lists. Even
+blockquotes look like quoted passages of text, assuming the reader has
+used email.
+.PP
+.SS Block Elements
+.TF W
+.PD
+.TP
+Paragraphs and Line Breaks
+A paragraph is one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing but spaces or tabs is considered
+blank.) Normal paragraphs should not be indented with spaces or tabs.
+.IP
+Lines may be freely broken for readability; Markdown
+does not translate source line breaks to
+.B <br />
+tags. To request generation of
+.B <br />
+in the output, end a line with two or more spaces, then a newline.
+.TP
+Headings
+Headings can be marked in two ways, called
+.I setext
+and
+.IR atx .
+.IP
+Setext-style headings are
+``underlined'' using equal signs (for first-level
+headings) and dashes (for second-level headings).
+.IP
+Atx-style headings use 1-6 hash characters at the start of the line,
+corresponding to
+.SM HTML
+.BR <h^(1-6)^> .
+Optional closing hashes may follow
+the heading text.
+.TP
+Blockquotes
+Lines beginning with
+.B >
+are output in blockquotes.
+Blockquotes can be nested
+by multiple levels of
+.BR >> .
+Blockquotes can contain other Markdown elements, including
+headings, lists, and code blocks.
+.TP
+Lists
+Markdown supports ordered (numbered) and unordered (bulleted) lists.
+List markers typically start at the left margin, but may be indented by
+up to three spaces.
+List markers must be followed by one or more spaces
+or a tab, then the list item text.
+A newline terminates each list item.
+.IP
+Unordered lists use asterisks, pluses, and hyphens interchangeably as
+list markers.
+.IP
+Ordered lists use integers followed by periods as list markers.
+The order of the integers is not interpreted,
+but the list should begin with
+.BR 1 .
+.IP
+If list items are separated by blank lines, Markdown will wrap each list
+item in
+.B <p>
+tags in the
+.SM HTML
+output.
+.IP
+List items may consist of multiple paragraphs.
+Each subsequent
+paragraph within a list item must be indented by either 4 spaces
+or one tab.
+To put a blockquote within a list item, the blockquote's
+.B >
+marker needs to be indented.
+To put a code block within a list item, the code block needs
+to be indented
+.I twice
+-- 8 spaces or two tabs.
+.TP
+Code Blocks
+To produce a code block, indent every line of the
+block by at least 4 spaces or 1 tab.
+A code block continues until it reaches a line that is not indented.
+.IP
+Rather than forming normal paragraphs, the lines
+of a code block are interpreted literally.
+Regular Markdown syntax is not processed within code blocks.
+Markdown wraps a code block in both
+.B <pre>
+and
+.B <code>
+tags.
+One level of indentation -- 4
+spaces or 1 tab -- is removed from each line of the code block in
+the output.
+.TP
+Horizontal Rules
+Produce a horizontal rule tag
+.RB ( <hr\ /> )
+by placing three or
+more hyphens, asterisks, or underscores on a line by themselves.
+.SS Span Elements
+.TF W
+.PD
+.TP
+Links
+Markdown supports two styles of links:
+.I inline
+and
+.IR reference .
+In both styles, the link text is delimited by square brackets
+.RB ( [] ).
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket.
+Inside the parentheses,
+put the link URL, along with an optional
+title for the link surrounded in double quotes.
+For example:
+.IP
+.EX
+ An [example](http://example.com/ "Title") inline link.
+.EE
+.IP
+Reference-style links use a second set of square brackets, inside
+which you place a label of your choosing to identify the link:
+.IP
+.EX
+ An [example][id] reference-style link.
+.EE
+.IP
+The label is then assigned a value on its own line, anywhere in the document:
+.IP
+.EX
+ [id]: http://example.com/ "Optional Title"
+.EE
+.IP
+Link label names may consist of letters, numbers, spaces, and
+punctuation.
+Labels are not case sensitive.
+An empty label bracket
+set after a reference-style link implies the link label is equivalent to
+the link text.
+A URL value can then be assigned to the link by referencing
+the link text as the label name.
+.TP
+Emphasis
+Markdown treats asterisks
+.RB ( * )
+and underscores
+.RB ( _ )
+as indicators of emphasis.
+Text surrounded with single asterisks or underscores
+will be wrapped with an
+.SM HTML
+.B <em>
+tag.
+Double asterisks or underscores generate an
+.SM HTML
+.B <strong>
+tag.
+.TP
+Code
+To indicate a span of code, wrap it with backtick quotes
+.RB ( ` ).
+Unlike a code block, a code span indicates code within a
+normal paragraph.
+To include a literal backtick character within a code span, you can use
+multiple backticks as the opening and closing delimiters:
+.IP
+.EX
+ ``There is a literal backtick (`) here.``
+.EE
+.TP
+Images
+Markdown image syntax is intended to resemble that
+for links, allowing for two styles, once again
+.I inline
+and
+.IR reference .
+The syntax is as for each respective style of link, described above, but
+prefixed with an exclamation mark character
+.RB ( ! ).
+Inline image syntax looks like this:
+.IP
+.EX
+ ![Alt text](/path/to/img.jpg "Optional title")
+.EE
+.IP
+That is:
+An exclamation mark;
+followed by a set of square brackets containing the `alt'
+attribute text for the image;
+followed by a set of parentheses containing the URL or path to
+the image, and an optional `title' attribute enclosed in double
+or single quotes.
+.IP
+Reference-style image syntax looks like this:
+.IP
+.EX
+ ![Alt text][id]
+.EE
+.IP
+Where
+.I id
+is a label used as for reference-style URL links, described above.
+.SS Convenience
+.TF W
+.PD
+.TP
+Automatic Links
+There is a shortcut style for creating ``automatic''
+links for URLs and email addresses.
+Surround the URL
+or address with angle brackets.
+.TP
+Backslash Escapes
+Use backslash escapes to generate literal
+characters which would otherwise have special meaning in Markdown's
+formatting syntax.
+.TP
+Inline HTML
+For markup that is not covered by Markdown's
+syntax, simply use the
+.SM HTML
+directly.
+The only restrictions are that block-level
+.SM HTML
+elements --
+.BR <div> ,
+.BR <table> ,
+.BR <pre> ,
+.BR <p> ,
+etc. -- must be separated from surrounding
+content by blank lines, and the start and end tags of the block should
+not be indented with tabs or spaces. Markdown formatting syntax is
+not processed within block-level
+.SM HTML
+tags.
+.IP
+Span-level
+.SM HTML
+tags -- e.g.
+.BR <span> ,
+.BR <cite> ,
+or
+.B <del>
+-- can be
+used anywhere in a Markdown
+paragraph, list item, or heading.
+It is permitted to use
+.SM HTML
+tags instead of Markdown formatting; e.g.
+.SM HTML
+.B <a>
+or
+.B <img>
+tags instead of Markdown's
+link or image syntax.
+Unlike block-level
+.SM HTML
+tags, Markdown
+syntax
+.I is
+processed within the elements of span-level tags.
+.TP
+Automatic Special Character Escapes
+To be displayed literally in a user agent, the characters
+.B <
+and
+.B &
+must appear as escaped entities in
+.SM HTML
+source, e.g.
+.B <
+and
+.BR & .
+Markdown
+allows natural use of these characters, taking care of
+the necessary escaping.
+The ampersand part of a directly-used
+.SM HTML
+entity remains unchanged; otherwise it will be translated
+into
+.BR & .
+Inside code spans and blocks, angle brackets and
+ampersands are always encoded automatically.
+This makes it easy to use Markdown to write about
+.SM HTML
+code.
+.PP
+.SS Smarty Pants
+The
+.IR markdown (1)
+utility transforms a few plain text symbols into their typographically-fancier
+.SM HTML
+entity equivalents.
+These are extensions to the standard Markdown syntax.
+.TF W
+.PD
+.TP
+Punctuation
+Input single- and double-quotes are transformed
+into ``curly'' quote entities in the output (e.g.,
+.B 'text'
+becomes
+.BR ‘text’ ).
+Input double-dashes
+.RB ( -- )
+and triple-dashes become en- and em-dashes, respectively,
+while a series of three dots
+.RB ( ... )
+in the input becomes an ellipsis entity
+.RB ( … )
+in the
+.SM HTML
+output.
+.TP
+Symbols
+Three other transformations replace the common plain-text shorthands
+.BR (c) ,
+.BR (r) ,
+and
+.BR (tm)
+from the input with their respective
+.SM HTML
+entities. (As in
+.B (c)
+becoming
+.BR © ,
+the Copyright symbol entity.)
+.TP
+Fractions
+A small set of plain-text shorthands for fractions is recognized.
+.B 1/4
+becomes
+.BR ¼ ,
+for example. These fraction notations are replaced with their
+.SM HTML
+entity equivalents:
+.BR 1/4 ,
+.BR 1/2 ,
+.BR 3/4 .
+.B 1/4th
+and
+.B 3/4ths
+are replaced with their entity and the indicated ordinal suffix letters.
+.PP
+Like the basic Markdown syntax, none of the ``Smarty Pants'' extensions are processed
+inside code blocks or spans.
+.PP
+.SS Discount Extensions
+.IR Markdown (1)
+recognizes some extensions to the Markdown format,
+many of them adopted or adapted from other Markdown
+interpreters or document formatting systems.
+.TF W
+.PD
+.TP
+Pandoc Headers
+If
+.I markdown
+was configured with
+.BR --enable-pandoc-header ,
+the markdown source can have a 3-line Pandoc header in the format of
+.IP
+.EX
+% Title
+% Author
+% Date
+.EE
+.IP
+whose data is available to the
+.IR mkd_doc_title ,
+.IR mkd_doc_author ,
+and
+.I mkd_doc_date
+(in
+.IR markdown (2))
+functions.
+.TP
+Embedded Stylesheets
+Stylesheets may be defined and modified in a
+.B <style>
+block. A style block is parsed like any other block-level
+.SM HTML;
+.B <style>
+starting on column 1, raw
+.SM HTML
+(or, in this case,
+.SM CSS \)
+following it, and either ending with a
+.B </style>
+at the end of the line or at the beginning of a subsequent line.
+.IP
+Style blocks apply to the entire document regardless of where they are defined.
+.TP
+Image Dimensions
+Image specification has been extended with an argument describing image dimensions:
+.BI = height x width.
+For an image 400 pixels high and 300 wide, the new syntax is:
+.IP
+.EX
+ ![Alt text](/path/to/image.jpg =400x300 "Title")
+.EE
+.TP
+Pseudo-Protocols
+Pseudo-protocols that may replace the common
+.B http:
+or
+.B mailto:
+have been added to the link syntax described above.
+.IP
+.BR abbr :
+Text following is used as the
+.B title
+attribute of an
+.B abbr
+tag wrapping the link text. So
+.B [LT](abbr:Link Text)
+gives
+.B <abbr title="Link Text">LT</abbr>.
+.IP
+.BR id :
+The link text is marked up and written to the output, wrapped with
+.B <a id=text following>
+and
+.BR </a> .
+.IP
+.BR class :
+ The link text is marked up and written to the output, wrapped with
+.B <span class=text following>
+and
+.BR </span> .
+.IP
+.BR raw :
+Text following is written to the output with no further processing.
+The link text is discarded.
+.TP
+Alphabetic Lists
+If
+.I markdown
+was configured with
+.BR --enable-alpha-list ,
+.IP
+.EX
+a. this
+b. is
+c. an alphabetic
+d. list
+.EE
+.IP
+yields an
+.SM HTML
+.B ol
+ordered list.
+.TP
+Definition Lists
+If configured with
+.BR --enable-dl-tag ,
+markup for definition lists is enabled. A definition list item is defined as
+.IP
+.EX
+=term=
+ definition
+.EE
+.TP
+Tables
+Tables are specified with a pipe
+.RB ( | )
+and dash
+.RB ( - )
+marking. The markdown text
+.IP
+.EX
+header0|header1
+-------|-------
+ textA|textB
+ textC|textD
+.EE
+.IP
+will produce an
+.SM HTML
+.B table
+of two columns and three rows.
+A header row is designated by ``underlining'' with dashes.
+Declare a column's alignment by affixing a colon
+.RB ( : )
+to the left or right end of the dashes underlining its header.
+In the output, this
+yields the corresponding value for the
+.B align
+attribute on each
+.B td
+cell in the column.
+A colon at both ends of a column's header dashes indicates center alignment.
+.TP
+Relaxed Emphasis
+If configured with
+.BR --relaxed-emphasis ,
+the rules for emphasis are changed so that a single
+.B _
+will not count as an emphasis character in the middle of a word.
+This is useful for documenting some code where
+.B _
+appears frequently, and would normally require a backslash escape.
+.PD
+.SH SEE ALSO
+.IR markdown (1),
+.IR markdown (2)
+.PP
+http://daringfireball.net/projects/markdown/syntax/,
+``Markdown: Syntax''.
+.PP
+http://daringfireball.net/projects/smartypants/,
+``Smarty Pants''.
+.PP
+http://michelf.com/projects/php-markdown/extra/#table,
+``PHP Markdown Extra: Tables''.
diff --git a/Plan9/mkfile b/Plan9/mkfile
new file mode 100644
index 0000000..f15f987
--- /dev/null
+++ b/Plan9/mkfile
@@ -0,0 +1,37 @@
+BIN=/$objtype/bin
+CC='cc -D_BSD_EXTENSION -D_C99_SNPRINTF_EXTENSION'
+
+markdown:
+ ape/psh -c 'cd .. && make'
+
+none:V: markdown
+
+test: markdown
+ ape/psh -c 'cd ..&& make test'
+
+install: markdown
+ cp ../markdown $BIN/markdown
+
+install.progs: install
+ cp ../makepage $BIN/makepage
+ cp ../mkd2html $BIN/mkd2html
+
+install.libs: install
+ cp ../mkdio.h /sys/include/ape/mkdio.h
+ cp ../libmarkdown.a /$objtype/lib/ape/libmarkdown.a
+
+install.man: install
+ cp markdown.1 /sys/man/1/markdown
+ cp markdown.2 /sys/man/2/markdown
+ cp markdown.6 /sys/man/6/markdown
+
+installall:V: install.libs install.man install.progs
+
+config:
+ ape/psh -c 'cd .. && ./configure.sh $CONFIG'
+
+clean:
+ ape/psh -c 'cd .. && make clean'
+
+nuke:
+ ape/psh -c 'cd .. && make distclean'
diff --git a/README b/README
new file mode 100644
index 0000000..84d38e9
--- /dev/null
+++ b/README
@@ -0,0 +1,16 @@
+DISCOUNT is a implementation of John Gruber's Markdown markup
+language. It implements, as far as I can tell, all of the
+language as described in
+<http://daringfireball.net/projects/markdown/syntax>
+and passes the Markdown test suite at
+<http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip>
+
+DISCOUNT is free software written by David Parsons <orc at pell.chi.il.us>;
+it is released under a BSD-style license that allows you to do
+as you wish with it as long as you don't attempt to claim it as
+your own work.
+
+Most of the programs included in the DISCOUNT distribution have
+manual pages describing how they work.
+
+The file INSTALL describes how to build and install discount
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..2165f8f
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+2.0.4
diff --git a/amalloc.c b/amalloc.c
new file mode 100644
index 0000000..bb20ab6
--- /dev/null
+++ b/amalloc.c
@@ -0,0 +1,111 @@
+/*
+ * debugging malloc()/realloc()/calloc()/free() that attempts
+ * to keep track of just what's been allocated today.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define MAGIC 0x1f2e3d4c
+
+struct alist { int magic, size; struct alist *next, *last; };
+
+static struct alist list = { 0, 0, 0, 0 };
+
+static int mallocs=0;
+static int reallocs=0;
+static int frees=0;
+
+void *
+acalloc(int size, int count)
+{
+ struct alist *ret = calloc(size + sizeof(struct alist), count);
+
+ if ( ret ) {
+ ret->magic = MAGIC;
+ ret->size = size * count;
+ if ( list.next ) {
+ ret->next = list.next;
+ ret->last = &list;
+ ret->next->last = ret;
+ list.next = ret;
+ }
+ else {
+ ret->last = ret->next = &list;
+ list.next = list.last = ret;
+ }
+ ++mallocs;
+ return ret+1;
+ }
+ return 0;
+}
+
+
+void*
+amalloc(int size)
+{
+ return acalloc(size,1);
+}
+
+
+void
+afree(void *ptr)
+{
+ struct alist *p2 = ((struct alist*)ptr)-1;
+
+ if ( p2->magic == MAGIC ) {
+ p2->last->next = p2->next;
+ p2->next->last = p2->last;
+ ++frees;
+ free(p2);
+ }
+ else
+ free(ptr);
+}
+
+
+void *
+arealloc(void *ptr, int size)
+{
+ struct alist *p2 = ((struct alist*)ptr)-1;
+ struct alist save;
+
+ if ( p2->magic == MAGIC ) {
+ save.next = p2->next;
+ save.last = p2->last;
+ p2 = realloc(p2, sizeof(*p2) + size);
+
+ if ( p2 ) {
+ p2->size = size;
+ p2->next->last = p2;
+ p2->last->next = p2;
+ ++reallocs;
+ return p2+1;
+ }
+ else {
+ save.next->last = save.last;
+ save.last->next = save.next;
+ return 0;
+ }
+ }
+ return realloc(ptr, size);
+}
+
+
+void
+adump()
+{
+ struct alist *p;
+
+
+ for ( p = list.next; p && (p != &list); p = p->next ) {
+ fprintf(stderr, "allocated: %d byte%s\n", p->size, (p->size==1) ? "" : "s");
+ fprintf(stderr, " [%.*s]\n", p->size, (char*)(p+1));
+ }
+
+ if ( getenv("AMALLOC_STATISTICS") ) {
+ fprintf(stderr, "%d malloc%s\n", mallocs, (mallocs==1)?"":"s");
+ fprintf(stderr, "%d realloc%s\n", reallocs, (reallocs==1)?"":"s");
+ fprintf(stderr, "%d free%s\n", frees, (frees==1)?"":"s");
+ }
+}
diff --git a/amalloc.h b/amalloc.h
new file mode 100644
index 0000000..43ca985
--- /dev/null
+++ b/amalloc.h
@@ -0,0 +1,29 @@
+/*
+ * debugging malloc()/realloc()/calloc()/free() that attempts
+ * to keep track of just what's been allocated today.
+ */
+#ifndef AMALLOC_D
+#define AMALLOC_D
+
+#include "config.h"
+
+#ifdef USE_AMALLOC
+
+extern void *amalloc(int);
+extern void *acalloc(int,int);
+extern void *arealloc(void*,int);
+extern void afree(void*);
+extern void adump();
+
+#define malloc amalloc
+#define calloc acalloc
+#define realloc arealloc
+#define free afree
+
+#else
+
+#define adump() (void)1
+
+#endif
+
+#endif/*AMALLOC_D*/
diff --git a/basename.c b/basename.c
new file mode 100644
index 0000000..237022a
--- /dev/null
+++ b/basename.c
@@ -0,0 +1,43 @@
+/*
+ * mkdio -- markdown front end input functions
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "mkdio.h"
+#include "cstring.h"
+#include "amalloc.h"
+
+static char *
+e_basename(const char *string, const int size, void *context)
+{
+ char *ret;
+ char *base = (char*)context;
+
+ if ( base && string && (*string == '/') && (ret=malloc(strlen(base)+size+2)) ) {
+ strcpy(ret, base);
+ strncat(ret, string, size);
+ return ret;
+ }
+ return 0;
+}
+
+static void
+e_free(char *string, void *context)
+{
+ if ( string ) free(string);
+}
+
+void
+mkd_basename(MMIOT *document, char *base)
+{
+ mkd_e_url(document, e_basename);
+ mkd_e_data(document, base);
+ mkd_e_free(document, e_free);
+}
diff --git a/configure.inc b/configure.inc
new file mode 100755
index 0000000..5a8dac2
--- /dev/null
+++ b/configure.inc
@@ -0,0 +1,1660 @@
+# @(#) configure.inc 1.42@(#)
+# Copyright (c) 1999-2007 David Parsons. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. My name may not be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
+# PARSONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+
+#
+# this preamble code is executed when this file is sourced and it picks
+# interesting things off the command line.
+#
+ac_default_path="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin"
+
+ac_standard="--src=DIR where the source lives (.)
+--prefix=DIR where to install the final product (/usr/local)
+--execdir=DIR where to put executables (prefix/bin)
+--sbindir=DIR where to put static executables (prefix/sbin)
+--confdir=DIR where to put configuration information (/etc)
+--libdir=DIR where to put libraries (prefix/lib)
+--libexecdir=DIR where to put private executables
+--mandir=DIR where to put manpages"
+
+__fail=exit
+
+if dirname B/A 2>/dev/null >/dev/null; then
+__ac_dirname() {
+ dirname "$1"
+}
+else
+__ac_dirname() {
+ echo "$1" | sed -e 's:/[^/]*$::'
+}
+fi
+
+ac_progname=$0
+ac_configure_command=
+Q=\'
+for x in "$@"; do
+ ac_configure_command="$ac_configure_command $Q$x$Q"
+done
+# ac_configure_command="$*"
+
+__d=`__ac_dirname "$ac_progname"`
+if [ "$__d" = "$ac_progname" ]; then
+ AC_SRCDIR=`pwd`
+else
+ AC_SRCDIR=`cd $__d;pwd`
+fi
+
+__ac_dir() {
+ if test -d "$1"; then
+ (cd "$1";pwd)
+ else
+ echo "$1";
+ fi
+}
+
+#
+# echo w/o newline
+#
+echononl()
+{
+ ${ac_echo:-echo} "${@}$ac_echo_nonl"
+}
+
+#
+# log something to the terminal and to a logfile.
+#
+LOG () {
+ echo "$@"
+ echo "$@" 1>&5
+}
+
+#
+# log something to the terminal without a newline, and to a logfile with
+# a newline
+#
+LOGN () {
+ echononl "$@" 1>&5
+ echo "$@"
+}
+
+#
+# log something to the terminal
+#
+TLOG () {
+ echo "$@" 1>&5
+}
+
+#
+# log something to the terminal, no newline
+#
+TLOGN () {
+ echononl "$@" 1>&5
+}
+
+
+#
+# AC_CONTINUE tells configure not to bomb if something fails, but to
+# continue blithely along
+#
+AC_CONTINUE () {
+ __fail="return"
+}
+
+
+#
+# generate a .o file from sources
+#
+__MAKEDOTO() {
+ AC_PROG_CC
+
+ if $AC_CC -c -o /tmp/doto$$.o "$@" $AC_LIBS 2>/tmp/doto$$.err; then
+ rm -f /tmp/doto$$.o /tmp/doto$$.err
+ TLOG " (found)"
+ return 0
+ fi
+ rm -f /tmp/doto$$.o
+ TLOG " (not found)"
+ echo "test failed: command was $AC_CC -c -o /tmp/doto$$.o" "$@" $AC_LIBS
+ echo "output:"
+ cat /tmp/doto$$.err
+ rm -f /tmp/doto$$.err
+ echo "offending sources:"
+ for x in "$@"; do
+ echo "$x:"
+ cat $x
+ done
+ return 1
+}
+
+
+#
+# Emulate gnu autoconf's AC_CHECK_HEADERS() function
+#
+AC_CHECK_HEADERS () {
+
+ echo "/* AC_CHECK_HEADERS */" > /tmp/ngc$$.c
+ for hdr in $*; do
+ echo "#include <$hdr>" >> /tmp/ngc$$.c
+ done
+ echo "main() { }" >> /tmp/ngc$$.c
+
+ LOGN "looking for header $hdr"
+
+ if __MAKEDOTO /tmp/ngc$$.c; then
+ AC_DEFINE 'HAVE_'`echo $hdr | $AC_UPPERCASE | tr './' '_'` 1
+ rc=0
+ else
+ rc=1
+ fi
+ rm -f /tmp/ngc$$.c
+ return $rc
+}
+
+
+#
+# emulate GNU autoconf's AC_CHECK_FUNCS function
+#
+AC_CHECK_FUNCS () {
+ AC_PROG_CC
+
+ B=`echo "$1" | sed -e 's/(.*)//'`
+
+ case "$B" in
+ "$1") F="$1()" ;;
+ *) F="$1" ;;
+ esac
+
+ shift
+ rm -f /tmp/ngc$$.c
+
+ while [ "$1" ]; do
+ echo "#include <$1>" >> /tmp/ngc$$.c
+ shift
+ done
+
+ cat >> /tmp/ngc$$.c << EOF
+main()
+{
+
+ $F;
+}
+EOF
+
+ LOGN "looking for the $B function"
+
+ if $AC_CC -o /tmp/ngc$$ /tmp/ngc$$.c $LIBS; then
+ AC_DEFINE `echo ${2:-HAVE_$B} | $AC_UPPERCASE` 1
+ TLOG " (found)"
+ rc=0
+ else
+ echo "offending command was:"
+ cat /tmp/ngc$$.c
+ echo "$AC_CC -o /tmp/ngc$$ /tmp/ngc$$.c $LIBS"
+ TLOG " (not found)"
+ rc=1
+ fi
+ rm -f /tmp/ngc$$.c /tmp/ngc$$
+ return $rc
+}
+
+
+#
+# check to see if some structure exists
+#
+# usage: AC_CHECK_STRUCT structure {include ...}
+#
+AC_CHECK_STRUCT () {
+ struct=$1
+ shift
+
+ rm -f /tmp/ngc$$.c
+
+ for include in $*; do
+ echo "#include <$include>" >> /tmp/ngc$$.c
+ done
+
+ cat >> /tmp/ngc$$.c << EOF
+main()
+{
+ struct $struct foo;
+}
+EOF
+
+ LOGN "looking for struct $struct"
+
+ if __MAKEDOTO /tmp/ngc$$.c; then
+ AC_DEFINE HAVE_STRUCT_`echo ${struct} | $AC_UPPERCASE`
+ rc=0
+ else
+ rc=1
+ fi
+ rm -f /tmp/ngc$$.c
+ return $rc
+}
+
+
+#
+# check to see if some type exists
+#
+# usage: AC_CHECK_TYPE type {include ...}
+#
+AC_CHECK_TYPE () {
+ type=$1
+ shift
+
+ rm -f /tmp/ngc$$.c
+
+ for include in $*; do
+ echo "#include <$include>" >> /tmp/ngc$$.c
+ done
+
+ cat >> /tmp/ngc$$.c << EOF
+main()
+{
+ $type foo;
+}
+EOF
+
+ LOGN "looking for $type type"
+
+ if __MAKEDOTO /tmp/ngc$$.c; then
+ AC_DEFINE HAVE_TYPE_`echo ${type} | $AC_UPPERCASE`
+ rc=0
+ else
+ rc=1
+ fi
+ rm -f /tmp/ngc$$.c
+ return $rc
+}
+
+
+#
+# check to see if some structure contains a field
+#
+# usage: AC_CHECK_FIELD structure field {include ...}
+#
+AC_CHECK_FIELD () {
+
+ struct=$1
+ field=$2
+ shift 2
+
+ rm -f /tmp/ngc$$.c
+
+ for include in $*;do
+ echo "#include <$include>" >> /tmp/ngc$$.c
+ done
+
+ cat >> /tmp/ngc$$.c << EOF
+main()
+{
+ struct $struct foo;
+
+ foo.$field;
+}
+EOF
+
+ LOGN "checking that struct $struct has a $field field"
+
+ if __MAKEDOTO /tmp/ngc$$.c; then
+ AC_DEFINE HAVE_`echo ${struct}_$field | $AC_UPPERCASE`
+ rc=0
+ else
+ rc=1
+ fi
+ rm -f /tmp/ngc$$.c
+ return $rc
+}
+
+
+#
+# check that the C compiler works
+#
+AC_PROG_CC () {
+ test "$AC_CC" && return 0
+
+ cat > /tmp/ngc$$.c << \EOF
+#include <stdio.h>
+main()
+{
+ puts("hello, sailor");
+}
+EOF
+
+ TLOGN "checking the C compiler"
+
+ unset AC_CFLAGS AC_LDFLAGS
+
+ if [ "$CC" ] ; then
+ AC_CC="$CC"
+ elif [ "$WITH_PATH" ]; then
+ AC_CC=`acLookFor cc`
+ elif [ "`acLookFor cc`" ]; then
+ # don't specify the full path if the user is looking in their $PATH
+ # for a C compiler.
+ AC_CC=cc
+ fi
+
+ # finally check for POSIX c89
+ test "$AC_CC" || AC_CC=`acLookFor c89`
+
+ if [ ! "$AC_CC" ]; then
+ TLOG " (no C compiler found)"
+ $__fail 1
+ fi
+ echo "checking out the C compiler"
+
+ $AC_CC -o /tmp/ngc$$ /tmp/ngc$$.c
+ status=$?
+
+ TLOGN " ($AC_CC)"
+ if [ $status -eq 0 ]; then
+ if $AC_CC -v 2>&1 | grep 'gcc version' >/dev/null; then
+ TLOG " oh ick, it looks like gcc"
+ IS_BROKEN_CC=T
+ else
+ TLOG " ok"
+ fi
+
+ # check that the CFLAGS and LDFLAGS aren't bogus
+
+ unset AC_CFLAGS AC_LDFLAGS
+
+ if [ "$CFLAGS" ]; then
+ test "$CFLAGS" && echo "validating CFLAGS=${CFLAGS}"
+ if $AC_CC $CFLAGS -o /tmp/ngc$$.o /tmp/ngc$$.c ; then
+ AC_CFLAGS=${CFLAGS:-"-g"}
+ test "$CFLAGS" && echo "CFLAGS=\"${CFLAGS}\" are okay"
+ elif [ "$CFLAGS" ]; then
+ echo "ignoring bogus CFLAGS=\"${CFLAGS}\""
+ fi
+ else
+ AC_CFLAGS=-g
+ fi
+ if [ "$LDFLAGS" ]; then
+ test "$LDFLAGS" && echo "validating LDFLAGS=${LDFLAGS}"
+ if $AC_CC $LDFLAGS -o /tmp/ngc$$ /tmp/ngc$$.o; then
+ AC_LDFLAGS=${LDFLAGS:-"-g"}
+ test "$LDFLAGS" && TLOG "LDFLAGS=\"${LDFLAGS}\" are okay"
+ elif [ "$LDFLAGS" ]; then
+ TLOG "ignoring bogus LDFLAGS=\"${LDFLAGS}\""
+ fi
+ else
+ AC_LDFLAGS=${CFLAGS:-"-g"}
+ fi
+ else
+ AC_FAIL " does not compile code properly"
+ fi
+
+ AC_SUB 'CC' "$AC_CC"
+
+ rm -f /tmp/ngc$$ /tmp/ngc$$.c /tmp/ngc$$.o
+
+ return $status
+}
+
+
+#
+# acLookFor actually looks for a program, without setting anything.
+#
+acLookFor () {
+ path=${AC_PATH:-$ac_default_path}
+ case "X$1" in
+ X-[rx]) __mode=$1
+ shift
+ ;;
+ *) __mode=-x
+ ;;
+ esac
+ oldifs="$IFS"
+ for program in $*; do
+ IFS=":"
+ for x in $path; do
+ if [ $__mode $x/$program -a -f $x/$program ]; then
+ echo $x/$program
+ break 2
+ fi
+ done
+ done
+ IFS="$oldifs"
+ unset __mode
+}
+
+
+#
+# check that a program exists and set its path
+#
+MF_PATH_INCLUDE () {
+ SYM=$1; shift
+
+ case X$1 in
+ X-[rx]) __mode=$1
+ shift
+ ;;
+ *) unset __mode
+ ;;
+ esac
+
+ TLOGN "looking for $1"
+
+ DEST=`acLookFor $__mode $*`
+
+ __sym=`echo "$SYM" | $AC_UPPERCASE`
+ if [ "$DEST" ]; then
+ TLOG " ($DEST)"
+ echo "$1 is $DEST"
+ AC_MAK $SYM
+ AC_DEFINE PATH_$__sym \""$DEST"\"
+ AC_SUB $__sym "$DEST"
+ eval CF_$SYM=$DEST
+ return 0
+ else
+ #AC_SUB $__sym ''
+ echo "$1 is not found"
+ TLOG " (not found)"
+ return 1
+ fi
+}
+
+#
+# AC_INIT starts the ball rolling
+#
+# After AC_INIT, fd's 1 and 2 point to config.log
+# and fd 5 points to what used to be fd 1
+#
+AC_INIT () {
+ __config_files="config.cmd config.sub config.h config.mak config.log"
+ rm -f $__config_files
+ __cwd=`pwd`
+ exec 5>&1 1>$__cwd/config.log 2>&1
+ AC_CONFIGURE_FOR=__AC_`echo $1 | sed -e 's/\..$//' | $AC_UPPERCASE | tr ' ' '_'`_D
+
+ # check to see whether to use echo -n or echo ...\c
+ #
+ echo -n hello > $$
+ echo world >> $$
+ if grep "helloworld" $$ >/dev/null; then
+ ac_echo="echo -n"
+ echo "[echo -n] works"
+ else
+ ac_echo="echo"
+ echo 'hello\c' > $$
+ echo 'world' >> $$
+ if grep "helloworld" $$ >/dev/null; then
+ ac_echo_nonl='\c'
+ echo "[echo ...\\c] works"
+ fi
+ fi
+ rm -f $$
+
+ LOG "Configuring for [$1]"
+
+ cat > $__cwd/config.h << EOF
+/*
+ * configuration for $1${2:+" ($2)"}, generated `date`
+ * by ${LOGNAME:-`whoami`}@`hostname`
+ */
+#ifndef $AC_CONFIGURE_FOR
+#define $AC_CONFIGURE_FOR 1
+
+
+EOF
+
+ unset __share
+ if [ -d $AC_PREFIX/share/man ]; then
+ for t in 1 2 3 4 5 6 7 8 9; do
+ if [ -d $AC_PREFIX/share/man/man$t ]; then
+ __share=/share
+ elif [ -d $AC_PREFIX/share/man/cat$t ]; then
+ __share=/share
+ fi
+ done
+ else
+ __share=
+ fi
+
+ if [ -d $AC_PREFIX/libexec ]; then
+ __libexec=libexec
+ else
+ __libexec=lib
+ fi
+
+
+ AC_PREFIX=${AC_PREFIX:-/usr/local}
+ AC_EXECDIR=${AC_EXECDIR:-$AC_PREFIX/bin}
+ AC_SBINDIR=${AC_SBINDIR:-$AC_PREFIX/sbin}
+ AC_LIBDIR=${AC_LIBDIR:-$AC_PREFIX/lib}
+ AC_MANDIR=${AC_MANDIR:-$AC_PREFIX$__share/man}
+ AC_LIBEXEC=${AC_LIBEXEC:-$AC_PREFIX/$__libexec}
+ AC_CONFDIR=${AC_CONFDIR:-/etc}
+
+ AC_PATH=${WITH_PATH:-$PATH}
+ AC_PROG_CPP
+ AC_PROG_INSTALL
+
+ ac_os=`uname -s`
+ _os=`echo $ac_os | $AC_UPPERCASE | sed -e 's/[^A-Z0-9_].*$//'`
+ AC_DEFINE OS_$_os 1
+ eval OS_${_os}=1
+ unset _os
+}
+
+
+#
+# AC_LIBRARY checks to see if a given library exists and contains the
+# given function.
+# usage: AC_LIBRARY function library [alternate ...]
+#
+AC_LIBRARY() {
+ SRC=$1
+ shift
+
+ __acllibs=
+ __aclhdrs=
+
+ for x in "$@"; do
+ case X"$x" in
+ X-l*) __acllibs="$__acllibs $x" ;;
+ *) __aclhdrs="$__aclhdrs $x" ;;
+ esac
+ done
+
+ # first see if the function can be found in any of the
+ # current libraries
+ AC_QUIET AC_CHECK_FUNCS $SRC $__aclhdrs && return 0
+
+ # then search through the list of libraries
+ __libs="$LIBS"
+ for x in $__acllibs; do
+ LIBS="$__libs $x"
+ if AC_QUIET AC_CHECK_FUNCS $SRC $__aclhdrs; then
+ AC_LIBS="$AC_LIBS $x"
+ return 0
+ fi
+ done
+ return 1
+}
+
+
+#
+# AC_PROG_LEX checks to see if LEX exists, and if it's lex or flex.
+#
+AC_PROG_LEX() {
+ TLOGN "looking for lex "
+
+ DEST=`acLookFor lex`
+ if [ "$DEST" ]; then
+ AC_MAK LEX
+ AC_DEFINE PATH_LEX \"$DEST\"
+ AC_SUB 'LEX' "$DEST"
+ echo "lex is $DEST"
+ else
+ DEST=`acLookFor flex`
+ if [ "$DEST" ]; then
+ AC_MAK FLEX
+ AC_DEFINE 'LEX' \"$DEST\"
+ AC_SUB 'LEX', "$DEST"
+ echo "lex is $DEST"
+ else
+ AC_SUB LEX ''
+ echo "neither lex or flex found"
+ TLOG " (not found)"
+ return 1
+ fi
+ fi
+
+ if AC_LIBRARY yywrap -ll -lfl; then
+ TLOG "($DEST)"
+ return 0
+ fi
+ TLOG "(no lex library found)"
+ return 1
+}
+
+
+#
+# AC_PROG_YACC checks to see if YACC exists, and if it's bison or
+# not.
+#
+AC_PROG_YACC () {
+
+ TLOGN "looking for yacc "
+
+ DEST=`acLookFor yacc`
+ if [ "$DEST" ]; then
+ AC_MAK YACC
+ AC_DEFINE PATH_YACC \"$DEST\"
+ AC_SUB 'YACC' "$DEST"
+ TLOG "($DEST)"
+ echo "yacc is $DEST"
+ else
+ DEST=`acLookFor bison`
+ if [ "$DEST" ]; then
+ AC_MAK BISON
+ AC_DEFINE 'YACC' \"$DEST\"
+ AC_SUB 'YACC' "$DEST -y"
+ echo "yacc is $DEST -y"
+ TLOG "($DEST -y)"
+ else
+ AC_SUB 'YACC' ''
+ echo "neither yacc or bison found"
+ TLOG " (not found)"
+ return 1
+ fi
+ fi
+ return 0
+}
+
+
+#
+# AC_PROG looks for a program
+#
+AC_PROG () {
+ PN=`basename $1 | $AC_UPPERCASE | tr -dc $AC_UPPER_PAT`
+
+ if set | grep -v PROG_$PN >/dev/null; then
+ TLOGN "looking for $1"
+ DEST=`acLookFor $1`
+ if [ "$DEST" ]; then
+ eval PROG_$PN="$DEST"
+ AC_SUB $PN $DEST
+ TLOG " ($DEST)"
+ return 0
+ fi
+ AC_SUB $PN true
+ TLOG " (not found)"
+ return 1
+ fi
+}
+
+
+#
+# AC_PROG_LN_S checks to see if ln exists, and, if so, if ln -s works
+#
+AC_PROG_LN_S () {
+ test "$AC_FIND_PROG" || AC_PROG_FIND
+
+ test "$AC_FIND_PROG" || return 1
+
+ TLOGN "looking for \"ln -s\""
+ DEST=`acLookFor ln`
+
+ if [ "$DEST" ]; then
+ rm -f /tmp/b$$
+ $DEST -s /tmp/a$$ /tmp/b$$
+ if [ "`$AC_FIND_PROG /tmp/b$$ -type l -print`" ]; then
+ TLOG " ($DEST)"
+ echo "$DEST exists, and ln -s works"
+ PROG_LN_S="$DEST -s"
+ AC_SUB 'LN_S' "$DEST -s"
+ rm -f /tmp/b$$
+ else
+ AC_SUB 'LN_S' ''
+ TLOG " ($DEST exists, but -s does not seem to work)"
+ echo "$DEST exists, but ln -s doesn't seem to work"
+ rm -f /tmp/b$$
+ return 1
+ fi
+ else
+ AC_SUB 'LN_S' ''
+ echo "ln not found"
+ TLOG " (not found)"
+ return 1
+ fi
+}
+
+
+#
+# AC_PROG_FIND looks for the find program and sets the FIND environment
+# variable
+#
+AC_PROG_FIND () {
+ if test -z "$AC_FIND_PROG"; then
+ MF_PATH_INCLUDE FIND find
+ rc=$?
+ AC_FIND_PROG=$DEST
+ return $rc
+ fi
+ return 0
+}
+
+
+#
+# AC_PROG_AWK looks for the awk program and sets the AWK environment
+# variable
+#
+AC_PROG_AWK () {
+ if test -z "$AC_AWK_PROG"; then
+ MF_PATH_INCLUDE AWK awk
+ rc=$?
+ AC_AWK_PROG=$DEST
+ return $rc
+ fi
+ return 0
+}
+
+
+#
+# AC_PROG_SED looks for the sed program and sets the SED environment
+# variable
+#
+AC_PROG_SED () {
+ if test -z "$AC_SED_PROG"; then
+ MF_PATH_INCLUDE SED sed
+ rc=$?
+ AC_SED_PROG=$DEST
+ return $rc
+ fi
+ return 0
+}
+
+
+#
+# AC_HEADER_SYS_WAIT looks for sys/wait.h
+#
+AC_HEADER_SYS_WAIT () {
+ AC_CHECK_HEADERS sys/wait.h || return 1
+}
+
+#
+# AC_TYPE_PID_T checks to see if the pid_t type exists
+#
+AC_TYPE_PID_T () {
+
+ AC_CHECK_TYPE pid_t sys/types.h
+ return $?
+}
+
+
+#
+# AC_C_CONST checks to see if the compiler supports the const keyword
+#
+AC_C_CONST () {
+ cat > /tmp/pd$$.c << EOF
+const char me=1;
+EOF
+ LOGN "checking for \"const\" keyword"
+
+ if __MAKEDOTO /tmp/pd$$.c; then
+ rc=0
+ else
+ AC_DEFINE 'const' '/**/'
+ rc=1
+ fi
+ rm -f /tmp/pd$$.c
+ return $rc
+}
+
+
+#
+# AC_C_VOLATILE checks to see if the compiler supports the volatile keyword
+#
+AC_C_VOLATILE () {
+ cat > /tmp/pd$$.c << EOF
+f() { volatile char me=1; }
+EOF
+ LOGN "checking for \"volatile\" keyword"
+
+ if __MAKEDOTO /tmp/pd$$.c; then
+ rc=0
+ else
+ AC_DEFINE 'volatile' '/**/'
+ rc=1
+ fi
+ rm -f /tmp/pd$$.c
+ return $rc
+}
+
+
+#
+# AC_SCALAR_TYPES checks to see if the compiler can generate 2 and 4 byte ints.
+#
+AC_SCALAR_TYPES () {
+ cat > /tmp/pd$$.c << EOF
+#include <stdio.h>
+#include <string.h>
+
+int pound_define = 1;
+
+void
+say(char *w, char *v)
+{
+ printf(pound_define ? "#define %s %s\n"
+ : "s:@%s@:%s:g\n", w, v);
+}
+
+void
+main(argc, argv)
+char **argv;
+{
+ unsigned long v_long;
+ unsigned int v_int;
+ unsigned short v_short;
+
+ if ( argc > 1 && strcmp(argv[1], "sub") == 0 )
+ pound_define = 0;
+
+ if (sizeof v_long == 4)
+ say("DWORD", "unsigned long");
+ else if (sizeof v_int == 4)
+ say("DWORD", "unsigned int");
+ else
+ exit(1);
+
+ if (sizeof v_int == 2)
+ say("WORD", "unsigned int");
+ else if (sizeof v_short == 2)
+ say("WORD", "unsigned short");
+ else
+ exit(2);
+ say("BYTE", "unsigned char");
+ exit(0);
+}
+EOF
+ rc=1
+ LOGN "defining WORD & DWORD scalar types"
+ if $AC_CC /tmp/pd$$.c -o /tmp/pd$$; then
+ while [ "$1" ]; do
+ case "$1" in
+ sub)if /tmp/pd$$ sub >> $__cwd/config.sub; then
+ rc=0
+ fi;;
+ *) if /tmp/pd$$ >> $__cwd/config.h; then
+ rc=0
+ fi ;;
+ esac
+ shift
+ done
+ if [ "$rc" != 0 ]; then
+ if /tmp/pd$$ >> $__cwd/config.h; then
+ rc=1
+ fi
+ fi
+ fi
+ case "$rc" in
+ 0) TLOG "" ;;
+ *) AC_FAIL " ** FAILED **" ;;
+ esac
+ rm -f /tmp/pd$$ /tmp/pd$$.c
+}
+
+
+#
+# AC_OUTPUT generates makefiles from makefile.in's
+#
+AC_OUTPUT () {
+ cd $__cwd
+ AC_SUB 'LIBS' "$AC_LIBS"
+ AC_SUB 'CONFIGURE_FILES' "$__config_files"
+ AC_SUB 'GENERATED_FILES' "$*"
+ AC_SUB 'CFLAGS' "$AC_CFLAGS"
+ AC_SUB 'LDFLAGS' "$AC_LDFLAGS"
+ AC_SUB 'srcdir' "$AC_SRCDIR"
+ AC_SUB 'prefix' "$AC_PREFIX"
+ AC_SUB 'exedir' "$AC_EXECDIR"
+ AC_SUB 'sbindir' "$AC_SBINDIR"
+ AC_SUB 'libdir' "$AC_LIBDIR"
+ AC_SUB 'libexec' "$AC_LIBEXEC"
+ AC_SUB 'confdir' "$AC_CONFDIR"
+ AC_SUB 'mandir' "$AC_MANDIR"
+
+ if echo "$__config_files" | grep -v librarian.sh >/dev/null; then
+ # write a librarian that works with static libraries
+ AC_PROG_LN_S
+ AC_PROG ar
+ AC_PROG ranlib
+ AC_SUB LD_LIBRARY_PATH HERE
+ AC
+ __config_files="$__config_files librarian.sh"
+ cat > librarian.sh << EOF
+#! /bin/sh
+#
+# Build static libraries, hiding (some) ickiness from the makefile
+
+ACTION=\$1; shift
+LIBRARY=\$1; shift
+VERSION=\$1; shift
+
+case "\$ACTION" in
+make) ${PROG_AR} crv \$LIBRARY.a "\$@"
+ ${PROG_RANLIB} \$LIBRARY.a
+ rm -f \$LIBRARY
+ ${PROG_LN_S} \$LIBRARY.a \$LIBRARY
+ ;;
+files) echo "\${LIBRARY}.a"
+ ;;
+install)$PROG_INSTALL -m 644 \${LIBRARY}.a \$1
+ ;;
+esac
+EOF
+ fi
+ chmod +x librarian.sh
+
+ if [ -r config.sub ]; then
+ test "$AC_SED_PROG" || AC_PROG_SED
+ test "$AC_SED_PROG" || return 1
+
+ echo >> config.h
+ echo "#endif/* ${AC_CONFIGURE_FOR} */" >> config.h
+
+ rm -f config.cmd
+ Q=\'
+ cat - > config.cmd << EOF
+#! /bin/sh
+${CC:+CC=${Q}${CC}${Q}} ${CFLAGS:+CFLAGS=${Q}${CFLAGS}${Q}} $ac_progname $ac_configure_command
+EOF
+ chmod +x config.cmd
+
+ __d=$AC_SRCDIR
+ for makefile in $*;do
+ if test -r $__d/${makefile}.in; then
+ LOG "generating $makefile"
+ ./config.md `__ac_dirname ./$makefile` 2>/dev/null
+ $AC_SED_PROG -f config.sub < $__d/${makefile}.in > $makefile
+ __config_files="$__config_files $makefile"
+ else
+ LOG "WARNING: ${makefile}.in does not exist!"
+ fi
+ done
+ unset __d
+
+ else
+ echo
+ fi
+}
+
+#
+# AC_CHECK_FLOCK checks to see if flock() exists and if the LOCK_NB argument
+# works properly.
+#
+AC_CHECK_FLOCK() {
+
+ AC_CHECK_HEADERS sys/types.h sys/file.h fcntl.h
+
+ cat << EOF > $$.c
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+main()
+{
+ int x = open("$$.c", O_RDWR, 0666);
+ int y = open("$$.c", O_RDWR, 0666);
+
+ if (flock(x, LOCK_EX) != 0)
+ exit(1);
+ if (flock(y, LOCK_EX|LOCK_NB) == 0)
+ exit(1);
+ exit(0);
+}
+EOF
+
+ LOGN "checking flock() sanity"
+ HAS_FLOCK=0
+ if $AC_CC -o flock $$.c ; then
+ if ./flock ; then
+ LOG " (good)"
+ HAS_FLOCK=1
+ AC_DEFINE HAS_FLOCK
+ else
+ LOG " (bad)"
+ fi
+ else
+ LOG " (not found)"
+ fi
+
+ rm -f flock $$.c
+
+ case "$HAS_FLOCK" in
+ 0) return 1 ;;
+ *) return 0 ;;
+ esac
+}
+
+
+#
+# AC_CHECK_RESOLVER finds out whether the berkeley resolver is
+# present on this system.
+#
+AC_CHECK_RESOLVER () {
+ AC_PROG_CC
+
+ TLOGN "looking for the Berkeley resolver library"
+
+ __ACR_rc=0
+
+ cat > /tmp/ngc$$.c << EOF
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+main()
+{
+ char bfr[256];
+
+ res_init();
+ res_query("hello", C_IN, T_A, bfr, sizeof bfr);
+}
+EOF
+
+ if $AC_CC -o /tmp/ngc$$ /tmp/ngc$$.c; then
+ TLOG " (found)"
+ elif $AC_CC -o /tmp/ngc$$ /tmp/ngc$$.c -lresolv; then
+ TLOG " (found, needs -lresolv)"
+ AC_LIBS="$AC_LIBS -lresolv"
+ elif $AC_CC -DBIND_8_COMPAT -o /tmp/ngc$$ /tmp/ngc$$.c; then
+ TLOG " (found, needs BIND_8_COMPAT)"
+ AC_DEFINE BIND_8_COMPAT 1
+ elif $AC_CC -DBIND_8_COMPAT -o /tmp/ngc$$ /tmp/ngc$$.c -lresolv; then
+ TLOG " (found, needs BIND_8_COMPAT & -lresolv)"
+ AC_DEFINE BIND_8_COMPAT 1
+ else
+ TLOG " (not found)"
+ __ACR_rc=1
+ fi
+ rm -f /tmp/ngc$$.c
+ return $__ACR_rc
+}
+
+
+#
+# AC_CHECK_ALLOCA looks for alloca
+#
+AC_CHECK_ALLOCA () {
+
+ AC_PROG_CC
+ AC_CHECK_HEADERS stdlib.h
+
+ cat - > /tmp/ngc$$.c << EOF
+#if T
+# include <alloca.h>
+#else
+# include <stdlib.h>
+#endif
+main()
+{
+ alloca(10);
+}
+EOF
+
+ LOGN "looking for the alloca function"
+ if $AC_CC -DT /tmp/ngc$$.c -o /tmp/ngc$$; then
+ AC_DEFINE 'HAVE_ALLOCA_H' 1
+ status=0
+ TLOG " (found in alloca.h)"
+ elif $AC_CC /tmp/ngc$$.c -o /tmp/ngc$$; then
+ TLOG " (found)"
+ status=0
+ else
+ TLOG " (not found)"
+ status=1
+ fi
+ rm -f /tmp/ngc$$.c /tmp/ngc
+ return $status
+
+}
+
+
+#
+# AC_CHECK_BASENAME looks for a copy of basename that does NOT use
+# a local static buffer to hold results in.
+#
+AC_CHECK_BASENAME() {
+ TLOGN "looking for a reentrant basename "
+
+ cat > /tmp/ngc$$.c << EOF
+#include <string.h>
+
+main()
+{
+ char *a = basename("/a/test");
+ char *b = basename("/a/nother");
+
+ return (strcmp(a,b) != 0) ? 0 : 1;
+
+}
+EOF
+
+ if $AC_CC -o /tmp/ngc$$ /tmp/ngc$$.c $LIBS; then
+ if /tmp/ngc$$; then
+ TLOG "(found)"
+ AC_DEFINE 'HAVE_BASENAME' 1
+ AC_CHECK_HEADERS libgen.h
+ else
+ TLOG "(broken)"
+ fi
+ else
+ TLOG "(not found)"
+ fi
+ rm -f /tmp/ngc$$ /tmp/ngc$$.c
+}
+
+#
+# AC_COMPILER_PIC checks for the compiler option to produce position independent
+# code. At the moment we assume gcc semantics.
+#
+AC_COMPILER_PIC () {
+ AC_PROG_CC
+
+ LOGN "checking for C compiler option to produce PIC "
+ echo "int some_variable = 0;" > /tmp/ngc$$.c
+
+ if $AC_CC -c -fPIC -o /tmp/ngc$$ /tmp/ngc$$.c $LIBS; then
+ AC_CFLAGS="$AC_CFLAGS -fPIC"
+ LOG "(-fPIC)"
+ __rc=0
+ else
+ LOG "(none)"
+ __rc=1
+ fi
+ rm -f /tmp/ngc$$ /tmp/ngc$$.c
+ return $__rc
+}
+
+#
+# AC_CC_SHLIBS checks if the C compiler can produce shared libraries
+# and if it can writes a librarian that handles those libraries for us.
+#
+AC_CC_SHLIBS () {
+ AC_PROG_CC
+ AC_PROG_LN_S
+ AC_PROG_INSTALL
+ LOGN "checking whether the C compiler can build shared libraries "
+
+ echo "int some_variable = 0;" > /tmp/ngc$$.c
+
+ if $AC_CC $AC_PICFLAG -shared -o /tmp/ngc$$.so /tmp/ngc$$.c; then
+ AC_SUB LD_LIBRARY_PATH LD_LIBRARY_PATH
+ # -Wl option probably works, but be paranoid anyway
+ _VFLAGS="$AC_PICFLAG -shared -Wl,-soname,ngc$$.so.1"
+ if $AC_CC $_VFLAGS -o /tmp/ngc$$.so /tmp/ngc$$.c; then
+ USE_SONAME=T
+ fi
+ LDCONFIG=`AC_PATH=/sbin:/usr/sbin:/usr/local/sbin acLookFor ldconfig`
+ __config_files="$__config_files librarian.sh"
+ cat > librarian.sh << EOF
+#! /bin/sh
+#
+# Build ELF shared libraries, hiding (some) ickiness from the makefile
+
+ACTION=\$1; shift
+LIBRARY=\$1; shift
+
+eval \`awk -F. '{ printf "MAJOR=%d\n", \$1;
+ printf "VERSION=%d.%d.%d\n", \$1, \$2, \$3; }' \$1\`
+shift
+
+LIBNAME=\$LIBRARY.so
+FULLNAME=\$LIBNAME.\$VERSION
+
+case "\$ACTION" in
+make) FLAGS="$AC_CFLAGS -shared"
+ unset VFLAGS
+ test "$USE_SONAME" && VFLAGS="-Wl,-soname,\$LIBNAME.\$MAJOR"
+
+ rm -f \$LIBRARY \$LIBNAME \$LIBNAME.\$MAJOR
+ if $AC_CC \$FLAGS \$VFLAGS -o \$FULLNAME "\$@"; then
+ $PROG_LN_S \$FULLNAME \$LIBRARY
+ $PROG_LN_S \$FULLNAME \$LIBNAME
+ $PROG_LN_S \$FULLNAME \$LIBNAME.\$MAJOR
+ fi
+ ;;
+files) echo "\$FULLNAME" "\$LIBNAME" "\$LIBNAME.\$MAJOR"
+ ;;
+install)$PROG_INSTALL -c \$FULLNAME "\$1"
+ $PROG_LN_S -f \$FULLNAME \$1/\$LIBNAME.\$MAJOR
+ $PROG_LN_S -f \$FULLNAME \$1/\$LIBNAME
+ test "$LDCONFIG" && $LDCONFIG "\$1"
+ ;;
+esac
+EOF
+ chmod +x librarian.sh
+ LOG "(yes; -shared)"
+ __rc=0
+ elif $AC_CC $AC_PICFLAG -dynamiclib -o /tmp/ngc$$.so /tmp/ngc$$.c; then
+ # macosx
+ AC_SUB LD_LIBRARY_PATH DYLD_LIBRARY_PATH
+ __config_files="$__config_files librarian.sh"
+ cat > librarian.sh << EOF
+#! /bin/sh
+#
+# Build MacOS shared libraries, hiding (some) ickiness from the makefile
+
+ACTION=\$1; shift
+LIBRARY=\$1; shift
+
+eval \`awk -F. '{ printf "MAJOR=%d\n", \$1;
+ printf "VERSION=%d.%d.%d\n", \$1, \$2, \$3; }' \$1\`
+shift
+
+LIBNAME=\$LIBRARY.dylib
+FULLNAME=\$LIBNAME
+
+case "\$ACTION" in
+make) FLAGS="$AC_CFLAGS -dynamiclib"
+ VFLAGS="-current_version \$VERSION -compatibility_version \$MAJOR"
+
+ rm -f \$LIBRARY
+ if $AC_CC \$FLAGS \$VFLAGS -o \$FULLNAME "\$@"; then
+ $PROG_LN_S \$FULLNAME \$LIBRARY
+ fi
+ ;;
+files) echo "\$FULLNAME"
+ ;;
+install)$PROG_INSTALL -c \$FULLNAME "\$1"
+ ;;
+esac
+EOF
+ chmod +x librarian.sh
+ LOG "(yes; macos dylib)"
+ __rc=0
+ else
+ LOG "(no)"
+ __rc=1
+ fi
+
+ rm -f /tmp/ngc$$.so /tmp/ngc$$.c
+
+ return $__rc
+}
+
+
+#
+# AC_PROG_INSTALL finds the install program and guesses whether it's a
+# Berkeley or GNU install program
+#
+AC_PROG_INSTALL () {
+
+ DEST=`acLookFor install`
+
+ LOGN "looking for install"
+ unset IS_BSD
+ if [ "$DEST" ]; then
+ # BSD install or GNU install? Let's find out...
+ touch /tmp/a$$
+
+ $DEST /tmp/a$$ /tmp/b$$
+
+ if test -r /tmp/a$$; then
+ LOG " ($DEST)"
+ else
+ IS_BSD=1
+ LOG " ($DEST) bsd install"
+ fi
+ rm -f /tmp/a$$ /tmp/b$$
+ else
+ DEST=`acLookFor ginstall`
+ if [ "$DEST" ]; then
+ LOG " ($DEST)"
+ else
+ DEST="false"
+ LOG " (not found)"
+ fi
+ fi
+
+ if [ "$IS_BSD" ]; then
+ PROG_INSTALL="$DEST -c"
+ else
+ PROG_INSTALL="$DEST"
+ fi
+
+ AC_SUB 'INSTALL' "$PROG_INSTALL"
+ AC_SUB 'INSTALL_PROGRAM' "$PROG_INSTALL -s -m 755"
+ AC_SUB 'INSTALL_DATA' "$PROG_INSTALL -m 444"
+
+ # finally build a little directory installer
+ # if mkdir -p works, use that, otherwise use install -d,
+ # otherwise build a script to do it by hand.
+ # in every case, test to see if the directory exists before
+ # making it.
+
+ if mkdir -p $$a/b; then
+ # I like this method best.
+ __mkdir="mkdir -p"
+ rmdir $$a/b
+ rmdir $$a
+ elif $PROG_INSTALL -d $$a/b; then
+ __mkdir="$PROG_INSTALL -d"
+ rmdir $$a/b
+ rmdir $$a
+ fi
+
+ __config_files="$__config_files config.md"
+ AC_SUB 'INSTALL_DIR' "$__cwd/config.md"
+ echo "#! /bin/sh" > $__cwd/config.md
+ echo "# script generated" `date` "by configure.sh" >> $__cwd/config.md
+ echo >> $__cwd/config.md
+ if [ "$__mkdir" ]; then
+ echo "test -d \"\$1\" || $__mkdir \"\$1\"" >> $__cwd/config.md
+ echo "exit $?" >> $__cwd/config.md
+ else
+ cat - >> $__cwd/config.md << \EOD
+pieces=`IFS=/; for x in $1; do echo $x; done`
+dir=
+for x in $pieces; do
+ dir="$dir$x"
+ mkdir $dir || exit 1
+ dir="$dir/"
+done
+exit 0
+EOD
+ fi
+ chmod +x $__cwd/config.md
+}
+
+#
+# acCheckCPP is a local that runs a C preprocessor with a given set of
+# compiler options
+#
+acCheckCPP () {
+ cat > /tmp/ngc$$.c << EOF
+#define FOO BAR
+
+FOO
+EOF
+
+ if $1 $2 /tmp/ngc$$.c > /tmp/ngc$$.o; then
+ if grep -v '#define' /tmp/ngc$$.o | grep -s BAR >/dev/null; then
+ echo "CPP=[$1], CPPFLAGS=[$2]"
+ AC_SUB 'CPP' "$1"
+ AC_SUB 'CPPFLAGS' "$2"
+ rm /tmp/ngc$$.c /tmp/ngc$$.o
+ return 0
+ fi
+ fi
+ rm /tmp/ngc$$.c /tmp/ngc$$.o
+ return 1
+}
+
+
+#
+# AC_PROG_CPP checks for cpp, then checks to see which CPPFLAGS are needed
+# to run it as a filter.
+#
+AC_PROG_CPP () {
+ if [ "$AC_CPP_PROG" ]; then
+ DEST=$AC_CPP_PROG
+ else
+ __ac_path="$AC_PATH"
+ AC_PATH="/lib:/usr/lib:${__ac_path:-$ac_default_path}"
+ DEST=`acLookFor cpp`
+ AC_PATH="$__ac_path"
+ fi
+
+ unset fail
+ LOGN "Looking for cpp"
+ if [ "$DEST" ]; then
+ TLOGN " ($DEST)"
+ acCheckCPP $DEST "$CPPFLAGS" || \
+ acCheckCPP $DEST -traditional-cpp -E || \
+ acCheckCPP $DEST -E || \
+ acCheckCPP $DEST -traditional-cpp -pipe || \
+ acCheckCPP $DEST -pipe || fail=1
+
+ if [ "$fail" ]; then
+ AC_FAIL " (can't run cpp as a pipeline)"
+ else
+ TLOG " ok"
+ return 0
+ fi
+ fi
+ AC_FAIL " (not found)"
+}
+
+#
+# AC_FAIL spits out an error message, then __fail's
+AC_FAIL() {
+ LOG "$*"
+ $__fail 1
+}
+
+#
+# AC_SUB writes a substitution into config.sub
+AC_SUB() {
+ ( _subst=`echo $2 | sed -e 's/;/\\;/g'`
+ echo "s;@$1@;$_subst;g" ) >> $__cwd/config.sub
+}
+
+#
+# AC_MAK writes a define into config.mak
+AC_MAK() {
+ echo "HAVE_$1 = 1" >> $__cwd/config.mak
+}
+
+#
+# AC_DEFINE adds a #define to config.h
+AC_DEFINE() {
+ echo "#define $1 ${2:-1}" >> $__cwd/config.h
+}
+
+#
+# AC_INCLUDE adds a #include to config.h
+AC_INCLUDE() {
+ echo "#include \"$1\"" >> $__cwd/config.h
+}
+
+#
+# AC_CONFIG adds a configuration setting to all the config files
+AC_CONFIG() {
+ AC_DEFINE "PATH_$1" \""$2"\"
+ AC_MAK "$1"
+ AC_SUB "$1" "$2"
+}
+
+#
+# AC_QUIET does something quietly
+AC_QUIET() {
+ eval $* 5>/dev/null
+}
+
+
+AC_TR=`acLookFor tr`
+if [ "$AC_TR" ]; then
+ # try posix-style tr
+ ABC=`echo abc | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`
+ if [ "$ABC" = "ABC" ]; then
+ AC_UPPERCASE="$AC_TR abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ AC_UPPER_PAT="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ else
+ ABC=`echo abc | tr a-z A-Z`
+ if [ "$ABC" = "ABC" ]; then
+ AC_UPPERCASE="$AC_TR a-z A-Z"
+ AC_UPPER_PAT="A-Z"
+ else
+ ABC=`echo abc | tr '[a-z]' '[A-Z]'`
+ if [ "$ABC" = "ABC" ]; then
+ AC_UPPERCASE="$AC_TR '[a-z]' '[A-Z]'"
+ AC_UPPER_PAT="'[A-Z]'"
+ else
+ AC_FAIL "$AC_TR cannot translate lowercase to uppercase"
+ return 0
+ fi
+ fi
+ fi
+else
+ AC_FAIL "configure requires a functional version of tr"
+fi
+
+while [ $# -gt 0 ]; do
+ unset matched
+
+ case X"$1" in
+ X--src|X--srcdir)
+ AC_SRCDIR=`__ac_dir "$2"`
+ _set_srcdir=1
+ shift 2;;
+
+ X--src=*|X--srcdir=*)
+ __d=`echo "$1" | sed -e 's/^[^=]*=//'`
+ AC_SRCDIR=`__ac_dir "$__d"`
+ _set_srcdir=1
+ shift 1 ;;
+
+ X--prefix)
+ AC_PREFIX=`__ac_dir "$2"`
+ _set_prefix=1
+ shift 2;;
+
+ X--prefix=*)
+ __d=`echo "$1"| sed -e 's/^[^=]*=//'`
+ AC_PREFIX=`__ac_dir "$__d"`
+ _set_prefix=1
+ shift 1;;
+
+ X--confdir)
+ AC_CONFDIR=`__ac_dir "$2"`
+ _set_confdir=1
+ shift 2;;
+
+ X--confdir=*)
+ __d=`echo "$1" | sed -e 's/^[^=]*=//'`
+ AC_CONFDIR=`__ac_dir "$__d"`
+ _set_confdir=1
+ shift 1;;
+
+ X--libexec|X--libexecdir)
+ AC_LIBEXEC=`__ac_dir "$2"`
+ _set_libexec=1
+ shift 2;;
+
+ X--libexec=*|X--libexecdir=*)
+ __d=`echo "$1" | sed -e 's/^[^=]*=//'`
+ AC_LIBEXEC=`__ac_dir "$__d"`
+ _set_libexec=1
+ shift 1;;
+
+ X--lib|X--libdir)
+ AC_LIBDIR=`__ac_dir "$2"`
+ _set_libdir=1
+ shift 2;;
+
+ X--lib=*|X--libdir=*)
+ __d=`echo "$1" | sed -e 's/^[^=]*=//'`
+ AC_LIBDIR=`__ac_dir "$__d"`
+ _set_libdir=1
+ shift 1;;
+
+ X--exec|X--execdir)
+ AC_EXECDIR=`__ac_dir "$2"`
+ _set_execdir=1
+ shift 2;;
+
+ X--exec=*|X--execdir=*)
+ __d=`echo "$1" | sed -e 's/^[^=]*=//'`
+ AC_EXECDIR=`__ac_dir "$__d"`
+ _set_execdir=1
+ shift 1;;
+
+ X--sbin|X--sbindir)
+ AC_SBINDIR=`__ac_dir "$2"`
+ _set_sbindir=1
+ shift 2;;
+
+ X--sbin=*|X--sbindir=*)
+ __d=`echo "$1" | sed -e 's/^[^=]*=//'`
+ AC_SBINDIR=`__ac_dir "$__d"`
+ _set_sbindir=1
+ shift 1;;
+
+ X--man|X--mandir)
+ AC_MANDIR=`__ac_dir "$2"`
+ _set_mandir=1
+ shift 2;;
+
+ X--man=*|X--mandir=*)
+ __d=`echo "$1" | sed -e 's/^[^=]*=//'`
+ AC_MANDIR=`__ac_dir "$__d"`
+ _set_mandir=1
+ shift 1;;
+
+ X--use-*=*)
+ _var=`echo "$1"| sed -n 's/^--use-\([A-Za-z][-A-Za-z0-9_]*\)=.*$/\1/p'`
+ if [ "$_var" ]; then
+ _val=`echo "$1" | sed -e 's/^--use-[^=]*=\(.*\)$/\1/'`
+ _v=`echo $_var | $AC_UPPERCASE | tr '-' '_'`
+ case X"$_val" in
+ X[Yy][Ee][Ss]|X[Tt][Rr][Uu][Ee]) eval USE_${_v}=T ;;
+ X[Nn][Oo]|X[Ff][Aa][Ll][Ss][Ee]) eval unset USE_${_v} ;;
+ *) echo "Bad value for --use-$_var ; must be yes or no"
+ exit 1 ;;
+ esac
+ else
+ echo "Bad option $1. Use --help to show options" 1>&2
+ exit 1
+ fi
+ shift 1 ;;
+
+ X--use-*)
+ _var=`echo "$1"|sed -n 's/^--use-\([A-Za-z][-A-Za-z0-9_]*\)$/\1/p'`
+ _v=`echo $_var | $AC_UPPERCASE | tr '-' '_'`
+ eval USE_${_v}=T
+ shift 1;;
+
+ X--with-*=*)
+ _var=`echo "$1"| sed -n 's/^--with-\([A-Za-z][-A-Za-z0-9_]*\)=.*$/\1/p'`
+ if [ "$_var" ]; then
+ _val=`echo "$1" | sed -e 's/^--with-[^=]*=\(.*\)$/\1/'`
+ _v=`echo $_var | $AC_UPPERCASE | tr '-' '_'`
+ eval WITH_${_v}=\"$_val\"
+ else
+ echo "Bad option $1. Use --help to show options" 1>&2
+ exit 1
+ fi
+ shift 1 ;;
+
+ X--with-*)
+ _var=`echo "$1" | sed -n 's/^--with-\([A-Za-z][A-Za-z0-9_-]*\)$/\1/p'`
+ if [ "$_var" ]; then
+ _v=`echo $_var | $AC_UPPERCASE | tr '-' '_'`
+ eval WITH_${_v}=1
+ else
+ echo "Bad option $1. Use --help to show options" 1>&2
+ exit 1
+ fi
+ shift 1 ;;
+
+ X--help)
+ echo "$ac_standard"
+ test "$ac_help" && echo "$ac_help"
+ exit 0;;
+
+ *) if [ "$LOCAL_AC_OPTIONS" ]; then
+ eval "$LOCAL_AC_OPTIONS"
+ else
+ ac_error=T
+ fi
+ if [ "$ac_error" ]; then
+ echo "Bad option $1. Use --help to show options" 1>&2
+ exit 1
+ fi ;;
+ esac
+done
+
diff --git a/configure.sh b/configure.sh
new file mode 100755
index 0000000..870181d
--- /dev/null
+++ b/configure.sh
@@ -0,0 +1,141 @@
+#! /bin/sh
+
+# local options: ac_help is the help message that describes them
+# and LOCAL_AC_OPTIONS is the script that interprets them. LOCAL_AC_OPTIONS
+# is a script that's processed with eval, so you need to be very careful to
+# make certain that what you quote is what you want to quote.
+
+# load in the configuration file
+#
+ac_help='--enable-amalloc Enable memory allocation debugging
+--with-tabstops=N Set tabstops to N characters (default is 4)
+--with-dl=X Use Discount, Extra, or Both types of definition list
+--enable-all-features Turn on all stable optional features
+--shared Build shared libraries (default is static)'
+
+LOCAL_AC_OPTIONS='
+set=`locals $*`;
+if [ "$set" ]; then
+ eval $set
+ shift 1
+else
+ ac_error=T;
+fi'
+
+locals() {
+ K=`echo $1 | $AC_UPPERCASE`
+ case "$K" in
+ --SHARED)
+ echo TRY_SHARED=T
+ ;;
+ --ENABLE-ALL|--ENABLE-ALL-FEATURES)
+ echo WITH_AMALLOC=T
+ ;;
+ --ENABLE-*) enable=`echo $K | sed -e 's/--ENABLE-//' | tr '-' '_'`
+ echo WITH_${enable}=T ;;
+ esac
+}
+
+TARGET=markdown
+. ./configure.inc
+
+AC_INIT $TARGET
+
+__DL=`echo "$WITH_DL" | $AC_UPPERCASE`
+
+case "$__DL" in
+EXTRA) AC_DEFINE 'USE_EXTRA_DL' 1 ;;
+DISCOUNT|1|"") AC_DEFINE 'USE_DISCOUNT_DL' 1 ;;
+BOTH) AC_DEFINE 'USE_EXTRA_DL' 1
+ AC_DEFINE 'USE_DISCOUNT_DL' 1 ;;
+*) AC_FAIL "Unknown value <$WITH_DL> for --with-dl (want 'discount', 'extra', or 'both')" ;;
+esac
+
+AC_PROG_CC
+
+test "$TRY_SHARED" && AC_COMPILER_PIC && AC_CC_SHLIBS
+
+case "$AC_CC $AC_CFLAGS" in
+*-Wall*) AC_DEFINE 'while(x)' 'while( (x) != 0 )'
+ AC_DEFINE 'if(x)' 'if( (x) != 0 )' ;;
+esac
+
+AC_PROG ar || AC_FAIL "$TARGET requires ar"
+AC_PROG ranlib
+
+AC_C_VOLATILE
+AC_C_CONST
+AC_SCALAR_TYPES sub hdr
+AC_CHECK_BASENAME
+
+AC_CHECK_HEADERS sys/types.h pwd.h && AC_CHECK_FUNCS getpwuid
+
+if AC_CHECK_FUNCS srandom; then
+ AC_DEFINE 'INITRNG(x)' 'srandom((unsigned int)x)'
+elif AC_CHECK_FUNCS srand; then
+ AC_DEFINE 'INITRNG(x)' 'srand((unsigned int)x)'
+else
+ AC_DEFINE 'INITRNG(x)' '(void)1'
+fi
+
+if AC_CHECK_FUNCS 'bzero((char*)0,0)'; then
+ : # Yay
+elif AC_CHECK_FUNCS 'memset((char*)0,0,0)'; then
+ AC_DEFINE 'bzero(p,s)' 'memset(p,s,0)'
+else
+ AC_FAIL "$TARGET requires bzero or memset"
+fi
+
+if AC_CHECK_FUNCS random; then
+ AC_DEFINE 'COINTOSS()' '(random()&1)'
+elif AC_CHECK_FUNCS rand; then
+ AC_DEFINE 'COINTOSS()' '(rand()&1)'
+else
+ AC_DEFINE 'COINTOSS()' '1'
+fi
+
+if AC_CHECK_FUNCS strcasecmp; then
+ :
+elif AC_CHECK_FUNCS stricmp; then
+ AC_DEFINE strcasecmp stricmp
+else
+ AC_FAIL "$TARGET requires either strcasecmp() or stricmp()"
+fi
+
+if AC_CHECK_FUNCS strncasecmp; then
+ :
+elif AC_CHECK_FUNCS strnicmp; then
+ AC_DEFINE strncasecmp strnicmp
+else
+ AC_FAIL "$TARGET requires either strncasecmp() or strnicmp()"
+fi
+
+if AC_CHECK_FUNCS fchdir || AC_CHECK_FUNCS getcwd ; then
+ AC_SUB 'THEME' ''
+else
+ AC_SUB 'THEME' '#'
+fi
+
+if [ -z "$WITH_TABSTOPS" ]; then
+ TABSTOP=4
+elif [ "$WITH_TABSTOPS" -eq 1 ]; then
+ TABSTOP=8
+else
+ TABSTOP=$WITH_TABSTOPS
+fi
+AC_DEFINE 'TABSTOP' $TABSTOP
+AC_SUB 'TABSTOP' $TABSTOP
+
+
+if [ "$WITH_AMALLOC" ]; then
+ AC_DEFINE 'USE_AMALLOC' 1
+ AC_SUB 'AMALLOC' 'amalloc.o'
+else
+ AC_SUB 'AMALLOC' ''
+fi
+
+[ "$OS_FREEBSD" -o "$OS_DRAGONFLY" ] || AC_CHECK_HEADERS malloc.h
+
+[ "$WITH_PANDOC_HEADER" ] && AC_DEFINE 'PANDOC_HEADER' '1'
+
+AC_OUTPUT Makefile version.c mkdio.h
diff --git a/css.c b/css.c
new file mode 100644
index 0000000..3eb30b3
--- /dev/null
+++ b/css.c
@@ -0,0 +1,85 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2009 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "config.h"
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+
+/*
+ * dump out stylesheet sections.
+ */
+static void
+stylesheets(Paragraph *p, Cstring *f)
+{
+ Line* q;
+
+ for ( ; p ; p = p->next ) {
+ if ( p->typ == STYLE ) {
+ for ( q = p->text; q ; q = q->next ) {
+ Cswrite(f, T(q->text), S(q->text));
+ Csputc('\n', f);
+ }
+ }
+ if ( p->down )
+ stylesheets(p->down, f);
+ }
+}
+
+
+/* dump any embedded styles to a string
+ */
+int
+mkd_css(Document *d, char **res)
+{
+ Cstring f;
+ int size;
+
+ if ( res && d && d->compiled ) {
+ *res = 0;
+ CREATE(f);
+ RESERVE(f, 100);
+ stylesheets(d->code, &f);
+
+ if ( (size = S(f)) > 0 ) {
+ EXPAND(f) = 0;
+ /* HACK ALERT! HACK ALERT! HACK ALERT! */
+ *res = T(f);/* we know that a T(Cstring) is a character pointer */
+ /* so we can simply pick it up and carry it away, */
+ /* leaving the husk of the Ctring on the stack */
+ /* END HACK ALERT */
+ }
+ else
+ DELETE(f);
+ return size;
+ }
+ return EOF;
+}
+
+
+/* dump any embedded styles to a file
+ */
+int
+mkd_generatecss(Document *d, FILE *f)
+{
+ char *res;
+ int written = EOF, size = mkd_css(d, &res);
+
+ if ( size > 0 )
+ written = fwrite(res, 1, size, f);
+ if ( res )
+ free(res);
+ return (written == size) ? size : EOF;
+}
diff --git a/cstring.h b/cstring.h
new file mode 100644
index 0000000..96ad841
--- /dev/null
+++ b/cstring.h
@@ -0,0 +1,77 @@
+/* two template types: STRING(t) which defines a pascal-style string
+ * of element (t) [STRING(char) is the closest to the pascal string],
+ * and ANCHOR(t) which defines a baseplate that a linked list can be
+ * built up from. [The linked list /must/ contain a ->next pointer
+ * for linking the list together with.]
+ */
+#ifndef _CSTRING_D
+#define _CSTRING_D
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef __WITHOUT_AMALLOC
+# include "amalloc.h"
+#endif
+
+/* expandable Pascal-style string.
+ */
+#define STRING(type) struct { type *text; int size, alloc; }
+
+#define CREATE(x) ( (T(x) = (void*)0), (S(x) = (x).alloc = 0) )
+#define EXPAND(x) (S(x)++)[(S(x) < (x).alloc) \
+ ? (T(x)) \
+ : (T(x) = T(x) ? realloc(T(x), sizeof T(x)[0] * ((x).alloc += 100)) \
+ : malloc(sizeof T(x)[0] * ((x).alloc += 100)) )]
+
+#define DELETE(x) ALLOCATED(x) ? (free(T(x)), S(x) = (x).alloc = 0) \
+ : ( S(x) = 0 )
+#define CLIP(t,i,sz) \
+ ( ((i) >= 0) && ((sz) > 0) && (((i)+(sz)) <= S(t)) ) ? \
+ (memmove(&T(t)[i], &T(t)[i+sz], (S(t)-(i+sz)+1)*sizeof(T(t)[0])), \
+ S(t) -= (sz)) : -1
+
+#define RESERVE(x, sz) T(x) = ((x).alloc > S(x) + (sz) \
+ ? T(x) \
+ : T(x) \
+ ? realloc(T(x), sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))) \
+ : malloc(sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))))
+#define SUFFIX(t,p,sz) \
+ memcpy(((S(t) += (sz)) - (sz)) + \
+ (T(t) = T(t) ? realloc(T(t), sizeof T(t)[0] * ((t).alloc += sz)) \
+ : malloc(sizeof T(t)[0] * ((t).alloc += sz))), \
+ (p), sizeof(T(t)[0])*(sz))
+
+#define PREFIX(t,p,sz) \
+ RESERVE( (t), (sz) ); \
+ if ( S(t) ) { memmove(T(t)+(sz), T(t), S(t)); } \
+ memcpy( T(t), (p), (sz) ); \
+ S(t) += (sz)
+
+/* reference-style links (and images) are stored in an array
+ */
+#define T(x) (x).text
+#define S(x) (x).size
+#define ALLOCATED(x) (x).alloc
+
+/* abstract anchor type that defines a list base
+ * with a function that attaches an element to
+ * the end of the list.
+ *
+ * the list base field is named .text so that the T()
+ * macro will work with it.
+ */
+#define ANCHOR(t) struct { t *text, *end; }
+#define E(t) ((t).end)
+
+#define ATTACH(t, p) ( T(t) ? ( (E(t)->next = (p)), (E(t) = (p)) ) \
+ : ( (T(t) = E(t) = (p)) ) )
+
+typedef STRING(char) Cstring;
+
+extern void Csputc(int, Cstring *);
+extern int Csprintf(Cstring *, char *, ...);
+extern int Cswrite(Cstring *, char *, int);
+extern void Csreparse(Cstring *, char *, int, int);
+
+#endif/*_CSTRING_D*/
diff --git a/docheader.c b/docheader.c
new file mode 100644
index 0000000..073f6da
--- /dev/null
+++ b/docheader.c
@@ -0,0 +1,49 @@
+/*
+ * docheader -- get values from the document header
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+static char *
+onlyifset(Line *l)
+{
+ char *ret = T(l->text) + l->dle;
+
+ return ret[0] ? ret : 0;
+}
+
+char *
+mkd_doc_title(Document *doc)
+{
+ if ( doc && doc->title )
+ return onlyifset(doc->title);
+ return 0;
+}
+
+
+char *
+mkd_doc_author(Document *doc)
+{
+ if ( doc && doc->author )
+ return onlyifset(doc->author);
+ return 0;
+}
+
+
+char *
+mkd_doc_date(Document *doc)
+{
+ if ( doc && doc->date )
+ return onlyifset(doc->date);
+ return 0;
+}
diff --git a/dumptree.c b/dumptree.c
new file mode 100644
index 0000000..0680848
--- /dev/null
+++ b/dumptree.c
@@ -0,0 +1,152 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include <stdio.h>
+#include "markdown.h"
+#include "cstring.h"
+#include "amalloc.h"
+
+struct frame {
+ int indent;
+ char c;
+};
+
+typedef STRING(struct frame) Stack;
+
+static char *
+Pptype(int typ)
+{
+ switch (typ) {
+ case WHITESPACE: return "whitespace";
+ case CODE : return "code";
+ case QUOTE : return "quote";
+ case MARKUP : return "markup";
+ case HTML : return "html";
+ case DL : return "dl";
+ case UL : return "ul";
+ case OL : return "ol";
+ case LISTITEM : return "item";
+ case HDR : return "header";
+ case HR : return "hr";
+ case TABLE : return "table";
+ case SOURCE : return "source";
+ case STYLE : return "style";
+ default : return "mystery node!";
+ }
+}
+
+static void
+pushpfx(int indent, char c, Stack *sp)
+{
+ struct frame *q = &EXPAND(*sp);
+
+ q->indent = indent;
+ q->c = c;
+}
+
+
+static void
+poppfx(Stack *sp)
+{
+ S(*sp)--;
+}
+
+
+static void
+changepfx(Stack *sp, char c)
+{
+ char ch;
+
+ if ( !S(*sp) ) return;
+
+ ch = T(*sp)[S(*sp)-1].c;
+
+ if ( ch == '+' || ch == '|' )
+ T(*sp)[S(*sp)-1].c = c;
+}
+
+
+static void
+printpfx(Stack *sp, FILE *f)
+{
+ int i;
+ char c;
+
+ if ( !S(*sp) ) return;
+
+ c = T(*sp)[S(*sp)-1].c;
+
+ if ( c == '+' || c == '-' ) {
+ fprintf(f, "--%c", c);
+ T(*sp)[S(*sp)-1].c = (c == '-') ? ' ' : '|';
+ }
+ else
+ for ( i=0; i < S(*sp); i++ ) {
+ if ( i )
+ fprintf(f, " ");
+ fprintf(f, "%*s%c", T(*sp)[i].indent + 2, " ", T(*sp)[i].c);
+ if ( T(*sp)[i].c == '`' )
+ T(*sp)[i].c = ' ';
+ }
+ fprintf(f, "--");
+}
+
+
+static void
+dumptree(Paragraph *pp, Stack *sp, FILE *f)
+{
+ int count;
+ Line *p;
+ int d;
+ static char *Begin[] = { 0, "P", "center" };
+
+ while ( pp ) {
+ if ( !pp->next )
+ changepfx(sp, '`');
+ printpfx(sp, f);
+
+ d = fprintf(f, "[%s", Pptype(pp->typ));
+ if ( pp->ident )
+ d += fprintf(f, " %s", pp->ident);
+ if ( pp->align )
+ d += fprintf(f, ", <%s>", Begin[pp->align]);
+
+ for (count=0, p=pp->text; p; ++count, (p = p->next) )
+ ;
+
+ if ( count )
+ d += fprintf(f, ", %d line%s", count, (count==1)?"":"s");
+
+ d += fprintf(f, "]");
+
+ if ( pp->down ) {
+ pushpfx(d, pp->down->next ? '+' : '-', sp);
+ dumptree(pp->down, sp, f);
+ poppfx(sp);
+ }
+ else fputc('\n', f);
+ pp = pp->next;
+ }
+}
+
+
+int
+mkd_dump(Document *doc, FILE *out, int flags, char *title)
+{
+ Stack stack;
+
+ if (mkd_compile(doc, flags) ) {
+
+ CREATE(stack);
+ pushpfx(fprintf(out, "%s", title), doc->code->next ? '+' : '-', &stack);
+ dumptree(doc->code, &stack, out);
+ DELETE(stack);
+
+ mkd_cleanup(doc);
+ return 0;
+ }
+ return -1;
+}
diff --git a/emmatch.c b/emmatch.c
new file mode 100644
index 0000000..8d362c5
--- /dev/null
+++ b/emmatch.c
@@ -0,0 +1,188 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2010 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "config.h"
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+
+/* emmatch: the emphasis mangler that's run after a block
+ * of html has been generated.
+ *
+ * It should create MarkdownTest_1.0 (and _1.0.3)
+ * compatable emphasis for non-pathological cases
+ * and it should fail in a standards-compliant way
+ * when someone attempts to feed it junk.
+ *
+ * Emmatching is done after the input has been
+ * processed into a STRING (f->Q) of text and
+ * emphasis blocks. After ___mkd_emblock() finishes,
+ * it truncates f->Q and leaves the rendered paragraph
+ * if f->out.
+ */
+
+
+/* empair() -- find the NEAREST matching emphasis token (or
+ * subtoken of a 3+ long emphasis token.
+ */
+static int
+empair(MMIOT *f, int first, int last, int match)
+{
+
+ int i;
+ block *begin, *p;
+
+ begin = &T(f->Q)[first];
+
+ for (i=first+1; i <= last; i++) {
+ p = &T(f->Q)[i];
+
+ if ( (p->b_type != bTEXT) && (p->b_count <= 0) )
+ continue; /* break? */
+
+ if ( p->b_type == begin->b_type ) {
+ if ( p->b_count == match ) /* exact match */
+ return i;
+
+ if ( p->b_count > 2 ) /* fuzzy match */
+ return i;
+ }
+ }
+ return 0;
+} /* empair */
+
+
+/* emfill() -- if an emphasis token has leftover stars or underscores,
+ * convert them back into character and append them to b_text.
+ */
+static void
+emfill(block *p)
+{
+ int j;
+
+ if ( p->b_type == bTEXT )
+ return;
+
+ for (j=0; j < p->b_count; j++)
+ EXPAND(p->b_text) = p->b_char;
+ p->b_count = 0;
+} /* emfill */
+
+
+static void
+emclose(MMIOT *f, int first, int last)
+{
+ int j;
+
+ for (j=first+1; j<last-1; j++)
+ emfill(&T(f->Q)[j]);
+}
+
+
+static struct emtags {
+ char open[10];
+ char close[10];
+ int size;
+} emtags[] = { { "<em>" , "</em>", 5 }, { "<strong>", "</strong>", 9 } };
+
+
+static void emblock(MMIOT*,int,int);
+
+
+/* emmatch() -- match emphasis for a single emphasis token.
+ */
+static void
+emmatch(MMIOT *f, int first, int last)
+{
+ block *start = &T(f->Q)[first];
+ int e, e2, match;
+
+ switch (start->b_count) {
+ case 2: if ( e = empair(f,first,last,match=2) )
+ break;
+ case 1: e = empair(f,first,last,match=1);
+ break;
+ case 0: return;
+ default:
+ e = empair(f,first,last,1);
+ e2= empair(f,first,last,2);
+
+ if ( e2 >= e ) {
+ e = e2;
+ match = 2;
+ }
+ else
+ match = 1;
+ break;
+ }
+
+ if ( e ) {
+ /* if we found emphasis to match, match it, recursively call
+ * emblock to match emphasis inside the new html block, add
+ * the emphasis markers for the block, then (tail) recursively
+ * call ourself to match any remaining emphasis on this token.
+ */
+ block *end = &T(f->Q)[e];
+
+ end->b_count -= match;
+ start->b_count -= match;
+
+ emblock(f, first, e);
+
+ PREFIX(start->b_text, emtags[match-1].open, emtags[match-1].size-1);
+ SUFFIX(end->b_post, emtags[match-1].close, emtags[match-1].size);
+
+ emmatch(f, first, last);
+ }
+} /* emmatch */
+
+
+/* emblock() -- walk a blocklist, attempting to match emphasis
+ */
+static void
+emblock(MMIOT *f, int first, int last)
+{
+ int i;
+
+ for ( i = first; i <= last; i++ )
+ if ( T(f->Q)[i].b_type != bTEXT )
+ emmatch(f, i, last);
+ emclose(f, first, last);
+} /* emblock */
+
+
+/* ___mkd_emblock() -- emblock a string of blocks, then concatenate the
+ * resulting text onto f->out.
+ */
+void
+___mkd_emblock(MMIOT *f)
+{
+ int i;
+ block *p;
+
+ emblock(f, 0, S(f->Q)-1);
+
+ for (i=0; i < S(f->Q); i++) {
+ p = &T(f->Q)[i];
+ emfill(p);
+
+ if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post));
+ DELETE(p->b_post); }
+ if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text));
+ DELETE(p->b_text); }
+ }
+
+ S(f->Q) = 0;
+} /* ___mkd_emblock */
diff --git a/flags.c b/flags.c
new file mode 100644
index 0000000..4021a8b
--- /dev/null
+++ b/flags.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include "markdown.h"
+
+struct flagnames {
+ DWORD flag;
+ char *name;
+};
+
+static struct flagnames flagnames[] = {
+ { MKD_NOLINKS, "!LINKS" },
+ { MKD_NOIMAGE, "!IMAGE" },
+ { MKD_NOPANTS, "!PANTS" },
+ { MKD_NOHTML, "!HTML" },
+ { MKD_STRICT, "STRICT" },
+ { MKD_TAGTEXT, "TAGTEXT" },
+ { MKD_NO_EXT, "!EXT" },
+ { MKD_CDATA, "CDATA" },
+ { MKD_NOSUPERSCRIPT, "!SUPERSCRIPT" },
+ { MKD_NORELAXED, "!RELAXED" },
+ { MKD_NOTABLES, "!TABLES" },
+ { MKD_NOSTRIKETHROUGH,"!STRIKETHROUGH" },
+ { MKD_TOC, "TOC" },
+ { MKD_1_COMPAT, "MKD_1_COMPAT" },
+ { MKD_AUTOLINK, "AUTOLINK" },
+ { MKD_SAFELINK, "SAFELINK" },
+ { MKD_NOHEADER, "!HEADER" },
+ { MKD_TABSTOP, "TABSTOP" },
+ { MKD_NODIVQUOTE, "!DIVQUOTE" },
+ { MKD_NOALPHALIST, "!ALPHALIST" },
+ { MKD_NODLIST, "!DLIST" },
+};
+#define NR(x) (sizeof x/sizeof x[0])
+
+
+void
+mkd_flags_are(FILE *f, DWORD flags, int htmlplease)
+{
+ int i;
+ int not, set, even=1;
+ char *name;
+
+ if ( htmlplease )
+ fprintf(f, "<table class=\"mkd_flags_are\">\n");
+ for (i=0; i < NR(flagnames); i++) {
+ set = flags & flagnames[i].flag;
+ name = flagnames[i].name;
+ if ( not = (*name == '!') ) {
+ ++name;
+ set = !set;
+ }
+
+ if ( htmlplease ) {
+ if ( even ) fprintf(f, " <tr>");
+ fprintf(f, "<td>");
+ }
+ else
+ fputc(' ', f);
+
+ if ( !set )
+ fprintf(f, htmlplease ? "<s>" : "!");
+
+ fprintf(f, "%s", name);
+
+ if ( htmlplease ) {
+ if ( !set )
+ fprintf(f, "</s>");
+ fprintf(f, "</td>");
+ if ( !even ) fprintf(f, "</tr>\n");
+ }
+ even = !even;
+ }
+ if ( htmlplease ) {
+ if ( even ) fprintf(f, "</tr>\n");
+ fprintf(f, "</table>\n");
+ }
+}
+
+void
+mkd_mmiot_flags(FILE *f, MMIOT *m, int htmlplease)
+{
+ if ( m )
+ mkd_flags_are(f, m->flags, htmlplease);
+}
diff --git a/generate.c b/generate.c
new file mode 100644
index 0000000..f39dcc9
--- /dev/null
+++ b/generate.c
@@ -0,0 +1,1643 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "config.h"
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+typedef int (*stfu)(const void*,const void*);
+typedef void (*spanhandler)(MMIOT*,int);
+
+/* forward declarations */
+static void text(MMIOT *f);
+static Paragraph *display(Paragraph*, MMIOT*);
+
+/* externals from markdown.c */
+int __mkd_footsort(Footnote *, Footnote *);
+
+/*
+ * push text into the generator input buffer
+ */
+static void
+push(char *bfr, int size, MMIOT *f)
+{
+ while ( size-- > 0 )
+ EXPAND(f->in) = *bfr++;
+}
+
+
+/* look <i> characters ahead of the cursor.
+ */
+static int
+peek(MMIOT *f, int i)
+{
+
+ i += (f->isp-1);
+
+ return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF;
+}
+
+
+/* pull a byte from the input buffer
+ */
+static int
+pull(MMIOT *f)
+{
+ return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF;
+}
+
+
+/* return a pointer to the current position in the input buffer.
+ */
+static char*
+cursor(MMIOT *f)
+{
+ return T(f->in) + f->isp;
+}
+
+
+static int
+isthisspace(MMIOT *f, int i)
+{
+ int c = peek(f, i);
+
+ return isspace(c) || (c == EOF);
+}
+
+
+static int
+isthisalnum(MMIOT *f, int i)
+{
+ int c = peek(f, i);
+
+ return (c != EOF) && isalnum(c);
+}
+
+
+static int
+isthisnonword(MMIOT *f, int i)
+{
+ return isthisspace(f, i) || ispunct(peek(f,i));
+}
+
+
+/* return/set the current cursor position
+ */
+#define mmiotseek(f,x) (f->isp = x)
+#define mmiottell(f) (f->isp)
+
+
+/* move n characters forward ( or -n characters backward) in the input buffer.
+ */
+static void
+shift(MMIOT *f, int i)
+{
+ if (f->isp + i >= 0 )
+ f->isp += i;
+}
+
+
+/* Qchar()
+ */
+static void
+Qchar(int c, MMIOT *f)
+{
+ block *cur;
+
+ if ( S(f->Q) == 0 ) {
+ cur = &EXPAND(f->Q);
+ memset(cur, 0, sizeof *cur);
+ cur->b_type = bTEXT;
+ }
+ else
+ cur = &T(f->Q)[S(f->Q)-1];
+
+ EXPAND(cur->b_text) = c;
+
+}
+
+
+/* Qstring()
+ */
+static void
+Qstring(char *s, MMIOT *f)
+{
+ while (*s)
+ Qchar(*s++, f);
+}
+
+
+/* Qwrite()
+ */
+static void
+Qwrite(char *s, int size, MMIOT *f)
+{
+ while (size-- > 0)
+ Qchar(*s++, f);
+}
+
+
+/* Qprintf()
+ */
+static void
+Qprintf(MMIOT *f, char *fmt, ...)
+{
+ char bfr[80];
+ va_list ptr;
+
+ va_start(ptr,fmt);
+ vsnprintf(bfr, sizeof bfr, fmt, ptr);
+ va_end(ptr);
+ Qstring(bfr, f);
+}
+
+
+/* Qem()
+ */
+static void
+Qem(MMIOT *f, char c, int count)
+{
+ block *p = &EXPAND(f->Q);
+
+ memset(p, 0, sizeof *p);
+ p->b_type = (c == '*') ? bSTAR : bUNDER;
+ p->b_char = c;
+ p->b_count = count;
+
+ memset(&EXPAND(f->Q), 0, sizeof(block));
+}
+
+
+/* generate html from a markup fragment
+ */
+void
+___mkd_reparse(char *bfr, int size, int flags, MMIOT *f)
+{
+ MMIOT sub;
+
+ ___mkd_initmmiot(&sub, f->footnotes);
+
+ sub.flags = f->flags | flags;
+ sub.cb = f->cb;
+
+ push(bfr, size, &sub);
+ EXPAND(sub.in) = 0;
+ S(sub.in)--;
+
+ text(&sub);
+ ___mkd_emblock(&sub);
+
+ Qwrite(T(sub.out), S(sub.out), f);
+
+ ___mkd_freemmiot(&sub, f->footnotes);
+}
+
+
+/*
+ * write out a url, escaping problematic characters
+ */
+static void
+puturl(char *s, int size, MMIOT *f, int display)
+{
+ unsigned char c;
+
+ while ( size-- > 0 ) {
+ c = *s++;
+
+ if ( c == '\\' && size-- > 0 ) {
+ c = *s++;
+
+ if ( !( ispunct(c) || isspace(c) ) )
+ Qchar('\\', f);
+ }
+
+ if ( c == '&' )
+ Qstring("&", f);
+ else if ( c == '<' )
+ Qstring("<", f);
+ else if ( c == '"' )
+ Qstring("%22", f);
+ else if ( isalnum(c) || ispunct(c) || (display && isspace(c)) )
+ Qchar(c, f);
+ else if ( c == 003 ) /* untokenize ^C */
+ Qstring(" ", f);
+ else
+ Qprintf(f, "%%%02X", c);
+ }
+}
+
+
+/* advance forward until the next character is not whitespace
+ */
+static int
+eatspace(MMIOT *f)
+{
+ int c;
+
+ for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) )
+ ;
+ return c;
+}
+
+
+/* (match (a (nested (parenthetical (string.)))))
+ */
+static int
+parenthetical(int in, int out, MMIOT *f)
+{
+ int size, indent, c;
+
+ for ( indent=1,size=0; indent; size++ ) {
+ if ( (c = pull(f)) == EOF )
+ return EOF;
+ else if ( (c == '\\') && (peek(f,1) == out || peek(f,1) == in) ) {
+ ++size;
+ pull(f);
+ }
+ else if ( c == in )
+ ++indent;
+ else if ( c == out )
+ --indent;
+ }
+ return size ? (size-1) : 0;
+}
+
+
+/* extract a []-delimited label from the input stream.
+ */
+static int
+linkylabel(MMIOT *f, Cstring *res)
+{
+ char *ptr = cursor(f);
+ int size;
+
+ if ( (size = parenthetical('[',']',f)) != EOF ) {
+ T(*res) = ptr;
+ S(*res) = size;
+ return 1;
+ }
+ return 0;
+}
+
+
+/* see if the quote-prefixed linky segment is actually a title.
+ */
+static int
+linkytitle(MMIOT *f, char quote, Footnote *ref)
+{
+ int whence = mmiottell(f);
+ char *title = cursor(f);
+ char *e;
+ register int c;
+
+ while ( (c = pull(f)) != EOF ) {
+ e = cursor(f);
+ if ( c == quote ) {
+ if ( (c = eatspace(f)) == ')' ) {
+ T(ref->title) = 1+title;
+ S(ref->title) = (e-title)-2;
+ return 1;
+ }
+ }
+ }
+ mmiotseek(f, whence);
+ return 0;
+}
+
+
+/* extract a =HHHxWWW size from the input stream
+ */
+static int
+linkysize(MMIOT *f, Footnote *ref)
+{
+ int height=0, width=0;
+ int whence = mmiottell(f);
+ int c;
+
+ if ( isspace(peek(f,0)) ) {
+ pull(f); /* eat '=' */
+
+ for ( c = pull(f); isdigit(c); c = pull(f))
+ width = (width * 10) + (c - '0');
+
+ if ( c == 'x' ) {
+ for ( c = pull(f); isdigit(c); c = pull(f))
+ height = (height*10) + (c - '0');
+
+ if ( isspace(c) )
+ c = eatspace(f);
+
+ if ( (c == ')') || ((c == '\'' || c == '"') && linkytitle(f, c, ref)) ) {
+ ref->height = height;
+ ref->width = width;
+ return 1;
+ }
+ }
+ }
+ mmiotseek(f, whence);
+ return 0;
+}
+
+
+/* extract a <...>-encased url from the input stream.
+ * (markdown 1.0.2b8 compatibility; older versions
+ * of markdown treated the < and > as syntactic
+ * sugar that didn't have to be there. 1.0.2b8
+ * requires a closing >, and then falls into the
+ * title or closing )
+ */
+static int
+linkybroket(MMIOT *f, int image, Footnote *p)
+{
+ int c;
+ int good = 0;
+
+ T(p->link) = cursor(f);
+ for ( S(p->link)=0; (c = pull(f)) != '>'; ++S(p->link) ) {
+ /* pull in all input until a '>' is found, or die trying.
+ */
+ if ( c == EOF )
+ return 0;
+ else if ( (c == '\\') && ispunct(peek(f,2)) ) {
+ ++S(p->link);
+ pull(f);
+ }
+ }
+
+ c = eatspace(f);
+
+ /* next nonspace needs to be a title, a size, or )
+ */
+ if ( ( c == '\'' || c == '"' ) && linkytitle(f,c,p) )
+ good=1;
+ else if ( image && (c == '=') && linkysize(f,p) )
+ good=1;
+ else
+ good=( c == ')' );
+
+ if ( good ) {
+ if ( peek(f, 1) == ')' )
+ pull(f);
+
+ ___mkd_tidy(&p->link);
+ }
+
+ return good;
+} /* linkybroket */
+
+
+/* extract a (-prefixed url from the input stream.
+ * the label is either of the format `<link>`, where I
+ * extract until I find a >, or it is of the format
+ * `text`, where I extract until I reach a ')', a quote,
+ * or (if image) a '='
+ */
+static int
+linkyurl(MMIOT *f, int image, Footnote *p)
+{
+ int c;
+ int mayneedtotrim=0;
+
+ if ( (c = eatspace(f)) == EOF )
+ return 0;
+
+ if ( c == '<' ) {
+ pull(f);
+ if ( !(f->flags & MKD_1_COMPAT) )
+ return linkybroket(f,image,p);
+ mayneedtotrim=1;
+ }
+
+ T(p->link) = cursor(f);
+ for ( S(p->link)=0; (c = peek(f,1)) != ')'; ++S(p->link) ) {
+ if ( c == EOF )
+ return 0;
+ else if ( (c == '"' || c == '\'') && linkytitle(f, c, p) )
+ break;
+ else if ( image && (c == '=') && linkysize(f, p) )
+ break;
+ else if ( (c == '\\') && ispunct(peek(f,2)) ) {
+ ++S(p->link);
+ pull(f);
+ }
+ pull(f);
+ }
+ if ( peek(f, 1) == ')' )
+ pull(f);
+
+ ___mkd_tidy(&p->link);
+
+ if ( mayneedtotrim && (T(p->link)[S(p->link)-1] == '>') )
+ --S(p->link);
+
+ return 1;
+}
+
+
+
+/* prefixes for <automatic links>
+ */
+static struct _protocol {
+ char *name;
+ int nlen;
+} protocol[] = {
+#define _aprotocol(x) { x, (sizeof x)-1 }
+ _aprotocol( "https:" ),
+ _aprotocol( "http:" ),
+ _aprotocol( "news:" ),
+ _aprotocol( "ftp:" ),
+#undef _aprotocol
+};
+#define NRPROTOCOLS (sizeof protocol / sizeof protocol[0])
+
+
+static int
+isautoprefix(char *text, int size)
+{
+ int i;
+ struct _protocol *p;
+
+ for (i=0, p=protocol; i < NRPROTOCOLS; i++, p++)
+ if ( (size >= p->nlen) && strncasecmp(text, p->name, p->nlen) == 0 )
+ return 1;
+ return 0;
+}
+
+
+/*
+ * all the tag types that linkylinky can produce are
+ * defined by this structure.
+ */
+typedef struct linkytype {
+ char *pat;
+ int szpat;
+ char *link_pfx; /* tag prefix and link pointer (eg: "<a href="\"" */
+ char *link_sfx; /* link suffix (eg: "\"" */
+ int WxH; /* this tag allows width x height arguments */
+ char *text_pfx; /* text prefix (eg: ">" */
+ char *text_sfx; /* text suffix (eg: "</a>" */
+ int flags; /* reparse flags */
+ int kind; /* tag is url or something else? */
+#define IS_URL 0x01
+} linkytype;
+
+static linkytype imaget = { 0, 0, "<img src=\"", "\"",
+ 1, " alt=\"", "\" />", MKD_NOIMAGE|MKD_TAGTEXT, IS_URL };
+static linkytype linkt = { 0, 0, "<a href=\"", "\"",
+ 0, ">", "</a>", MKD_NOLINKS, IS_URL };
+
+/*
+ * pseudo-protocols for [][];
+ *
+ * id: generates <a id="link">tag</a>
+ * class: generates <span class="link">tag</span>
+ * raw: just dump the link without any processing
+ */
+static linkytype specials[] = {
+ { "id:", 3, "<span id=\"", "\"", 0, ">", "</span>", 0, 0 },
+ { "raw:", 4, 0, 0, 0, 0, 0, MKD_NOHTML, 0 },
+ { "lang:", 5, "<span lang=\"", "\"", 0, ">", "</span>", 0, 0 },
+ { "abbr:", 5, "<abbr title=\"", "\"", 0, ">", "</abbr>", 0, 0 },
+ { "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0, 0 },
+} ;
+
+#define NR(x) (sizeof x / sizeof x[0])
+
+/* see if t contains one of our pseudo-protocols.
+ */
+static linkytype *
+pseudo(Cstring t)
+{
+ int i;
+ linkytype *r;
+
+ for ( i=0, r=specials; i < NR(specials); i++,r++ ) {
+ if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) )
+ return r;
+ }
+ return 0;
+}
+
+
+/* print out the start of an `img' or `a' tag, applying callbacks as needed.
+ */
+static void
+printlinkyref(MMIOT *f, linkytype *tag, char *link, int size)
+{
+ char *edit;
+
+ if ( f->flags & IS_LABEL )
+ return;
+
+ Qstring(tag->link_pfx, f);
+
+ if ( tag->kind & IS_URL ) {
+ if ( f->cb && f->cb->e_url && (edit = (*f->cb->e_url)(link, size, f->cb->e_data)) ) {
+ puturl(edit, strlen(edit), f, 0);
+ if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data);
+ }
+ else
+ puturl(link + tag->szpat, size - tag->szpat, f, 0);
+ }
+ else
+ ___mkd_reparse(link + tag->szpat, size - tag->szpat, MKD_TAGTEXT, f);
+
+ Qstring(tag->link_sfx, f);
+
+ if ( f->cb && f->cb->e_flags && (edit = (*f->cb->e_flags)(link, size, f->cb->e_data)) ) {
+ Qchar(' ', f);
+ Qstring(edit, f);
+ if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data);
+ }
+} /* printlinkyref */
+
+
+/* print out a linky (or fail if it's Not Allowed)
+ */
+static int
+linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
+{
+ linkytype *tag;
+
+ if ( image )
+ tag = &imaget;
+ else if ( tag = pseudo(ref->link) ) {
+ if ( f->flags & (MKD_NO_EXT|MKD_SAFELINK) )
+ return 0;
+ }
+ else if ( (f->flags & MKD_SAFELINK) && T(ref->link)
+ && (T(ref->link)[0] != '/')
+ && !isautoprefix(T(ref->link), S(ref->link)) )
+ /* if MKD_SAFELINK, only accept links that are local or
+ * a well-known protocol
+ */
+ return 0;
+ else
+ tag = &linkt;
+
+ if ( f->flags & tag->flags )
+ return 0;
+
+ if ( f->flags & IS_LABEL )
+ ___mkd_reparse(T(text), S(text), tag->flags, f);
+ else if ( tag->link_pfx ) {
+ printlinkyref(f, tag, T(ref->link), S(ref->link));
+
+ if ( tag->WxH ) {
+ if ( ref->height ) Qprintf(f," height=\"%d\"", ref->height);
+ if ( ref->width ) Qprintf(f, " width=\"%d\"", ref->width);
+ }
+
+ if ( S(ref->title) ) {
+ Qstring(" title=\"", f);
+ ___mkd_reparse(T(ref->title), S(ref->title), MKD_TAGTEXT, f);
+ Qchar('"', f);
+ }
+
+ Qstring(tag->text_pfx, f);
+ ___mkd_reparse(T(text), S(text), tag->flags, f);
+ Qstring(tag->text_sfx, f);
+ }
+ else
+ Qwrite(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, f);
+
+ return 1;
+} /* linkyformat */
+
+
+/*
+ * process embedded links and images
+ */
+static int
+linkylinky(int image, MMIOT *f)
+{
+ int start = mmiottell(f);
+ Cstring name;
+ Footnote key, *ref;
+
+ int status = 0;
+
+ CREATE(name);
+ memset(&key, 0, sizeof key);
+
+ if ( linkylabel(f, &name) ) {
+ if ( peek(f,1) == '(' ) {
+ pull(f);
+ if ( linkyurl(f, image, &key) )
+ status = linkyformat(f, name, image, &key);
+ }
+ else {
+ int goodlink, implicit_mark = mmiottell(f);
+
+ if ( isspace(peek(f,1)) )
+ pull(f);
+
+ if ( peek(f,1) == '[' ) {
+ pull(f); /* consume leading '[' */
+ goodlink = linkylabel(f, &key.tag);
+ }
+ else {
+ /* new markdown implicit name syntax doesn't
+ * require a second []
+ */
+ mmiotseek(f, implicit_mark);
+ goodlink = !(f->flags & MKD_1_COMPAT);
+ }
+
+ if ( goodlink ) {
+ if ( !S(key.tag) ) {
+ DELETE(key.tag);
+ T(key.tag) = T(name);
+ S(key.tag) = S(name);
+ }
+
+ if ( ref = bsearch(&key, T(*f->footnotes), S(*f->footnotes),
+ sizeof key, (stfu)__mkd_footsort) )
+ status = linkyformat(f, name, image, ref);
+ else if ( f->flags & IS_LABEL )
+ status = linkyformat(f, name, image, &imaget);
+ }
+ }
+ }
+
+ DELETE(name);
+ ___mkd_freefootnote(&key);
+
+ if ( status == 0 )
+ mmiotseek(f, start);
+
+ return status;
+}
+
+
+/* write a character to output, doing text escapes ( & -> &,
+ * > -> > < -> < )
+ */
+static void
+cputc(int c, MMIOT *f)
+{
+ switch (c) {
+ case '&': Qstring("&", f); break;
+ case '>': Qstring(">", f); break;
+ case '<': Qstring("<", f); break;
+ default : Qchar(c, f); break;
+ }
+}
+
+
+/*
+ * convert an email address to a string of nonsense
+ */
+static void
+mangle(char *s, int len, MMIOT *f)
+{
+ while ( len-- > 0 ) {
+ Qstring("&#", f);
+ Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *((unsigned char*)(s++)) );
+ }
+}
+
+
+/* nrticks() -- count up a row of tick marks
+ */
+static int
+nrticks(int offset, int tickchar, MMIOT *f)
+{
+ int tick = 0;
+
+ while ( peek(f, offset+tick) == tickchar ) tick++;
+
+ return tick;
+} /* nrticks */
+
+
+/* matchticks() -- match a certain # of ticks, and if that fails
+ * match the largest subset of those ticks.
+ *
+ * if a subset was matched, return the # of ticks
+ * that were matched.
+ */
+static int
+matchticks(MMIOT *f, int tickchar, int ticks, int *endticks)
+{
+ int size, count, c;
+ int subsize=0, subtick=0;
+
+ *endticks = ticks;
+ for (size = 0; (c=peek(f,size+ticks)) != EOF; size ++) {
+ if ( (c == tickchar) && ( count = nrticks(size+ticks,tickchar,f)) ) {
+ if ( count == ticks )
+ return size;
+ else if ( count ) {
+ if ( (count > subtick) && (count < ticks) ) {
+ subsize = size;
+ subtick = count;
+ }
+ size += count;
+ }
+ }
+ }
+ if ( subsize ) {
+ *endticks = subtick;
+ return subsize;
+ }
+ return 0;
+} /* matchticks */
+
+
+/* code() -- write a string out as code. The only characters that have
+ * special meaning in a code block are * `<' and `&' , which
+ * are /always/ expanded to < and &
+ */
+static void
+code(MMIOT *f, char *s, int length)
+{
+ int i,c;
+
+ for ( i=0; i < length; i++ )
+ if ( (c = s[i]) == 003) /* ^C: expand back to 2 spaces */
+ Qstring(" ", f);
+ else
+ cputc(c, f);
+} /* code */
+
+
+/* delspan() -- write out a chunk of text, blocking with <del>...</del>
+ */
+static void
+delspan(MMIOT *f, int size)
+{
+ Qstring("<del>", f);
+ ___mkd_reparse(cursor(f)-1, size, 0, f);
+ Qstring("</del>", f);
+}
+
+
+/* codespan() -- write out a chunk of text as code, trimming one
+ * space off the front and/or back as appropriate.
+ */
+static void
+codespan(MMIOT *f, int size)
+{
+ int i=0;
+
+ if ( size > 1 && peek(f, size-1) == ' ' ) --size;
+ if ( peek(f,i) == ' ' ) ++i, --size;
+
+ Qstring("<code>", f);
+ code(f, cursor(f)+(i-1), size);
+ Qstring("</code>", f);
+} /* codespan */
+
+
+/* before letting a tag through, validate against
+ * MKD_NOLINKS and MKD_NOIMAGE
+ */
+static int
+forbidden_tag(MMIOT *f)
+{
+ int c = toupper(peek(f, 1));
+
+ if ( f->flags & MKD_NOHTML )
+ return 1;
+
+ if ( c == 'A' && (f->flags & MKD_NOLINKS) && !isthisalnum(f,2) )
+ return 1;
+ if ( c == 'I' && (f->flags & MKD_NOIMAGE)
+ && strncasecmp(cursor(f)+1, "MG", 2) == 0
+ && !isthisalnum(f,4) )
+ return 1;
+ return 0;
+}
+
+
+/* Check a string to see if it looks like a mail address
+ * "looks like a mail address" means alphanumeric + some
+ * specials, then a `@`, then alphanumeric + some specials,
+ * but with a `.`
+ */
+static int
+maybe_address(char *p, int size)
+{
+ int ok = 0;
+
+ for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size)
+ ;
+
+ if ( ! (size && *p == '@') )
+ return 0;
+
+ --size, ++p;
+
+ if ( size && *p == '.' ) return 0;
+
+ for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size )
+ if ( *p == '.' && size > 1 ) ok = 1;
+
+ return size ? 0 : ok;
+}
+
+
+/* The size-length token at cursor(f) is either a mailto:, an
+ * implicit mailto:, one of the approved url protocols, or just
+ * plain old text. If it's a mailto: or an approved protocol,
+ * linkify it, otherwise say "no"
+ */
+static int
+process_possible_link(MMIOT *f, int size)
+{
+ int address= 0;
+ int mailto = 0;
+ char *text = cursor(f);
+
+ if ( f->flags & MKD_NOLINKS ) return 0;
+
+ if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) {
+ /* if it says it's a mailto, it's a mailto -- who am
+ * I to second-guess the user?
+ */
+ address = 1;
+ mailto = 7; /* 7 is the length of "mailto:"; we need this */
+ }
+ else
+ address = maybe_address(text, size);
+
+ if ( address ) {
+ Qstring("<a href=\"", f);
+ if ( !mailto ) {
+ /* supply a mailto: protocol if one wasn't attached */
+ mangle("mailto:", 7, f);
+ }
+ mangle(text, size, f);
+ Qstring("\">", f);
+ mangle(text+mailto, size-mailto, f);
+ Qstring("</a>", f);
+ return 1;
+ }
+ else if ( isautoprefix(text, size) ) {
+ printlinkyref(f, &linkt, text, size);
+ Qchar('>', f);
+ puturl(text,size,f, 1);
+ Qstring("</a>", f);
+ return 1;
+ }
+ return 0;
+} /* process_possible_link */
+
+
+/* a < may be just a regular character, the start of an embedded html
+ * tag, or the start of an <automatic link>. If it's an automatic
+ * link, we also need to know if it's an email address because if it
+ * is we need to mangle it in our futile attempt to cut down on the
+ * spaminess of the rendered page.
+ */
+static int
+maybe_tag_or_link(MMIOT *f)
+{
+ int c, size;
+ int maybetag = 1;
+
+ if ( f->flags & MKD_TAGTEXT )
+ return 0;
+
+ for ( size=0; (c = peek(f, size+1)) != '>'; size++) {
+ if ( c == EOF )
+ return 0;
+ else if ( c == '\\' ) {
+ maybetag=0;
+ if ( peek(f, size+2) != EOF )
+ size++;
+ }
+ else if ( isspace(c) )
+ break;
+ else if ( ! (c == '/' || isalnum(c) ) )
+ maybetag=0;
+ }
+
+ if ( size ) {
+ if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
+
+ /* It is not a html tag unless we find the closing '>' in
+ * the same block.
+ */
+ while ( (c = peek(f, size+1)) != '>' )
+ if ( c == EOF )
+ return 0;
+ else
+ size++;
+
+ if ( forbidden_tag(f) )
+ return 0;
+
+ Qchar('<', f);
+ while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
+ Qchar(pull(f), f);
+ return 1;
+ }
+ else if ( !isspace(c) && process_possible_link(f, size) ) {
+ shift(f, size+1);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* autolinking means that all inline html is <a href'ified>. A
+ * autolink url is alphanumerics, slashes, periods, underscores,
+ * the at sign, colon, and the % character.
+ */
+static int
+maybe_autolink(MMIOT *f)
+{
+ register int c;
+ int size;
+
+ /* greedily scan forward for the end of a legitimate link.
+ */
+ for ( size=0; (c=peek(f, size+1)) != EOF; size++ )
+ if ( c == '\\' ) {
+ if ( peek(f, size+2) != EOF )
+ ++size;
+ }
+ else if ( isspace(c) || strchr("'\"()[]{}<>`", c) )
+ break;
+
+ if ( (size > 1) && process_possible_link(f, size) ) {
+ shift(f, size);
+ return 1;
+ }
+ return 0;
+}
+
+
+/* smartyquote code that's common for single and double quotes
+ */
+static int
+smartyquote(int *flags, char typeofquote, MMIOT *f)
+{
+ int bit = (typeofquote == 's') ? 0x01 : 0x02;
+
+ if ( bit & (*flags) ) {
+ if ( isthisnonword(f,1) ) {
+ Qprintf(f, "&r%cquo;", typeofquote);
+ (*flags) &= ~bit;
+ return 1;
+ }
+ }
+ else if ( isthisnonword(f,-1) && peek(f,1) != EOF ) {
+ Qprintf(f, "&l%cquo;", typeofquote);
+ (*flags) |= bit;
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+islike(MMIOT *f, char *s)
+{
+ int len;
+ int i;
+
+ if ( s[0] == '<' ) {
+ if ( !isthisnonword(f, -1) )
+ return 0;
+ ++s;
+ }
+
+ if ( !(len = strlen(s)) )
+ return 0;
+
+ if ( s[len-1] == '>' ) {
+ if ( !isthisnonword(f,len-1) )
+ return 0;
+ len--;
+ }
+
+ for (i=1; i < len; i++)
+ if (tolower(peek(f,i)) != s[i])
+ return 0;
+ return 1;
+}
+
+
+static struct smarties {
+ char c0;
+ char *pat;
+ char *entity;
+ int shift;
+} smarties[] = {
+ { '\'', "'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, 3 },
+} ;
+#define NRSMART ( sizeof smarties / sizeof smarties[0] )
+
+
+/* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
+ */
+static int
+smartypants(int c, int *flags, MMIOT *f)
+{
+ int i;
+
+ if ( f->flags & (MKD_NOPANTS|MKD_TAGTEXT|IS_LABEL) )
+ return 0;
+
+ for ( i=0; i < NRSMART; i++)
+ if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) {
+ if ( smarties[i].entity )
+ Qprintf(f, "&%s;", smarties[i].entity);
+ shift(f, smarties[i].shift);
+ return 1;
+ }
+
+ switch (c) {
+ case '<' : return 0;
+ case '\'': if ( smartyquote(flags, 's', f) ) return 1;
+ break;
+
+ case '"': if ( smartyquote(flags, 'd', f) ) return 1;
+ break;
+
+ case '`': if ( peek(f, 1) == '`' ) {
+ int j = 2;
+
+ while ( (c=peek(f,j)) != EOF ) {
+ if ( c == '\\' )
+ j += 2;
+ else if ( c == '`' )
+ break;
+ else if ( c == '\'' && peek(f, j+1) == '\'' ) {
+ Qstring("“", f);
+ ___mkd_reparse(cursor(f)+1, j-2, 0, f);
+ Qstring("”", f);
+ shift(f,j+1);
+ return 1;
+ }
+ else ++j;
+ }
+
+ }
+ break;
+ }
+ return 0;
+} /* smartypants */
+
+
+/* process a body of text encased in some sort of tick marks. If it
+ * works, generate the output and return 1, otherwise just return 0 and
+ * let the caller figure it out.
+ */
+static int
+tickhandler(MMIOT *f, int tickchar, int minticks, spanhandler spanner)
+{
+ int endticks, size;
+ int tick = nrticks(0, tickchar, f);
+
+ if ( (tick >= minticks) && (size = matchticks(f,tickchar,tick,&endticks)) ) {
+ if ( endticks < tick ) {
+ size += (tick - endticks);
+ tick = endticks;
+ }
+
+ shift(f, tick);
+ (*spanner)(f,size);
+ shift(f, size+tick-1);
+ return 1;
+ }
+ return 0;
+}
+
+#define tag_text(f) (f->flags & MKD_TAGTEXT)
+
+
+static void
+text(MMIOT *f)
+{
+ int c, j;
+ int rep;
+ int smartyflags = 0;
+
+ while (1) {
+ if ( (f->flags & MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) )
+ maybe_autolink(f);
+
+ c = pull(f);
+
+ if (c == EOF)
+ break;
+
+ if ( smartypants(c, &smartyflags, f) )
+ continue;
+ switch (c) {
+ case 0: break;
+
+ case 3: Qstring(tag_text(f) ? " " : "<br/>", f);
+ break;
+
+ case '>': if ( tag_text(f) )
+ Qstring(">", f);
+ else
+ Qchar(c, f);
+ break;
+
+ case '"': if ( tag_text(f) )
+ Qstring(""", f);
+ else
+ Qchar(c, f);
+ break;
+
+ case '!': if ( peek(f,1) == '[' ) {
+ pull(f);
+ if ( tag_text(f) || !linkylinky(1, f) )
+ Qstring("![", f);
+ }
+ else
+ Qchar(c, f);
+ break;
+ case '[': if ( tag_text(f) || !linkylinky(0, f) )
+ Qchar(c, f);
+ break;
+ /* A^B -> A<sup>B</sup> */
+ case '^': if ( (f->flags & (MKD_NOSUPERSCRIPT|MKD_STRICT|MKD_TAGTEXT))
+ || (isthisnonword(f,-1) && peek(f,-1) != ')')
+ || isthisspace(f,1) )
+ Qchar(c,f);
+ else {
+ char *sup = cursor(f);
+ int len = 0;
+
+ if ( peek(f,1) == '(' ) {
+ int here = mmiottell(f);
+ pull(f);
+
+ if ( (len = parenthetical('(',')',f)) <= 0 ) {
+ mmiotseek(f,here);
+ Qchar(c, f);
+ break;
+ }
+ sup++;
+ }
+ else {
+ while ( isthisalnum(f,1+len) )
+ ++len;
+ if ( !len ) {
+ Qchar(c,f);
+ break;
+ }
+ shift(f,len);
+ }
+ Qstring("<sup>",f);
+ ___mkd_reparse(sup, len, 0, f);
+ Qstring("</sup>", f);
+ }
+ break;
+ case '_':
+ /* Underscores don't count if they're in the middle of a word */
+ if ( !(f->flags & (MKD_NORELAXED|MKD_STRICT))
+ && isthisalnum(f,-1)
+ && isthisalnum(f,1) ) {
+ Qchar(c, f);
+ break;
+ }
+ case '*':
+ /* Underscores & stars don't count if they're out in the middle
+ * of whitespace */
+ if ( isthisspace(f,-1) && isthisspace(f,1) ) {
+ Qchar(c, f);
+ break;
+ }
+ /* else fall into the regular old emphasis case */
+ if ( tag_text(f) )
+ Qchar(c, f);
+ else {
+ for (rep = 1; peek(f,1) == c; pull(f) )
+ ++rep;
+ Qem(f,c,rep);
+ }
+ break;
+
+ case '~': if ( (f->flags & (MKD_NOSTRIKETHROUGH|MKD_TAGTEXT|MKD_STRICT)) || !tickhandler(f,c,2,delspan) )
+ Qchar(c, f);
+ break;
+
+ case '`': if ( tag_text(f) || !tickhandler(f,c,1,codespan) )
+ Qchar(c, f);
+ break;
+
+ case '\\': switch ( c = pull(f) ) {
+ case '&': Qstring("&", f);
+ break;
+ case '<': Qstring("<", f);
+ break;
+ case '^': if ( f->flags & (MKD_STRICT|MKD_NOSUPERSCRIPT) ) {
+ Qchar('\\', f);
+ shift(f,-1);
+ break;
+ }
+ Qchar(c, f);
+ break;
+
+ case '>': case '#': case '.': case '-':
+ case '+': case '{': case '}': case ']':
+ case '!': case '[': case '*': case '_':
+ case '\\':case '(': case ')':
+ case '`': Qchar(c, f);
+ break;
+ default:
+ Qchar('\\', f);
+ if ( c != EOF )
+ shift(f,-1);
+ break;
+ }
+ break;
+
+ case '<': if ( !maybe_tag_or_link(f) )
+ Qstring("<", f);
+ break;
+
+ case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
+ while ( isthisalnum(f,j) )
+ ++j;
+
+ if ( peek(f,j) != ';' )
+ Qstring("&", f);
+ else
+ Qchar(c, f);
+ break;
+
+ default: Qchar(c, f);
+ break;
+ }
+ }
+ /* truncate the input string after we've finished processing it */
+ S(f->in) = f->isp = 0;
+} /* text */
+
+
+/* print a header block
+ */
+static void
+printheader(Paragraph *pp, MMIOT *f)
+{
+ Qprintf(f, "<h%d", pp->hnumber);
+ if ( f->flags & MKD_TOC ) {
+ Qprintf(f, " id=\"", pp->hnumber);
+ mkd_string_to_anchor(T(pp->text->text), S(pp->text->text), Qchar, f, 1);
+ Qchar('"', f);
+ }
+ Qchar('>', f);
+ push(T(pp->text->text), S(pp->text->text), f);
+ text(f);
+ Qprintf(f, "</h%d>", pp->hnumber);
+}
+
+
+enum e_alignments { a_NONE, a_CENTER, a_LEFT, a_RIGHT };
+
+static char* alignments[] = { "", " align=\"center\"", " align=\"left\"",
+ " align=\"right\"" };
+
+typedef STRING(int) Istring;
+
+static int
+splat(Line *p, char *block, Istring align, int force, MMIOT *f)
+{
+ int first,
+ idx = 0,
+ colno = 0;
+
+ Qstring("<tr>\n", f);
+ while ( idx < S(p->text) ) {
+ first = idx;
+ if ( force && (colno >= S(align)-1) )
+ idx = S(p->text);
+ else
+ while ( (idx < S(p->text)) && (T(p->text)[idx] != '|') )
+ ++idx;
+
+ Qprintf(f, "<%s%s>",
+ block,
+ alignments[ (colno < S(align)) ? T(align)[colno] : a_NONE ]);
+ ___mkd_reparse(T(p->text)+first, idx-first, 0, f);
+ Qprintf(f, "</%s>\n", block);
+ idx++;
+ colno++;
+ }
+ if ( force )
+ while (colno < S(align) ) {
+ Qprintf(f, "<%s></%s>\n", block, block);
+ ++colno;
+ }
+ Qstring("</tr>\n", f);
+ return colno;
+}
+
+
+static int
+printtable(Paragraph *pp, MMIOT *f)
+{
+ /* header, dashes, then lines of content */
+
+ Line *hdr, *dash, *body;
+ Istring align;
+ int start;
+ int hcols;
+ char *p;
+
+ if ( !(pp->text && pp->text->next) )
+ return 0;
+
+ hdr = pp->text;
+ dash= hdr->next;
+ body= dash->next;
+
+ /* first figure out cell alignments */
+
+ CREATE(align);
+
+ for (p=T(dash->text), start=0; start < S(dash->text); ) {
+ char first, last;
+ int end;
+
+ last=first=0;
+ for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) {
+ if ( !isspace(p[end]) ) {
+ if ( !first) first = p[end];
+ last = p[end];
+ }
+ }
+ EXPAND(align) = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT)
+ : (( last == ':') ? a_RIGHT : a_NONE );
+ start = 1+end;
+ }
+
+ Qstring("<table>\n", f);
+ Qstring("<thead>\n", f);
+ hcols = splat(hdr, "th", align, 0, f);
+ Qstring("</thead>\n", f);
+
+ if ( hcols < S(align) )
+ S(align) = hcols;
+ else
+ while ( hcols > S(align) )
+ EXPAND(align) = a_NONE;
+
+ Qstring("<tbody>\n", f);
+ for ( ; body; body = body->next)
+ splat(body, "td", align, 1, f);
+ Qstring("</tbody>\n", f);
+ Qstring("</table>\n", f);
+
+ DELETE(align);
+ return 1;
+}
+
+
+static int
+printblock(Paragraph *pp, MMIOT *f)
+{
+ Line *t = pp->text;
+ static char *Begin[] = { "", "<p>", "<p style=\"text-align:center;\">" };
+ static char *End[] = { "", "</p>","</p>" };
+
+ while (t) {
+ if ( S(t->text) ) {
+ if ( t->next && S(t->text) > 2
+ && T(t->text)[S(t->text)-2] == ' '
+ && T(t->text)[S(t->text)-1] == ' ' ) {
+ push(T(t->text), S(t->text)-2, f);
+ push("\003\n", 2, f);
+ }
+ else {
+ ___mkd_tidy(&t->text);
+ push(T(t->text), S(t->text), f);
+ if ( t->next )
+ push("\n", 1, f);
+ }
+ }
+ t = t->next;
+ }
+ Qstring(Begin[pp->align], f);
+ text(f);
+ Qstring(End[pp->align], f);
+ return 1;
+}
+
+
+static void
+printcode(Line *t, MMIOT *f)
+{
+ int blanks;
+
+ Qstring("<pre><code>", f);
+ for ( blanks = 0; t ; t = t->next ) {
+ if ( S(t->text) > t->dle ) {
+ while ( blanks ) {
+ Qchar('\n', f);
+ --blanks;
+ }
+ code(f, T(t->text), S(t->text));
+ Qchar('\n', f);
+ }
+ else blanks++;
+ }
+ Qstring("</code></pre>", f);
+}
+
+
+static void
+printhtml(Line *t, MMIOT *f)
+{
+ int blanks;
+
+ for ( blanks=0; t ; t = t->next )
+ if ( S(t->text) ) {
+ for ( ; blanks; --blanks )
+ Qchar('\n', f);
+
+ Qwrite(T(t->text), S(t->text), f);
+ Qchar('\n', f);
+ }
+ else
+ blanks++;
+}
+
+
+static void
+htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f)
+{
+ ___mkd_emblock(f);
+ if ( block )
+ Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments);
+ ___mkd_emblock(f);
+
+ while (( p = display(p, f) )) {
+ ___mkd_emblock(f);
+ Qstring("\n\n", f);
+ }
+
+ if ( block )
+ Qprintf(f, "</%s>", block);
+ ___mkd_emblock(f);
+}
+
+
+static void
+definitionlist(Paragraph *p, MMIOT *f)
+{
+ Line *tag;
+
+ if ( p ) {
+ Qstring("<dl>\n", f);
+
+ for ( ; p ; p = p->next) {
+ for ( tag = p->text; tag; tag = tag->next ) {
+ Qstring("<dt>", f);
+ ___mkd_reparse(T(tag->text), S(tag->text), 0, f);
+ Qstring("</dt>\n", f);
+ }
+
+ htmlify(p->down, "dd", p->ident, f);
+ Qchar('\n', f);
+ }
+
+ Qstring("</dl>", f);
+ }
+}
+
+
+static void
+listdisplay(int typ, Paragraph *p, MMIOT* f)
+{
+ if ( p ) {
+ Qprintf(f, "<%cl", (typ==UL)?'u':'o');
+ if ( typ == AL )
+ Qprintf(f, " type=\"a\"");
+ Qprintf(f, ">\n");
+
+ for ( ; p ; p = p->next ) {
+ htmlify(p->down, "li", p->ident, f);
+ Qchar('\n', f);
+ }
+
+ Qprintf(f, "</%cl>\n", (typ==UL)?'u':'o');
+ }
+}
+
+
+/* dump out a Paragraph in the desired manner
+ */
+static Paragraph*
+display(Paragraph *p, MMIOT *f)
+{
+ if ( !p ) return 0;
+
+ switch ( p->typ ) {
+ case STYLE:
+ case WHITESPACE:
+ break;
+
+ case HTML:
+ printhtml(p->text, f);
+ break;
+
+ case CODE:
+ printcode(p->text, f);
+ break;
+
+ case QUOTE:
+ htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f);
+ break;
+
+ case UL:
+ case OL:
+ case AL:
+ listdisplay(p->typ, p->down, f);
+ break;
+
+ case DL:
+ definitionlist(p->down, f);
+ break;
+
+ case HR:
+ Qstring("<hr />", f);
+ break;
+
+ case HDR:
+ printheader(p, f);
+ break;
+
+ case TABLE:
+ printtable(p, f);
+ break;
+
+ case SOURCE:
+ htmlify(p->down, 0, 0, f);
+ break;
+
+ default:
+ printblock(p, f);
+ break;
+ }
+ return p->next;
+}
+
+
+/* return a pointer to the compiled markdown
+ * document.
+ */
+int
+mkd_document(Document *p, char **res)
+{
+ int size;
+
+ if ( p && p->compiled ) {
+ if ( ! p->html ) {
+ htmlify(p->code, 0, 0, p->ctx);
+ p->html = 1;
+ }
+
+ size = S(p->ctx->out);
+
+ if ( (size == 0) || T(p->ctx->out)[size-1] )
+ EXPAND(p->ctx->out) = 0;
+
+ *res = T(p->ctx->out);
+ return size;
+ }
+ return EOF;
+}
+
diff --git a/html5.c b/html5.c
new file mode 100644
index 0000000..8b86988
--- /dev/null
+++ b/html5.c
@@ -0,0 +1,24 @@
+/* block-level tags for passing html5 blocks through the blender
+ */
+#include "tags.h"
+
+void
+mkd_with_html5_tags()
+{
+ static int populated = 0;
+
+ if ( populated ) return;
+ populated = 1;
+
+ mkd_prepare_tags();
+
+ mkd_define_tag("ASIDE", 0);
+ mkd_define_tag("FOOTER", 0);
+ mkd_define_tag("HEADER", 0);
+ mkd_define_tag("HGROUP", 0);
+ mkd_define_tag("NAV", 0);
+ mkd_define_tag("SECTION", 0);
+ mkd_define_tag("ARTICLE", 0);
+
+ mkd_sort_tags();
+}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..736dc1f
--- /dev/null
+++ b/main.c
@@ -0,0 +1,235 @@
+/*
+ * markdown: convert a single markdown document into html
+ */
+/*
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <mkdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "config.h"
+#include "amalloc.h"
+
+#if HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#ifndef HAVE_BASENAME
+#include <string.h>
+
+char*
+basename(char *p)
+{
+ char *ret = strrchr(p, '/');
+
+ return ret ? (1+ret) : p;
+}
+#endif
+
+
+char *pgm = "markdown";
+
+static struct {
+ char *name;
+ int off;
+ int flag;
+} opts[] = {
+ { "tabstop", 0, MKD_TABSTOP },
+ { "image", 1, MKD_NOIMAGE },
+ { "links", 1, MKD_NOLINKS },
+ { "relax", 1, MKD_STRICT },
+ { "strict", 0, MKD_STRICT },
+ { "tables", 1, MKD_NOTABLES },
+ { "header", 1, MKD_NOHEADER },
+ { "html", 1, MKD_NOHTML },
+ { "ext", 1, MKD_NO_EXT },
+ { "cdata", 0, MKD_CDATA },
+ { "pants", 1, MKD_NOPANTS },
+ { "smarty", 1, MKD_NOPANTS },
+ { "toc", 0, MKD_TOC },
+ { "autolink", 0, MKD_AUTOLINK },
+ { "safelink", 0, MKD_SAFELINK },
+ { "del", 1, MKD_NOSTRIKETHROUGH },
+ { "strikethrough", 1, MKD_NOSTRIKETHROUGH },
+ { "superscript", 1, MKD_NOSUPERSCRIPT },
+ { "emphasis", 0, MKD_NORELAXED },
+ { "divquote", 1, MKD_NODIVQUOTE },
+ { "alphalist", 1, MKD_NOALPHALIST },
+ { "definitionlist",1, MKD_NODLIST },
+ { "1.0", 0, MKD_1_COMPAT },
+} ;
+
+#define NR(x) (sizeof x / sizeof x[0])
+
+
+void
+set(int *flags, char *optionstring)
+{
+ int i;
+ int enable;
+ char *arg;
+
+ for ( arg = strtok(optionstring, ","); arg; arg = strtok(NULL, ",") ) {
+ if ( *arg == '+' || *arg == '-' )
+ enable = (*arg++ == '+') ? 1 : 0;
+ else if ( strncasecmp(arg, "no", 2) == 0 ) {
+ arg += 2;
+ enable = 0;
+ }
+ else
+ enable = 1;
+
+ for ( i=0; i < NR(opts); i++ )
+ if ( strcasecmp(arg, opts[i].name) == 0 )
+ break;
+
+ if ( i < NR(opts) ) {
+ if ( opts[i].off )
+ enable = !enable;
+
+ if ( enable )
+ *flags |= opts[i].flag;
+ else
+ *flags &= ~opts[i].flag;
+ }
+ else
+ fprintf(stderr, "%s: unknown option <%s>\n", pgm, arg);
+ }
+}
+
+
+char *
+e_flags(const char *text, const int size, void *context)
+{
+ return (char*)context;
+}
+
+
+float
+main(int argc, char **argv)
+{
+ int opt;
+ int rc;
+ int flags = 0;
+ int debug = 0;
+ int toc = 0;
+ int version = 0;
+ int with_html5 = 0;
+ int use_mkd_line = 0;
+ char *urlflags = 0;
+ char *text = 0;
+ char *ofile = 0;
+ char *urlbase = 0;
+ char *q;
+ MMIOT *doc;
+
+ if ( q = getenv("MARKDOWN_FLAGS") )
+ flags = strtol(q, 0, 0);
+
+ pgm = basename(argv[0]);
+ opterr = 1;
+
+ while ( (opt=getopt(argc, argv, "5b:df:E:F:o:s:t:TV")) != EOF ) {
+ switch (opt) {
+ case '5': with_html5 = 1;
+ break;
+ case 'b': urlbase = optarg;
+ break;
+ case 'd': debug = 1;
+ break;
+ case 'V': version++;
+ break;
+ case 'E': urlflags = optarg;
+ break;
+ case 'F': flags = strtol(optarg, 0, 0);
+ break;
+ case 'f': set(&flags, optarg);
+ break;
+ case 't': text = optarg;
+ use_mkd_line = 1;
+ break;
+ case 'T': toc = 1;
+ break;
+ case 's': text = optarg;
+ break;
+ case 'o': if ( ofile ) {
+ fprintf(stderr, "Too many -o options\n");
+ exit(1);
+ }
+ if ( !freopen(ofile = optarg, "w", stdout) ) {
+ perror(ofile);
+ exit(1);
+ }
+ break;
+ default: fprintf(stderr, "usage: %s [-dTV] [-b url-base]"
+ " [-F bitmap] [-f {+-}flags]"
+ " [-o ofile] [-s text]"
+ " [-t text] [file]\n", pgm);
+ exit(1);
+ }
+ }
+
+ if ( version ) {
+ printf("%s: discount %s%s", pgm, markdown_version,
+ with_html5 ? " +html5":"");
+ if ( version > 1 )
+ mkd_flags_are(stdout, flags, 0);
+ putchar('\n');
+ exit(0);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ( with_html5 )
+ mkd_with_html5_tags();
+
+ if ( use_mkd_line )
+ rc = mkd_generateline( text, strlen(text), stdout, flags);
+ else {
+ if ( text ) {
+ if ( (doc = mkd_string(text, strlen(text), flags)) == 0 ) {
+ perror(text);
+ exit(1);
+ }
+ }
+ else {
+ if ( argc && !freopen(argv[0], "r", stdin) ) {
+ perror(argv[0]);
+ exit(1);
+ }
+ if ( (doc = mkd_in(stdin,flags)) == 0 ) {
+ perror(argc ? argv[0] : "stdin");
+ exit(1);
+ }
+ }
+ if ( urlbase )
+ mkd_basename(doc, urlbase);
+ if ( urlflags ) {
+ mkd_e_data(doc, urlflags);
+ mkd_e_flags(doc, e_flags);
+ }
+
+ if ( debug )
+ rc = mkd_dump(doc, stdout, 0, argc ? basename(argv[0]) : "stdin");
+ else {
+ rc = 1;
+ if ( mkd_compile(doc, flags) ) {
+ rc = 0;
+ if ( toc )
+ mkd_generatetoc(doc, stdout);
+ mkd_generatehtml(doc, stdout);
+ mkd_cleanup(doc);
+ }
+ }
+ }
+ adump();
+ exit( (rc == 0) ? 0 : errno );
+}
diff --git a/makepage.1 b/makepage.1
new file mode 100644
index 0000000..0a78018
--- /dev/null
+++ b/makepage.1
@@ -0,0 +1,34 @@
+.\" %A%
+.\"
+.Dd January 10, 2010
+.Dt MAKEPAGE 1
+.Os MASTODON
+.Sh NAME
+.Nm makepage
+.Nd convert markdown input to a fully-formed xhtml page
+.Sh SYNOPSIS
+.Nm
+.Op Pa file
+.Sh DESCRIPTION
+The
+.Nm
+utility parses a
+.Xr markdown 7 Ns -formatted
+.Pa textfile
+.Pq or stdin if not specified,
+compiles it, then prints a fully-formed xhtml page to stdout.
+.Pp
+.Nm
+is part of discount.
+.Sh RETURN VALUES
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 .
+.Sh AUTHOR
+.An David Parsons
+.Pq Li orc at pell.chi.il.us
diff --git a/makepage.c b/makepage.c
new file mode 100644
index 0000000..8fa55ac
--- /dev/null
+++ b/makepage.c
@@ -0,0 +1,27 @@
+/*
+ * makepage: Use mkd_xhtmlpage() to convert markdown input to a
+ * fully-formed xhtml page.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <mkdio.h>
+
+float
+main(argc, argv)
+int argc;
+char **argv;
+{
+ MMIOT *doc;
+
+ if ( (argc > 1) && !freopen(argv[1], "r", stdin) ) {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ if ( (doc = mkd_in(stdin, 0)) == 0 ) {
+ perror( (argc > 1) ? argv[1] : "stdin" );
+ exit(1);
+ }
+
+ exit(mkd_xhtmlpage(doc, 0, stdout));
+}
diff --git a/markdown.1 b/markdown.1
new file mode 100644
index 0000000..f5e3949
--- /dev/null
+++ b/markdown.1
@@ -0,0 +1,164 @@
+.\" %A%
+.\"
+.Dd January 7, 2008
+.Dt MARKDOWN 1
+.Os MASTODON
+.Sh NAME
+.Nm markdown
+.Nd text to html conversion tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl T
+.Op Fl V
+.Op Fl b Ar url-base
+.Op Fl F Pa bitmap
+.Op Fl f Ar flags
+.Op Fl o Pa file
+.Op Fl s Pa text
+.Op Fl t Pa text
+.Op Pa textfile
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the
+.Xr markdown 7 Ns -formatted
+.Pa textfile
+.Pq or stdin if not specified,
+compiles it, and writes the html output
+to stdout.
+.Pp
+The options are as follows:
+.Bl -tag -width "-o file"
+.It Fl b Ar url-base
+Links in source begining with / will be prefixed with
+.Ar url-base
+in the output.
+.It Fl d
+Instead of writing the html file, dump a parse
+tree to stdout.
+.It Fl f Ar flags
+Set or clear various translation flags. The flags
+are in a comma-delimited list, with an optional
+.Ar +
+(enable),
+.Ar -
+(disable), or
+.Ar no
+(disable) lprefix on each flag.
+.Bl -tag -width "definitionlist"
+.It Ar links
+Allow links.
+.It Ar image
+Allow images.
+.It Ar smarty
+Enable smartypants.
+.It Ar pants
+Enable smartypants.
+.It Ar html
+Allow raw html.
+.It Ar strict
+Disable superscript, strikethrough & relaxed emphasis.
+.It Ar ext
+Enable pseudo-protocols.
+.It Ar cdata
+Generate code for xml
+.Em ![CDATA[...]] .
+.It Ar superscript
+Enable superscript processing.
+.It Ar emphasis
+Emphasis happens
+.Em everywhere .
+.It Ar tables
+Don't process PHP Markdown Extra tables.
+.It Ar del
+Enable
+.Em ~~strikethrough~~ .
+.It Ar strikethrough
+Enable
+.Em ~~strikethrough~~ .
+.It Ar toc
+Enable table-of-contents processing.
+.It Ar 1.0
+Compatibility with MarkdownTest_1.0
+.It Ar autolink
+Make
+.Pa http://foo.com
+a link even without
+.Em <> .
+.It Ar safelink
+Paranoid check for link protocol.
+.It Ar header
+Process pandoc-style header blocks.
+.It Ar tabstop
+Expand tabs to 4 spaces.
+.It Ar divquote
+Allow
+.Pa >%class%
+blocks.
+.It Ar alphalist
+Allow alphabetic lists.
+.It Ar definitionlist
+Allow definition lists.
+.El
+.Pp
+As an example, the option
+.Fl f Ar nolinks,smarty
+tells
+.Nm
+to not allow \<a tags, and to do smarty
+pants processing.
+.It Fl F Ar bitmap
+Set translation flags.
+.Ar Bitmap
+is a bit map of the various configuration options
+described in
+.Xr markdown 3
+(the flag values are defined in
+.Pa mkdio.h )
+.It Fl V
+Show the version# and compile-time configuration data.
+.Pp
+If the version includes the string
+.Em DEBUG ,
+.Nm
+was configured with memory allocation debugging.
+.Pp
+If the version includes the string
+.Em TAB ,
+.Nm
+was configured to use the specified tabstop.
+.It Fl VV
+Show the version#, the compile-time configuration, and the
+run-time configuration.
+.It Fl o Pa file
+Write the generated html to
+.Pa file .
+.It Fl t Ar text
+Use
+.Xr mkd_text 3
+to format
+.Ar text
+instead of processing stdin with the
+.Xr markdown 3
+function.
+.It Fl T
+If run with the table-of-content flag on, dump the
+table of contents before the formatted text.
+.It Fl s Ar text
+Use the
+.Xr markdown 3
+function to format
+.Ar text .
+.El
+.Sh RETURN VALUES
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr markdown 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 .
+.Sh AUTHOR
+.An David Parsons
+.Pq Li orc at pell.chi.il.us
diff --git a/markdown.3 b/markdown.3
new file mode 100644
index 0000000..4a23ac8
--- /dev/null
+++ b/markdown.3
@@ -0,0 +1,136 @@
+.\"
+.Dd December 20, 2007
+.Dt MARKDOWN 3
+.Os Mastodon
+.Sh NAME
+.Nm markdown
+.Nd process Markdown documents
+.Sh LIBRARY
+Markdown
+.Pq libmarkdown , -lmarkdown
+.Sh SYNOPSIS
+.Fd #include <mkdio.h>
+.Ft MMIOT
+.Fn *mkd_in "FILE *input" "int flags"
+.Ft MMIOT
+.Fn *mkd_string "char *string" "int size" "int flags"
+.Ft int
+.Fn markdown "MMIOT *doc" "FILE *output" "int flags"
+.Sh DESCRIPTION
+These functions
+convert
+.Em Markdown
+documents and strings into HTML.
+.Fn markdown
+processes an entire document, while
+.Fn mkd_text
+processes a single string.
+.Pp
+To process a file, you pass a FILE* to
+.Fn mkd_in ,
+and if it returns a nonzero value you pass that in to
+.Fn markdown ,
+which then writes the converted document to the specified
+.Em FILE* .
+If your input has already been written into a string (generated
+input or a file opened
+with
+.Xr mmap 2 )
+you can feed that string to
+.Fn mkd_string
+and pass its return value to
+.Fn markdown.
+.Pp
+.Fn Markdown
+accepts the following flag values (or-ed together if needed)
+to restrict how it processes input:
+.Bl -tag -width MKD_NOSTRIKETHROUGH -compact
+.It Ar MKD_NOLINKS
+Don't do link processing, block
+.Em <a>
+tags.
+.It Ar MKD_NOIMAGE
+Don't do image processing, block
+.Em <img> .
+.It Ar MKD_NOPANTS
+Don't run
+.Em smartypants() .
+.It Ar MKD_NOHTML
+Don't allow raw html through AT ALL
+.It Ar MKD_STRICT
+Disable
+superscript and relaxed emphasis.
+.It Ar MKD_TAGTEXT
+Process text inside an html tag; no
+.Em <em> ,
+no
+.Em <bold> ,
+no html or
+.Em []
+expansion.
+.It Ar MKD_NO_EXT
+Don't allow pseudo-protocols.
+.It Ar MKD_CDATA
+Generate code for xml
+.Em ![CDATA[...]] .
+.It Ar MKD_NOSUPERSCRIPT
+Don't generate superscripts.
+Emphasis happens _everywhere_
+.It Ar MKD_NOTABLES
+Disallow tables.
+.It Ar MKD_NOSTRIKETHROUGH
+Forbid
+.Em ~~strikethrough~~ .
+.It Ar MKD_TOC
+Do table-of-contents processing.
+.It Ar MKD_1_COMPAT
+Compatibility with MarkdownTest_1.0
+.It Ar MKD_AUTOLINK
+Make
+.Em http://foo.com
+into a link even without
+.Em <> s.
+.It Ar MKD_SAFELINK
+Paranoid check for link protocol.
+.It Ar MKD_NOHEADER
+Don't process header blocks.
+.It Ar MKD_TABSTOP
+Expand tabs to 4 spaces.
+.It Ar MKD_NODIVQUOTE
+Forbid
+.Em >%class%
+blocks.
+.It Ar MKD_NOALPHALIST
+Forbid alphabetic lists.
+.It Ar MKD_NODLIST
+Forbid definition lists.
+.El
+.Sh RETURN VALUES
+.Fn markdown
+returns 0 on success, 1 on failure.
+The
+.Fn mkd_in
+and
+.Fn mkd_string
+functions return a MMIOT* on success, null on failure.
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr mkd-callbacks 3 ,
+.Xr mkd-functions 3 ,
+.Xr mkd-line 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 ,
+.Xr mmap 2 .
+.Pp
+http://daringfireball.net/projects/markdown/syntax
+.Sh BUGS
+Error handling is minimal at best.
+.Pp
+The
+.Ar MMIOT
+created by
+.Fn mkd_string
+is deleted by the
+.Nm
+function.
+
diff --git a/markdown.7 b/markdown.7
new file mode 100644
index 0000000..7c76a2c
--- /dev/null
+++ b/markdown.7
@@ -0,0 +1,1020 @@
+.\"
+.Dd Dec 22, 2007
+.Dt MARKDOWN 7
+.Os MASTODON
+.Sh NAME
+.Nm Markdown
+.Nd The Markdown text formatting syntax
+.Sh DESCRIPTION
+.Ss Philosophy
+.Nm Markdown
+is intended to be as easy-to-read and easy-to-write as is feasible.
+.Pp
+Readability, however, is emphasized above all else. A Markdown-formatted
+document should be publishable as-is, as plain text, without looking
+like it's been marked up with tags or formatting instructions. While
+Markdown's syntax has been influenced by several existing text-to-HTML
+filters -- including
+.Em Setext ,
+.Em atx ,
+.Em Textile ,
+.Em reStructuredText ,
+.Em Grutatext ,
+and
+.Em EtText
+\-\- the single biggest source of
+inspiration for
+Markdown's
+syntax is the format of plain text email.
+.Pp
+To this end, Markdown's syntax is comprised entirely of punctuation
+characters, which punctuation characters have been carefully chosen so
+as to look like what they mean. E.g., asterisks around a word actually
+look like *emphasis*. Markdown lists look like, well, lists. Even
+blockquotes look like quoted passages of text, assuming you've ever
+used email.
+.Ss Inline HTML
+Markdown's syntax is intended for one purpose: to be used as a
+format for
+.Em writing
+for the web.
+.Pp
+.Nm
+is not a replacement for HTML, or even close to it. Its
+syntax is very small, corresponding only to a very small subset of
+HTML tags. The idea is
+.Em not
+to create a syntax that makes it easier
+to insert HTML tags. In my opinion, HTML tags are already easy to
+insert. The idea for Markdown is to make it easy to read, write, and
+edit prose. HTML is a
+.Em publishing
+format; Markdown is a
+.Em writing
+format. Thus, Markdown's formatting syntax only addresses issues that
+can be conveyed in plain text.
+.Pp
+For any markup that is not covered by Markdown's syntax, you simply
+use HTML itself. There's no need to preface it or delimit it to
+indicate that you're switching from Markdown to HTML; you just use
+the tags.
+.Pp
+The only restrictions are that block-level HTML elements -- e.g.
+.Li \<div> ,
+.Li \<table> ,
+.Li \<pre> ,
+.Li \<p> ,
+etc. -- must be separated from surrounding
+content by blank lines, and the start and end tags of the block should
+not be indented with tabs or spaces. Markdown is smart enough not
+to add extra (unwanted)
+.Li \<p>
+tags around HTML block-level tags.
+.Pp
+For example, to add an HTML table to a Markdown article:
+.Bd -literal -offset indent
+ This is a regular paragraph.
+
+ <table>
+ <tr>
+ <td>Foo</td>
+ </tr>
+ </table>
+
+ This is another regular paragraph.
+.Ed
+.Pp
+Note that Markdown formatting syntax is not processed within block-level
+HTML tags. E.g., you can't use Markdown-style
+.Li *emphasis*
+inside an HTML block.
+.Pp
+Span-level HTML tags -- e.g.
+.Li \<span> ,
+.Li \<cite> ,
+or
+.Li \<del>
+\-\- can be
+used anywhere in a Markdown paragraph, list item, or header. If you
+want, you can even use HTML tags instead of Markdown formatting; e.g. if
+you'd prefer to use HTML
+.Li \<a>
+or
+.Li \<img>
+tags instead of Markdown's
+link or image syntax, go right ahead.
+.Pp
+Unlike block-level HTML tags, Markdown syntax *is* processed within
+span-level tags.
+.Ss Automatic Escaping for Special Characters
+In HTML, there are two characters that demand special treatment: `<`
+and `&`. Left angle brackets are used to start tags; ampersands are
+used to denote HTML entities. If you want to use them as literal
+characters, you must escape them as entities, e.g. `<`, and
+`&`.
+.Pp
+Ampersands in particular are bedeviling for web writers. If you want to
+write about 'AT&T', you need to write '`AT&T`'. You even need to
+escape ampersands within URLs. Thus, if you want to link to:
+.Bd -literal -offset indent
+ http://images.google.com/images?num=30&q=larry+bird
+.Ed
+.Pp
+you need to encode the URL as:
+.Bd -literal -offset indent
+ http://images.google.com/images?num=30&q=larry+bird
+.Ed
+.Pp
+in your anchor tag `href` attribute. Needless to say, this is easy to
+forget, and is probably the single most common source of HTML validation
+errors in otherwise well-marked-up web sites.
+.Pp
+.Nm
+allows you to use these characters naturally, taking care of
+all the necessary escaping for you. If you use an ampersand as part of
+an HTML entity, it remains unchanged; otherwise it will be translated
+into `&`.
+.Pp
+So, if you want to include a copyright symbol in your article, you can write:
+.Bd -literal -offset indent
+ ©
+.Ed
+.Pp
+and Markdown will leave it alone. But if you write:
+.Bd -literal -offset indent
+ AT&T
+.Ed
+.Pp
+.Nm
+will translate it to:
+.Bd -literal -offset indent
+ AT&T
+.Ed
+.Pp
+Similarly, because Markdown supports inline HTML, if you use
+angle brackets as delimiters for HTML tags, Markdown will treat them as
+such. But if you write:
+.Bd -literal -offset indent
+ 4 < 5
+.Ed
+.Pp
+.Nm
+will translate it to:
+.Bd -literal -offset indent
+ 4 < 5
+.Ed
+.Pp
+However, inside Markdown code spans and blocks, angle brackets and
+ampersands are *always* encoded automatically. This makes it easy to use
+Markdown to write about HTML code. (As opposed to raw HTML, which is a
+terrible format for writing about HTML syntax, because every single `<`
+and `&` in your example code needs to be escaped.)
+.Sh Block Elements
+.Ss Paragraphs and Line Breaks
+.Pp
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing but spaces or tabs is considered
+blank.) Normal paragraphs should not be indented with spaces or tabs.
+.Pp
+The implication of the
+.Qq one or more consecutive lines of text
+rule is
+that Markdown supports
+.Qq hard-wrapped
+Dtext paragraphs. This differs
+significantly from most other text-to-HTML formatters (including Movable
+Type's
+.Qq Convert Line Breaks
+option) which translate every line break
+character in a paragraph into a `<br />` tag.
+.Pp
+When you *do* want to insert a `<br />` break tag using Markdown, you
+end a line with two or more spaces, then type return.
+.Pp
+Yes, this takes a tad more effort to create a `<br />`, but a simplistic
+"every line break is a `<br />`" rule wouldn't work for Markdown.
+Markdown's email-style
+.Sx blockquoting
+ and multi-paragraph
+.Sx list items
+work best -- and look better -- when you format them with hard breaks.
+.Ss Headers
+.Nm
+supports two styles of headers,
+.Em Setext
+and
+.Em atx .
+.Pp
+Setext-style headers are
+.Sq underlined
+using equal signs (for first-level
+headers) and dashes (for second-level headers). For example:
+.Bd -literal -offset indent
+ This is an H1
+ =============
+
+ This is an H2
+ -------------
+.Ed
+.Pp
+Any number of underlining `=`'s or `-`'s will work.
+.Pp
+Atx-style headers use 1-6 hash characters at the start of the line,
+corresponding to header levels 1-6. For example:
+.Bd -literal -offset indent
+ # This is an H1
+
+ ## This is an H2
+
+ ###### This is an H6
+.Ed
+.Pp
+Optionally, you may
+.Qq close
+atx-style headers. This is purely
+cosmetic -- you can use this if you think it looks better. The
+closing hashes don't even need to match the number of hashes
+used to open the header. (The number of opening hashes
+determines the header level.) :
+.Bd -literal -offset indent
+ # This is an H1 #
+
+ ## This is an H2 ##
+
+ ### This is an H3 ######
+.Ed
+.Pp
+.Ss Blockquotes
+.Nm
+uses email-style `>` characters for blockquoting. If you're
+familiar with quoting passages of text in an email message, then you
+know how to create a blockquote in Markdown. It looks best if you hard
+wrap the text and put a `>` before every line:
+.Bd -literal -offset indent
+ > This is a blockquote with two paragraphs. Lorem ipsum
+ > dolor sit amet, consectetuer adipiscing elit. Aliquam
+ > hendrerit mi posuere lectus. Vestibulum enim wisi,
+ > viverra nec, fringilla in, laoreet vitae, risus.
+ >
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet
+ > velit. Suspendisse id sem consectetuer libero luctus
+ > adipiscing.
+.Ed
+.Pp
+.Nm
+allows you to be lazy and only put the `>` before the first
+line of a hard-wrapped paragraph:
+.Bd -literal -offset indent
+ > This is a blockquote with two paragraphs. Lorem ipsum
+ dolor sit amet, consectetuer adipiscing elit. Aliquam
+ hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet
+ velit. Suspendisse id sem consectetuer libero luctus
+ adipiscing.
+.Ed
+.Pp
+Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
+adding additional levels of `>`:
+.Bd -literal -offset indent
+ > This is the first level of quoting.
+ >
+ > > This is nested blockquote.
+ >
+ > Back to the first level.
+.Ed
+.Pp
+Blockquotes can contain other Markdown elements, including headers, lists,
+and code blocks:
+.Bd -literal -offset indent
+ > ## This is a header.
+ >
+ > 1. This is the first list item.
+ > 2. This is the second list item.
+ >
+ > Here's some example code:
+ >
+ > return shell_exec("echo $input | $markdown_script");
+.Ed
+.Pp
+Any decent text editor should make email-style quoting easy. For
+example, with BBEdit, you can make a selection and choose Increase
+Quote Level from the Text menu.
+.Ss Lists
+.Nm
+supports ordered (numbered) and unordered (bulleted) lists.
+.Pp
+Unordered lists use asterisks, pluses, and hyphens -- interchangably
+\-- as list markers:
+.Bd -literal -offset indent
+ * Red
+ * Green
+ * Blue
+.Ed
+.Pp
+is equivalent to:
+.Bd -literal -offset indent
+ + Red
+ + Green
+ + Blue
+.Ed
+.Pp
+and:
+.Bd -literal -offset indent
+ - Red
+ - Green
+ - Blue
+.Ed
+.Pp
+Ordered lists use numbers followed by periods:
+.Bd -literal -offset indent
+ 1. Bird
+ 2. McHale
+ 3. Parish
+.Ed
+.Pp
+It's important to note that the actual numbers you use to mark the
+list have no effect on the HTML output Markdown produces. The HTML
+Markdown produces from the above list is:
+.Bd -literal -offset indent
+ <ol>
+ <li>Bird</li>
+ <li>McHale</li>
+ <li>Parish</li>
+ </ol>
+.Ed
+.Pp
+If you instead wrote the list in Markdown like this:
+.Bd -literal -offset indent
+ 1. Bird
+ 1. McHale
+ 1. Parish
+.Ed
+.Pp
+or even:
+.Bd -literal -offset indent
+ 3. Bird
+ 1. McHale
+ 8. Parish
+.Ed
+.Pp
+you'd get the exact same HTML output. The point is, if you want to,
+you can use ordinal numbers in your ordered Markdown lists, so that
+the numbers in your source match the numbers in your published HTML.
+But if you want to be lazy, you don't have to.
+.Pp
+If you do use lazy list numbering, however, you should still start the
+list with the number 1. At some point in the future, Markdown may support
+starting ordered lists at an arbitrary number.
+.Pp
+List markers typically start at the left margin, but may be indented by
+up to three spaces. List markers must be followed by one or more spaces
+or a tab.
+.Pp
+To make lists look nice, you can wrap items with hanging indents:
+.Bd -literal -offset indent
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing
+ elit. Aliquam hendrerit mi posuere lectus. Vestibulum
+ enim wisi, viverra nec, fringilla in, laoreet vitae,
+ risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet
+ velit. Suspendisse id sem consectetuer libero luctus
+ adipiscing.
+.Ed
+.Pp
+But if you want to be lazy, you don't have to:
+.Bd -literal -offset indent
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing
+ elit. Aliquam hendrerit mi posuere lectus. Vestibulum
+ enim wisi, viverra nec, fringilla in, laoreet vitae,
+ risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet
+ velit. Suspendisse id sem consectetuer libero luctus
+ adipiscing.
+.Ed
+.Pp
+If list items are separated by blank lines, Markdown will wrap the
+items in `<p>` tags in the HTML output. For example, this input:
+.Bd -literal -offset indent
+ * Bird
+ * Magic
+.Ed
+.Pp
+will turn into:
+.Bd -literal -offset indent
+ <ul>
+ <li>Bird</li>
+ <li>Magic</li>
+ </ul>
+.Ed
+.Pp
+But this:
+.Bd -literal -offset indent
+ * Bird
+
+ * Magic
+.Ed
+.Pp
+will turn into:
+.Bd -literal -offset indent
+ <ul>
+ <li><p>Bird</p></li>
+ <li><p>Magic</p></li>
+ </ul>
+.Ed
+.Pp
+List items may consist of multiple paragraphs. Each subsequent
+paragraph in a list item must be intended by either 4 spaces
+or one tab:
+.Bd -literal -offset indent
+ 1. This is a list item with two paragraphs. Lorem ipsum
+ dolor sit amet, consectetuer adipiscing elit. Aliquam
+ hendrerit mi posuere lectus.
+
+ Vestibulum enim wisi, viverra nec, fringilla in,
+ laoreet vitae, risus. Donec sit amet nisl. Aliquam
+ semper ipsum sit amet velit.
+
+ 2. Suspendisse id sem consectetuer libero luctus
+ adipiscing.
+.Ed
+.Pp
+It looks nice if you indent every line of the subsequent
+paragraphs, but here again, Markdown will allow you to be
+lazy:
+.Bd -literal -offset indent
+ * This is a list item with two paragraphs.
+
+ This is the second paragraph in the list item.
+ You're only required to indent the first line. Lorem
+ ipsum dolor sit amet, consectetuer adipiscing elit.
+
+ * Another item in the same list.
+.Ed
+.Pp
+To put a blockquote within a list item, the blockquote's `>`
+delimiters need to be indented:
+.Bd -literal -offset indent
+ * A list item with a blockquote:
+
+ > This is a blockquote
+ > inside a list item.
+.Ed
+.Pp
+To put a code block within a list item, the code block needs
+to be indented *twice* -- 8 spaces or two tabs:
+.Bd -literal -offset indent
+ * A list item with a code block:
+
+ <code goes here>
+.Ed
+.Pp
+It's worth noting that it's possible to trigger an ordered list by
+accident, by writing something like this:
+.Bd -literal -offset indent
+ 1986. What a great season.
+.Ed
+.Pp
+In other words, a *number-period-space* sequence at the beginning of a
+line. To avoid this, you can backslash-escape the period:
+.Bd -literal -offset indent
+ 1986\\. What a great season.
+.Ed
+.Pp
+.Ss Code Blocks
+Pre-formatted code blocks are used for writing about programming or
+markup source code. Rather than forming normal paragraphs, the lines
+of a code block are interpreted literally. Markdown wraps a code block
+in both `<pre>` and `<code>` tags.
+.Pp
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab. For example, given this input:
+.Bd -literal -offset indent
+ This is a normal paragraph:
+
+ This is a code block.
+.Ed
+.Pp
+.Nm
+will generate:
+.Bd -literal -offset indent
+ <p>This is a normal paragraph:</p>
+
+ <pre><code>This is a code block.
+ </code></pre>
+.Ed
+.Pp
+One level of indentation -- 4 spaces or 1 tab -- is removed from each
+line of the code block. For example, this:
+.Bd -literal -offset indent
+ Here is an example of AppleScript:
+
+ tell application "Foo"
+ beep
+ end tell
+.Ed
+.Pp
+will turn into:
+.Bd -literal -offset indent
+ <p>Here is an example of AppleScript:</p>
+
+ <pre><code>tell application "Foo"
+ beep
+ end tell
+ </code></pre>
+.Ed
+.Pp
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+.Pp
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+.Bd -literal -offset indent
+ <div class="footer">
+ © 2004 Foo Corporation
+ </div>
+.Ed
+.Pp
+will turn into:
+.Bd -literal -offset indent
+ <pre><code><div class="footer">
+ © 2004 Foo Corporation
+ </div>
+ </code></pre>
+.Ed
+.Pp
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+.Ss Horizontal Rules
+You can produce a horizontal rule tag (`<hr />`) by placing three or
+more hyphens, asterisks, or underscores on a line by themselves. If you
+wish, you may use spaces between the hyphens or asterisks. Each of the
+following lines will produce a horizontal rule:
+.Bd -literal -offset indent
+ * * *
+
+ ***
+
+ *****
+
+ - - -
+
+ ---------------------------------------
+.Ed
+.Pp
+.Sh Span Elements
+.Ss Links
+.Nm
+supports two style of links:
+.Em inline
+and
+.Em reference .
+.Pp
+In both styles, the link text is delimited by [square brackets].
+.Pp
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+.Bd -literal -offset indent
+ This is [an example](http://example.com/ "Title") inline link.
+
+ [This link](http://example.net/) has no title attribute.
+.Ed
+.Pp
+Will produce:
+.Bd -literal -offset indent
+ <p>This is <a href="http://example.com/" title="Title">
+ an example</a> inline link.</p>
+
+ <p><a href="http://example.net/">This link</a> has no
+ title attribute.</p>
+.Ed
+.Pp
+If you're referring to a local resource on the same server, you can
+use relative paths:
+.Bd -literal -offset indent
+ See my [About](/about/) page for details.
+.Ed
+.Pp
+Reference-style links use a second set of square brackets, inside
+which you place a label of your choosing to identify the link:
+.Bd -literal -offset indent
+ This is [an example][id] reference-style link.
+.Ed
+.Pp
+You can optionally use a space to separate the sets of brackets:
+.Bd -literal -offset indent
+ This is [an example] [id] reference-style link.
+.Ed
+.Pp
+Then, anywhere in the document, you define your link label like this,
+on a line by itself:
+.Bd -literal -offset indent
+ [id]: http://example.com/ "Optional Title Here"
+.Ed
+.Pp
+That is:
+.Bl -bullet
+.It
+Square brackets containing the link identifier (optionally
+indented from the left margin using up to three spaces);
+.It
+followed by a colon;
+.It
+followed by one or more spaces (or tabs);
+.It
+followed by the URL for the link;
+.It
+optionally followed by a title attribute for the link, enclosed
+in double or single quotes, or enclosed in parentheses.
+.El
+.Pp
+The following three link definitions are equivalent:
+.Bd -literal -offset indent
+ [foo]: http://example.com/ "Optional Title Here"
+ [foo]: http://example.com/ 'Optional Title Here'
+ [foo]: http://example.com/ (Optional Title Here)
+.Ed
+.Pp
+.Em Note :
+There is a known bug in Markdown.pl 1.0.1 which prevents
+single quotes from being used to delimit link titles.
+.Pp
+The link URL may, optionally, be surrounded by angle brackets:
+.Bd -literal -offset indent
+ [id]: <http://example.com/> "Optional Title Here"
+.Ed
+.Pp
+You can put the title attribute on the next line and use extra spaces
+or tabs for padding, which tends to look better with longer URLs:
+.Bd -literal -offset indent
+ [id]: http://example.com/longish/path/to/resource/here
+ "Optional Title Here"
+.Ed
+.Pp
+Link definitions are only used for creating links during Markdown
+processing, and are stripped from your document in the HTML output.
+.Pp
+Link definition names may constist of letters, numbers, spaces, and
+punctuation -- but they are
+.Em not
+case sensitive. E.g. these two
+links:
+.Bd -literal -offset indent
+ [link text][a]
+ [link text][A]
+.Ed
+.Pp
+are equivalent.
+.Pp
+The
+.Em implicit link name
+shortcut allows you to omit the name of the
+link, in which case the link text itself is used as the name.
+Just use an empty set of square brackets -- e.g., to link the word
+.Qq Google
+to the google.com web site, you could simply write:
+.Bd -literal -offset indent
+ [Google][]
+.Ed
+.Pp
+And then define the link:
+.Bd -literal -offset indent
+ [Google]: http://google.com/
+.Ed
+.Pp
+Because link names may contain spaces, this shortcut even works for
+multiple words in the link text:
+.Bd -literal -offset indent
+ Visit [Daring Fireball][] for more information.
+.Ed
+.Pp
+And then define the link:
+.Bd -literal -offset indent
+ [Daring Fireball]: http://daringfireball.net/
+.Ed
+.Pp
+Link definitions can be placed anywhere in your Markdown document. I
+tend to put them immediately after each paragraph in which they're
+used, but if you want, you can put them all at the end of your
+document, sort of like footnotes.
+.Pp
+Here's an example of reference links in action:
+.Bd -literal -offset indent
+ I get 10 times more traffic from [Google] [1] than from
+ [Yahoo] [2] or [MSN] [3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+.Ed
+.Pp
+Using the implicit link name shortcut, you could instead write:
+.Bd -literal -offset indent
+ I get 10 times more traffic from [Google][] than from
+ [Yahoo][] or [MSN][].
+
+ [google]: http://google.com/ "Google"
+ [yahoo]: http://search.yahoo.com/ "Yahoo Search"
+ [msn]: http://search.msn.com/ "MSN Search"
+.Ed
+.Pp
+Both of the above examples will produce the following HTML output:
+.Bd -literal -offset indent
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from
+ <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
+ or
+ <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
+.Ed
+.Pp
+For comparison, here is the same paragraph written using
+Markdown's inline link style:
+.Bd -literal -offset indent
+ I get 10 times more traffic from
+ [Google](http://google.com/ "Google") than from
+ [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
+ [MSN](http://search.msn.com/ "MSN Search").
+.Ed
+.Pp
+The point of reference-style links is not that they're easier to
+write. The point is that with reference-style links, your document
+source is vastly more readable. Compare the above examples: using
+reference-style links, the paragraph itself is only 81 characters
+long; with inline-style links, it's 176 characters; and as raw HTML,
+it's 234 characters. In the raw HTML, there's more markup than there
+is text.
+.Pp
+With Markdown's reference-style links, a source document much more
+closely resembles the final output, as rendered in a browser. By
+allowing you to move the markup-related metadata out of the paragraph,
+you can add links without interrupting the narrative flow of your
+prose.
+.Ss Emphasis
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`<strong>` tag. E.g., this input:
+.Bd -literal -offset indent
+ *single asterisks*
+
+ _single underscores_
+
+ **double asterisks**
+
+ __double underscores__
+.Ed
+.Pp
+will produce:
+.Bd -literal -offset indent
+ <em>single asterisks</em>
+
+ <em>single underscores</em>
+
+ <strong>double asterisks</strong>
+
+ <strong>double underscores</strong>
+.Ed
+.Pp
+You can use whichever style you prefer; the lone restriction is that
+the same character must be used to open and close an emphasis span.
+.Pp
+Emphasis can be used in the middle of a word:
+.Bd -literal -offset indent
+ un*fucking*believable
+.Ed
+.Pp
+But if you surround an `*` or `_` with spaces, it'll be treated as a
+literal asterisk or underscore.
+.Pp
+To produce a literal asterisk or underscore at a position where it
+would otherwise be used as an emphasis delimiter, you can backslash
+escape it:
+.Bd -literal -offset indent
+ \\*this text is surrounded by literal asterisks\\*
+.Ed
+.Pp
+.Ss Code
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+.Bd -literal -offset indent
+ Use the `printf()` function.
+.Ed
+.Pp
+will produce:
+.Bd -literal -offset indent
+ <p>Use the <code>printf()</code> function.</p>
+.Ed
+.Pp
+To include a literal backtick character within a code span, you can use
+multiple backticks as the opening and closing delimiters:
+.Bd -literal -offset indent
+ ``There is a literal backtick (`) here.``
+.Ed
+.Pp
+which will produce this:
+.Bd -literal -offset indent
+ <p><code>There is a literal backtick (`) here.</code></p>
+.Ed
+.Pp
+The backtick delimiters surrounding a code span may include spaces --
+one after the opening, one before the closing. This allows you to place
+literal backtick characters at the beginning or end of a code span:
+.Bd -literal -offset indent
+ A single backtick in a code span: `` ` ``
+
+ A backtick-delimited string in a code span: `` `foo` ``
+.Ed
+.Pp
+will produce:
+.Bd -literal -offset indent
+ <p>A single backtick in a code span: <code>`</code></p>
+
+ <p>A backtick-delimited string in a code span: <code>`foo`</code></p>
+.Ed
+.Pp
+With a code span, ampersands and angle brackets are encoded as HTML
+entities automatically, which makes it easy to include example HTML
+tags. Markdown will turn this:
+.Bd -literal -offset indent
+ Please don't use any `<blink>` tags.
+.Ed
+.Pp
+into:
+.Bd -literal -offset indent
+ <p>Please don't use any <code><blink></code> tags.</p>
+.Ed
+.Pp
+You can write this:
+.Bd -literal -offset indent
+ `—` is the decimal-encoded equivalent of `—`.
+.Ed
+.Pp
+to produce:
+.Bd -literal -offset indent
+ <p><code>—</code> is the decimal-encoded
+ equivalent of <code>—</code>.</p>
+.Ed
+.Pp
+.Ss Images
+Admittedly, it's fairly difficult to devise a
+.Qq natural
+syntax for placing images into a plain text document format.
+.Pp
+Markdown uses an image syntax that is intended to resemble the syntax
+for links, allowing for two styles:
+.Em inline
+and
+.Em reference .
+.Pp
+Inline image syntax looks like this:
+.Bd -literal -offset indent
+ ![Alt text](/path/to/img.jpg)
+
+ ![Alt text](/path/to/img.jpg =Optional size "Optional title")
+.Ed
+.Pp
+That is:
+.Bl -bullet
+.It
+An exclamation mark: `!`;
+.It
+followed by a set of square brackets, containing the `alt`
+attribute text for the image;
+.It
+followed by a set of parentheses, containing the URL or path to
+the image, an optional `size` attribute (in
+.Ar width Li c Ar height
+format) prefixed with a `=`,
+and an optional `title` attribute enclosed in double
+or single quotes.
+.El
+.Pp
+Reference-style image syntax looks like this:
+.Bd -literal -offset indent
+ ![Alt text][id]
+.Ed
+.Pp
+Where
+.Qq id
+is the name of a defined image reference. Image references
+are defined using syntax identical to link references:
+.Bd -literal -offset indent
+ [id]: url/to/image =Optional size "Optional title attribute"
+.Ed
+.Pp
+.Sh Miscellaneous
+.Ss Automatic Links
+.Nm
+supports a shortcut style for creating
+.Qq automatic
+links for URLs and email addresses: simply surround the URL or email
+address with angle brackets. What this means is that if you want to
+ show the actual text of a URL or email address, and also have it be
+ a clickable link, you can do this:
+.Bd -literal -offset indent
+ <http://example.com/>
+.Ed
+.Pp
+.Nm
+will turn this into:
+.Bd -literal -offset indent
+ <a href="http://example.com/">http://example.com/</a>
+.Ed
+.Pp
+Automatic links for email addresses work similarly, except that
+Markdown will also perform a bit of randomized decimal and hex
+entity-encoding to help obscure your address from address-harvesting
+spambots. For example, Markdown will turn this:
+.Bd -literal -offset indent
+ <address at example.com>
+.Ed
+.Pp
+into something like this:
+.Bd -literal -offset indent
+ <a href="mailto:addre
+ ss@example.co
+ m">address@exa
+ mple.com</a>
+.Ed
+.Pp
+which will render in a browser as a clickable link to
+.Qq address at example.com .
+.Pp
+(This sort of entity-encoding trick will indeed fool many, if not
+most, address-harvesting bots, but it definitely won't fool all of
+them. It's better than nothing, but an address published in this way
+will probably eventually start receiving spam.)
+.Ss Backslash Escapes
+.Nm
+allows you to use backslash escapes to generate literal
+characters which would otherwise have special meaning in Markdown's
+formatting syntax. For example, if you wanted to surround a word with
+literal asterisks (instead of an HTML `<em>` tag), you add backslashes
+before the asterisks, like this:
+.Bd -literal -offset indent
+ \\*literal asterisks\\*
+.Ed
+.Pp
+.Nm
+provides backslash escapes for the following characters:
+.Bl -tag -compact
+.It \&\
+backslash
+.It \`
+backtick
+.It *
+asterisk
+.It _
+underscore
+.It \{\}
+curly braces
+.It []
+square brackets
+.It ()
+parentheses
+.It #
+hash mark
+.It +
+plus sign
+.It \-
+minus sign (hyphen)
+.It \.
+dot
+.It \!
+exclamation mark
+.El
+.Sh BUGS
+.Nm
+assumes that tabs are set to 4 spaces.
+.Sh AUTHOR
+John Gruber
+.%T http://daringfireball.net/
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr mkd-callbacks 3 ,
+.Xr mkd-functions 3 ,
+.Xr mkd-extensions 7 .
+.Pp
+.%T http://daringfireball.net/projects/markdown
+.br
+.%T http://docutils.sourceforge.net/mirror/setext.html
+.br
+.%T http://www.aaronsw.com/2002/atx/
+.br
+.%T http://textism.com/tools/textile/
+.br
+.%T http://docutils.sourceforge.net/rst.html
+.br
+.%T http://www.triptico.com/software/grutatxt.html
+.br
+.%T http://ettext.taint.org/doc/
diff --git a/markdown.c b/markdown.c
new file mode 100644
index 0000000..b239265
--- /dev/null
+++ b/markdown.c
@@ -0,0 +1,1215 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+#include "tags.h"
+
+typedef int (*stfu)(const void*,const void*);
+
+typedef ANCHOR(Paragraph) ParagraphRoot;
+
+/* case insensitive string sort for Footnote tags.
+ */
+int
+__mkd_footsort(Footnote *a, Footnote *b)
+{
+ int i;
+ char ac, bc;
+
+ if ( S(a->tag) != S(b->tag) )
+ return S(a->tag) - S(b->tag);
+
+ for ( i=0; i < S(a->tag); i++) {
+ ac = tolower(T(a->tag)[i]);
+ bc = tolower(T(b->tag)[i]);
+
+ if ( isspace(ac) && isspace(bc) )
+ continue;
+ if ( ac != bc )
+ return ac - bc;
+ }
+ return 0;
+}
+
+
+/* find the first blank character after position <i>
+ */
+static int
+nextblank(Line *t, int i)
+{
+ while ( (i < S(t->text)) && !isspace(T(t->text)[i]) )
+ ++i;
+ return i;
+}
+
+
+/* find the next nonblank character after position <i>
+ */
+static int
+nextnonblank(Line *t, int i)
+{
+ while ( (i < S(t->text)) && isspace(T(t->text)[i]) )
+ ++i;
+ return i;
+}
+
+
+/* find the first nonblank character on the Line.
+ */
+int
+mkd_firstnonblank(Line *p)
+{
+ return nextnonblank(p,0);
+}
+
+
+static int
+blankline(Line *p)
+{
+ return ! (p && (S(p->text) > p->dle) );
+}
+
+
+static Line *
+skipempty(Line *p)
+{
+ while ( p && (p->dle == S(p->text)) )
+ p = p->next;
+ return p;
+}
+
+
+void
+___mkd_tidy(Cstring *t)
+{
+ while ( S(*t) && isspace(T(*t)[S(*t)-1]) )
+ --S(*t);
+}
+
+
+static struct kw comment = { "!--", 3, 0 };
+
+static struct kw *
+isopentag(Line *p)
+{
+ int i=0, len;
+ char *line;
+
+ if ( !p ) return 0;
+
+ line = T(p->text);
+ len = S(p->text);
+
+ if ( len < 3 || line[0] != '<' )
+ return 0;
+
+ if ( line[1] == '!' && line[2] == '-' && line[3] == '-' )
+ /* comments need special case handling, because
+ * the !-- doesn't need to end in a whitespace
+ */
+ return &comment;
+
+ /* find how long the tag is so we can check to see if
+ * it's a block-level tag
+ */
+ for ( i=1; i < len && T(p->text)[i] != '>'
+ && T(p->text)[i] != '/'
+ && !isspace(T(p->text)[i]); ++i )
+ ;
+
+
+ return mkd_search_tags(T(p->text)+1, i-1);
+}
+
+
+typedef struct _flo {
+ Line *t;
+ int i;
+} FLO;
+
+#define floindex(x) (x.i)
+
+
+static int
+flogetc(FLO *f)
+{
+ if ( f && f->t ) {
+ if ( f->i < S(f->t->text) )
+ return T(f->t->text)[f->i++];
+ f->t = f->t->next;
+ f->i = 0;
+ return flogetc(f);
+ }
+ return EOF;
+}
+
+
+static void
+splitline(Line *t, int cutpoint)
+{
+ if ( t && (cutpoint < S(t->text)) ) {
+ Line *tmp = calloc(1, sizeof *tmp);
+
+ tmp->next = t->next;
+ t->next = tmp;
+
+ tmp->dle = t->dle;
+ SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint);
+ S(t->text) = cutpoint;
+ }
+}
+
+
+static Line *
+commentblock(Paragraph *p, int *unclosed)
+{
+ Line *t, *ret;
+ char *end;
+
+ for ( t = p->text; t ; t = t->next) {
+ if ( end = strstr(T(t->text), "-->") ) {
+ splitline(t, 3 + (end - T(t->text)) );
+ ret = t->next;
+ t->next = 0;
+ return ret;
+ }
+ }
+ *unclosed = 1;
+ return t;
+
+}
+
+
+static Line *
+htmlblock(Paragraph *p, struct kw *tag, int *unclosed)
+{
+ Line *ret;
+ FLO f = { p->text, 0 };
+ int c;
+ int i, closing, depth=0;
+
+ *unclosed = 0;
+
+ if ( tag == &comment )
+ return commentblock(p, unclosed);
+
+ if ( tag->selfclose ) {
+ ret = f.t->next;
+ f.t->next = 0;
+ return ret;
+ }
+
+ while ( (c = flogetc(&f)) != EOF ) {
+ if ( c == '<' ) {
+ /* tag? */
+ c = flogetc(&f);
+ if ( c == '!' ) { /* comment? */
+ if ( flogetc(&f) == '-' && flogetc(&f) == '-' ) {
+ /* yes */
+ while ( (c = flogetc(&f)) != EOF ) {
+ if ( c == '-' && flogetc(&f) == '-'
+ && flogetc(&f) == '>')
+ /* consumed whole comment */
+ break;
+ }
+ }
+ }
+ else {
+ if ( closing = (c == '/') ) c = flogetc(&f);
+
+ for ( i=0; i < tag->size; c=flogetc(&f) ) {
+ if ( tag->id[i++] != toupper(c) )
+ break;
+ }
+
+ if ( (i == tag->size) && !isalnum(c) ) {
+ depth = depth + (closing ? -1 : 1);
+ if ( depth == 0 ) {
+ while ( c != EOF && c != '>' ) {
+ /* consume trailing gunk in close tag */
+ c = flogetc(&f);
+ }
+ if ( c == EOF )
+ break;
+ if ( !f.t )
+ return 0;
+ splitline(f.t, floindex(f));
+ ret = f.t->next;
+ f.t->next = 0;
+ return ret;
+ }
+ }
+ }
+ }
+ }
+ *unclosed = 1;
+ return 0;
+}
+
+
+/* tables look like
+ * header|header{|header}
+ * ------|------{|......}
+ * {body lines}
+ */
+static int
+istable(Line *t)
+{
+ char *p;
+ Line *dashes = t->next;
+ int contains = 0; /* found character bits; 0x01 is |, 0x02 is - */
+
+ /* two lines, first must contain | */
+ if ( !(dashes && memchr(T(t->text), '|', S(t->text))) )
+ return 0;
+
+ /* second line must contain - or | and nothing
+ * else except for whitespace or :
+ */
+ for ( p = T(dashes->text)+S(dashes->text)-1; p >= T(dashes->text); --p)
+ if ( *p == '|' )
+ contains |= 0x01;
+ else if ( *p == '-' )
+ contains |= 0x02;
+ else if ( ! ((*p == ':') || isspace(*p)) )
+ return 0;
+
+ return (contains & 0x03);
+}
+
+
+/* footnotes look like ^<whitespace>{0,3}[stuff]: <content>$
+ */
+static int
+isfootnote(Line *t)
+{
+ int i;
+
+ if ( ( (i = t->dle) > 3) || (T(t->text)[i] != '[') )
+ return 0;
+
+ for ( ++i; i < S(t->text) ; ++i ) {
+ if ( T(t->text)[i] == '[' )
+ return 0;
+ else if ( T(t->text)[i] == ']' )
+ return ( T(t->text)[i+1] == ':' ) ;
+ }
+ return 0;
+}
+
+
+static int
+isquote(Line *t)
+{
+ int j;
+
+ for ( j=0; j < 4; j++ )
+ if ( T(t->text)[j] == '>' )
+ return 1;
+ else if ( !isspace(T(t->text)[j]) )
+ return 0;
+ return 0;
+}
+
+
+static int
+dashchar(char c)
+{
+ return (c == '*') || (c == '-') || (c == '_');
+}
+
+
+static int
+iscode(Line *t)
+{
+ return (t->dle >= 4);
+}
+
+
+static int
+ishr(Line *t)
+{
+ int i, count=0;
+ char dash = 0;
+ char c;
+
+ if ( iscode(t) ) return 0;
+
+ for ( i = 0; i < S(t->text); i++) {
+ c = T(t->text)[i];
+ if ( (dash == 0) && dashchar(c) )
+ dash = c;
+
+ if ( c == dash ) ++count;
+ else if ( !isspace(c) )
+ return 0;
+ }
+ return (count >= 3);
+}
+
+
+static int
+issetext(Line *t, int *htyp)
+{
+ int i;
+ /* then check for setext-style HEADER
+ * ======
+ */
+
+ if ( t->next ) {
+ char *q = T(t->next->text);
+ int last = S(t->next->text);
+
+ if ( (*q == '=') || (*q == '-') ) {
+ /* ignore trailing whitespace */
+ while ( (last > 1) && isspace(q[last-1]) )
+ --last;
+
+ for (i=1; i < last; i++)
+ if ( q[0] != q[i] )
+ return 0;
+ *htyp = SETEXT;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static int
+ishdr(Line *t, int *htyp)
+{
+ int i;
+
+
+ /* first check for etx-style ###HEADER###
+ */
+
+ /* leading run of `#`'s ?
+ */
+ for ( i=0; T(t->text)[i] == '#'; ++i)
+ ;
+
+ /* ANY leading `#`'s make this into an ETX header
+ */
+ if ( i && (i < S(t->text) || i > 1) ) {
+ *htyp = ETX;
+ return 1;
+ }
+
+ return issetext(t, htyp);
+}
+
+
+static Line*
+is_discount_dt(Line *t, int *clip)
+{
+#if USE_DISCOUNT_DL
+ if ( t && t->next
+ && (S(t->text) > 2)
+ && (t->dle == 0)
+ && (T(t->text)[0] == '=')
+ && (T(t->text)[S(t->text)-1] == '=') ) {
+ if ( t->next->dle >= 4 ) {
+ *clip = 4;
+ return t;
+ }
+ else
+ return is_discount_dt(t->next, clip);
+ }
+#endif
+ return 0;
+}
+
+
+static int
+is_extra_dd(Line *t)
+{
+ return (t->dle < 4) && (T(t->text)[t->dle] == ':')
+ && isspace(T(t->text)[t->dle+1]);
+}
+
+
+static Line*
+is_extra_dt(Line *t, int *clip)
+{
+#if USE_EXTRA_DL
+ int i;
+
+ if ( t && t->next && T(t->text)[0] != '='
+ && T(t->text)[S(t->text)-1] != '=') {
+ Line *x;
+
+ if ( iscode(t) || blankline(t) || ishdr(t,&i) || ishr(t) )
+ return 0;
+
+ if ( (x = skipempty(t->next)) && is_extra_dd(x) ) {
+ *clip = x->dle+2;
+ return t;
+ }
+
+ if ( x=is_extra_dt(t->next, clip) )
+ return x;
+ }
+#endif
+ return 0;
+}
+
+
+static Line*
+isdefinition(Line *t, int *clip, int *kind)
+{
+ Line *ret;
+
+ *kind = 1;
+ if ( ret = is_discount_dt(t,clip) )
+ return ret;
+
+ *kind=2;
+ return is_extra_dt(t,clip);
+}
+
+
+static int
+islist(Line *t, int *clip, DWORD flags, int *list_type)
+{
+ int i, j;
+ char *q;
+
+ if ( /*iscode(t) ||*/ blankline(t) || ishdr(t,&i) || ishr(t) )
+ return 0;
+
+ if ( !(flags & (MKD_NODLIST|MKD_STRICT)) && isdefinition(t,clip,list_type) )
+ return DL;
+
+ if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) {
+ i = nextnonblank(t, t->dle+1);
+ *clip = (i > 4) ? 4 : i;
+ *list_type = UL;
+ return AL;
+ }
+
+ if ( (j = nextblank(t,t->dle)) > t->dle ) {
+ if ( T(t->text)[j-1] == '.' ) {
+
+ if ( !(flags & (MKD_NOALPHALIST|MKD_STRICT))
+ && (j == t->dle + 2)
+ && isalpha(T(t->text)[t->dle]) ) {
+ j = nextnonblank(t,j);
+ *clip = (j > 4) ? 4 : j;
+ *list_type = AL;
+ return AL;
+ }
+
+ strtoul(T(t->text)+t->dle, &q, 10);
+ if ( (q > T(t->text)+t->dle) && (q == T(t->text) + (j-1)) ) {
+ j = nextnonblank(t,j);
+ *clip = (j > 4) ? 4 : j;
+ *list_type = OL;
+ return AL;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static Line *
+headerblock(Paragraph *pp, int htyp)
+{
+ Line *ret = 0;
+ Line *p = pp->text;
+ int i, j;
+
+ switch (htyp) {
+ case SETEXT:
+ /* p->text is header, p->next->text is -'s or ='s
+ */
+ pp->hnumber = (T(p->next->text)[0] == '=') ? 1 : 2;
+
+ ret = p->next->next;
+ ___mkd_freeLine(p->next);
+ p->next = 0;
+ break;
+
+ case ETX:
+ /* p->text is ###header###, so we need to trim off
+ * the leading and trailing `#`'s
+ */
+
+ for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1)
+ && (i < 6); i++)
+ ;
+
+ pp->hnumber = i;
+
+ while ( (i < S(p->text)) && isspace(T(p->text)[i]) )
+ ++i;
+
+ CLIP(p->text, 0, i);
+
+ for (j=S(p->text); (j > 1) && (T(p->text)[j-1] == '#'); --j)
+ ;
+
+ while ( j && isspace(T(p->text)[j-1]) )
+ --j;
+
+ S(p->text) = j;
+
+ ret = p->next;
+ p->next = 0;
+ break;
+ }
+ return ret;
+}
+
+
+static Line *
+codeblock(Paragraph *p)
+{
+ Line *t = p->text, *r;
+
+ for ( ; t; t = r ) {
+ CLIP(t->text,0,4);
+ t->dle = mkd_firstnonblank(t);
+
+ if ( !( (r = skipempty(t->next)) && iscode(r)) ) {
+ ___mkd_freeLineRange(t,r);
+ t->next = 0;
+ return r;
+ }
+ }
+ return t;
+}
+
+
+static int
+centered(Line *first, Line *last)
+{
+
+ if ( first&&last ) {
+ int len = S(last->text);
+
+ if ( (len > 2) && (strncmp(T(first->text), "->", 2) == 0)
+ && (strncmp(T(last->text)+len-2, "<-", 2) == 0) ) {
+ CLIP(first->text, 0, 2);
+ S(last->text) -= 2;
+ return CENTER;
+ }
+ }
+ return 0;
+}
+
+
+static int
+endoftextblock(Line *t, int toplevelblock, DWORD flags)
+{
+ int z;
+
+ if ( blankline(t)||isquote(t)||ishdr(t,&z)||ishr(t) )
+ return 1;
+
+ /* HORRIBLE STANDARDS KLUDGE: non-toplevel paragraphs absorb adjacent
+ * code blocks
+ */
+ if ( toplevelblock && iscode(t) )
+ return 1;
+
+ /* HORRIBLE STANDARDS KLUDGE: Toplevel paragraphs eat absorb adjacent
+ * list items, but sublevel blocks behave properly.
+ */
+ return toplevelblock ? 0 : islist(t,&z,flags, &z);
+}
+
+
+static Line *
+textblock(Paragraph *p, int toplevel, DWORD flags)
+{
+ Line *t, *next;
+
+ for ( t = p->text; t ; t = next ) {
+ if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) {
+ p->align = centered(p->text, t);
+ t->next = 0;
+ return next;
+ }
+ }
+ return t;
+}
+
+
+/* length of the id: or class: kind in a special div-not-quote block
+ */
+static int
+szmarkerclass(char *p)
+{
+ if ( strncasecmp(p, "id:", 3) == 0 )
+ return 3;
+ if ( strncasecmp(p, "class:", 6) == 0 )
+ return 6;
+ return 0;
+}
+
+
+/*
+ * check if the first line of a quoted block is the special div-not-quote
+ * marker %[kind:]name%
+ */
+static int
+isdivmarker(Line *p, int start, DWORD flags)
+{
+ char *s;
+ int len, i;
+
+ if ( flags & (MKD_NODIVQUOTE|MKD_STRICT) )
+ return 0;
+
+ len = S(p->text);
+ s = T(p->text);
+
+ if ( !(len && s[start] == '%' && s[len-1] == '%') ) return 0;
+
+ i = szmarkerclass(s+start+1)+start;
+ len -= start+1;
+
+ while ( ++i < len )
+ if ( !isalnum(s[i]) )
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * accumulate a blockquote.
+ *
+ * one sick horrible thing about blockquotes is that even though
+ * it just takes ^> to start a quote, following lines, if quoted,
+ * assume that the prefix is ``>''. This means that code needs
+ * to be indented *5* spaces from the leading '>', but *4* spaces
+ * from the start of the line. This does not appear to be
+ * documented in the reference implementation, but it's the
+ * way the markdown sample web form at Daring Fireball works.
+ */
+static Line *
+quoteblock(Paragraph *p, DWORD flags)
+{
+ Line *t, *q;
+ int qp;
+
+ for ( t = p->text; t ; t = q ) {
+ if ( isquote(t) ) {
+ /* clip leading spaces */
+ for (qp = 0; T(t->text)[qp] != '>'; qp ++)
+ /* assert: the first nonblank character on this line
+ * will be a >
+ */;
+ /* clip '>' */
+ qp++;
+ /* clip next space, if any */
+ if ( T(t->text)[qp] == ' ' )
+ qp++;
+ CLIP(t->text, 0, qp);
+ t->dle = mkd_firstnonblank(t);
+ }
+
+ q = skipempty(t->next);
+
+ if ( (q == 0) || ((q != t->next) && (!isquote(q) || isdivmarker(q,1,flags))) ) {
+ ___mkd_freeLineRange(t, q);
+ t = q;
+ break;
+ }
+ }
+ if ( isdivmarker(p->text,0,flags) ) {
+ char *prefix = "class";
+ int i;
+
+ q = p->text;
+ p->text = p->text->next;
+
+ if ( (i = szmarkerclass(1+T(q->text))) == 3 )
+ /* and this would be an "%id:" prefix */
+ prefix="id";
+
+ if ( p->ident = malloc(4+strlen(prefix)+S(q->text)) )
+ sprintf(p->ident, "%s=\"%.*s\"", prefix, S(q->text)-(i+2),
+ T(q->text)+(i+1) );
+
+ ___mkd_freeLine(q);
+ }
+ return t;
+}
+
+
+/*
+ * A table block starts with a table header (see istable()), and continues
+ * until EOF or a line that /doesn't/ contain a |.
+ */
+static Line *
+tableblock(Paragraph *p)
+{
+ Line *t, *q;
+
+ for ( t = p->text; t && (q = t->next); t = t->next ) {
+ if ( !memchr(T(q->text), '|', S(q->text)) ) {
+ t->next = 0;
+ return q;
+ }
+ }
+ return 0;
+}
+
+
+static Paragraph *Pp(ParagraphRoot *, Line *, int);
+static Paragraph *compile(Line *, int, MMIOT *);
+
+typedef int (*linefn)(Line *);
+
+
+/*
+ * pull in a list block. A list block starts with a list marker and
+ * runs until the next list marker, the next non-indented paragraph,
+ * or EOF. You do not have to indent nonblank lines after the list
+ * marker, but multiple paragraphs need to start with a 4-space indent.
+ */
+static Line *
+listitem(Paragraph *p, int indent, DWORD flags, linefn check)
+{
+ Line *t, *q;
+ int clip = indent;
+ int z;
+
+ for ( t = p->text; t ; t = q) {
+ CLIP(t->text, 0, clip);
+ t->dle = mkd_firstnonblank(t);
+
+ if ( (q = skipempty(t->next)) == 0 ) {
+ ___mkd_freeLineRange(t,q);
+ return 0;
+ }
+
+ /* after a blank line, the next block needs to start with a line
+ * that's indented 4(? -- reference implementation allows a 1
+ * character indent, but that has unfortunate side effects here)
+ * spaces, but after that the line doesn't need any indentation
+ */
+ if ( q != t->next ) {
+ if (q->dle < indent) {
+ q = t->next;
+ t->next = 0;
+ return q;
+ }
+ /* indent at least 2, and at most as
+ * as far as the initial line was indented. */
+ indent = clip ? clip : 2;
+ }
+
+ if ( (q->dle < indent) && (ishr(q) || islist(q,&z,flags,&z)
+ || (check && (*check)(q)))
+ && !issetext(q,&z) ) {
+ q = t->next;
+ t->next = 0;
+ return q;
+ }
+
+ clip = (q->dle > indent) ? indent : q->dle;
+ }
+ return t;
+}
+
+
+static Line *
+definition_block(Paragraph *top, int clip, MMIOT *f, int kind)
+{
+ ParagraphRoot d = { 0, 0 };
+ Paragraph *p;
+ Line *q = top->text, *text = 0, *labels;
+ int z, para;
+
+ while (( labels = q )) {
+
+ if ( (q = isdefinition(labels, &z, &kind)) == 0 )
+ break;
+
+ if ( (text = skipempty(q->next)) == 0 )
+ break;
+
+ if (( para = (text != q->next) ))
+ ___mkd_freeLineRange(q, text);
+
+ q->next = 0;
+ if ( kind == 1 /* discount dl */ )
+ for ( q = labels; q; q = q->next ) {
+ CLIP(q->text, 0, 1);
+ S(q->text)--;
+ }
+
+ dd_block:
+ p = Pp(&d, text, LISTITEM);
+
+ text = listitem(p, clip, f->flags, (kind==2) ? is_extra_dd : 0);
+ p->down = compile(p->text, 0, f);
+ p->text = labels; labels = 0;
+
+ if ( para && p->down ) p->down->align = PARA;
+
+ if ( (q = skipempty(text)) == 0 )
+ break;
+
+ if (( para = (q != text) )) {
+ Line anchor;
+
+ anchor.next = text;
+ ___mkd_freeLineRange(&anchor,q);
+ text = q;
+
+ }
+
+ if ( kind == 2 && is_extra_dd(q) )
+ goto dd_block;
+ }
+ top->text = 0;
+ top->down = T(d);
+ return text;
+}
+
+
+static Line *
+enumerated_block(Paragraph *top, int clip, MMIOT *f, int list_class)
+{
+ ParagraphRoot d = { 0, 0 };
+ Paragraph *p;
+ Line *q = top->text, *text;
+ int para = 0, z;
+
+ while (( text = q )) {
+
+ p = Pp(&d, text, LISTITEM);
+ text = listitem(p, clip, f->flags, 0);
+
+ p->down = compile(p->text, 0, f);
+ p->text = 0;
+
+ if ( para && p->down ) p->down->align = PARA;
+
+ if ( (q = skipempty(text)) == 0
+ || islist(q, &clip, f->flags, &z) != list_class )
+ break;
+
+ if ( para = (q != text) ) {
+ Line anchor;
+
+ anchor.next = text;
+ ___mkd_freeLineRange(&anchor, q);
+
+ if ( p->down ) p->down->align = PARA;
+ }
+ }
+ top->text = 0;
+ top->down = T(d);
+ return text;
+}
+
+
+static int
+tgood(char c)
+{
+ switch (c) {
+ case '\'':
+ case '"': return c;
+ case '(': return ')';
+ }
+ return 0;
+}
+
+
+/*
+ * add a new (image or link) footnote to the footnote table
+ */
+static Line*
+addfootnote(Line *p, MMIOT* f)
+{
+ int j, i;
+ int c;
+ Line *np = p->next;
+
+ Footnote *foot = &EXPAND(*f->footnotes);
+
+ CREATE(foot->tag);
+ CREATE(foot->link);
+ CREATE(foot->title);
+ foot->height = foot->width = 0;
+
+ for (j=i=p->dle+1; T(p->text)[j] != ']'; j++)
+ EXPAND(foot->tag) = T(p->text)[j];
+
+ EXPAND(foot->tag) = 0;
+ S(foot->tag)--;
+ j = nextnonblank(p, j+2);
+
+ while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
+ EXPAND(foot->link) = T(p->text)[j++];
+ EXPAND(foot->link) = 0;
+ S(foot->link)--;
+ j = nextnonblank(p,j);
+
+ if ( T(p->text)[j] == '=' ) {
+ sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height);
+ while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
+ ++j;
+ j = nextnonblank(p,j);
+ }
+
+
+ if ( (j >= S(p->text)) && np && np->dle && tgood(T(np->text)[np->dle]) ) {
+ ___mkd_freeLine(p);
+ p = np;
+ np = p->next;
+ j = p->dle;
+ }
+
+ if ( (c = tgood(T(p->text)[j])) ) {
+ /* Try to take the rest of the line as a comment; read to
+ * EOL, then shrink the string back to before the final
+ * quote.
+ */
+ ++j; /* skip leading quote */
+
+ while ( j < S(p->text) )
+ EXPAND(foot->title) = T(p->text)[j++];
+
+ while ( S(foot->title) && T(foot->title)[S(foot->title)-1] != c )
+ --S(foot->title);
+ if ( S(foot->title) ) /* skip trailing quote */
+ --S(foot->title);
+ EXPAND(foot->title) = 0;
+ --S(foot->title);
+ }
+
+ ___mkd_freeLine(p);
+ return np;
+}
+
+
+/*
+ * allocate a paragraph header, link it to the
+ * tail of the current document
+ */
+static Paragraph *
+Pp(ParagraphRoot *d, Line *ptr, int typ)
+{
+ Paragraph *ret = calloc(sizeof *ret, 1);
+
+ ret->text = ptr;
+ ret->typ = typ;
+
+ return ATTACH(*d, ret);
+}
+
+
+
+static Line*
+consume(Line *ptr, int *eaten)
+{
+ Line *next;
+ int blanks=0;
+
+ for (; ptr && blankline(ptr); ptr = next, blanks++ ) {
+ next = ptr->next;
+ ___mkd_freeLine(ptr);
+ }
+ if ( ptr ) *eaten = blanks;
+ return ptr;
+}
+
+
+/*
+ * top-level compilation; break the document into
+ * style, html, and source blocks with footnote links
+ * weeded out.
+ */
+static Paragraph *
+compile_document(Line *ptr, MMIOT *f)
+{
+ ParagraphRoot d = { 0, 0 };
+ ANCHOR(Line) source = { 0, 0 };
+ Paragraph *p = 0;
+ struct kw *tag;
+ int eaten, unclosed;
+
+ while ( ptr ) {
+ if ( !(f->flags & MKD_NOHTML) && (tag = isopentag(ptr)) ) {
+ /* If we encounter a html/style block, compile and save all
+ * of the cached source BEFORE processing the html/style.
+ */
+ if ( T(source) ) {
+ E(source)->next = 0;
+ p = Pp(&d, 0, SOURCE);
+ p->down = compile(T(source), 1, f);
+ T(source) = E(source) = 0;
+ }
+ p = Pp(&d, ptr, strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML);
+ ptr = htmlblock(p, tag, &unclosed);
+ if ( unclosed ) {
+ p->typ = SOURCE;
+ p->down = compile(p->text, 1, f);
+ p->text = 0;
+ }
+ }
+ else if ( isfootnote(ptr) ) {
+ /* footnotes, like cats, sleep anywhere; pull them
+ * out of the input stream and file them away for
+ * later processing
+ */
+ ptr = consume(addfootnote(ptr, f), &eaten);
+ }
+ else {
+ /* source; cache it up to wait for eof or the
+ * next html/style block
+ */
+ ATTACH(source,ptr);
+ ptr = ptr->next;
+ }
+ }
+ if ( T(source) ) {
+ /* if there's any cached source at EOF, compile
+ * it now.
+ */
+ E(source)->next = 0;
+ p = Pp(&d, 0, SOURCE);
+ p->down = compile(T(source), 1, f);
+ }
+ return T(d);
+}
+
+
+/*
+ * break a collection of markdown input into
+ * blocks of lists, code, html, and text to
+ * be marked up.
+ */
+static Paragraph *
+compile(Line *ptr, int toplevel, MMIOT *f)
+{
+ ParagraphRoot d = { 0, 0 };
+ Paragraph *p = 0;
+ Line *r;
+ int para = toplevel;
+ int blocks = 0;
+ int hdr_type, list_type, list_class, indent;
+
+ ptr = consume(ptr, ¶);
+
+ while ( ptr ) {
+ if ( iscode(ptr) ) {
+ p = Pp(&d, ptr, CODE);
+
+ if ( f->flags & MKD_1_COMPAT) {
+ /* HORRIBLE STANDARDS KLUDGE: the first line of every block
+ * has trailing whitespace trimmed off.
+ */
+ ___mkd_tidy(&p->text->text);
+ }
+
+ ptr = codeblock(p);
+ }
+ else if ( ishr(ptr) ) {
+ p = Pp(&d, 0, HR);
+ r = ptr;
+ ptr = ptr->next;
+ ___mkd_freeLine(r);
+ }
+ else if (( list_class = islist(ptr, &indent, f->flags, &list_type) )) {
+ if ( list_class == DL ) {
+ p = Pp(&d, ptr, DL);
+ ptr = definition_block(p, indent, f, list_type);
+ }
+ else {
+ p = Pp(&d, ptr, list_type);
+ ptr = enumerated_block(p, indent, f, list_class);
+ }
+ }
+ else if ( isquote(ptr) ) {
+ p = Pp(&d, ptr, QUOTE);
+ ptr = quoteblock(p, f->flags);
+ p->down = compile(p->text, 1, f);
+ p->text = 0;
+ }
+ else if ( ishdr(ptr, &hdr_type) ) {
+ p = Pp(&d, ptr, HDR);
+ ptr = headerblock(p, hdr_type);
+ }
+ else if ( istable(ptr) && !(f->flags & (MKD_STRICT|MKD_NOTABLES)) ) {
+ p = Pp(&d, ptr, TABLE);
+ ptr = tableblock(p);
+ }
+ else {
+ p = Pp(&d, ptr, MARKUP);
+ ptr = textblock(p, toplevel, f->flags);
+ }
+
+ if ( (para||toplevel) && !p->align )
+ p->align = PARA;
+
+ blocks++;
+ para = toplevel || (blocks > 1);
+ ptr = consume(ptr, ¶);
+
+ if ( para && !p->align )
+ p->align = PARA;
+
+ }
+ return T(d);
+}
+
+
+/*
+ * the guts of the markdown() function, ripped out so I can do
+ * debugging.
+ */
+
+/*
+ * prepare and compile `text`, returning a Paragraph tree.
+ */
+int
+mkd_compile(Document *doc, DWORD flags)
+{
+ if ( !doc )
+ return 0;
+
+ if ( doc->compiled )
+ return 1;
+
+ doc->compiled = 1;
+ memset(doc->ctx, 0, sizeof(MMIOT) );
+ doc->ctx->cb = &(doc->cb);
+ doc->ctx->flags = flags & USER_FLAGS;
+ CREATE(doc->ctx->in);
+ doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]);
+ CREATE(*doc->ctx->footnotes);
+
+ mkd_initialize();
+
+ doc->code = compile_document(T(doc->content), doc->ctx);
+ qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes),
+ sizeof T(*doc->ctx->footnotes)[0],
+ (stfu)__mkd_footsort);
+ memset(&doc->content, 0, sizeof doc->content);
+ return 1;
+}
+
diff --git a/markdown.h b/markdown.h
new file mode 100644
index 0000000..5a6d705
--- /dev/null
+++ b/markdown.h
@@ -0,0 +1,169 @@
+#ifndef _MARKDOWN_D
+#define _MARKDOWN_D
+
+#include "cstring.h"
+
+/* reference-style links (and images) are stored in an array
+ * of footnotes.
+ */
+typedef struct footnote {
+ Cstring tag; /* the tag for the reference link */
+ Cstring link; /* what this footnote points to */
+ Cstring title; /* what it's called (TITLE= attribute) */
+ int height, width; /* dimensions (for image link) */
+ int dealloc; /* deallocation needed? */
+} Footnote;
+
+/* each input line is read into a Line, which contains the line,
+ * the offset of the first non-space character [this assumes
+ * that all tabs will be expanded to spaces!], and a pointer to
+ * the next line.
+ */
+typedef struct line {
+ Cstring text;
+ struct line *next;
+ int dle;
+} Line;
+
+
+/* a paragraph is a collection of Lines, with links to the next paragraph
+ * and (if it's a QUOTE, UL, or OL) to the reparsed contents of this
+ * paragraph.
+ */
+typedef struct paragraph {
+ struct paragraph *next; /* next paragraph */
+ struct paragraph *down; /* recompiled contents of this paragraph */
+ struct line *text; /* all the text in this paragraph */
+ char *ident; /* %id% tag for QUOTE */
+ enum { WHITESPACE=0, CODE, QUOTE, MARKUP,
+ HTML, STYLE, DL, UL, OL, AL, LISTITEM,
+ HDR, HR, TABLE, SOURCE } typ;
+ enum { IMPLICIT=0, PARA, CENTER} align;
+ int hnumber; /* <Hn> for typ == HDR */
+} Paragraph;
+
+enum { ETX, SETEXT }; /* header types */
+
+
+typedef struct block {
+ enum { bTEXT, bSTAR, bUNDER } b_type;
+ int b_count;
+ char b_char;
+ Cstring b_text;
+ Cstring b_post;
+} block;
+
+typedef STRING(block) Qblock;
+
+
+typedef char* (*mkd_callback_t)(const char*, const int, void*);
+typedef void (*mkd_free_t)(char*, void*);
+
+typedef struct callback_data {
+ void *e_data; /* private data for callbacks */
+ mkd_callback_t e_url; /* url edit callback */
+ mkd_callback_t e_flags; /* extra href flags callback */
+ mkd_free_t e_free; /* edit/flags callback memory deallocator */
+} Callback_data;
+
+
+/* a magic markdown io thing holds all the data structures needed to
+ * do the backend processing of a markdown document
+ */
+typedef struct mmiot {
+ Cstring out;
+ Cstring in;
+ Qblock Q;
+ int isp;
+ STRING(Footnote) *footnotes;
+ DWORD flags;
+#define MKD_NOLINKS 0x00000001
+#define MKD_NOIMAGE 0x00000002
+#define MKD_NOPANTS 0x00000004
+#define MKD_NOHTML 0x00000008
+#define MKD_STRICT 0x00000010
+#define MKD_TAGTEXT 0x00000020
+#define MKD_NO_EXT 0x00000040
+#define MKD_CDATA 0x00000080
+#define MKD_NOSUPERSCRIPT 0x00000100
+#define MKD_NORELAXED 0x00000200
+#define MKD_NOTABLES 0x00000400
+#define MKD_NOSTRIKETHROUGH 0x00000800
+#define MKD_TOC 0x00001000
+#define MKD_1_COMPAT 0x00002000
+#define MKD_AUTOLINK 0x00004000
+#define MKD_SAFELINK 0x00008000
+#define MKD_NOHEADER 0x00010000
+#define MKD_TABSTOP 0x00020000
+#define MKD_NODIVQUOTE 0x00040000
+#define MKD_NOALPHALIST 0x00080000
+#define MKD_NODLIST 0x00100000
+#define IS_LABEL 0x08000000
+#define USER_FLAGS 0x0FFFFFFF
+#define INPUT_MASK (MKD_NOHEADER|MKD_TABSTOP)
+
+ Callback_data *cb;
+} MMIOT;
+
+
+/*
+ * the mkdio text input functions return a document structure,
+ * which contains a header (retrieved from the document if
+ * markdown was configured * with the * --enable-pandoc-header
+ * and the document begins with a pandoc-style header) and the
+ * root of the linked list of Lines.
+ */
+typedef struct document {
+ int magic; /* "I AM VALID" magic number */
+#define VALID_DOCUMENT 0x19600731
+ Line *title;
+ Line *author;
+ Line *date;
+ ANCHOR(Line) content; /* uncompiled text, not valid after compile() */
+ Paragraph *code; /* intermediate code generated by compile() */
+ int compiled; /* set after mkd_compile() */
+ int html; /* set after (internal) htmlify() */
+ int tabstop; /* for properly expanding tabs (ick) */
+ MMIOT *ctx; /* backend buffers, flags, and structures */
+ Callback_data cb; /* callback functions & private data */
+} Document;
+
+
+extern int mkd_firstnonblank(Line *);
+extern int mkd_compile(Document *, DWORD);
+extern int mkd_document(Document *, char **);
+extern int mkd_generatehtml(Document *, FILE *);
+extern int mkd_css(Document *, char **);
+extern int mkd_generatecss(Document *, FILE *);
+#define mkd_style mkd_generatecss
+extern int mkd_xml(char *, int , char **);
+extern int mkd_generatexml(char *, int, FILE *);
+extern void mkd_cleanup(Document *);
+extern int mkd_line(char *, int, char **, DWORD);
+extern int mkd_generateline(char *, int, FILE*, DWORD);
+#define mkd_text mkd_generateline
+extern void mkd_basename(Document*, char *);
+extern void mkd_string_to_anchor(char*,int, void(*)(int,void*), void*, int);
+
+extern Document *mkd_in(FILE *, DWORD);
+extern Document *mkd_string(char*,int, DWORD);
+
+extern void mkd_initialize();
+extern void mkd_shlib_destructor();
+
+/* internal resource handling functions.
+ */
+extern void ___mkd_freeLine(Line *);
+extern void ___mkd_freeLines(Line *);
+extern void ___mkd_freeParagraph(Paragraph *);
+extern void ___mkd_freefootnote(Footnote *);
+extern void ___mkd_freefootnotes(MMIOT *);
+extern void ___mkd_initmmiot(MMIOT *, void *);
+extern void ___mkd_freemmiot(MMIOT *, void *);
+extern void ___mkd_freeLineRange(Line *, Line *);
+extern void ___mkd_xml(char *, int, FILE *);
+extern void ___mkd_reparse(char *, int, int, MMIOT*);
+extern void ___mkd_emblock(MMIOT*);
+extern void ___mkd_tidy(Cstring *);
+
+#endif/*_MARKDOWN_D*/
diff --git a/mkd-callbacks.3 b/mkd-callbacks.3
new file mode 100644
index 0000000..be14c2a
--- /dev/null
+++ b/mkd-callbacks.3
@@ -0,0 +1,71 @@
+.\"
+.Dd January 18, 2008
+.Dt MKD_CALLBACKS 3
+.Os Mastodon
+.Sh NAME
+.Nm mkd_callbacks
+.Nd functions that modify link targets
+.Sh LIBRARY
+Markdown
+.Pq libmarkdown , -lmarkdown
+.Sh SYNOPSIS
+.Fd #include <mkdio.h>
+.Ft char*
+.Fn (*mkd_callback_t) "const char*" "const int" "void*"
+.Ft void
+.Fn (*mkd_free_t) "char *" "void*"
+.Ft void
+.Fn mkd_e_url "MMIOT *document" "mkd_callback_t edit"
+.Ft void
+.Fn mkd_e_flags "MMIOT *document" "mkd_callback_t edit"
+.Ft void
+.Fn mkd_e_free "MMIOT *document" "mkd_free_t dealloc"
+.Ft void
+.Fn mkd_e_data "MMIOT *document" "void *data"
+.Sh DESCRIPTION
+.Pp
+.Nm Discount
+provides a small set of data access functions to let a
+library user modify the targets given in a `[]' link, and to
+add additional flags to the generated link.
+.Pp
+The data access functions are passed a character pointer to
+the url being generated, the size of the url, and a data pointer
+pointing to a user data area (set by the
+.Fn mkd_e_data
+function.) After the callback function is called (either
+.Fn mkd_e_url
+or
+.Fn mkd_e_flags )
+the data freeing function (if supplied) is called and passed the
+character pointer and user data pointer.
+.Sh EXAMPLE
+The
+.Fn mkd_basename
+function (in the module basename.c) is implemented by means of
+mkd callbacks; it modifies urls that start with a `/' so that
+they begin with a user-supplied url base by allocating a new
+string and filling it with the base + the url. Discount plugs
+that url in in place of the original, then calls the basename
+free function (it only does this when
+.Fn mkd_e_url
+or
+.Fn mkd_e_flags
+returns nonzero) to deallocate this memory.
+.Pp
+Note that only one level of callbacks are supported; if you
+wish to do multiple callbacks, you need to write your own
+code to handle them all.
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr mkd-line 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 ,
+.Xr mmap 2 .
+.Pp
+basename.c
+.Pp
+http://daringfireball.net/projects/markdown/syntax
+.Sh BUGS
+Error handling is minimal at best.
diff --git a/mkd-extensions.7 b/mkd-extensions.7
new file mode 100644
index 0000000..d1d2600
--- /dev/null
+++ b/mkd-extensions.7
@@ -0,0 +1,190 @@
+.\"
+.Dd Dec 22, 2007
+.Dt MKD-EXTENSIONS 7
+.Os MASTODON
+.Sh NAME
+.Nm mkd-extensions
+.Nd Extensions to the Markdown text formatting syntax
+.Sh DESCRIPTION
+This version of markdown has been extended in a few ways by
+extending existing markup, creating new markup from scratch,
+and borrowing markup from other markup languages.
+.Ss Image dimensions
+Markdown embedded images have been extended to allow specifying
+the dimensions of the image by adding a new argument
+.Em =/height/x/width/
+to the link description.
+.Pp
+The new image syntax is
+.nf
+ ![alt text](image =/height/x/width/ "title")
+.fi
+.Ss pseudo-protocols
+Five pseudo-protocols have been added to links
+.Bl -tag -width XXXXX
+.It Ar id:
+The
+.Ar "alt text"
+is marked up and written to the output, wrapped with
+.Em "<a id=id>"
+and
+.Em "</a>" .
+.It Ar class:
+The
+.Ar "alt text"
+is marked up and written to the output, wrapped with
+.Em "<span class=class>"
+and
+.Em "</span>" .
+.It Ar raw:
+The
+.Ar title
+is written
+.Em -- with no further processing --
+to the output. The
+.Ar "alt text"
+is discarded.
+.It Ar abbr:
+The
+.Ar "alt text"
+is marked up and written to the output, wrapped with
+.Em "<abbr title=abbr>"
+and
+.Em "</abbr>" .
+.It Ar lang:
+The
+.Ar "alt text"
+s marked up and written to the output, wrapped with
+.Em "<span lang=lang>"
+and
+.Em "</span>" .
+.El
+.Ss Pandoc headers
+The markdown source document can have a 3-line
+.Xr Pandoc
+header in the format of
+.nf
+ % title
+ % author(s)
+ % date
+.fi
+which will be made available to the
+.Fn mkd_doc_title ,
+.Fn mkd_doc_author ,
+and
+.Fn mkd_doc_date
+functions.
+.Ss Definition lists
+A definition list item
+is defined as
+.nf
+=tag=
+ description
+.fi
+(that is a
+.Ar = ,
+followed by text, another
+.Ar = ,
+a newline, 4 spaces of intent, and then more text.)
+.Pp
+.Ss embedded stylesheets
+Stylesheets may be defined and modified in a
+.Em <style>
+block. A style block is parsed like any other
+block level html;
+.Em <style>
+starting on column 1, raw html (or, in this case, css) following
+it, and either ending with a
+.Em </style>
+at the end of the line or a
+.Em </style>
+at the beginning of a subsequent line.
+.Pp
+Be warned that style blocks work like footnote links -- no matter
+where you define them they are valid for the entire document.
+.Ss relaxed emphasis
+The rules for emphasis are changed so that a single
+.Ar _
+will
+.Em not
+count as a emphasis character if it's in the middle of a word.
+This is primarily for documenting code, if you don't wish to
+have to backquote all code references.
+.Ss alpha lists
+Alphabetic lists (like regular numeric lists, but with alphabetic
+items) are supported. So:
+.nf
+ a. this
+ b. is
+ c. an alphabetic
+ d. list
+.fi
+will produce:
+.nf
+ <ol type=a>
+ <li>this</li>
+ <li>is</li>
+ <li>an alphabetic</li>
+ <li>list</li>
+ </ol>
+.fi
+.Ss tables
+.Ar "PHP Markdown Extra"
+tables are supported; input of the form
+.nf
+ header|header
+ ------|------
+ text | text
+.fi
+will produce:
+.nf
+ <table>
+ <thead>
+ <tr>
+ <th>header</th>
+ <th>header</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>text</td>
+ <td>text</td>
+ </tr>
+ </tbody>
+ </table>
+.fi
+The dashed line can also contain
+.Em :
+characters for formatting; if a
+.Em :
+is at the start of a column, it tells
+.Nm discount
+to align the cell contents to the left; if it's at the end, it
+aligns right, and if there's one at the start and at the
+end, it centers.
+.Ss strikethrough
+A strikethrough syntax is supported in much the same way that
+.Ar `
+is used to define a section of code. If you enclose text with
+two or more tildes, such as
+.Em ~~erased text~~
+it will be written as
+.Em "<del>erased text</del>" .
+Like code sections, you may use as many
+.Ar ~
+as you want, but there must be as many starting tildes as closing
+tildes.
+.Sh AUTHOR
+David Parsons
+.%T http://www.pell.portland.or.us/~orc/
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr mkd-callbacks 3 ,
+.Xr mkd-functions 3 ,
+.Xr mkd-line 3 ,
+.Xr mkd-extensions 7 .
+.Pp
+.%T http://daringfireball.net/projects/markdown
+.Pp
+.%T http://michelf.com/projects/php-markdown
diff --git a/mkd-functions.3 b/mkd-functions.3
new file mode 100644
index 0000000..19dee7c
--- /dev/null
+++ b/mkd-functions.3
@@ -0,0 +1,182 @@
+.\"
+.Dd January 18, 2008
+.Dt MKD_FUNCTIONS 3
+.Os Mastodon
+.Sh NAME
+.Nm mkd_functions
+.Nd access and process Markdown documents.
+.Sh LIBRARY
+Markdown
+.Pq libmarkdown , -lmarkdown
+.Sh SYNOPSIS
+.Fd #include <mkdio.h>
+.Ft int
+.Fn mkd_compile "MMIOT *document" "int flags"
+.Ft int
+.Fn mkd_css "MMIOT *document" "char **doc"
+.Ft int
+.Fn mkd_generatecss "MMIOT *document" "FILE *output"
+.Ft int
+.Fn mkd_document "MMIOT *document" "char **doc"
+.Ft int
+.Fn mkd_generatehtml "MMIOT *document" "FILE *output"
+.Ft int
+.Fn mkd_xhtmlpage "MMIOT *document" "int flags" "FILE *output"
+.Ft int
+.Fn mkd_toc "MMIOT *document" "char **doc"
+.Ft void
+.Fn mkd_generatetoc "MMIOT *document" "FILE *output"
+.Ft void
+.Fn mkd_cleanup "MMIOT*"
+.Ft char*
+.Fn mkd_doc_title "MMIOT*"
+.Ft char*
+.Fn mkd_doc_author "MMIOT*"
+.Ft char*
+.Fn mkd_doc_date "MMIOT*"
+.Sh DESCRIPTION
+.Pp
+The
+.Nm markdown
+format supported in this implementation includes
+Pandoc-style header and inline
+.Ar \<style\>
+blocks, and the standard
+.Xr markdown 3
+functions do not provide access to
+the data provided by either of those extensions.
+These functions give you access to that data, plus
+they provide a finer-grained way of converting
+.Em Markdown
+documents into HTML.
+.Pp
+Given a
+.Ar MMIOT*
+generated by
+.Fn mkd_in
+or
+.Fn mkd_string ,
+.Fn mkd_compile
+compiles the document into
+.Em \<style\> ,
+.Em Pandoc ,
+and
+.Em html
+sections.
+.Pp
+Once compiled, the document can be examined and written
+by the
+.Fn mkd_css ,
+.Fn mkd_document ,
+.Fn mkd_generatecss ,
+.Fn mkd_generatehtml ,
+.Fn mkd_generatetoc ,
+.Fn mkd_toc ,
+.Fn mkd_xhtmlpage ,
+.Fn mkd_doc_title ,
+.Fn mkd_doc_author ,
+and
+.Fn mkd_doc_date
+functions.
+.Pp
+.Fn mkd_css
+allocates a string and populates it with any \<style\> sections
+provided in the document,
+.Fn mkd_generatecss
+writes any \<style\> sections to the output,
+.Fn mkd_document
+points
+.Ar text
+to the text of the document and returns the
+size of the document,
+.Fn mkd_generatehtml
+writes the rest of the document to the output,
+and
+.Fn mkd_doc_title ,
+.Fn mkd_doc_author ,
+.Fn mkd_doc_date
+are used to read the contents of a Pandoc header,
+if any.
+.Pp
+.Fn mkd_xhtmlpage
+writes a xhtml page containing the document. The regular set of
+flags can be passed.
+.Pp
+.Fn mkd_toc
+writes a document outline, in the form of a collection of nested
+lists with links to each header in the document, into a string
+allocated with
+.Fn malloc ,
+and returns the size.
+.Pp
+.Fn mkd_generatetoc
+is like
+.Fn mkd_toc ,
+except that it writes the document outline to the given
+.Pa FILE*
+argument.
+.Pp
+.Fn mkd_cleanup
+deletes a
+.Ar MMIOT*
+after processing is done.
+.Pp
+.Fn mkd_compile
+accepts the same flags that
+.Fn markdown
+and
+.Fn mkd_string
+do;
+.Bl -tag -width MKD_NOSTRIKETHROUGH -compact
+.It Ar MKD_NOIMAGE
+Do not process `![]' and
+remove
+.Em \<img\>
+tags from the output.
+.It Ar MKD_NOLINKS
+Do not process `[]' and remove
+.Em \<a\>
+tags from the output.
+.It Ar MKD_NOPANTS
+Do not do Smartypants-style mangling of quotes, dashes, or ellipses.
+.It Ar MKD_TAGTEXT
+Process the input as if you were inside a html tag. This means that
+no html tags will be generated, and
+.Fn mkd_compile
+will attempt to escape anything that might terribly confuse a
+web browser.
+.It Ar MKD_NO_EXT
+Do not process any markdown pseudo-protocols when
+handing
+.Ar [][]
+links.
+.It Ar MKD_NOHEADER
+Do not attempt to parse any Pandoc-style headers.
+.It Ar MKD_TOC
+Label all headers for use with the
+.Fn mkd_generatetoc
+function.
+.It Ar MKD_1_COMPAT
+MarkdownTest_1.0 compatibility flag; trim trailing spaces from the
+first line of code blocks and disable implicit reference links.
+.It Ar MKD_NOSTRIKETHROUGH
+Disable strikethrough support.
+.El
+.Sh RETURN VALUES
+The functions
+.Fn mkd_compile ,
+.Fn mkd_style ,
+and
+.Fn mkd_generatehtml
+return 0 on success, -1 on failure.
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr mkd-line 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 ,
+.Xr mmap 2 .
+.Pp
+http://daringfireball.net/projects/markdown/syntax
+.Sh BUGS
+Error handling is minimal at best.
diff --git a/mkd-line.3 b/mkd-line.3
new file mode 100644
index 0000000..7a35446
--- /dev/null
+++ b/mkd-line.3
@@ -0,0 +1,41 @@
+.\"
+.Dd January 18, 2008
+.Dt MKD_LINE 3
+.Os Mastodon
+.Sh NAME
+.Nm mkd_line
+.Nd do Markdown translation of small items
+.Sh LIBRARY
+Markdown
+.Pq libmarkdown , -lmarkdown
+.Sh SYNOPSIS
+.Fd #include <mkdio.h>
+.Ft int
+.Fn mkd_line "char *string" "int size" "char **doc" "int flags"
+.Ft int
+.Fn mkd_generateline "char *string" "int size" "FILE *output" "int flags"
+.Sh DESCRIPTION
+.Pp
+Occasionally one might want to do markdown translations on fragments of
+data, like the title of an weblog article, a date, or a simple signature
+line.
+.Nm mkd_line
+and
+.Nm mkd_generateline
+allow you to do markdown translations on small blocks of text.
+.Nm mkd_line
+allocates a buffer, then writes the translated text into that buffer,
+and
+.Nm mkd_generateline
+writes the output to the specified
+.Ar FILE* .
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 ,
+.Xr mmap 2 .
+.Pp
+http://daringfireball.net/projects/markdown/syntax
+.Sh BUGS
+Error handling is minimal at best.
diff --git a/mkd2html.1 b/mkd2html.1
new file mode 100644
index 0000000..44688d9
--- /dev/null
+++ b/mkd2html.1
@@ -0,0 +1,52 @@
+.\" %A%
+.\"
+.Dd January 10, 2010
+.Dt MKD2HTML 1
+.Os MASTODON
+.Sh NAME
+.Nm mkd2html
+.Nd markdown to html converter
+.Sh SYNOPSIS
+.Nm
+.Op Fl css Pa file
+.Op Fl header Pa string
+.Op Fl footer Pa string
+.Op Pa file
+.Sh DESCRIPTION
+.Nm
+utility parses a
+.Xr markdown 7 Ns -formatted
+.Pa textfile
+.Pq or stdin if not specified,
+and generates a web page. It
+reads
+.Ar file
+or
+.Ar file.text
+ and writes the result in
+.Ar file.html
+.Pq where file is the passed argument.
+.Pp
+.Nm
+is part of discount.
+.Sh OPTIONS
+.Bl -tag -width "-header string"
+.It Fl css Ar file
+Specifies a CSS file.
+.It Fl header Ar string
+Specifies a line to add to the <header> tag.
+.It Fl footer Ar string
+Specifies a line to add before the <\/body> tag.
+.El
+.Sh RETURN VALUES
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 .
+.Sh AUTHOR
+.An David Parsons
+.Pq Li orc at pell.chi.il.us
diff --git a/mkd2html.c b/mkd2html.c
new file mode 100644
index 0000000..ae15320
--- /dev/null
+++ b/mkd2html.c
@@ -0,0 +1,185 @@
+/*
+ * mkd2html: parse a markdown input file and generate a web page.
+ *
+ * usage: mkd2html [options] filename
+ * or mkd2html [options] < markdown > html
+ *
+ * options
+ * -css css-file
+ * -header line-to-add-to-<HEADER>
+ * -footer line-to-add-before-</BODY>
+ *
+ * example:
+ *
+ * mkd2html -cs /~orc/pages.css syntax
+ * ( read syntax OR syntax.text, write syntax.html )
+ */
+/*
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_BASENAME
+# ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+# else
+# include <unistd.h>
+# endif
+#endif
+#include <stdarg.h>
+
+#include "mkdio.h"
+#include "cstring.h"
+#include "amalloc.h"
+
+char *pgm = "mkd2html";
+
+#ifndef HAVE_BASENAME
+char *
+basename(char *path)
+{
+ char *p;
+
+ if (( p = strrchr(path, '/') ))
+ return 1+p;
+ return path;
+}
+#endif
+
+void
+fail(char *why, ...)
+{
+ va_list ptr;
+
+ va_start(ptr,why);
+ fprintf(stderr, "%s: ", pgm);
+ vfprintf(stderr, why, ptr);
+ fputc('\n', stderr);
+ va_end(ptr);
+ exit(1);
+}
+
+
+void
+main(argc, argv)
+char **argv;
+{
+ char *h;
+ char *source = 0, *dest = 0;
+ MMIOT *mmiot;
+ int i;
+ FILE *input, *output;
+ STRING(char*) css, headers, footers;
+
+
+ CREATE(css);
+ CREATE(headers);
+ CREATE(footers);
+ pgm = basename(argv[0]);
+
+ while ( argc > 2 ) {
+ if ( strcmp(argv[1], "-css") == 0 ) {
+ EXPAND(css) = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+ else if ( strcmp(argv[1], "-header") == 0 ) {
+ EXPAND(headers) = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+ else if ( strcmp(argv[1], "-footer") == 0 ) {
+ EXPAND(footers) = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+ }
+
+
+ if ( argc > 1 ) {
+ char *p, *dot;
+
+ source = malloc(strlen(argv[1]) + 6);
+ dest = malloc(strlen(argv[1]) + 6);
+
+ if ( !(source && dest) )
+ fail("out of memory allocating name buffers");
+
+ strcpy(source, argv[1]);
+ if (( p = strrchr(source, '/') ))
+ p = source;
+ else
+ ++p;
+
+ if ( (input = fopen(source, "r")) == 0 ) {
+ strcat(source, ".text");
+ if ( (input = fopen(source, "r")) == 0 )
+ fail("can't open either %s or %s", argv[1], source);
+ }
+ strcpy(dest, source);
+
+ if (( dot = strrchr(dest, '.') ))
+ *dot = 0;
+ strcat(dest, ".html");
+
+ if ( (output = fopen(dest, "w")) == 0 )
+ fail("can't write to %s", dest);
+ }
+ else {
+ input = stdin;
+ output = stdout;
+ }
+
+ if ( (mmiot = mkd_in(input, 0)) == 0 )
+ fail("can't read %s", source ? source : "stdin");
+
+ if ( !mkd_compile(mmiot, 0) )
+ fail("couldn't compile input");
+
+
+ h = mkd_doc_title(mmiot);
+
+ /* print a header */
+
+ fprintf(output,
+ "<!doctype html public \"-//W3C//DTD HTML 4.0 Transitional //EN\">\n"
+ "<html>\n"
+ "<head>\n"
+ " <meta name=\"GENERATOR\" content=\"mkd2html %s\">\n", markdown_version);
+
+ fprintf(output," <meta http-equiv=\"Content-Type\"\n"
+ " content=\"text/html; charset-us-ascii\">");
+
+ for ( i=0; i < S(css); i++ )
+ fprintf(output, " <link rel=\"stylesheet\"\n"
+ " type=\"text/css\"\n"
+ " href=\"%s\" />\n", T(css)[i]);
+
+ if ( h ) {
+ fprintf(output," <title>");
+ mkd_generateline(h, strlen(h), output, 0);
+ fprintf(output, "</title>\n");
+ }
+ for ( i=0; i < S(headers); i++ )
+ fprintf(output, " %s\n", T(headers)[i]);
+ fprintf(output, "</head>\n"
+ "<body>\n");
+
+ /* print the compiled body */
+
+ mkd_generatehtml(mmiot, output);
+
+ for ( i=0; i < S(footers); i++ )
+ fprintf(output, "%s\n", T(footers)[i]);
+
+ fprintf(output, "</body>\n"
+ "</html>\n");
+
+ mkd_cleanup(mmiot);
+ exit(0);
+}
diff --git a/mkdio.c b/mkdio.c
new file mode 100644
index 0000000..13784fd
--- /dev/null
+++ b/mkdio.c
@@ -0,0 +1,344 @@
+/*
+ * mkdio -- markdown front end input functions
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+typedef ANCHOR(Line) LineAnchor;
+
+/* create a new blank Document
+ */
+static Document*
+new_Document()
+{
+ Document *ret = calloc(sizeof(Document), 1);
+
+ if ( ret ) {
+ if (( ret->ctx = calloc(sizeof(MMIOT), 1) )) {
+ ret->magic = VALID_DOCUMENT;
+ return ret;
+ }
+ free(ret);
+ }
+ return 0;
+}
+
+
+/* add a line to the markdown input chain
+ */
+static void
+queue(Document* a, Cstring *line)
+{
+ Line *p = calloc(sizeof *p, 1);
+ unsigned char c;
+ int xp = 0;
+ int size = S(*line);
+ unsigned char *str = (unsigned char*)T(*line);
+
+ CREATE(p->text);
+ ATTACH(a->content, p);
+
+ while ( size-- ) {
+ if ( (c = *str++) == '\t' ) {
+ /* expand tabs into ->tabstop spaces. We use ->tabstop
+ * because the ENTIRE FREAKING COMPUTER WORLD uses editors
+ * that don't do ^T/^D, but instead use tabs for indentation,
+ * and, of course, set their tabs down to 4 spaces
+ */
+ do {
+ EXPAND(p->text) = ' ';
+ } while ( ++xp % a->tabstop );
+ }
+ else if ( c >= ' ' ) {
+ EXPAND(p->text) = c;
+ ++xp;
+ }
+ }
+ EXPAND(p->text) = 0;
+ S(p->text)--;
+ p->dle = mkd_firstnonblank(p);
+}
+
+
+/* trim leading blanks from a header line
+ */
+static void
+header_dle(Line *p)
+{
+ CLIP(p->text, 0, 1);
+ p->dle = mkd_firstnonblank(p);
+}
+
+
+/* build a Document from any old input.
+ */
+typedef int (*getc_func)(void*);
+
+Document *
+populate(getc_func getc, void* ctx, int flags)
+{
+ Cstring line;
+ Document *a = new_Document();
+ int c;
+ int pandoc = 0;
+
+ if ( !a ) return 0;
+
+ a->tabstop = (flags & MKD_TABSTOP) ? 4 : TABSTOP;
+
+ CREATE(line);
+
+ while ( (c = (*getc)(ctx)) != EOF ) {
+ if ( c == '\n' ) {
+ if ( pandoc != EOF && pandoc < 3 ) {
+ if ( S(line) && (T(line)[0] == '%') )
+ pandoc++;
+ else
+ pandoc = EOF;
+ }
+ queue(a, &line);
+ S(line) = 0;
+ }
+ else if ( isprint(c) || isspace(c) || (c & 0x80) )
+ EXPAND(line) = c;
+ }
+
+ if ( S(line) )
+ queue(a, &line);
+
+ DELETE(line);
+
+ if ( (pandoc == 3) && !(flags & (MKD_NOHEADER|MKD_STRICT)) ) {
+ /* the first three lines started with %, so we have a header.
+ * clip the first three lines out of content and hang them
+ * off header.
+ */
+ Line *headers = T(a->content);
+
+ a->title = headers; header_dle(a->title);
+ a->author= headers->next; header_dle(a->author);
+ a->date = headers->next->next; header_dle(a->date);
+
+ T(a->content) = headers->next->next->next;
+ }
+
+ return a;
+}
+
+
+/* convert a file into a linked list
+ */
+Document *
+mkd_in(FILE *f, DWORD flags)
+{
+ return populate((getc_func)fgetc, f, flags & INPUT_MASK);
+}
+
+
+/* return a single character out of a buffer
+ */
+struct string_ctx {
+ char *data; /* the unread data */
+ int size; /* and how much is there? */
+} ;
+
+
+static int
+strget(struct string_ctx *in)
+{
+ if ( !in->size ) return EOF;
+
+ --(in->size);
+
+ return *(in->data)++;
+}
+
+
+/* convert a block of text into a linked list
+ */
+Document *
+mkd_string(char *buf, int len, DWORD flags)
+{
+ struct string_ctx about;
+
+ about.data = buf;
+ about.size = len;
+
+ return populate((getc_func)strget, &about, flags & INPUT_MASK);
+}
+
+
+/* write the html to a file (xmlified if necessary)
+ */
+int
+mkd_generatehtml(Document *p, FILE *output)
+{
+ char *doc;
+ int szdoc;
+
+ if ( (szdoc = mkd_document(p, &doc)) != EOF ) {
+ if ( p->ctx->flags & MKD_CDATA )
+ mkd_generatexml(doc, szdoc, output);
+ else
+ fwrite(doc, szdoc, 1, output);
+ putc('\n', output);
+ return 0;
+ }
+ return -1;
+}
+
+
+/* convert some markdown text to html
+ */
+int
+markdown(Document *document, FILE *out, int flags)
+{
+ if ( mkd_compile(document, flags) ) {
+ mkd_generatehtml(document, out);
+ mkd_cleanup(document);
+ return 0;
+ }
+ return -1;
+}
+
+
+/* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
+ */
+void
+mkd_string_to_anchor(char *s, int len, void(*outchar)(int,void*),
+ void *out, int labelformat)
+{
+ unsigned char c;
+
+ int i, size;
+ char *line;
+
+ size = mkd_line(s, len, &line, IS_LABEL);
+
+ if ( labelformat && size && !isalpha(line[0]) )
+ (*outchar)('L',out);
+ for ( i=0; i < size ; i++ ) {
+ c = line[i];
+ if ( labelformat ) {
+ if ( isalnum(c) || (c == '_') || (c == ':') || (c == '-') || (c == '.' ) )
+ (*outchar)(c, out);
+ else
+ (*outchar)('.',out);
+ }
+ else
+ (*outchar)(c,out);
+ }
+
+ if (line)
+ free(line);
+}
+
+
+/* ___mkd_reparse() a line
+ */
+static void
+mkd_parse_line(char *bfr, int size, MMIOT *f, int flags)
+{
+ ___mkd_initmmiot(f, 0);
+ f->flags = flags & USER_FLAGS;
+ ___mkd_reparse(bfr, size, 0, f);
+ ___mkd_emblock(f);
+}
+
+
+/* ___mkd_reparse() a line, returning it in malloc()ed memory
+ */
+int
+mkd_line(char *bfr, int size, char **res, DWORD flags)
+{
+ MMIOT f;
+ int len;
+
+ mkd_parse_line(bfr, size, &f, flags);
+
+ if ( len = S(f.out) ) {
+ /* kludge alert; we know that T(f.out) is malloced memory,
+ * so we can just steal it away. This is awful -- there
+ * should be an opaque method that transparently moves
+ * the pointer out of the embedded Cstring.
+ */
+ EXPAND(f.out) = 0;
+ *res = T(f.out);
+ T(f.out) = 0;
+ S(f.out) = ALLOCATED(f.out) = 0;
+ }
+ else {
+ *res = 0;
+ len = EOF;
+ }
+ ___mkd_freemmiot(&f, 0);
+ return len;
+}
+
+
+/* ___mkd_reparse() a line, writing it to a FILE
+ */
+int
+mkd_generateline(char *bfr, int size, FILE *output, DWORD flags)
+{
+ MMIOT f;
+
+ mkd_parse_line(bfr, size, &f, flags);
+ if ( flags & MKD_CDATA )
+ mkd_generatexml(T(f.out), S(f.out), output);
+ else
+ fwrite(T(f.out), S(f.out), 1, output);
+
+ ___mkd_freemmiot(&f, 0);
+ return 0;
+}
+
+
+/* set the url display callback
+ */
+void
+mkd_e_url(Document *f, mkd_callback_t edit)
+{
+ if ( f )
+ f->cb.e_url = edit;
+}
+
+
+/* set the url options callback
+ */
+void
+mkd_e_flags(Document *f, mkd_callback_t edit)
+{
+ if ( f )
+ f->cb.e_flags = edit;
+}
+
+
+/* set the url display/options deallocator
+ */
+void
+mkd_e_free(Document *f, mkd_free_t dealloc)
+{
+ if ( f )
+ f->cb.e_free = dealloc;
+}
+
+
+/* set the url display/options context data field
+ */
+void
+mkd_e_data(Document *f, void *data)
+{
+ if ( f )
+ f->cb.e_data = data;
+}
diff --git a/mkdio.h.in b/mkdio.h.in
new file mode 100644
index 0000000..7ab658c
--- /dev/null
+++ b/mkdio.h.in
@@ -0,0 +1,100 @@
+#ifndef _MKDIO_D
+#define _MKDIO_D
+
+#include <stdio.h>
+
+typedef void MMIOT;
+
+typedef @DWORD@ mkd_flag_t;
+
+/* line builder for markdown()
+ */
+MMIOT *mkd_in(FILE*,mkd_flag_t); /* assemble input from a file */
+MMIOT *mkd_string(char*,int,mkd_flag_t); /* assemble input from a buffer */
+
+void mkd_basename(MMIOT*,char*);
+
+void mkd_initialize();
+void mkd_shlib_destructor();
+
+/* compilation, debugging, cleanup
+ */
+int mkd_compile(MMIOT*, mkd_flag_t);
+int mkd_cleanup(MMIOT*);
+
+/* markup functions
+ */
+int mkd_dump(MMIOT*, FILE*, int, char*);
+int markdown(MMIOT*, FILE*, mkd_flag_t);
+int mkd_line(char *, int, char **, mkd_flag_t);
+void mkd_string_to_anchor(char *, int, int (*)(int,void*), void*, int);
+int mkd_xhtmlpage(MMIOT*,int,FILE*);
+
+/* header block access
+ */
+char* mkd_doc_title(MMIOT*);
+char* mkd_doc_author(MMIOT*);
+char* mkd_doc_date(MMIOT*);
+
+/* compiled data access
+ */
+int mkd_document(MMIOT*, char**);
+int mkd_toc(MMIOT*, char**);
+int mkd_css(MMIOT*, char **);
+int mkd_xml(char *, int, char **);
+
+/* write-to-file functions
+ */
+int mkd_generatehtml(MMIOT*,FILE*);
+int mkd_generatetoc(MMIOT*,FILE*);
+int mkd_generatexml(char *, int,FILE*);
+int mkd_generatecss(MMIOT*,FILE*);
+#define mkd_style mkd_generatecss
+int mkd_generateline(char *, int, FILE*, mkd_flag_t);
+#define mkd_text mkd_generateline
+
+/* url generator callbacks
+ */
+typedef char * (*mkd_callback_t)(const char*, const int, void*);
+typedef void (*mkd_free_t)(char*, void*);
+
+void mkd_e_url(void *, mkd_callback_t);
+void mkd_e_flags(void *, mkd_callback_t);
+void mkd_e_free(void *, mkd_free_t );
+void mkd_e_data(void *, void *);
+
+/* version#.
+ */
+extern char markdown_version[];
+
+/* special flags for markdown() and mkd_text()
+ */
+#define MKD_NOLINKS 0x00000001 /* don't do link processing, block <a> tags */
+#define MKD_NOIMAGE 0x00000002 /* don't do image processing, block <img> */
+#define MKD_NOPANTS 0x00000004 /* don't run smartypants() */
+#define MKD_NOHTML 0x00000008 /* don't allow raw html through AT ALL */
+#define MKD_STRICT 0x00000010 /* disable SUPERSCRIPT, RELAXED_EMPHASIS */
+#define MKD_TAGTEXT 0x00000020 /* process text inside an html tag; no
+ * <em>, no <bold>, no html or [] expansion */
+#define MKD_NO_EXT 0x00000040 /* don't allow pseudo-protocols */
+#define MKD_CDATA 0x00000080 /* generate code for xml ![CDATA[...]] */
+#define MKD_NOSUPERSCRIPT 0x00000100 /* no A^B */
+#define MKD_NORELAXED 0x00000200 /* emphasis happens /everywhere/ */
+#define MKD_NOTABLES 0x00000400 /* disallow tables */
+#define MKD_NOSTRIKETHROUGH 0x00000800 /* forbid ~~strikethrough~~ */
+#define MKD_TOC 0x00001000 /* do table-of-contents processing */
+#define MKD_1_COMPAT 0x00002000 /* compatibility with MarkdownTest_1.0 */
+#define MKD_AUTOLINK 0x00004000 /* make http://foo.com link even without <>s */
+#define MKD_SAFELINK 0x00008000 /* paranoid check for link protocol */
+#define MKD_NOHEADER 0x00010000 /* don't process header blocks */
+#define MKD_TABSTOP 0x00020000 /* expand tabs to 4 spaces */
+#define MKD_NODIVQUOTE 0x00040000 /* forbid >%class% blocks */
+#define MKD_NOALPHALIST 0x00080000 /* forbid alphabetic lists */
+#define MKD_NODLIST 0x00100000 /* forbid definition lists */
+#define MKD_EMBED MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT
+
+/* special flags for mkd_in() and mkd_string()
+ */
+
+
+#endif/*_MKDIO_D*/
diff --git a/resource.c b/resource.c
new file mode 100644
index 0000000..1dee8e3
--- /dev/null
+++ b/resource.c
@@ -0,0 +1,157 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "config.h"
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+/* free a (single) line
+ */
+void
+___mkd_freeLine(Line *ptr)
+{
+ DELETE(ptr->text);
+ free(ptr);
+}
+
+
+/* free a list of lines
+ */
+void
+___mkd_freeLines(Line *p)
+{
+ if (p->next)
+ ___mkd_freeLines(p->next);
+ ___mkd_freeLine(p);
+}
+
+
+/* bye bye paragraph.
+ */
+void
+___mkd_freeParagraph(Paragraph *p)
+{
+ if (p->next)
+ ___mkd_freeParagraph(p->next);
+ if (p->down)
+ ___mkd_freeParagraph(p->down);
+ if (p->text)
+ ___mkd_freeLines(p->text);
+ if (p->ident)
+ free(p->ident);
+ free(p);
+}
+
+
+/* bye bye footnote.
+ */
+void
+___mkd_freefootnote(Footnote *f)
+{
+ DELETE(f->tag);
+ DELETE(f->link);
+ DELETE(f->title);
+}
+
+
+/* bye bye footnotes.
+ */
+void
+___mkd_freefootnotes(MMIOT *f)
+{
+ int i;
+
+ if ( f->footnotes ) {
+ for (i=0; i < S(*f->footnotes); i++)
+ ___mkd_freefootnote( &T(*f->footnotes)[i] );
+ DELETE(*f->footnotes);
+ free(f->footnotes);
+ }
+}
+
+
+/* initialize a new MMIOT
+ */
+void
+___mkd_initmmiot(MMIOT *f, void *footnotes)
+{
+ if ( f ) {
+ memset(f, 0, sizeof *f);
+ CREATE(f->in);
+ CREATE(f->out);
+ CREATE(f->Q);
+ if ( footnotes )
+ f->footnotes = footnotes;
+ else {
+ f->footnotes = malloc(sizeof f->footnotes[0]);
+ CREATE(*f->footnotes);
+ }
+ }
+}
+
+
+/* free the contents of a MMIOT, but leave the object alone.
+ */
+void
+___mkd_freemmiot(MMIOT *f, void *footnotes)
+{
+ if ( f ) {
+ DELETE(f->in);
+ DELETE(f->out);
+ DELETE(f->Q);
+ if ( f->footnotes != footnotes )
+ ___mkd_freefootnotes(f);
+ memset(f, 0, sizeof *f);
+ }
+}
+
+
+/* free lines up to an barrier.
+ */
+void
+___mkd_freeLineRange(Line *anchor, Line *stop)
+{
+ Line *r = anchor->next;
+
+ if ( r != stop ) {
+ while ( r && (r->next != stop) )
+ r = r->next;
+ if ( r ) r->next = 0;
+ ___mkd_freeLines(anchor->next);
+ }
+ anchor->next = 0;
+}
+
+
+/* clean up everything allocated in __mkd_compile()
+ */
+void
+mkd_cleanup(Document *doc)
+{
+ if ( doc && (doc->magic == VALID_DOCUMENT) ) {
+ if ( doc->ctx ) {
+ ___mkd_freemmiot(doc->ctx, 0);
+ free(doc->ctx);
+ }
+
+ if ( doc->code) ___mkd_freeParagraph(doc->code);
+ if ( doc->title) ___mkd_freeLine(doc->title);
+ if ( doc->author) ___mkd_freeLine(doc->author);
+ if ( doc->date) ___mkd_freeLine(doc->date);
+ if ( T(doc->content) ) ___mkd_freeLines(T(doc->content));
+ memset(doc, 0, sizeof doc[0]);
+ free(doc);
+ }
+}
diff --git a/setup.c b/setup.c
new file mode 100644
index 0000000..79f47aa
--- /dev/null
+++ b/setup.c
@@ -0,0 +1,47 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+#include "tags.h"
+
+static int need_to_setup = 1;
+static int need_to_initrng = 1;
+
+void
+mkd_initialize()
+{
+
+ if ( need_to_initrng ) {
+ need_to_initrng = 0;
+ INITRNG(time(0));
+ }
+ if ( need_to_setup ) {
+ need_to_setup = 0;
+ mkd_prepare_tags();
+ }
+}
+
+
+void
+mkd_shlib_destructor()
+{
+ if ( !need_to_setup ) {
+ need_to_setup = 1;
+ mkd_deallocate_tags();
+ }
+}
+
diff --git a/tags.c b/tags.c
new file mode 100644
index 0000000..00affc1
--- /dev/null
+++ b/tags.c
@@ -0,0 +1,123 @@
+/* block-level tags for passing html blocks through the blender
+ */
+#define __WITHOUT_AMALLOC 1
+#include "cstring.h"
+#include "tags.h"
+
+STRING(struct kw) blocktags;
+
+
+/* define a html block tag
+ */
+void
+mkd_define_tag(char *id, int selfclose)
+{
+ struct kw *p = &EXPAND(blocktags);
+
+ p->id = id;
+ p->size = strlen(id);
+ p->selfclose = selfclose;
+}
+
+
+/* case insensitive string sort (for qsort() and bsearch() of block tags)
+ */
+static int
+casort(struct kw *a, struct kw *b)
+{
+ if ( a->size != b->size )
+ return a->size - b->size;
+ return strncasecmp(a->id, b->id, b->size);
+}
+
+
+/* stupid cast to make gcc shut up about the function types being
+ * passed into qsort() and bsearch()
+ */
+typedef int (*stfu)(const void*,const void*);
+
+
+/* sort the list of html block tags for later searching
+ */
+void
+mkd_sort_tags()
+{
+ qsort(T(blocktags), S(blocktags), sizeof(struct kw), (stfu)casort);
+}
+
+
+
+/* look for a token in the html block tag list
+ */
+struct kw*
+mkd_search_tags(char *pat, int len)
+{
+ struct kw key;
+
+ key.id = pat;
+ key.size = len;
+
+ return bsearch(&key, T(blocktags), S(blocktags), sizeof key, (stfu)casort);
+}
+
+
+static int populated = 0;
+
+
+/* load in the standard collection of html tags that markdown supports
+ */
+void
+mkd_prepare_tags()
+{
+
+#define KW(x) mkd_define_tag(x, 0)
+#define SC(x) mkd_define_tag(x, 1)
+
+ if ( populated ) return;
+ populated = 1;
+
+ KW("STYLE");
+ KW("SCRIPT");
+ KW("ADDRESS");
+ KW("BDO");
+ KW("BLOCKQUOTE");
+ KW("CENTER");
+ KW("DFN");
+ KW("DIV");
+ KW("OBJECT");
+ KW("H1");
+ KW("H2");
+ KW("H3");
+ KW("H4");
+ KW("H5");
+ KW("H6");
+ KW("LISTING");
+ KW("NOBR");
+ KW("UL");
+ KW("P");
+ KW("OL");
+ KW("DL");
+ KW("PLAINTEXT");
+ KW("PRE");
+ KW("TABLE");
+ KW("WBR");
+ KW("XMP");
+ SC("HR");
+ SC("BR");
+ KW("IFRAME");
+ KW("MAP");
+
+ mkd_sort_tags();
+} /* mkd_prepare_tags */
+
+
+/* destroy the blocktags list (for shared libraries)
+ */
+void
+mkd_deallocate_tags()
+{
+ if ( S(blocktags) > 0 ) {
+ populated = 0;
+ DELETE(blocktags);
+ }
+} /* mkd_deallocate_tags */
diff --git a/tags.h b/tags.h
new file mode 100644
index 0000000..72d7647
--- /dev/null
+++ b/tags.h
@@ -0,0 +1,19 @@
+/* block-level tags for passing html blocks through the blender
+ */
+#ifndef _TAGS_D
+#define _TAGS_D
+
+struct kw {
+ char *id;
+ int size;
+ int selfclose;
+} ;
+
+
+struct kw* mkd_search_tags(char *, int);
+void mkd_prepare_tags();
+void mkd_deallocate_tags();
+void mkd_sort_tags();
+void mkd_define_tag(char *, int);
+
+#endif
diff --git a/tests/autolink.t b/tests/autolink.t
new file mode 100644
index 0000000..b19dcb3
--- /dev/null
+++ b/tests/autolink.t
@@ -0,0 +1,27 @@
+. tests/functions.sh
+
+title 'Reddit-style automatic links'
+rc=0
+
+try -fautolink 'single link' \
+ 'http://www.pell.portland.or.us/~orc/Code/discount' \
+ '<p><a href="http://www.pell.portland.or.us/~orc/Code/discount">http://www.pell.portland.or.us/~orc/Code/discount</a></p>'
+
+try -fautolink '[!](http://a.com "http://b.com")' \
+ '[!](http://a.com "http://b.com")' \
+ '<p><a href="http://a.com" title="http://b.com">!</a></p>'
+
+try -fautolink 'link surrounded by text' \
+ 'here http://it is?' \
+ '<p>here <a href="http://it">http://it</a> is?</p>'
+
+try -fautolink 'naked @' '@' '<p>@</p>'
+
+try -fautolink 'parenthesised (url)' \
+ '(http://here)' \
+ '<p>(<a href="http://here">http://here</a>)</p>'
+
+try -fautolink 'token with trailing @' 'orc@' '<p>orc@</p>'
+
+summary $0
+exit $rc
diff --git a/tests/automatic.t b/tests/automatic.t
new file mode 100644
index 0000000..c0df571
--- /dev/null
+++ b/tests/automatic.t
@@ -0,0 +1,27 @@
+. tests/functions.sh
+
+title "automatic links"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'http url' '<http://here>' '<p><a href="http://here">http://here</a></p>'
+try 'ftp url' '<ftp://here>' '<p><a href="ftp://here">ftp://here</a></p>'
+try 'http://foo/bar' '<http://foo/bar>' '<p><a href="http://foo/bar">http://foo/bar</a></p>'
+try 'http:/foo/bar' '<http:/foo/bar>' '<p><a href="http:/foo/bar">http:/foo/bar</a></p>'
+try 'http:foo/bar' '<http:foo/bar>' '<p><a href="http:foo/bar">http:foo/bar</a></p>'
+try '</foo/bar>' '</foo/bar>' '<p></foo/bar></p>'
+match '<orc at pell.portland.or.us>' '<orc at pell.portland.or.us>' '<a href='
+match '<orc at pell.com.>' '<orc at pell.com.>' '<a href='
+try 'invalid <orc@>' '<orc@>' '<p><orc@></p>'
+try 'invalid <@pell>' '<@pell>' '<p><@pell></p>'
+try 'invalid <orc at pell>' '<orc at pell>' '<p><orc at pell></p>'
+try 'invalid <orc at .pell>' '<orc at .pell>' '<p><orc at .pell></p>'
+try 'invalid <orc at pell.>' '<orc at pell.>' '<p><orc at pell.></p>'
+match '<mailto:orc at pell>' '<mailto:orc at pell>' '<a href='
+match '<mailto:orc at pell.com>' '<mailto:orc at pell.com>' '<a href='
+match '<mailto:orc@>' '<mailto:orc@>' '<a href='
+match '<mailto:@pell>' '<mailto:@pell>' '<a href='
+
+summary $0
+exit $rc
diff --git a/tests/backslash.t b/tests/backslash.t
new file mode 100644
index 0000000..f3cdf2e
--- /dev/null
+++ b/tests/backslash.t
@@ -0,0 +1,16 @@
+. tests/functions.sh
+
+title "backslash escapes"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'backslashes in []()' '[foo](http://\this\is\.a\test\(here\))' \
+'<p><a href="http://\this\is.a\test(here)">foo</a></p>'
+
+try -fautolink 'autolink url with trailing \' \
+ 'http://a.com/\' \
+ '<p><a href="http://a.com/\">http://a.com/\</a></p>'
+
+summary $0
+exit $rc
diff --git a/tests/callbacks.t b/tests/callbacks.t
new file mode 100644
index 0000000..28217bb
--- /dev/null
+++ b/tests/callbacks.t
@@ -0,0 +1,17 @@
+. tests/functions.sh
+
+title "callbacks"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try -bZZZ 'url modification' \
+'[a](/b)' \
+'<p><a href="ZZZ/b">a</a></p>'
+
+try -EZZZ 'additional flags' \
+'[a](/b)' \
+'<p><a href="/b" ZZZ>a</a></p>'
+
+summary $0
+exit $rc
diff --git a/tests/chrome.text b/tests/chrome.text
new file mode 100644
index 0000000..d97b47b
--- /dev/null
+++ b/tests/chrome.text
@@ -0,0 +1,13 @@
+->###chrome with my markdown###<-
+
+1. `(c)` -> `©` (c)
+2. `(r)` -> `®` (r)
+3. `(tm)` -> `™` (tm)
+4. `...` -> `…` ...
+5. `--` -> `&emdash;` --
+6. `-` -> `–` - (but not if it's between-words)
+7. "fancy quoting"
+8. 'fancy quoting (#2)'
+9. don't do it unless it's a real quote.
+10. `` (`) ``
+
diff --git a/tests/code.t b/tests/code.t
new file mode 100644
index 0000000..8e8f20c
--- /dev/null
+++ b/tests/code.t
@@ -0,0 +1,35 @@
+. tests/functions.sh
+
+title "code blocks"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'format for code block html' \
+' this is
+ code' \
+ '<pre><code>this is
+code
+</code></pre>'
+
+try 'mismatched backticks' '```tick``' '<p><code>`tick</code></p>'
+try 'mismatched backticks(2)' '``tick```' '<p>``tick```</p>'
+try 'unclosed single backtick' '`hi there' '<p>`hi there</p>'
+try 'unclosed double backtick' '``hi there' '<p>``hi there</p>'
+try 'triple backticks' '```hi there```' '<p><code>hi there</code></p>'
+try 'quadruple backticks' '````hi there````' '<p><code>hi there</code></p>'
+try 'remove space around code' '`` hi there ``' '<p><code>hi there</code></p>'
+try 'code containing backticks' '`` a```b ``' '<p><code>a```b</code></p>'
+try 'backslash before backtick' '`a\`' '<p><code>a\</code></p>'
+try '`>`' '`>`' '<p><code>></code></p>'
+try '`` ` ``' '`` ` ``' '<p><code>`</code></p>'
+try '````` ``` `' '````` ``` `' '<p><code>``</code> `</p>'
+try '````` ` ```' '````` ` ```' '<p><code>`` `</code></p>'
+try 'backslashes in code(1)' ' printf "%s: \n", $1;' \
+'<pre><code>printf "%s: \n", $1;
+</code></pre>'
+try 'backslashes in code(2)' '`printf "%s: \n", $1;`' \
+'<p><code>printf "%s: \n", $1;</code></p>'
+
+summary $0
+exit $rc
diff --git a/tests/compat.t b/tests/compat.t
new file mode 100644
index 0000000..af39e49
--- /dev/null
+++ b/tests/compat.t
@@ -0,0 +1,29 @@
+. tests/functions.sh
+
+title "markdown 1.0 compatibility"
+
+rc=0
+MARKDOWN_FLAGS=
+
+LINKY='[this] is a test
+
+[this]: /this'
+
+try 'implicit reference links' "$LINKY" '<p><a href="/this">this</a> is a test</p>'
+try -f1.0 'implicit reference links (-f1.0)' "$LINKY" '<p>[this] is a test</p>'
+
+WSP=' '
+WHITESPACE="
+ white space$WSP
+ and more"
+
+try 'trailing whitespace' "$WHITESPACE" '<pre><code>white space ''
+and more
+</code></pre>'
+
+try -f1.0 'trailing whitespace (-f1.0)' "$WHITESPACE" '<pre><code>white space''
+and more
+</code></pre>'
+
+summary $0
+exit $rc
diff --git a/tests/crash.t b/tests/crash.t
new file mode 100644
index 0000000..b958c4a
--- /dev/null
+++ b/tests/crash.t
@@ -0,0 +1,31 @@
+. tests/functions.sh
+
+title "crashes"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'zero-length input' '' ''
+
+try 'hanging quote in list' \
+' * > this should not die
+
+no.' \
+'<ul>
+<li><blockquote><p>this should not die</p></blockquote></li>
+</ul>
+
+
+<p>no.</p>'
+
+try 'dangling list item' ' - ' \
+'<ul>
+<li></li>
+</ul>'
+
+try -bHOHO 'empty []() with baseurl' '[]()' '<p><a href=""></a></p>'
+try 'unclosed html block' '<table></table' '<p><table></table</p>'
+try 'unclosed style block' '<style>' '<p><style></p>'
+
+summary $0
+exit $rc
diff --git a/tests/div.t b/tests/div.t
new file mode 100644
index 0000000..17ca298
--- /dev/null
+++ b/tests/div.t
@@ -0,0 +1,45 @@
+. tests/functions.sh
+
+title "%div% blocks"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'simple >%div% block' \
+'>%this%
+this this' \
+'<div class="this"><p>this this</p></div>'
+
+try 'two >%div% blocks in a row' \
+'>%this%
+this this
+
+>%that%
+that that' \
+'<div class="this"><p>this this</p></div>
+
+<div class="that"><p>that that</p></div>'
+
+try '>%class:div%' \
+'>%class:this%
+this this' \
+'<div class="this"><p>this this</p></div>'
+
+try '>%id:div%' \
+'>%id:this%
+this this' \
+'<div id="this"><p>this this</p></div>'
+
+try 'nested >%div%' \
+'>%this%
+>>%that%
+>>that
+
+>%more%
+more' \
+'<div class="this"><div class="that"><p>that</p></div></div>
+
+<div class="more"><p>more</p></div>'
+
+summary $0
+exit $rc
diff --git a/tests/dl.t b/tests/dl.t
new file mode 100644
index 0000000..99d1fe4
--- /dev/null
+++ b/tests/dl.t
@@ -0,0 +1,96 @@
+. tests/functions.sh
+
+title "definition lists"
+
+eval `./markdown -V | tr ' ' '\n' | grep '^DL='`
+
+DL=${DL:-BOTH}
+
+rc=0
+MARKDOWN_FLAGS=
+
+SRC='
+=this=
+ is an ugly
+=test=
+ eh?'
+
+RSLT='<dl>
+<dt>this</dt>
+<dd>is an ugly</dd>
+<dt>test</dt>
+<dd>eh?</dd>
+</dl>'
+
+if [ "$DL" = "DISCOUNT" -o "$DL" = "BOTH" ]; then
+ try -fdefinitionlist '=tag= generates definition lists' "$SRC" "$RSLT"
+
+ try 'one item with two =tags=' \
+ '=this=
+=is=
+ A test, eh?' \
+ '<dl>
+<dt>this</dt>
+<dt>is</dt>
+<dd>A test, eh?</dd>
+</dl>'
+
+
+ try -fnodefinitionlist '=tag= does nothing' "$SRC" \
+ '<p>=this=</p>
+
+<pre><code>is an ugly
+</code></pre>
+
+<p>=test=</p>
+
+<pre><code>eh?
+</code></pre>'
+fi
+
+if [ "$DL" = "EXTRA" -o "$DL" = "BOTH" ]; then
+ try 'markdown extra-style definition lists' \
+'foo
+: bar' \
+'<dl>
+<dt>foo</dt>
+<dd>bar</dd>
+</dl>'
+
+ try '... with two <dt>s in a row' \
+'foo
+bar
+: baz' \
+'<dl>
+<dt>foo</dt>
+<dt>bar</dt>
+<dd>baz</dd>
+</dl>'
+
+ try '... with two <dd>s in a row' \
+'foo
+: bar
+: baz' \
+'<dl>
+<dt>foo</dt>
+<dd>bar</dd>
+<dd>baz</dd>
+</dl>'
+
+ try '... with blanks between list items' \
+'foo
+: bar
+
+zip
+: zap' \
+'<dl>
+<dt>foo</dt>
+<dd>bar</dd>
+<dt>zip</dt>
+<dd>zap</dd>
+</dl>'
+
+fi
+
+summary $0
+exit $rc
diff --git a/tests/embedlinks.text b/tests/embedlinks.text
new file mode 100644
index 0000000..a79ef1b
--- /dev/null
+++ b/tests/embedlinks.text
@@ -0,0 +1,9 @@
+* [![an image](http://dustmite.org/mite.jpg =50x50)] (http://dustmite.org)
+* [[an embedded link](http://wontwork.org)](http://willwork.org)
+* [![dustmite][]] (http:/dustmite.org)
+* ![dustmite][]
+* ![dustmite][dustmite]
+* [<a href="http://cheating.us">cheat me</a>](http://I.am.cheating)
+
+[dustmite]: http://dustmite.org/mite.jpg =25x25 "here I am!"
+
diff --git a/tests/emphasis.t b/tests/emphasis.t
new file mode 100644
index 0000000..622f55f
--- /dev/null
+++ b/tests/emphasis.t
@@ -0,0 +1,19 @@
+. tests/functions.sh
+
+title "emphasis"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try '*hi* -> <em>hi</em>' '*hi*' '<p><em>hi</em></p>'
+try '* -> *' 'A * A' '<p>A * A</p>'
+try -fstrict '***A**B*' '***A**B*' '<p><em><strong>A</strong>B</em></p>'
+try -fstrict '***A*B**' '***A*B**' '<p><strong><em>A</em>B</strong></p>'
+try -fstrict '**A*B***' '**A*B***' '<p><strong>A<em>B</em></strong></p>'
+try -fstrict '*A**B***' '*A**B***' '<p><em>A<strong>B</strong></em></p>'
+
+try -frelax '_A_B with -frelax' '_A_B' '<p>_A_B</p>'
+try -fstrict '_A_B with -fstrict' '_A_B' '<p><em>A</em>B</p>'
+
+summary $0
+exit $rc
diff --git a/tests/flow.t b/tests/flow.t
new file mode 100644
index 0000000..44cf53a
--- /dev/null
+++ b/tests/flow.t
@@ -0,0 +1,33 @@
+. tests/functions.sh
+
+title "paragraph flow"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'header followed by paragraph' \
+ '###Hello, sailor###
+And how are you today?' \
+ '<h3>Hello, sailor</h3>
+
+<p>And how are you today?</p>'
+
+try 'two lists punctuated with a HR' \
+ '* A
+* * *
+* B
+* C' \
+ '<ul>
+<li>A</li>
+</ul>
+
+
+<hr />
+
+<ul>
+<li>B</li>
+<li>C</li>
+</ul>'
+
+summary $0
+exit $rc
diff --git a/tests/footnotes.t b/tests/footnotes.t
new file mode 100644
index 0000000..2c923b0
--- /dev/null
+++ b/tests/footnotes.t
@@ -0,0 +1,16 @@
+. tests/functions.sh
+
+title "footnotes"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'a line with multiple []s' '[a][] [b][]:' '<p>[a][] [b][]:</p>'
+try 'a valid footnote' \
+ '[alink][]
+
+[alink]: link_me' \
+ '<p><a href="link_me">alink</a></p>'
+
+summary $0
+exit $rc
diff --git a/tests/functions.sh b/tests/functions.sh
new file mode 100644
index 0000000..d280d65
--- /dev/null
+++ b/tests/functions.sh
@@ -0,0 +1,77 @@
+__tests=0
+__passed=0
+__failed=0
+__title=
+
+title() {
+ __title="$*"
+ if [ "$VERBOSE" ]; then
+ ./echo "$__title"
+ else
+ ./echo -n "$__title" \
+'.................................................................' | ./cols 54
+ fi
+}
+
+
+summary() {
+ if [ -z "$VERBOSE" ]; then
+ if [ $__failed -eq 0 ]; then
+ ./echo " OK"
+ else
+ ./echo
+ ./echo "$1: $__tests tests; $__failed failed/$__passed passed"
+ ./echo
+ fi
+ fi
+}
+
+
+try() {
+ unset FLAGS
+ case "$1" in
+ -*) FLAGS=$1
+ shift ;;
+ esac
+
+ testcase=`./echo -n " $1" '........................................................' | ./cols 50`
+ __tests=`expr $__tests + 1`
+
+
+ test "$VERBOSE" && ./echo -n "$testcase"
+
+ case "$2" in
+ -t*) Q=`./markdown $FLAGS "$2"` ;;
+ *) Q=`./echo "$2" | ./markdown $FLAGS` ;;
+ esac
+
+ if [ "$3" = "$Q" ]; then
+ __passed=`expr $__passed + 1`
+ test $VERBOSE && ./echo " ok"
+ else
+ __failed=`expr $__failed + 1`
+ if [ -z "$VERBOSE" ]; then
+ ./echo
+ ./echo "$1"
+ fi
+ ./echo "wanted: $3"
+ ./echo "got : $Q"
+ rc=1
+ fi
+}
+
+match() {
+ testcase=`./echo -n " $1" '........................................................' | ./cols 50`
+
+ test $VERBOSE && ./echo -n "$testcase"
+
+ if ./echo "$2" | ./markdown | grep "$3" >/dev/null; then
+ test $VERBOSE && ./echo " ok"
+ else
+ if [ -z "$VERBOSE" ]; then
+ ./echo
+ ./echo "$testcase"
+ fi
+ rc=1
+ fi
+}
diff --git a/tests/header.t b/tests/header.t
new file mode 100644
index 0000000..d72a516
--- /dev/null
+++ b/tests/header.t
@@ -0,0 +1,26 @@
+. tests/functions.sh
+
+title "headers"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'single #' '#' '<p>#</p>'
+try 'empty ETX' '##' '<h1>#</h1>'
+try 'single-char ETX (##W)' '##W' '<h2>W</h2>'
+try 'single-char ETX (##W )' '##W ' '<h2>W</h2>'
+try 'single-char ETX (## W)' '## W' '<h2>W</h2>'
+try 'single-char ETX (## W )' '## W ' '<h2>W</h2>'
+try 'single-char ETX (##W##)' '##W##' '<h2>W</h2>'
+try 'single-char ETX (##W ##)' '##W ##' '<h2>W</h2>'
+try 'single-char ETX (## W##)' '## W##' '<h2>W</h2>'
+try 'single-char ETX (## W ##)' '## W ##' '<h2>W</h2>'
+
+try 'multiple-char ETX (##Hello##)' '##Hello##' '<h2>Hello</h2>'
+
+try 'SETEXT with trailing whitespace' \
+'hello
+===== ' '<h1>hello</h1>'
+
+summary $0
+exit $rc
diff --git a/tests/html.t b/tests/html.t
new file mode 100644
index 0000000..6d98226
--- /dev/null
+++ b/tests/html.t
@@ -0,0 +1,141 @@
+. tests/functions.sh
+
+title "html blocks"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'self-closing block tags (hr)' \
+ '<hr>
+
+text' \
+ '<hr>
+
+
+<p>text</p>'
+
+try 'self-closing block tags (hr/)' \
+ '<hr/>
+
+text' \
+ '<hr/>
+
+
+<p>text</p>'
+
+try 'self-closing block tags (br)' \
+ '<br>
+
+text' \
+ '<br>
+
+
+<p>text</p>'
+
+try 'html comments' \
+ '<!--
+**hi**
+-->' \
+ '<!--
+**hi**
+-->'
+
+try 'no smartypants inside tags (#1)' \
+ '<img src="linky">' \
+ '<p><img src="linky"></p>'
+
+try 'no smartypants inside tags (#2)' \
+ '<img src="linky" alt=":)" />' \
+ '<p><img src="linky" alt=":)" /></p>'
+
+try -fnohtml 'block html with -fnohtml' '<b>hi!</b>' '<p><b>hi!</b></p>'
+try -fnohtml 'malformed tag injection' '<x <script>' '<p><x <script></p>'
+try -fhtml 'allow html with -fhtml' '<b>hi!</b>' '<p><b>hi!</b></p>'
+
+
+# check that nested raw html blocks terminate properly.
+#
+BLOCK1SRC='Markdown works fine *here*.
+
+*And* here.
+
+<div><pre>
+</pre></div>
+
+Markdown here is *not* parsed by RDiscount.
+
+Nor in *this* paragraph, and there are no paragraph breaks.'
+
+BLOCK1OUT='<p>Markdown works fine <em>here</em>.</p>
+
+<p><em>And</em> here.</p>
+
+<div><pre>
+</pre></div>
+
+
+<p>Markdown here is <em>not</em> parsed by RDiscount.</p>
+
+<p>Nor in <em>this</em> paragraph, and there are no paragraph breaks.</p>'
+
+try 'nested html blocks (1)' "$BLOCK1SRC" "$BLOCK1OUT"
+
+try 'nested html blocks (2)' \
+ '<div>This is inside a html block
+<div>This is, too</div>and
+so is this</div>' \
+ '<div>This is inside a html block
+<div>This is, too</div>and
+so is this</div>'
+
+try 'unfinished tags' '<foo bar' '<p><foo bar</p>'
+
+
+try 'comment with trailing text' '<!-- this is -->a test' \
+'<!-- this is -->
+
+
+<p>a test</p>'
+
+try 'block with trailing text' '<p>this is</p>a test' \
+'<p>this is</p>
+
+
+<p>a test</p>'
+
+
+COMMENTS='<!-- 1. -->line 1
+
+<!-- 2. -->line 2'
+
+try 'two comments' "$COMMENTS" \
+'<!-- 1. -->
+
+
+<p>line 1</p>
+
+<!-- 2. -->
+
+
+<p>line 2</p>'
+
+COMMENTS='<!-- 1. -->line 1
+<!-- 2. -->line 2'
+
+try 'two adjacent comments' "$COMMENTS" \
+'<!-- 1. -->
+
+
+<p>line 1</p>
+
+<!-- 2. -->
+
+
+<p>line 2</p>'
+
+try 'comment, no white space' '<!--foo-->' '<!--foo-->'
+
+try 'unclosed block' '<p>here we go!' '<p><p>here we go!</p>'
+
+summary $0
+exit $rc
diff --git a/tests/html5.t b/tests/html5.t
new file mode 100644
index 0000000..0056f01
--- /dev/null
+++ b/tests/html5.t
@@ -0,0 +1,17 @@
+. tests/functions.sh
+
+title "html5 blocks (mkd_with_html5_tags)"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try -5 'html5 block elements enabled' \
+ '<aside>html5 does not suck</aside>' \
+ '<aside>html5 does not suck</aside>'
+
+try 'html5 block elements disabled' \
+ '<aside>html5 sucks</aside>' \
+ '<p><aside>html5 sucks</aside></p>'
+
+summary $0
+exit $rc
diff --git a/tests/links.text b/tests/links.text
new file mode 100644
index 0000000..ca40439
--- /dev/null
+++ b/tests/links.text
@@ -0,0 +1,14 @@
+ 1. <http://automatic>
+ 2. [automatic] (http://automatic "automatic link")
+ 3. [automatic](http://automatic "automatic link")
+ 4. [automatic](http://automatic)
+ 5. [automatic] (http://automatic)
+ 6. [automatic] []
+ 7. [automatic][]
+ 8. [my][automatic]
+ 9. [my] [automatic]
+
+ [automatic]: http://automatic "footnote"
+
+
+ [automatic] [
diff --git a/tests/linkylinky.t b/tests/linkylinky.t
new file mode 100644
index 0000000..a837e10
--- /dev/null
+++ b/tests/linkylinky.t
@@ -0,0 +1,130 @@
+. tests/functions.sh
+
+title "embedded links"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'url contains &' '[hehehe](u&rl)' '<p><a href="u&rl">hehehe</a></p>'
+try 'url contains +' '[hehehe](u+rl)' '<p><a href="u+rl">hehehe</a></p>'
+try 'url contains "' '[hehehe](u"rl)' '<p><a href="u%22rl">hehehe</a></p>'
+try 'url contains <' '[hehehe](u<rl)' '<p><a href="u<rl">hehehe</a></p>'
+try 'url contains whitespace' '[ha](r u)' '<p><a href="r%20u">ha</a></p>'
+try 'label contains escaped []s' '[a\[b\]c](d)' '<p><a href="d">a[b]c</a></p>'
+
+try '<label> w/o title' '[hello](<sailor>)' '<p><a href="sailor">hello</a></p>'
+try '<label> with title' '[hello](<sailor> "boy")' '<p><a href="sailor" title="boy">hello</a></p>'
+try '<label> with whitespace' '[hello]( <sailor> )' '<p><a href="sailor">hello</a></p>'
+
+try 'url contains whitespace & title' \
+ '[hehehe](r u "there")' \
+ '<p><a href="r%20u" title="there">hehehe</a></p>'
+
+try 'url contains escaped )' \
+ '[hehehe](u\))' \
+ '<p><a href="u)">hehehe</a></p>'
+
+try 'image label contains <' \
+ '![he<he<he](url)' \
+ '<p><img src="url" alt="he<he<he" /></p>'
+
+try 'image label contains >' \
+ '![he>he>he](url)' \
+ '<p><img src="url" alt="he>he>he" /></p>'
+
+try 'sloppy context link' \
+ '[heh]( url "how about it?" )' \
+ '<p><a href="url" title="how about it?">heh</a></p>'
+
+try 'footnote urls formed properly' \
+ '[hehehe]: hohoho "ha ha"
+
+[hehehe][]' \
+ '<p><a href="hohoho" title="ha ha">hehehe</a></p>'
+
+try 'linky-like []s work' \
+ '[foo]' \
+ '<p>[foo]</p>'
+
+try 'pseudo-protocol "id:"'\
+ '[foo](id:bar)' \
+ '<p><span id="bar">foo</span></p>'
+
+try 'pseudo-protocol "class:"' \
+ '[foo](class:bar)' \
+ '<p><span class="bar">foo</span></p>'
+
+try 'pseudo-protocol "abbr:"'\
+ '[foo](abbr:bar)' \
+ '<p><abbr title="bar">foo</abbr></p>'
+
+try 'nested [][]s' \
+ '[[z](y)](x)' \
+ '<p><a href="x">[z](y)</a></p>'
+
+try 'empty [][] tags' \
+ '[![][1]][2]
+
+[1]: image1
+[2]: image2' \
+ '<p><a href="image2"><img src="image1" alt="" /></a></p>'
+
+try 'footnote cuddled up to text' \
+'foo
+[bar]:bar' \
+ '<p>foo</p>'
+
+try 'mid-paragraph footnote' \
+'talk talk talk talk
+[bar]: bar
+talk talk talk talk' \
+'<p>talk talk talk talk
+talk talk talk talk</p>'
+
+try 'mid-blockquote footnote' \
+'>blockquote!
+[footnote]: here!
+>blockquote!' \
+'<blockquote><p>blockquote!
+blockquote!</p></blockquote>'
+
+try 'end-blockquote footnote' \
+'>blockquote!
+>blockquote!
+[footnote]: here!' \
+'<blockquote><p>blockquote!
+blockquote!</p></blockquote>'
+
+try 'start-blockquote footnote' \
+'[footnote]: here!
+>blockquote!
+>blockquote!' \
+'<blockquote><p>blockquote!
+blockquote!</p></blockquote>'
+
+try '[text] (text) not a link' \
+'[test] (me)' \
+'<p>[test] (me)</p>'
+
+try '[test] [this] w/ one space between' \
+'[test] [this]
+[test]: yay!
+[this]: nay!' \
+'<p><a href="nay!">test</a></p>'
+
+try '[test] [this] w/ two spaces between' \
+'[test] [this]
+[test]: yay!
+[this]: nay!' \
+'<p><a href="yay!">test</a> <a href="nay!">this</a></p>'
+
+try -f1.0 'link with <> (-f1.0)' \
+ '[this](<is a (test)>)' \
+ '<p><a href="is%20a%20(test">this</a>>)</p>'
+
+try 'link with <>' \
+ '[this](<is a (test)>)' \
+ '<p><a href="is%20a%20(test)">this</a></p>'
+
+summary $0
+exit $rc
diff --git a/tests/linkypix.t b/tests/linkypix.t
new file mode 100644
index 0000000..5c7572a
--- /dev/null
+++ b/tests/linkypix.t
@@ -0,0 +1,21 @@
+. tests/functions.sh
+
+title "embedded images"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'image with size extension' \
+ '![picture](pic =200x200)' \
+ '<p><img src="pic" height="200" width="200" alt="picture" /></p>'
+
+try 'image with height' \
+ '![picture](pic =x200)' \
+ '<p><img src="pic" height="200" alt="picture" /></p>'
+
+try 'image with width' \
+ '![picture](pic =200x)' \
+ '<p><img src="pic" width="200" alt="picture" /></p>'
+
+summary $0
+exit $rc
diff --git a/tests/list.t b/tests/list.t
new file mode 100644
index 0000000..550700d
--- /dev/null
+++ b/tests/list.t
@@ -0,0 +1,155 @@
+. tests/functions.sh
+title "lists"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'two separated items' \
+ ' * A
+
+* B' \
+ '<ul>
+<li><p>A</p></li>
+<li><p>B</p></li>
+</ul>'
+
+try 'two adjacent items' \
+ ' * A
+ * B' \
+ '<ul>
+<li>A</li>
+<li>B</li>
+</ul>'
+
+
+try 'two adjacent items, then space' \
+ ' * A
+* B
+
+space, the final frontier' \
+ '<ul>
+<li>A</li>
+<li>B</li>
+</ul>
+
+
+<p>space, the final frontier</p>'
+
+try 'nested lists (1)' \
+ ' * 1. Sub (list)
+ 2. Two (items)
+ 3. Here' \
+ '<ul>
+<li><ol>
+<li>Sub (list)</li>
+<li>Two (items)</li>
+<li>Here</li>
+</ol>
+</li>
+</ul>'
+
+try 'nested lists (2)' \
+ ' * A (list)
+
+ 1. Sub (list)
+ 2. Two (items)
+ 3. Here
+
+ Here
+ * B (list)' \
+ '<ul>
+<li><p>A (list)</p>
+
+<ol>
+<li> Sub (list)</li>
+<li> Two (items)</li>
+<li> Here</li>
+</ol>
+
+
+<p> Here</p></li>
+<li>B (list)</li>
+</ul>'
+
+try 'list inside blockquote' \
+ '>A (list)
+>
+>1. Sub (list)
+>2. Two (items)
+>3. Here' \
+ '<blockquote><p>A (list)</p>
+
+<ol>
+<li>Sub (list)</li>
+<li>Two (items)</li>
+<li>Here</li>
+</ol>
+</blockquote>'
+
+try 'blockquote inside list' \
+ ' * A (list)
+
+ > quote
+ > me
+
+ dont quote me' \
+ '<ul>
+<li><p>A (list)</p>
+
+<blockquote><p>quote
+me</p></blockquote>
+
+<p>dont quote me</p></li>
+</ul>'
+
+try 'empty list' \
+'
+-
+
+-
+' \
+'<ul>
+<li></li>
+<li></li>
+</ul>'
+
+
+try 'blockquote inside a list' \
+' * This is a list item.
+
+ > This is a quote insde a list item. ' \
+'<ul>
+<li><p> This is a list item.</p>
+
+<blockquote><p>This is a quote insde a list item.</p></blockquote></li>
+</ul>'
+
+try 'dl followed by non-dl' \
+ '=a=
+ test
+2. here' \
+'<dl>
+<dt>a</dt>
+<dd>test</dd>
+</dl>
+
+<ol>
+<li>here</li>
+</ol>'
+
+try 'non-dl followed by dl' \
+ '1. hello
+=sailor=
+ hi!' \
+'<ol>
+<li>hello</li>
+</ol>
+
+
+<dl>
+<dt>sailor</dt>
+<dd>hi!</dd>
+</dl>'
+
+summary $0
+exit $rc
diff --git a/tests/list3deep.t b/tests/list3deep.t
new file mode 100644
index 0000000..5909055
--- /dev/null
+++ b/tests/list3deep.t
@@ -0,0 +1,38 @@
+. tests/functions.sh
+title "deeply nested lists"
+
+rc=0
+MARKDOWN_FLAGS=
+
+LIST='
+ * top-level list ( list 1)
+ + second-level list (list 2)
+ * first item third-level list (list 3)
+ + * second item, third-level list, first item. (list 4)
+ * second item, third-level list, second item.
+ * top-level list again.'
+
+RSLT='<ul>
+<li>top-level list ( list 1)
+
+<ul>
+<li>second-level list (list 2)
+
+<ul>
+<li>first item third-level list (list 3)</li>
+</ul>
+</li>
+<li><ul>
+<li>second item, third-level list, first item. (list 4)</li>
+<li>second item, third-level list, second item.</li>
+</ul>
+</li>
+</ul>
+</li>
+<li>top-level list again.</li>
+</ul>'
+
+try 'thrice-nested lists' "$LIST" "$RSLT"
+
+summary $0
+exit $rc
diff --git a/tests/misc.t b/tests/misc.t
new file mode 100644
index 0000000..969ca3b
--- /dev/null
+++ b/tests/misc.t
@@ -0,0 +1,12 @@
+. tests/functions.sh
+
+title "misc"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'single paragraph' 'AAA' '<p>AAA</p>'
+try '< -> <' '<' '<p><</p>'
+
+summary $0
+exit $rc
diff --git a/tests/pandoc.t b/tests/pandoc.t
new file mode 100644
index 0000000..5d312c3
--- /dev/null
+++ b/tests/pandoc.t
@@ -0,0 +1,44 @@
+. tests/functions.sh
+
+title "pandoc headers"
+
+rc=0
+MARKDOWN_FLAGS=
+
+HEADER='% title
+% author(s)
+% date'
+
+
+try 'valid header' "$HEADER" ''
+try -F0x00010000 'valid header with -F0x00010000' "$HEADER" '<p>% title
+% author(s)
+% date</p>'
+
+try 'invalid header' \
+ '% title
+% author(s)
+a pony!' \
+ '<p>% title
+% author(s)
+a pony!</p>'
+
+try 'offset header' \
+ '
+% title
+% author(s)
+% date' \
+ '<p>% title
+% author(s)
+% date</p>'
+
+try 'indented header' \
+ ' % title
+% author(s)
+% date' \
+ '<p> % title
+% author(s)
+% date</p>'
+
+summary $0
+exit $rc
diff --git a/tests/para.t b/tests/para.t
new file mode 100644
index 0000000..29445e5
--- /dev/null
+++ b/tests/para.t
@@ -0,0 +1,19 @@
+. tests/functions.sh
+
+title "paragraph blocking"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'paragraph followed by code' \
+ 'a
+ b' \
+ '<p>a</p>
+
+<pre><code>b
+</code></pre>'
+
+try 'single-line paragraph' 'a' '<p>a</p>'
+
+summary $0
+exit $rc
diff --git a/tests/paranoia.t b/tests/paranoia.t
new file mode 100644
index 0000000..a12147b
--- /dev/null
+++ b/tests/paranoia.t
@@ -0,0 +1,12 @@
+. tests/functions.sh
+
+title "paranoia"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try -fsafelink 'bogus url (-fsafelink)' '[test](bad:protocol)' '<p>[test](bad:protocol)</p>'
+try -fnosafelink 'bogus url (-fnosafelink)' '[test](bad:protocol)' '<p><a href="bad:protocol">test</a></p>'
+
+summary $0
+exit $rc
diff --git a/tests/peculiarities.t b/tests/peculiarities.t
new file mode 100644
index 0000000..f97597f
--- /dev/null
+++ b/tests/peculiarities.t
@@ -0,0 +1,77 @@
+. tests/functions.sh
+
+title "markup peculiarities"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'list followed by header .......... ' \
+ "
+- AAA
+- BBB
+-" \
+ '<ul>
+<li>AAA
+
+<h2>– BBB</h2></li>
+</ul>'
+
+try 'ul with mixed item prefixes' \
+ '
+- A
+1. B' \
+ '<ul>
+<li>A</li>
+<li>B</li>
+</ul>'
+
+try 'ol with mixed item prefixes' \
+ '
+1. A
+- B
+' \
+ '<ol>
+<li>A</li>
+<li>B</li>
+</ol>'
+
+try 'nested lists and a header' \
+ '- A list item
+That goes over multiple lines
+
+ and paragraphs
+
+- Another list item
+
+ + with a
+ + sublist
+
+## AND THEN A HEADER' \
+'<ul>
+<li><p>A list item
+That goes over multiple lines</p>
+
+<p> and paragraphs</p></li>
+<li><p>Another list item</p>
+
+<ul>
+<li>with a</li>
+<li>sublist</li>
+</ul>
+</li>
+</ul>
+
+
+<h2>AND THEN A HEADER</h2>'
+
+try 'forcing a <br/>' 'this
+is' '<p>this<br/>
+is</p>'
+
+try 'trimming single spaces' 'this ' '<p>this</p>'
+try -fnohtml 'markdown <br/> with -fnohtml' 'foo
+is' '<p>foo<br/>
+is</p>'
+
+summary $0
+exit $rc
diff --git a/tests/pseudo.t b/tests/pseudo.t
new file mode 100644
index 0000000..82045a9
--- /dev/null
+++ b/tests/pseudo.t
@@ -0,0 +1,20 @@
+. tests/functions.sh
+
+title "pseudo-protocols"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try '[](id:) links' '[foo](id:bar)' '<p><span id="bar">foo</span></p>'
+try -fnoext '[](id:) links with -fnoext' '[foo](id:bar)' '<p>[foo](id:bar)</p>'
+try '[](class:) links' '[foo](class:bar)' '<p><span class="bar">foo</span></p>'
+try -fnoext '[](class:) links with -fnoext' '[foo](class:bar)' '<p>[foo](class:bar)</p>'
+try '[](lang:) links' '[foo](lang:en)' '<p><span lang="en">foo</span></p>'
+try -fnoext '[](lang:) links with -fnoext' '[foo](lang:en)' '<p>[foo](lang:en)</p>'
+try '[](raw:) links' '[foo](raw:bar)' '<p>bar</p>'
+try -fnoext '[](raw:) links with -fnoext' '[foo](raw:bar)' '<p>[foo](raw:bar)</p>'
+
+# try '[](id:) wrapping a href' '[[foo](bar)](id:baz)' '<p><span id="baz">foo</span></p>'
+
+summary $0
+exit $rc
diff --git a/tests/reddit.t b/tests/reddit.t
new file mode 100644
index 0000000..268cbcf
--- /dev/null
+++ b/tests/reddit.t
@@ -0,0 +1,27 @@
+. tests/functions.sh
+
+title "bugs & misfeatures found during the Reddit rollout"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'smiley faces?' '[8-9] <]:-( x ---> [4]' \
+ '<p>[8-9] <]:–( x —–> [4]</p>'
+
+try 'really long ETX headers' \
+ '#####################################################hi' \
+ '<h6>###############################################hi</h6>'
+
+try 'unescaping " " inside `code`' \
+'`foo
+bar`' \
+'<p><code>foo
+bar</code></p>'
+
+try 'unescaping " " inside []()' \
+'[foo](bar
+bar)' \
+'<p><a href="bar %0Abar">foo</a></p>'
+
+summary $0
+exit $rc
diff --git a/tests/reparse.t b/tests/reparse.t
new file mode 100644
index 0000000..93e11b5
--- /dev/null
+++ b/tests/reparse.t
@@ -0,0 +1,14 @@
+. tests/functions.sh
+
+title "footnotes inside reparse sections"
+
+rc=0
+
+try 'footnote inside [] section' \
+ '[![foo][]](bar)
+
+[foo]: bar2' \
+ '<p><a href="bar"><img src="bar2" alt="foo" /></a></p>'
+
+summary $0
+exit $rc
diff --git a/tests/schiraldi.t b/tests/schiraldi.t
new file mode 100644
index 0000000..a576fdd
--- /dev/null
+++ b/tests/schiraldi.t
@@ -0,0 +1,91 @@
+. tests/functions.sh
+
+title "Bugs & misfeatures reported by Mike Schiraldi"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try -fnohtml 'breaks with -fnohtml' 'foo
+bar' '<p>foo<br/>
+bar</p>'
+
+try 'links with trailing \)' \
+ '[foo](http://en.wikipedia.org/wiki/Link_(film\))' \
+ '<p><a href="http://en.wikipedia.org/wiki/Link_(film)">foo</a></p>'
+
+try -fautolink '(url) with -fautolink' \
+ '(http://tsfr.org)' \
+ '<p>(<a href="http://tsfr.org">http://tsfr.org</a>)</p>'
+
+try 'single #' \
+ '#' \
+ '<p>#</p>'
+
+try -frelax '* processing with -frelax' \
+ '2*4 = 8 * 1 = 2**3' \
+ '<p>2*4 = 8 * 1 = 2**3</p>'
+
+try -fnopants '[]() with a single quote mark' \
+ '[Poe'"'"'s law](http://rationalwiki.com/wiki/Poe'"'"'s_Law)' \
+ '<p><a href="http://rationalwiki.com/wiki/Poe'"'"'s_Law">Poe'"'"'s law</a></p>'
+
+try -fautolink 'autolink url with escaped spaces' \
+ 'http://\(here\ I\ am\)' \
+ '<p><a href="http://(here%20I%20am)">http://(here I am)</a></p>'
+
+try -fautolink 'autolink café_racer' \
+ 'http://en.wikipedia.org/wiki/café_racer' \
+ '<p><a href="http://en.wikipedia.org/wiki/caf%C3%A9_racer">http://en.wikipedia.org/wiki/caf%C3%A9_racer</a></p>'
+
+try -fautolink 'autolink url with arguments' \
+ 'http://foo.bar?a&b=c' \
+ '<p><a href="http://foo.bar?a&b=c">http://foo.bar?a&b=c</a></p>'
+
+try '\( escapes in []()' \
+ '[foo](http://a.com/\(foo\))' \
+ '<p><a href="http://a.com/(foo)">foo</a></p>'
+
+try -fautolink 'autolink url with escaped ()' \
+ 'http://a.com/\(foo\)' \
+ '<p><a href="http://a.com/(foo)">http://a.com/(foo)</a></p>'
+
+try -fautolink 'autolink url with escaped \' \
+ 'http://a.com/\\\)' \
+ '<p><a href="http://a.com/\)">http://a.com/\)</a></p>'
+
+try -fautolink 'autolink url with -' \
+ 'http://experts-exchange.com' \
+ '<p><a href="http://experts-exchange.com">http://experts-exchange.com</a></p>'
+
+try -fautolink 'autolink url with +' \
+ 'http://www67.wolframalpha.com/input/?i=how+old+was+jfk+jr+when+jfk+died' \
+ '<p><a href="http://www67.wolframalpha.com/input/?i=how+old+was+jfk+jr+when+jfk+died">http://www67.wolframalpha.com/input/?i=how+old+was+jfk+jr+when+jfk+died</a></p>'
+
+try -fautolink 'autolink url with &' \
+ 'http://foo.bar?a&b=c' \
+ '<p><a href="http://foo.bar?a&b=c">http://foo.bar?a&b=c</a></p>'
+
+
+try -fautolink 'autolink url with ,' \
+ 'http://www.spiegel.de/international/europe/0,1518,626171,00.html' \
+ '<p><a href="http://www.spiegel.de/international/europe/0,1518,626171,00.html">http://www.spiegel.de/international/europe/0,1518,626171,00.html</a></p>'
+
+try -fautolink 'autolink url with : & ;' \
+ 'http://www.biblegateway.com/passage/?search=Matthew%205:29-30;&version=31;' \
+ '<p><a href="http://www.biblegateway.com/passage/?search=Matthew%205:29-30;&version=31;">http://www.biblegateway.com/passage/?search=Matthew%205:29-30;&version=31;</a></p>'
+
+Q="'"
+try -fautolink 'security hole with \" in []()' \
+'[XSS](/ "\"=\"\"onmouseover='$Q'alert(String.fromCharCode(88,83,83))'$Q'")' \
+'<p><a href="/" title="\"=\"\"onmouseover='$Q'alert(String.fromCharCode(88,83,83))'$Q'">XSS</a></p>'
+
+try -fautolink 'autolink and prefix fragment' \
+'xxxxxxx http://x.com/
+
+xxx xxxxht' \
+'<p>xxxxxxx <a href="http://x.com/">http://x.com/</a></p>
+
+<p>xxx xxxxht</p>'
+
+summary $0
+exit $rc
diff --git a/tests/smarty.t b/tests/smarty.t
new file mode 100644
index 0000000..8c9ccc5
--- /dev/null
+++ b/tests/smarty.t
@@ -0,0 +1,24 @@
+. tests/functions.sh
+
+title "smarty pants"
+
+rc=0
+MARKDOWN_FLAGS=0x0; export MARKDOWN_FLAGS
+
+try '(c) -> ©' '(c)' '<p>©</p>'
+try '(r) -> ®' '(r)' '<p>®</p>'
+try '(tm) -> ™' '(tm)' '<p>™</p>'
+try '... -> …' '...' '<p>…</p>'
+
+try '"--" -> —' '--' '<p>—</p>'
+
+try '"-" -> –' 'regular -' '<p>regular –</p>'
+try 'A-B -> A-B' 'A-B' '<p>A-B</p>'
+try '"fancy" -> “fancy”' '"fancy"' '<p>“fancy”</p>'
+try "'fancy'" "'fancy'" '<p>‘fancy’</p>'
+try "don<b>'t -> don<b>’t" "don<b>'t" '<p>don<b>’t</p>'
+try "don't -> don’t" "don't" '<p>don’t</p>'
+try "it's -> it’s" "it's" '<p>it’s</p>'
+
+summary $0
+exit $rc
diff --git a/tests/snakepit.t b/tests/snakepit.t
new file mode 100644
index 0000000..79d71ba
--- /dev/null
+++ b/tests/snakepit.t
@@ -0,0 +1,29 @@
+. tests/functions.sh
+
+title "The snakepit of Markdown.pl compatibility"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try '[](single quote) text (quote)' \
+ "[foo](http://Poe's law) will make this fail ('no, it won't!') here."\
+ '<p><a href="http://Poe" title="s law) will make this fail ('"'no, it won't!"'">foo</a> here.</p>'
+
+try '-f1.0' '[](unclosed <url) (MKD_1_COMPAT)' '[foo](<http://no trailing gt)' \
+ '<p><a href="http://no%20trailing%20gt">foo</a></p>'
+
+try '[](unclosed <url)' '[foo](<http://no trailing gt)' \
+ '<p>[foo](<http://no trailing gt)</p>'
+
+try '<unfinished <tags> (1)' \
+'<foo [bar](foo) <s>hi</s>' \
+'<p><foo [bar](foo) <s>hi</s></p>'
+
+try '<unfinished &<tags> (2)' \
+'<foo [bar](foo) &<s>hi</s>' \
+'<p><foo [bar](foo) &<s>hi</s></p>'
+
+try 'paragraph <br/> oddity' 'EOF ' '<p>EOF</p>'
+
+summary $0
+exit $rc
diff --git a/tests/strikethrough.t b/tests/strikethrough.t
new file mode 100644
index 0000000..9016a5c
--- /dev/null
+++ b/tests/strikethrough.t
@@ -0,0 +1,15 @@
+. tests/functions.sh
+
+title "strikethrough"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'strikethrough' '~~deleted~~' '<p><del>deleted</del></p>'
+try -fnodel '... with -fnodel' '~~deleted~~' '<p>~~deleted~~</p>'
+try 'mismatched tildes' '~~~tick~~' '<p><del>~tick</del></p>'
+try 'mismatched tildes(2)' '~~tick~~~' '<p>~~tick~~~</p>'
+try 'single tildes' '~tick~' '<p>~tick~</p>'
+
+summary $0
+exit $rc
diff --git a/tests/style.t b/tests/style.t
new file mode 100644
index 0000000..3da3d1c
--- /dev/null
+++ b/tests/style.t
@@ -0,0 +1,34 @@
+. tests/functions.sh
+
+title "styles"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'single line' '<style> ul {display:none;} </style>' ''
+
+ASK='<style>
+ul {display:none;}
+</style>'
+
+try 'multiple lines' "$ASK" ''
+
+try 'unclosed' '<style>foo' '<p><style>foo</p>'
+
+UNCLOSED='<style>
+text
+
+text'
+
+RESULT='<p><style>
+text</p>
+
+<p>text</p>'
+
+
+try 'multiple lines unclosed' "$UNCLOSED" "$RESULT"
+
+try -fnohtml 'unclosed with -fnohtml' '<style>foo' '<p><style>foo</p>'
+
+summary $0
+exit $rc
diff --git a/tests/superscript.t b/tests/superscript.t
new file mode 100644
index 0000000..d04647a
--- /dev/null
+++ b/tests/superscript.t
@@ -0,0 +1,17 @@
+. tests/functions.sh
+
+title "superscript"
+
+rc=0
+MARKDOWN_FLAGS=0x0; export MARKDOWN_FLAGS
+
+try -frelax 'A^B -> A<sup>B</sup> (-frelax)' 'A^B' '<p>A<sup>B</sup></p>'
+try -fstrict 'A^B != A<sup>B</sup> (-fstrict)' 'A^B' '<p>A^B</p>'
+try -fnosuperscript 'A^B != A<sup>B</sup> (-fnosuperscript)' 'A^B' '<p>A^B</p>'
+try -frelax 'A^B in link title' '[link](here "A^B")' '<p><a href="here" title="A^B">link</a></p>'
+try 'A^(B+2)C^2' 'A^(B+2)C^2' '<p>A<sup>B+2</sup>C<sup>2</sup></p>'
+try 'A^((B+2))C^2' 'A^((B+2))C^2' '<p>A<sup>(B+2)</sup>C<sup>2</sup></p>'
+try 'A^B+C^D' 'A^B+C^D' '<p>A<sup>B</sup>+C<sup>D</sup></p>'
+
+summary $0
+exit $rc
diff --git a/tests/syntax.text b/tests/syntax.text
new file mode 100644
index 0000000..df740dd
--- /dev/null
+++ b/tests/syntax.text
@@ -0,0 +1,897 @@
+Markdown: Syntax
+================
+
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a href="/projects/markdown/basics" title="Markdown Basics">Basics</a></li>
+ <li><a class="selected" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+
+
+* [Overview](#overview)
+ * [Philosophy](#philosophy)
+ * [Inline HTML](#html)
+ * [Automatic Escaping for Special Characters](#autoescape)
+* [Block Elements](#block)
+ * [Paragraphs and Line Breaks](#p)
+ * [Headers](#header)
+ * [Blockquotes](#blockquote)
+ * [Lists](#list)
+ * [Code Blocks](#precode)
+ * [Horizontal Rules](#hr)
+* [Span Elements](#span)
+ * [Links](#link)
+ * [Emphasis](#em)
+ * [Code](#code)
+ * [Images](#img)
+* [Miscellaneous](#misc)
+ * [Backslash Escapes](#backslash)
+ * [Automatic Links](#autolink)
+
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL][src].
+
+ [src]: /projects/markdown/syntax.text
+
+* * *
+
+<h2 id="overview">Overview</h2>
+
+<h3 id="philosophy">Philosophy</h3>
+
+Markdown is intended to be as easy-to-read and easy-to-write as is feasible.
+
+Readability, however, is emphasized above all else. A Markdown-formatted
+document should be publishable as-is, as plain text, without looking
+like it's been marked up with tags or formatting instructions. While
+Markdown's syntax has been influenced by several existing text-to-HTML
+filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4],
+[Grutatext] [5], and [EtText] [6] -- the single biggest source of
+inspiration for Markdown's syntax is the format of plain text email.
+
+ [1]: http://docutils.sourceforge.net/mirror/setext.html
+ [2]: http://www.aaronsw.com/2002/atx/
+ [3]: http://textism.com/tools/textile/
+ [4]: http://docutils.sourceforge.net/rst.html
+ [5]: http://www.triptico.com/software/grutatxt.html
+ [6]: http://ettext.taint.org/doc/
+
+To this end, Markdown's syntax is comprised entirely of punctuation
+characters, which punctuation characters have been carefully chosen so
+as to look like what they mean. E.g., asterisks around a word actually
+look like \*emphasis\*. Markdown lists look like, well, lists. Even
+blockquotes look like quoted passages of text, assuming you've ever
+used email.
+
+
+
+<h3 id="html">Inline HTML</h3>
+
+Markdown's syntax is intended for one purpose: to be used as a
+format for *writing* for the web.
+
+Markdown is not a replacement for HTML, or even close to it. Its
+syntax is very small, corresponding only to a very small subset of
+HTML tags. The idea is *not* to create a syntax that makes it easier
+to insert HTML tags. In my opinion, HTML tags are already easy to
+insert. The idea for Markdown is to make it easy to read, write, and
+edit prose. HTML is a *publishing* format; Markdown is a *writing*
+format. Thus, Markdown's formatting syntax only addresses issues that
+can be conveyed in plain text.
+
+For any markup that is not covered by Markdown's syntax, you simply
+use HTML itself. There's no need to preface it or delimit it to
+indicate that you're switching from Markdown to HTML; you just use
+the tags.
+
+The only restrictions are that block-level HTML elements -- e.g. `<div>`,
+`<table>`, `<pre>`, `<p>`, etc. -- must be separated from surrounding
+content by blank lines, and the start and end tags of the block should
+not be indented with tabs or spaces. Markdown is smart enough not
+to add extra (unwanted) `<p>` tags around HTML block-level tags.
+
+For example, to add an HTML table to a Markdown article:
+
+ This is a regular paragraph.
+
+ <table>
+ <tr>
+ <td>Foo</td>
+ </tr>
+ </table>
+
+ This is another regular paragraph.
+
+Note that Markdown formatting syntax is not processed within block-level
+HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an
+HTML block.
+
+Span-level HTML tags -- e.g. `<span>`, `<cite>`, or `<del>` -- can be
+used anywhere in a Markdown paragraph, list item, or header. If you
+want, you can even use HTML tags instead of Markdown formatting; e.g. if
+you'd prefer to use HTML `<a>` or `<img>` tags instead of Markdown's
+link or image syntax, go right ahead.
+
+Unlike block-level HTML tags, Markdown syntax *is* processed within
+span-level tags.
+
+
+<h3 id="autoescape">Automatic Escaping for Special Characters</h3>
+
+In HTML, there are two characters that demand special treatment: `<`
+and `&`. Left angle brackets are used to start tags; ampersands are
+used to denote HTML entities. If you want to use them as literal
+characters, you must escape them as entities, e.g. `<`, and
+`&`.
+
+Ampersands in particular are bedeviling for web writers. If you want to
+write about 'AT&T', you need to write '`AT&T`'. You even need to
+escape ampersands within URLs. Thus, if you want to link to:
+
+ http://images.google.com/images?num=30&q=larry+bird
+
+you need to encode the URL as:
+
+ http://images.google.com/images?num=30&q=larry+bird
+
+in your anchor tag `href` attribute. Needless to say, this is easy to
+forget, and is probably the single most common source of HTML validation
+errors in otherwise well-marked-up web sites.
+
+Markdown allows you to use these characters naturally, taking care of
+all the necessary escaping for you. If you use an ampersand as part of
+an HTML entity, it remains unchanged; otherwise it will be translated
+into `&`.
+
+So, if you want to include a copyright symbol in your article, you can write:
+
+ ©
+
+and Markdown will leave it alone. But if you write:
+
+ AT&T
+
+Markdown will translate it to:
+
+ AT&T
+
+Similarly, because Markdown supports [inline HTML](#html), if you use
+angle brackets as delimiters for HTML tags, Markdown will treat them as
+such. But if you write:
+
+ 4 < 5
+
+Markdown will translate it to:
+
+ 4 < 5
+
+However, inside Markdown code spans and blocks, angle brackets and
+ampersands are *always* encoded automatically. This makes it easy to use
+Markdown to write about HTML code. (As opposed to raw HTML, which is a
+terrible format for writing about HTML syntax, because every single `<`
+and `&` in your example code needs to be escaped.)
+
+
+* * *
+
+
+<h2 id="block">Block Elements</h2>
+
+
+<h3 id="p">Paragraphs and Line Breaks</h3>
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing but spaces or tabs is considered
+blank.) Normal paragraphs should not be indented with spaces or tabs.
+
+The implication of the "one or more consecutive lines of text" rule is
+that Markdown supports "hard-wrapped" text paragraphs. This differs
+significantly from most other text-to-HTML formatters (including Movable
+Type's "Convert Line Breaks" option) which translate every line break
+character in a paragraph into a `<br />` tag.
+
+When you *do* want to insert a `<br />` break tag using Markdown, you
+end a line with two or more spaces, then type return.
+
+Yes, this takes a tad more effort to create a `<br />`, but a simplistic
+"every line break is a `<br />`" rule wouldn't work for Markdown.
+Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l]
+work best -- and look better -- when you format them with hard breaks.
+
+ [bq]: #blockquote
+ [l]: #list
+
+
+
+<h3 id="header">Headers</h3>
+
+Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
+
+Setext-style headers are "underlined" using equal signs (for first-level
+headers) and dashes (for second-level headers). For example:
+
+ This is an H1
+ =============
+
+ This is an H2
+ -------------
+
+Any number of underlining `=`'s or `-`'s will work.
+
+Atx-style headers use 1-6 hash characters at the start of the line,
+corresponding to header levels 1-6. For example:
+
+ # This is an H1
+
+ ## This is an H2
+
+ ###### This is an H6
+
+Optionally, you may "close" atx-style headers. This is purely
+cosmetic -- you can use this if you think it looks better. The
+closing hashes don't even need to match the number of hashes
+used to open the header. (The number of opening hashes
+determines the header level.) :
+
+ # This is an H1 #
+
+ ## This is an H2 ##
+
+ ### This is an H3 ######
+
+
+<h3 id="blockquote">Blockquotes</h3>
+
+Markdown uses email-style `>` characters for blockquoting. If you're
+familiar with quoting passages of text in an email message, then you
+know how to create a blockquote in Markdown. It looks best if you hard
+wrap the text and put a `>` before every line:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+ >
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ > id sem consectetuer libero luctus adipiscing.
+
+Markdown allows you to be lazy and only put the `>` before the first
+line of a hard-wrapped paragraph:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ id sem consectetuer libero luctus adipiscing.
+
+Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
+adding additional levels of `>`:
+
+ > This is the first level of quoting.
+ >
+ > > This is nested blockquote.
+ >
+ > Back to the first level.
+
+Blockquotes can contain other Markdown elements, including headers, lists,
+and code blocks:
+
+ > ## This is a header.
+ >
+ > 1. This is the first list item.
+ > 2. This is the second list item.
+ >
+ > Here's some example code:
+ >
+ > return shell_exec("echo $input | $markdown_script");
+
+Any decent text editor should make email-style quoting easy. For
+example, with BBEdit, you can make a selection and choose Increase
+Quote Level from the Text menu.
+
+
+<h3 id="list">Lists</h3>
+
+Markdown supports ordered (numbered) and unordered (bulleted) lists.
+
+Unordered lists use asterisks, pluses, and hyphens -- interchangably
+-- as list markers:
+
+ * Red
+ * Green
+ * Blue
+
+is equivalent to:
+
+ + Red
+ + Green
+ + Blue
+
+and:
+
+ - Red
+ - Green
+ - Blue
+
+Ordered lists use numbers followed by periods:
+
+ 1. Bird
+ 2. McHale
+ 3. Parish
+
+It's important to note that the actual numbers you use to mark the
+list have no effect on the HTML output Markdown produces. The HTML
+Markdown produces from the above list is:
+
+ <ol>
+ <li>Bird</li>
+ <li>McHale</li>
+ <li>Parish</li>
+ </ol>
+
+If you instead wrote the list in Markdown like this:
+
+ 1. Bird
+ 1. McHale
+ 1. Parish
+
+or even:
+
+ 3. Bird
+ 1. McHale
+ 8. Parish
+
+you'd get the exact same HTML output. The point is, if you want to,
+you can use ordinal numbers in your ordered Markdown lists, so that
+the numbers in your source match the numbers in your published HTML.
+But if you want to be lazy, you don't have to.
+
+If you do use lazy list numbering, however, you should still start the
+list with the number 1. At some point in the future, Markdown may support
+starting ordered lists at an arbitrary number.
+
+List markers typically start at the left margin, but may be indented by
+up to three spaces. List markers must be followed by one or more spaces
+or a tab.
+
+To make lists look nice, you can wrap items with hanging indents:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+But if you want to be lazy, you don't have to:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+If list items are separated by blank lines, Markdown will wrap the
+items in `<p>` tags in the HTML output. For example, this input:
+
+ * Bird
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li>Bird</li>
+ <li>Magic</li>
+ </ul>
+
+But this:
+
+ * Bird
+
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li><p>Bird</p></li>
+ <li><p>Magic</p></li>
+ </ul>
+
+List items may consist of multiple paragraphs. Each subsequent
+paragraph in a list item must be intended by either 4 spaces
+or one tab:
+
+ 1. This is a list item with two paragraphs. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+ mi posuere lectus.
+
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+ vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+ sit amet velit.
+
+ 2. Suspendisse id sem consectetuer libero luctus adipiscing.
+
+It looks nice if you indent every line of the subsequent
+paragraphs, but here again, Markdown will allow you to be
+lazy:
+
+ * This is a list item with two paragraphs.
+
+ This is the second paragraph in the list item. You're
+ only required to indent the first line. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit.
+
+ * Another item in the same list.
+
+To put a blockquote within a list item, the blockquote's `>`
+delimiters need to be indented:
+
+ * A list item with a blockquote:
+
+ > This is a blockquote
+ > inside a list item.
+
+To put a code block within a list item, the code block needs
+to be indented *twice* -- 8 spaces or two tabs:
+
+ * A list item with a code block:
+
+ <code goes here>
+
+
+It's worth noting that it's possible to trigger an ordered list by
+accident, by writing something like this:
+
+ 1986. What a great season.
+
+In other words, a *number-period-space* sequence at the beginning of a
+line. To avoid this, you can backslash-escape the period:
+
+ 1986\. What a great season.
+
+
+
+<h3 id="precode">Code Blocks</h3>
+
+Pre-formatted code blocks are used for writing about programming or
+markup source code. Rather than forming normal paragraphs, the lines
+of a code block are interpreted literally. Markdown wraps a code block
+in both `<pre>` and `<code>` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab. For example, given this input:
+
+ This is a normal paragraph:
+
+ This is a code block.
+
+Markdown will generate:
+
+ <p>This is a normal paragraph:</p>
+
+ <pre><code>This is a code block.
+ </code></pre>
+
+One level of indentation -- 4 spaces or 1 tab -- is removed from each
+line of the code block. For example, this:
+
+ Here is an example of AppleScript:
+
+ tell application "Foo"
+ beep
+ end tell
+
+will turn into:
+
+ <p>Here is an example of AppleScript:</p>
+
+ <pre><code>tell application "Foo"
+ beep
+ end tell
+ </code></pre>
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+ <div class="footer">
+ © 2004 Foo Corporation
+ </div>
+
+will turn into:
+
+ <pre><code><div class="footer">
+ © 2004 Foo Corporation
+ </div>
+ </code></pre>
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+
+
+<h3 id="hr">Horizontal Rules</h3>
+
+You can produce a horizontal rule tag (`<hr />`) by placing three or
+more hyphens, asterisks, or underscores on a line by themselves. If you
+wish, you may use spaces between the hyphens or asterisks. Each of the
+following lines will produce a horizontal rule:
+
+ * * *
+
+ ***
+
+ *****
+
+ - - -
+
+ ---------------------------------------
+
+
+* * *
+
+<h2 id="span">Span Elements</h2>
+
+<h3 id="link">Links</h3>
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+ This is [an example](http://example.com/ "Title") inline link.
+
+ [This link](http://example.net/) has no title attribute.
+
+Will produce:
+
+ <p>This is <a href="http://example.com/" title="Title">
+ an example</a> inline link.</p>
+
+ <p><a href="http://example.net/">This link</a> has no
+ title attribute.</p>
+
+If you're referring to a local resource on the same server, you can
+use relative paths:
+
+ See my [About](/about/) page for details.
+
+Reference-style links use a second set of square brackets, inside
+which you place a label of your choosing to identify the link:
+
+ This is [an example][id] reference-style link.
+
+You can optionally use a space to separate the sets of brackets:
+
+ This is [an example] [id] reference-style link.
+
+Then, anywhere in the document, you define your link label like this,
+on a line by itself:
+
+ [id]: http://example.com/ "Optional Title Here"
+
+That is:
+
+* Square brackets containing the link identifier (optionally
+ indented from the left margin using up to three spaces);
+* followed by a colon;
+* followed by one or more spaces (or tabs);
+* followed by the URL for the link;
+* optionally followed by a title attribute for the link, enclosed
+ in double or single quotes, or enclosed in parentheses.
+
+The following three link definitions are equivalent:
+
+ [foo]: http://example.com/ "Optional Title Here"
+ [foo]: http://example.com/ 'Optional Title Here'
+ [foo]: http://example.com/ (Optional Title Here)
+
+**Note:** There is a known bug in Markdown.pl 1.0.1 which prevents
+single quotes from being used to delimit link titles.
+
+The link URL may, optionally, be surrounded by angle brackets:
+
+ [id]: <http://example.com/> "Optional Title Here"
+
+You can put the title attribute on the next line and use extra spaces
+or tabs for padding, which tends to look better with longer URLs:
+
+ [id]: http://example.com/longish/path/to/resource/here
+ "Optional Title Here"
+
+Link definitions are only used for creating links during Markdown
+processing, and are stripped from your document in the HTML output.
+
+Link definition names may constist of letters, numbers, spaces, and
+punctuation -- but they are *not* case sensitive. E.g. these two
+links:
+
+ [link text][a]
+ [link text][A]
+
+are equivalent.
+
+The *implicit link name* shortcut allows you to omit the name of the
+link, in which case the link text itself is used as the name.
+Just use an empty set of square brackets -- e.g., to link the word
+"Google" to the google.com web site, you could simply write:
+
+ [Google][]
+
+And then define the link:
+
+ [Google]: http://google.com/
+
+Because link names may contain spaces, this shortcut even works for
+multiple words in the link text:
+
+ Visit [Daring Fireball][] for more information.
+
+And then define the link:
+
+ [Daring Fireball]: http://daringfireball.net/
+
+Link definitions can be placed anywhere in your Markdown document. I
+tend to put them immediately after each paragraph in which they're
+used, but if you want, you can put them all at the end of your
+document, sort of like footnotes.
+
+Here's an example of reference links in action:
+
+ I get 10 times more traffic from [Google] [1] than from
+ [Yahoo] [2] or [MSN] [3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+
+Using the implicit link name shortcut, you could instead write:
+
+ I get 10 times more traffic from [Google][] than from
+ [Yahoo][] or [MSN][].
+
+ [google]: http://google.com/ "Google"
+ [yahoo]: http://search.yahoo.com/ "Yahoo Search"
+ [msn]: http://search.msn.com/ "MSN Search"
+
+Both of the above examples will produce the following HTML output:
+
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from
+ <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
+ or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
+
+For comparison, here is the same paragraph written using
+Markdown's inline link style:
+
+ I get 10 times more traffic from [Google](http://google.com/ "Google")
+ than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
+ [MSN](http://search.msn.com/ "MSN Search").
+
+The point of reference-style links is not that they're easier to
+write. The point is that with reference-style links, your document
+source is vastly more readable. Compare the above examples: using
+reference-style links, the paragraph itself is only 81 characters
+long; with inline-style links, it's 176 characters; and as raw HTML,
+it's 234 characters. In the raw HTML, there's more markup than there
+is text.
+
+With Markdown's reference-style links, a source document much more
+closely resembles the final output, as rendered in a browser. By
+allowing you to move the markup-related metadata out of the paragraph,
+you can add links without interrupting the narrative flow of your
+prose.
+
+
+<h3 id="em">Emphasis</h3>
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`<strong>` tag. E.g., this input:
+
+ *single asterisks*
+
+ _single underscores_
+
+ **double asterisks**
+
+ __double underscores__
+
+will produce:
+
+ <em>single asterisks</em>
+
+ <em>single underscores</em>
+
+ <strong>double asterisks</strong>
+
+ <strong>double underscores</strong>
+
+You can use whichever style you prefer; the lone restriction is that
+the same character must be used to open and close an emphasis span.
+
+Emphasis can be used in the middle of a word:
+
+ un*fucking*believable
+
+But if you surround an `*` or `_` with spaces, it'll be treated as a
+literal asterisk or underscore.
+
+To produce a literal asterisk or underscore at a position where it
+would otherwise be used as an emphasis delimiter, you can backslash
+escape it:
+
+ \*this text is surrounded by literal asterisks\*
+
+
+
+<h3 id="code">Code</h3>
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+ Use the `printf()` function.
+
+will produce:
+
+ <p>Use the <code>printf()</code> function.</p>
+
+To include a literal backtick character within a code span, you can use
+multiple backticks as the opening and closing delimiters:
+
+ ``There is a literal backtick (`) here.``
+
+which will produce this:
+
+ <p><code>There is a literal backtick (`) here.</code></p>
+
+The backtick delimiters surrounding a code span may include spaces --
+one after the opening, one before the closing. This allows you to place
+literal backtick characters at the beginning or end of a code span:
+
+ A single backtick in a code span: `` ` ``
+
+ A backtick-delimited string in a code span: `` `foo` ``
+
+will produce:
+
+ <p>A single backtick in a code span: <code>`</code></p>
+
+ <p>A backtick-delimited string in a code span: <code>`foo`</code></p>
+
+With a code span, ampersands and angle brackets are encoded as HTML
+entities automatically, which makes it easy to include example HTML
+tags. Markdown will turn this:
+
+ Please don't use any `<blink>` tags.
+
+into:
+
+ <p>Please don't use any <code><blink></code> tags.</p>
+
+You can write this:
+
+ `—` is the decimal-encoded equivalent of `—`.
+
+to produce:
+
+ <p><code>—</code> is the decimal-encoded
+ equivalent of <code>—</code>.</p>
+
+
+
+<h3 id="img">Images</h3>
+
+Admittedly, it's fairly difficult to devise a "natural" syntax for
+placing images into a plain text document format.
+
+Markdown uses an image syntax that is intended to resemble the syntax
+for links, allowing for two styles: *inline* and *reference*.
+
+Inline image syntax looks like this:
+
+ ![Alt text](/path/to/img.jpg)
+
+ ![Alt text](/path/to/img.jpg "Optional title")
+
+That is:
+
+* An exclamation mark: `!`;
+* followed by a set of square brackets, containing the `alt`
+ attribute text for the image;
+* followed by a set of parentheses, containing the URL or path to
+ the image, and an optional `title` attribute enclosed in double
+ or single quotes.
+
+Reference-style image syntax looks like this:
+
+ ![Alt text][id]
+
+Where "id" is the name of a defined image reference. Image references
+are defined using syntax identical to link references:
+
+ [id]: url/to/image "Optional title attribute"
+
+As of this writing, Markdown has no syntax for specifying the
+dimensions of an image; if this is important to you, you can simply
+use regular HTML `<img>` tags.
+
+
+* * *
+
+
+<h2 id="misc">Miscellaneous</h2>
+
+<h3 id="autolink">Automatic Links</h3>
+
+Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:
+
+ <http://example.com/>
+
+Markdown will turn this into:
+
+ <a href="http://example.com/">http://example.com/</a>
+
+Automatic links for email addresses work similarly, except that
+Markdown will also perform a bit of randomized decimal and hex
+entity-encoding to help obscure your address from address-harvesting
+spambots. For example, Markdown will turn this:
+
+ <address at example.com>
+
+into something like this:
+
+ <a href="mailto:addre
+ ss@example.co
+ m">address@exa
+ mple.com</a>
+
+which will render in a browser as a clickable link to "address at example.com".
+
+(This sort of entity-encoding trick will indeed fool many, if not
+most, address-harvesting bots, but it definitely won't fool all of
+them. It's better than nothing, but an address published in this way
+will probably eventually start receiving spam.)
+
+
+
+<h3 id="backslash">Backslash Escapes</h3>
+
+Markdown allows you to use backslash escapes to generate literal
+characters which would otherwise have special meaning in Markdown's
+formatting syntax. For example, if you wanted to surround a word with
+literal asterisks (instead of an HTML `<em>` tag), you can backslashes
+before the asterisks, like this:
+
+ \*literal asterisks\*
+
+Markdown provides backslash escapes for the following characters:
+
+ \ backslash
+ ` backtick
+ * asterisk
+ _ underscore
+ {} curly braces
+ [] square brackets
+ () parentheses
+ # hash mark
+ + plus sign
+ - minus sign (hyphen)
+ . dot
+ ! exclamation mark
+
diff --git a/tests/tables.t b/tests/tables.t
new file mode 100644
index 0000000..f1fe24d
--- /dev/null
+++ b/tests/tables.t
@@ -0,0 +1,167 @@
+. tests/functions.sh
+
+title "tables"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'single-column table' \
+ '|hello
+|-----
+|sailor' \
+ '<table>
+<thead>
+<tr>
+<th></th>
+<th>hello</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td></td>
+<td>sailor</td>
+</tr>
+</tbody>
+</table>'
+
+
+try 'two-column table' \
+ '
+ a | b
+-----|------
+hello|sailor' \
+ '<table>
+<thead>
+<tr>
+<th> a </th>
+<th> b</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>hello</td>
+<td>sailor</td>
+</tr>
+</tbody>
+</table>'
+
+try 'three-column table' \
+'a|b|c
+-|-|-
+hello||sailor'\
+ '<table>
+<thead>
+<tr>
+<th>a</th>
+<th>b</th>
+<th>c</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>hello</td>
+<td></td>
+<td>sailor</td>
+</tr>
+</tbody>
+</table>'
+
+try 'two-column table with empty cells' \
+ '
+ a | b
+-----|------
+hello|
+ |sailor' \
+ '<table>
+<thead>
+<tr>
+<th> a </th>
+<th> b</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>hello</td>
+<td></td>
+</tr>
+<tr>
+<td> </td>
+<td>sailor</td>
+</tr>
+</tbody>
+</table>'
+
+try 'two-column table with alignment' \
+ '
+ a | b
+----:|:-----
+hello|sailor' \
+ '<table>
+<thead>
+<tr>
+<th align="right"> a </th>
+<th align="left"> b</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="right">hello</td>
+<td align="left">sailor</td>
+</tr>
+</tbody>
+</table>'
+
+try 'table with extra data column' \
+ '
+ a | b
+-----|------
+hello|sailor|boy' \
+ '<table>
+<thead>
+<tr>
+<th> a </th>
+<th> b</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>hello</td>
+<td>sailor|boy</td>
+</tr>
+</tbody>
+</table>'
+
+
+try -fnotables 'tables with -fnotables' \
+ 'a|b
+-|-
+hello|sailor' \
+ '<p>a|b
+–|–
+hello|sailor</p>'
+
+try 'deceptive non-table text' \
+ 'a | b | c
+
+text' \
+ '<p>a | b | c</p>
+
+<p>text</p>'
+
+try 'table headers only' \
+ 'a|b|c
+-|-|-' \
+ '<table>
+<thead>
+<tr>
+<th>a</th>
+<th>b</th>
+<th>c</th>
+</tr>
+</thead>
+<tbody>
+</tbody>
+</table>'
+
+summary $0
+exit $rc
diff --git a/tests/tabstop.t b/tests/tabstop.t
new file mode 100644
index 0000000..8d27881
--- /dev/null
+++ b/tests/tabstop.t
@@ -0,0 +1,48 @@
+. tests/functions.sh
+
+rc=0
+unset MARKDOWN_FLAGS
+unset MKD_TABSTOP
+
+eval `./markdown -V | tr ' ' '\n' | grep TAB`
+
+if [ "${TAB:-4}" -eq 8 ]; then
+ title "dealing with tabstop derangement"
+
+ LIST='
+ * A
+ * B
+ * C'
+
+ try 'markdown with TAB=8' \
+ "$LIST" \
+ '<ul>
+<li>A
+
+<ul>
+<li>B
+
+<ul>
+<li>C</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>'
+
+ try -F0x020000 'markdown with TAB=4' \
+ "$LIST" \
+ '<ul>
+<li>A
+
+<ul>
+<li>B</li>
+<li>C</li>
+</ul>
+</li>
+</ul>'
+
+
+summary $0
+fi
+exit $rc
diff --git a/tests/toc.t b/tests/toc.t
new file mode 100644
index 0000000..342f9cb
--- /dev/null
+++ b/tests/toc.t
@@ -0,0 +1,35 @@
+. tests/functions.sh
+
+title "table-of-contents support"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try '-T -ftoc' 'table of contents' \
+'#H1
+hi' \
+'<ul>
+ <li><a href="#H1">H1</a></li>
+</ul>
+<h1 id="H1">H1</h1>
+
+<p>hi</p>'
+
+try '-T -ftoc' 'toc item with link' \
+'##[H2](H2) here' \
+'<ul>
+ <li><ul>
+ <li><a href="#H2.here">H2 here</a></li>
+ </ul></li>
+</ul>
+<h2 id="H2.here"><a href="H2">H2</a> here</h2>'
+
+try '-T -ftoc' 'toc item with non-alpha start' \
+'#1 header' \
+'<ul>
+ <li><a href="#L1.header">1 header</a></li>
+</ul>
+<h1 id="L1.header">1 header</h1>'
+
+summary $0
+exit $rc
diff --git a/tests/xml.t b/tests/xml.t
new file mode 100644
index 0000000..17ac74a
--- /dev/null
+++ b/tests/xml.t
@@ -0,0 +1,18 @@
+. tests/functions.sh
+
+title "xml output with MKD_CDATA"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try -fcdata 'xml output from markdown()' 'hello,sailor' '<p>hello,sailor</p>'
+try -fcdata 'from mkd_generateline()' -t'"hello,sailor"' '“hello,sailor”'
+try -fnocdata 'html output from markdown()' '"hello,sailor"' '<p>“hello,sailor”</p>'
+try -fnocdata '... from mkd_generateline()' -t'"hello,sailor"' '“hello,sailor”'
+
+try -fcdata 'xml output with multibyte utf-8' \
+ 'tecnología y servicios más confiables' \
+ '<p>tecnología y servicios más confiables</p>'
+
+summary $0
+exit $rc
diff --git a/theme.1 b/theme.1
new file mode 100644
index 0000000..473b913
--- /dev/null
+++ b/theme.1
@@ -0,0 +1,142 @@
+.\" %A%
+.\"
+.Dd January 23, 2008
+.Dt THEME 1
+.Os MASTODON
+.Sh NAME
+.Nm theme
+.Nd create a web page from a template file
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Pa root
+.Op Fl f
+.Op Fl o Pa file
+.Op Fl p Pa pagename
+.Op Fl t Pa template
+.Op Fl V
+.Op Pa textfile
+.Sh DESCRIPTION
+The
+.Nm
+utility takes a
+.Xr markdown 7 Ns -formatted
+.Pa textfile
+.Pq or stdin if not specified,
+compiles it, and combines it with a
+.Em template
+.Po
+.Pa page.theme
+by default
+.Pc
+to produce a web page. If a path to the
+template is not specified,
+.Nm
+looks for
+.Pa page.theme
+in the current directory, then each parent directory up to the
+.Pa "document root"
+.Po
+set with
+.Fl d
+or, if unset, the
+.Em "root directory"
+of the system.
+.Pc
+If
+.Pa page.theme
+is found,
+.Nm
+copies it to the output, looking for
+.Em "<?theme action?>"
+html tags and processing the embedded
+.Ar action
+as appropriate.
+.Pp
+.Nm
+processes the following actions:
+.Bl -tag -width "include("
+.It Ar author
+Prints the author name(s) from the
+.Xr mkd_doc_author 3
+function.
+.It Ar body
+Prints the formatted
+.Xr markdown 7
+input file.
+.It Ar date
+Prints the date returned by
+.Xr mkd_doc_date 3
+or, if none, the
+date the input file was last modified.
+.It Ar dir
+Prints the directory part of the pagename
+.It Ar include Ns Pq Pa file
+Prints the contents of
+.Pa file .
+.Xr Markdown 7
+translation will
+.Em NOT
+be done on this file.
+.It Ar source
+The filename part of the pagename.
+.It Ar style
+Print any stylesheets
+.Pq see Xr mkd-extensions 7
+found in the input file.
+.It Ar title
+Print the title returned by
+.Xr mkd_doc_title 3 ,
+or, if that does not exist, the source filename.
+.It Ar version
+Print the version of
+.Xr discount 7
+that this copy of theme was compiled with.
+.El
+.Pp
+If input is coming from a file and the output was not set with the
+.Ar o
+option,
+.Nm writes the output to
+.Pa file-sans-text.html
+.Pq if
+.Ar file
+has a
+.Pa .text
+suffix, that will be stripped off and replaced with
+.Pa .html ;
+otherwise a
+.Pa .html
+will be appended to the end of the filename.)
+.Pp
+The options are as follows:
+.Bl -tag -width "-o file"
+.It Fl d Pa root
+Set the
+.Em "document root"
+to
+.Ar root
+.It Fl f
+Forcibly overwrite existing html files.
+.It Fl o Pa filename
+Write the output to
+.Ar filename .
+.It Fl p Ar path
+Set the pagename to
+.Ar path .
+.It Fl t Ar filename
+Use
+.Ar filename
+as the template file.
+.El
+.Sh RETURN VALUES
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr markdown 1 ,
+.Xr markdown 3 ,
+.Xr markdown 7 ,
+.Xr mkd-extensions 7 .
+.Sh AUTHOR
+.An David Parsons
+.Pq Li orc at pell.chi.il.us
diff --git a/theme.c b/theme.c
new file mode 100644
index 0000000..637fe5e
--- /dev/null
+++ b/theme.c
@@ -0,0 +1,609 @@
+/*
+ * theme: use a template to create a webpage (markdown-style)
+ *
+ * usage: theme [-d root] [-p pagename] [-t template] [-o html] [source]
+ *
+ */
+/*
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(HAVE_BASENAME) && defined(HAVE_LIBGEN_H)
+# include <libgen.h>
+#endif
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "mkdio.h"
+#include "cstring.h"
+#include "amalloc.h"
+
+char *pgm = "theme";
+char *output = 0;
+char *pagename = 0;
+char *root = 0;
+#if HAVE_PWD_H
+struct passwd *me = 0;
+#endif
+struct stat *infop = 0;
+
+
+#define INTAG 0x01
+#define INHEAD 0x02
+#define INBODY 0x04
+
+
+#ifndef HAVE_BASENAME
+char *
+basename(char *path)
+{
+ char *p;
+
+ if (( p = strrchr(path, '/') ))
+ return 1+p;
+ return path;
+}
+#endif
+
+#ifdef HAVE_FCHDIR
+typedef int HERE;
+#define NOT_HERE (-1)
+
+#define pushd(d) open(d, O_RDONLY)
+
+int
+popd(HERE pwd)
+{
+ int rc = fchdir(pwd);
+ close(pwd);
+ return rc;
+}
+
+#else
+
+typedef char* HERE;
+#define NOT_HERE 0
+
+HERE
+pushd(char *d)
+{
+ HERE cwd;
+ int size;
+
+ if ( chdir(d) == -1 )
+ return NOT_HERE;
+
+ for (cwd = malloc(size=40); cwd; cwd = realloc(cwd, size *= 2))
+ if ( getcwd(cwd, size) )
+ return cwd;
+
+ return NOT_HERE;
+}
+
+int
+popd(HERE pwd)
+{
+ if ( pwd ) {
+ int rc = chdir(pwd);
+ free(pwd);
+
+ return rc;
+ }
+ return -1;
+}
+#endif
+
+typedef STRING(int) Istring;
+
+void
+fail(char *why, ...)
+{
+ va_list ptr;
+
+ va_start(ptr,why);
+ fprintf(stderr, "%s: ", pgm);
+ vfprintf(stderr, why, ptr);
+ fputc('\n', stderr);
+ va_end(ptr);
+ exit(1);
+}
+
+
+/* open_template() -- start at the current directory and work up,
+ * looking for the deepest nested template.
+ * Stop looking when we reach $root or /
+ */
+FILE *
+open_template(char *template)
+{
+ char *cwd;
+ int szcwd;
+ HERE here = pushd(".");
+ FILE *ret;
+
+ if ( here == NOT_HERE )
+ fail("cannot access the current directory");
+
+ szcwd = root ? 1 + strlen(root) : 2;
+
+ if ( (cwd = malloc(szcwd)) == 0 )
+ return 0;
+
+ while ( !(ret = fopen(template, "r")) ) {
+ if ( getcwd(cwd, szcwd) == 0 ) {
+ if ( errno == ERANGE )
+ goto up;
+ break;
+ }
+
+ if ( root && (strcmp(root, cwd) == 0) )
+ break; /* ran out of paths to search */
+ else if ( (strcmp(cwd, "/") == 0) || (*cwd == 0) )
+ break; /* reached / */
+
+ up: if ( chdir("..") == -1 )
+ break;
+ }
+ free(cwd);
+ popd(here);
+ return ret;
+} /* open_template */
+
+
+static Istring inbuf;
+static int psp;
+
+static int
+prepare(FILE *input)
+{
+ int c;
+
+ CREATE(inbuf);
+ psp = 0;
+ while ( (c = getc(input)) != EOF )
+ EXPAND(inbuf) = c;
+ fclose(input);
+ return 1;
+}
+
+static int
+pull()
+{
+ return psp < S(inbuf) ? T(inbuf)[psp++] : EOF;
+}
+
+static int
+peek(int offset)
+{
+ int pos = (psp + offset)-1;
+
+ if ( pos >= 0 && pos < S(inbuf) )
+ return T(inbuf)[pos];
+
+ return EOF;
+}
+
+static int
+shift(int shiftwidth)
+{
+ psp += shiftwidth;
+ return psp;
+}
+
+static int*
+cursor()
+{
+ return T(inbuf) + psp;
+}
+
+
+static int
+thesame(int *p, char *pat)
+{
+ int i;
+
+ for ( i=0; pat[i]; i++ ) {
+ if ( pat[i] == ' ' ) {
+ if ( !isspace(peek(i+1)) ) {
+ return 0;
+ }
+ }
+ else if ( tolower(peek(i+1)) != pat[i] ) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+static int
+istag(int *p, char *pat)
+{
+ int c;
+
+ if ( thesame(p, pat) ) {
+ c = peek(strlen(pat)+1);
+ return (c == '>' || isspace(c));
+ }
+ return 0;
+}
+
+
+/* finclude() includes some (unformatted) source
+ */
+static void
+finclude(MMIOT *doc, FILE *out, int flags, int whence)
+{
+ int c;
+ Cstring include;
+ FILE *f;
+
+ CREATE(include);
+
+ while ( (c = pull()) != '(' )
+ ;
+
+ while ( (c=pull()) != ')' && c != EOF )
+ EXPAND(include) = c;
+
+ if ( c != EOF ) {
+ EXPAND(include) = 0;
+ S(include)--;
+
+ if (( f = fopen(T(include), "r") )) {
+ while ( (c = getc(f)) != EOF )
+ putc(c, out);
+ fclose(f);
+ }
+ }
+ DELETE(include);
+}
+
+
+/* fdirname() prints out the directory part of a path
+ */
+static void
+fdirname(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ char *p;
+
+ if ( pagename && (p = basename(pagename)) )
+ fwrite(pagename, strlen(pagename)-strlen(p), 1, output);
+}
+
+
+/* fbasename() prints out the file name part of a path
+ */
+static void
+fbasename(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ char *p;
+
+ if ( pagename ) {
+ p = basename(pagename);
+
+ if ( !p )
+ p = pagename;
+
+ if ( p )
+ fwrite(p, strlen(p), 1, output);
+ }
+}
+
+
+/* ftitle() prints out the document title
+ */
+static void
+ftitle(MMIOT *doc, FILE* output, int flags, int whence)
+{
+ char *h;
+ if ( (h = mkd_doc_title(doc)) == 0 && pagename )
+ h = pagename;
+
+ if ( h )
+ mkd_generateline(h, strlen(h), output, flags);
+}
+
+
+/* fdate() prints out the document date
+ */
+static void
+fdate(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ char *h;
+
+ if ( (h = mkd_doc_date(doc)) || ( infop && (h = ctime(&infop->st_mtime)) ) )
+ mkd_generateline(h, strlen(h), output, flags|MKD_TAGTEXT);
+}
+
+
+/* fauthor() prints out the document author
+ */
+static void
+fauthor(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ char *h = mkd_doc_author(doc);
+
+#if HAVE_PWD_H
+ if ( (h == 0) && me )
+ h = me->pw_gecos;
+#endif
+
+ if ( h )
+ mkd_generateline(h, strlen(h), output, flags);
+}
+
+
+/* fconfig() prints out a tabular version of
+ * tabular versions of the flags.
+ */
+static void
+fconfig(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ mkd_mmiot_flags(output, doc, (whence & (INHEAD|INTAG)) ? 0 : 1);
+}
+
+
+/* fversion() prints out the document version
+ */
+static void
+fversion(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ fwrite(markdown_version, strlen(markdown_version), 1, output);
+}
+
+
+/* fbody() prints out the document
+ */
+static void
+fbody(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ mkd_generatehtml(doc, output);
+}
+
+/* ftoc() prints out the table of contents
+ */
+static void
+ftoc(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ mkd_generatetoc(doc, output);
+}
+
+/* fstyle() prints out the document's style section
+ */
+static void
+fstyle(MMIOT *doc, FILE *output, int flags, int whence)
+{
+ mkd_generatecss(doc, output);
+}
+
+
+/*
+ * theme expansions we love:
+ * <?theme date?> -- the document date (file or header date)
+ * <?theme title?> -- the document title (header title or document name)
+ * <?theme author?> -- the document author (header author or document owner)
+ * <?theme version?> -- the version#
+ * <?theme body?> -- the document body
+ * <?theme source?> -- the filename part of the document name
+ * <?theme dir?> -- the directory part of the document name
+ * <?theme html?> -- the html file name
+ * <?theme style?> -- document-supplied style blocks
+ * <?theme include(file)?> -- include a file.
+ */
+static struct _keyword {
+ char *kw;
+ int where;
+ void (*what)(MMIOT*,FILE*,int,int);
+} keyword[] = {
+ { "author?>", 0xffff, fauthor },
+ { "body?>", INBODY, fbody },
+ { "toc?>", INBODY, ftoc },
+ { "date?>", 0xffff, fdate },
+ { "dir?>", 0xffff, fdirname },
+ { "include(", 0xffff, finclude },
+ { "source?>", 0xffff, fbasename },
+ { "style?>", INHEAD, fstyle },
+ { "title?>", 0xffff, ftitle },
+ { "version?>", 0xffff, fversion },
+ { "config?>", 0xffff, fconfig },
+};
+#define NR(x) (sizeof x / sizeof x[0])
+
+
+/* spin() - run through the theme template, looking for <?theme expansions
+ */
+void
+spin(FILE *template, MMIOT *doc, FILE *output)
+{
+ int c;
+ int *p;
+ int flags;
+ int where = 0x0;
+ int i;
+
+ prepare(template);
+
+ while ( (c = pull()) != EOF ) {
+ if ( c == '<' ) {
+ if ( peek(1) == '!' && peek(2) == '-' && peek(3) == '-' ) {
+ fputs("<!--", output);
+ shift(3);
+ do {
+ putc(c=pull(), output);
+ } while ( ! (c == '-' && peek(1) == '-' && peek(2) == '>') );
+ }
+ else if ( (peek(1) == '?') && thesame(cursor(), "?theme ") ) {
+ shift(strlen("?theme "));
+
+ while ( ((c = pull()) != EOF) && isspace(c) )
+ ;
+
+ shift(-1);
+ p = cursor();
+
+ if ( where & INTAG )
+ flags = MKD_TAGTEXT;
+ else if ( where & INHEAD )
+ flags = MKD_NOIMAGE|MKD_NOLINKS;
+ else
+ flags = 0;
+
+ for (i=0; i < NR(keyword); i++)
+ if ( thesame(p, keyword[i].kw) ) {
+ if ( keyword[i].where & where )
+ (*keyword[i].what)(doc,output,flags,where);
+ break;
+ }
+
+ while ( (c = pull()) != EOF && (c != '?' && peek(1) != '>') )
+ ;
+ shift(1);
+ }
+ else
+ putc(c, output);
+
+ if ( istag(cursor(), "head") ) {
+ where |= INHEAD;
+ where &= ~INBODY;
+ }
+ else if ( istag(cursor(), "body") ) {
+ where &= ~INHEAD;
+ where |= INBODY;
+ }
+ where |= INTAG;
+ continue;
+ }
+ else if ( c == '>' )
+ where &= ~INTAG;
+
+ putc(c, output);
+ }
+} /* spin */
+
+
+void
+main(argc, argv)
+char **argv;
+{
+ char *template = "page.theme";
+ char *source = "stdin";
+ FILE *tmplfile;
+ int opt;
+ int force = 0;
+ MMIOT *doc;
+ struct stat sourceinfo;
+
+ opterr=1;
+ pgm = basename(argv[0]);
+
+ while ( (opt=getopt(argc, argv, "fd:t:p:o:V")) != EOF ) {
+ switch (opt) {
+ case 'd': root = optarg;
+ break;
+ case 'p': pagename = optarg;
+ break;
+ case 'f': force = 1;
+ break;
+ case 't': template = optarg;
+ break;
+ case 'o': output = optarg;
+ break;
+ case 'V': printf("theme+discount %s\n", markdown_version);
+ exit(0);
+ default: fprintf(stderr, "usage: %s [-V] [-d dir] [-p pagename] [-t template] [-o html] [file]\n", pgm);
+ exit(1);
+ }
+ }
+
+ tmplfile = open_template(template);
+
+ argc -= optind;
+ argv += optind;
+
+
+ if ( argc > 0 ) {
+ int added_text=0;
+
+ if ( (source = malloc(strlen(argv[0]) + strlen("/index.text") + 1)) == 0 )
+ fail("out of memory allocating name buffer");
+
+ strcpy(source,argv[0]);
+ if ( (stat(source, &sourceinfo) == 0) && S_ISDIR(sourceinfo.st_mode) )
+ strcat(source, "/index");
+
+ if ( !freopen(source, "r", stdin) ) {
+ strcat(source, ".text");
+ added_text = 1;
+ if ( !freopen(source, "r", stdin) )
+ fail("can't open either %s or %s", argv[0], source);
+ }
+
+ if ( !output ) {
+ char *p, *q;
+ output = alloca(strlen(source) + strlen(".html") + 1);
+
+ strcpy(output, source);
+
+ if (( p = strchr(output, '/') ))
+ q = strrchr(p+1, '.');
+ else
+ q = strrchr(output, '.');
+
+ if ( q )
+ *q = 0;
+ else
+ q = output + strlen(output);
+
+ strcat(q, ".html");
+ }
+ }
+ if ( output ) {
+ if ( force )
+ unlink(output);
+ if ( !freopen(output, "w", stdout) )
+ fail("can't write to %s", output);
+ }
+
+ if ( !pagename )
+ pagename = source;
+
+ if ( (doc = mkd_in(stdin, 0)) == 0 )
+ fail("can't read %s", source ? source : "stdin");
+
+ if ( fstat(fileno(stdin), &sourceinfo) == 0 )
+ infop = &sourceinfo;
+
+#if HAVE_GETPWUID
+ me = getpwuid(infop ? infop->st_uid : getuid());
+
+ if ( (root = strdup(me->pw_dir)) == 0 )
+ fail("out of memory");
+#endif
+
+ if ( !mkd_compile(doc, MKD_TOC) )
+ fail("couldn't compile input");
+
+ if ( tmplfile )
+ spin(tmplfile,doc,stdout);
+ else
+ mkd_generatehtml(doc, stdout);
+
+ mkd_cleanup(doc);
+ exit(0);
+}
diff --git a/toc.c b/toc.c
new file mode 100644
index 0000000..f790614
--- /dev/null
+++ b/toc.c
@@ -0,0 +1,101 @@
+/*
+ * toc -- spit out a table of contents based on header blocks
+ *
+ * Copyright (C) 2008 Jjgod Jiang, David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+/* write an header index
+ */
+int
+mkd_toc(Document *p, char **doc)
+{
+ Paragraph *tp, *srcp;
+ int last_hnumber = 0;
+ Cstring res;
+ int size;
+
+ if ( !(doc && p && p->ctx) ) return -1;
+
+ *doc = 0;
+
+ if ( ! (p->ctx->flags & MKD_TOC) ) return 0;
+
+ CREATE(res);
+ RESERVE(res, 100);
+
+ for ( tp = p->code; tp ; tp = tp->next ) {
+ if ( tp->typ == SOURCE ) {
+ for ( srcp = tp->down; srcp; srcp = srcp->next ) {
+ if ( srcp->typ == HDR && srcp->text ) {
+
+ if ( last_hnumber >= srcp->hnumber ) {
+ while ( last_hnumber > srcp->hnumber ) {
+ Csprintf(&res, "%*s</ul></li>\n", last_hnumber-1,"");
+ --last_hnumber;
+ }
+ }
+
+ while ( srcp->hnumber > last_hnumber ) {
+ Csprintf(&res, "%*s%s<ul>\n", last_hnumber, "",
+ last_hnumber ? "<li>" : "");
+ ++last_hnumber;
+ }
+ Csprintf(&res, "%*s<li><a href=\"#", srcp->hnumber, "");
+ mkd_string_to_anchor(T(srcp->text->text),
+ S(srcp->text->text), Csputc, &res,1);
+ Csprintf(&res, "\">");
+ mkd_string_to_anchor(T(srcp->text->text),
+ S(srcp->text->text), Csputc, &res,0);
+ Csprintf(&res, "</a>");
+ Csprintf(&res, "</li>\n");
+ }
+ }
+ }
+ }
+
+ while ( last_hnumber > 0 ) {
+ --last_hnumber;
+ Csprintf(&res, last_hnumber ? "%*s</ul></li>\n" : "%*s</ul>\n", last_hnumber, "");
+ }
+
+ if ( (size = S(res)) > 0 ) {
+ EXPAND(res) = 0;
+ /* HACK ALERT! HACK ALERT! HACK ALERT! */
+ *doc = T(res); /* we know that a T(Cstring) is a character pointer
+ * so we can simply pick it up and carry it away,
+ * leaving the husk of the Ctring on the stack
+ * END HACK ALERT
+ */
+ }
+ else
+ DELETE(res);
+ return size;
+}
+
+
+/* write an header index
+ */
+int
+mkd_generatetoc(Document *p, FILE *out)
+{
+ char *buf = 0;
+ int sz = mkd_toc(p, &buf);
+ int ret = EOF;
+
+ if ( sz > 0 )
+ ret = fwrite(buf, 1, sz, out);
+
+ if ( buf ) free(buf);
+
+ return (ret == sz) ? ret : EOF;
+}
diff --git a/tools/checkbits.sh b/tools/checkbits.sh
new file mode 100755
index 0000000..afe2108
--- /dev/null
+++ b/tools/checkbits.sh
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+trap "rm -f in.markdown.h in.mkdio.h" EXIT
+
+grep '#define MKD_' markdown.h | awk '$3 ~ /0x0/ {print $2,$3;}' > in.markdown.h
+grep '#define MKD_' mkdio.h | awk '$3 ~ /0x0/ {print $2,$3;}' > in.mkdio.h
+
+diff -c -bw in.markdown.h in.mkdio.h
+retcode=$?
+
+exit $retcode
diff --git a/tools/cols.c b/tools/cols.c
new file mode 100644
index 0000000..68ecc59
--- /dev/null
+++ b/tools/cols.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+main(argc, argv)
+char **argv;
+{
+ register c;
+ int xp;
+ int width;
+
+ if ( argc != 2 ) {
+ fprintf(stderr, "usage: %s width\n", argv[0]);
+ exit(1);
+ }
+ else if ( (width=atoi(argv[1])) < 1 ) {
+ fprintf(stderr, "%s: please set width to > 0\n", argv[0]);
+ exit(1);
+ }
+
+
+ for ( xp = 1; (c = getchar()) != EOF; xp++ ) {
+ while ( c & 0xC0 ) {
+ /* assume that (1) the output device understands utf-8, and
+ * (2) the only c & 0x80 input is utf-8.
+ */
+ do {
+ if ( xp <= width )
+ putchar(c);
+ } while ( (c = getchar()) != EOF && (c & 0x80) && !(c & 0x40) );
+ ++xp;
+ }
+ if ( c == '\n' )
+ xp = 0;
+ if ( xp <= width )
+ putchar(c);
+ }
+ exit(0);
+}
diff --git a/tools/echo.c b/tools/echo.c
new file mode 100644
index 0000000..6ed30bd
--- /dev/null
+++ b/tools/echo.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <string.h>
+
+
+main(argc, argv)
+char **argv;
+{
+ int nl = 1;
+ int i;
+
+ if ( (argc > 1) && (strcmp(argv[1], "-n") == 0) ) {
+ ++argv;
+ --argc;
+ nl = 0;
+ }
+
+ for ( i=1; i < argc; i++ ) {
+ if ( i > 1 ) putchar(' ');
+ fputs(argv[i], stdout);
+ }
+ if (nl) putchar('\n');
+ exit(0);
+}
diff --git a/version.c.in b/version.c.in
new file mode 100644
index 0000000..219b935
--- /dev/null
+++ b/version.c.in
@@ -0,0 +1,22 @@
+#include "config.h"
+
+char markdown_version[] = VERSION
+#if @TABSTOP@ != 4
+ " TAB=@TABSTOP@"
+#endif
+#if USE_AMALLOC
+ " DEBUG"
+#endif
+#if USE_DISCOUNT_DL
+# if USE_EXTRA_DL
+ " DL=BOTH"
+# else
+ " DL=DISCOUNT"
+# endif
+#elif USE_EXTRA_DL
+ " DL=EXTRA"
+#else
+ " DL=NONE"
+#endif
+
+ ;
diff --git a/xml.c b/xml.c
new file mode 100644
index 0000000..5e58389
--- /dev/null
+++ b/xml.c
@@ -0,0 +1,82 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "config.h"
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+/* return the xml version of a character
+ */
+static char *
+mkd_xmlchar(unsigned char c)
+{
+ switch (c) {
+ case '<': return "<";
+ case '>': return ">";
+ case '&': return "&";
+ case '"': return """;
+ case '\'': return "'";
+ default: if ( isascii(c) || (c & 0x80) )
+ return 0;
+ return "";
+ }
+}
+
+
+/* write output in XML format
+ */
+int
+mkd_generatexml(char *p, int size, FILE *out)
+{
+ unsigned char c;
+ char *entity;
+
+ while ( size-- > 0 ) {
+ c = *p++;
+
+ if ( entity = mkd_xmlchar(c) )
+ fputs(entity, out);
+ else
+ fputc(c, out);
+ }
+ return 0;
+}
+
+
+/* build a xml'ed version of a string
+ */
+int
+mkd_xml(char *p, int size, char **res)
+{
+ unsigned char c;
+ char *entity;
+ Cstring f;
+
+ CREATE(f);
+ RESERVE(f, 100);
+
+ while ( size-- > 0 ) {
+ c = *p++;
+ if ( entity = mkd_xmlchar(c) )
+ Cswrite(&f, entity, strlen(entity));
+ else
+ Csputc(c, &f);
+ }
+ /* HACK ALERT! HACK ALERT! HACK ALERT! */
+ *res = T(f); /* we know that a T(Cstring) is a character pointer */
+ /* so we can simply pick it up and carry it away, */
+ return S(f); /* leaving the husk of the Ctring on the stack */
+ /* END HACK ALERT */
+}
diff --git a/xmlpage.c b/xmlpage.c
new file mode 100644
index 0000000..96ed2b7
--- /dev/null
+++ b/xmlpage.c
@@ -0,0 +1,48 @@
+/*
+ * xmlpage -- write a skeletal xhtml page
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "cstring.h"
+#include "markdown.h"
+#include "amalloc.h"
+
+
+int
+mkd_xhtmlpage(Document *p, int flags, FILE *out)
+{
+ char *title;
+ extern char *mkd_doc_title(Document *);
+
+ if ( mkd_compile(p, flags) ) {
+ fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ fprintf(out, "<!DOCTYPE html "
+ " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
+ " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
+
+ fprintf(out, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n");
+
+ fprintf(out, "<head>\n");
+ if ( title = mkd_doc_title(p) )
+ fprintf(out, "<title>%s</title>\n", title);
+ mkd_generatecss(p, out);
+ fprintf(out, "</head>\n");
+
+ fprintf(out, "<body>\n");
+ mkd_generatehtml(p, out);
+ fprintf(out, "</body>\n");
+ fprintf(out, "</html>\n");
+
+ mkd_cleanup(p);
+
+ return 0;
+ }
+ return -1;
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/discount.git
More information about the Reproducible-commits
mailing list