Browse Source

Added libdiscount to add markdown support for help files.

mingodad 13 years ago
parent
commit
77a5fc19d1
100 changed files with 13081 additions and 0 deletions
  1. 47 0
      discount/COPYRIGHT
  2. 35 0
      discount/CREDITS
  3. 61 0
      discount/Csio.c
  4. 41 0
      discount/INSTALL
  5. 135 0
      discount/Makefile
  6. 135 0
      discount/Makefile.in
  7. 40 0
      discount/Plan9/README
  8. 169 0
      discount/Plan9/markdown.1
  9. 332 0
      discount/Plan9/markdown.2
  10. 543 0
      discount/Plan9/markdown.6
  11. 37 0
      discount/Plan9/mkfile
  12. 16 0
      discount/README
  13. 1 0
      discount/VERSION
  14. 111 0
      discount/amalloc.c
  15. 29 0
      discount/amalloc.h
  16. 43 0
      discount/basename.c
  17. 34 0
      discount/blocktags
  18. 2 0
      discount/config.cmd
  19. 27 0
      discount/config.h
  20. 2 0
      discount/config.mak
  21. 5 0
      discount/config.md
  22. 35 0
      discount/config.sub
  23. 1695 0
      discount/configure.inc
  24. 149 0
      discount/configure.sh
  25. 85 0
      discount/css.c
  26. 77 0
      discount/cstring.h
  27. 58 0
      discount/discount.cbp
  28. 49 0
      discount/docheader.c
  29. 152 0
      discount/dumptree.c
  30. 188 0
      discount/emmatch.c
  31. 84 0
      discount/flags.c
  32. 1781 0
      discount/generate.c
  33. 22 0
      discount/html5.c
  34. 116 0
      discount/libdiscount.cbp
  35. 19 0
      discount/librarian.sh
  36. 208 0
      discount/main.c
  37. 9 0
      discount/main.cpp
  38. 45 0
      discount/makepage.1
  39. 86 0
      discount/makepage.c
  40. 171 0
      discount/markdown.1
  41. 137 0
      discount/markdown.3
  42. 1020 0
      discount/markdown.7
  43. 1302 0
      discount/markdown.c
  44. 193 0
      discount/markdown.h
  45. 49 0
      discount/markdowncpp.cbp
  46. 46 0
      discount/markdowncxx.cpp
  47. 9 0
      discount/markdowncxx.h
  48. 71 0
      discount/mkd-callbacks.3
  49. 205 0
      discount/mkd-extensions.7
  50. 186 0
      discount/mkd-functions.3
  51. 41 0
      discount/mkd-line.3
  52. 52 0
      discount/mkd2html.1
  53. 185 0
      discount/mkd2html.c
  54. 400 0
      discount/mkdio.c
  55. 111 0
      discount/mkdio.h
  56. 111 0
      discount/mkdio.h.in
  57. 91 0
      discount/mktags.c
  58. 135 0
      discount/pgm_options.c
  59. 9 0
      discount/pgm_options.h
  60. 157 0
      discount/resource.c
  61. 39 0
      discount/setup.c
  62. 91 0
      discount/tags.c
  63. 19 0
      discount/tags.h
  64. 27 0
      discount/tests/autolink.t
  65. 27 0
      discount/tests/automatic.t
  66. 16 0
      discount/tests/backslash.t
  67. 17 0
      discount/tests/callbacks.t
  68. 13 0
      discount/tests/chrome.text
  69. 91 0
      discount/tests/code.t
  70. 29 0
      discount/tests/compat.t
  71. 31 0
      discount/tests/crash.t
  72. 49 0
      discount/tests/div.t
  73. 91 0
      discount/tests/dl.t
  74. 9 0
      discount/tests/embedlinks.text
  75. 19 0
      discount/tests/emphasis.t
  76. 35 0
      discount/tests/extrafootnotes.t
  77. 33 0
      discount/tests/flow.t
  78. 16 0
      discount/tests/footnotes.t
  79. 85 0
      discount/tests/functions.sh
  80. 26 0
      discount/tests/header.t
  81. 141 0
      discount/tests/html.t
  82. 17 0
      discount/tests/html5.t
  83. 14 0
      discount/tests/links.text
  84. 130 0
      discount/tests/linkylinky.t
  85. 21 0
      discount/tests/linkypix.t
  86. 155 0
      discount/tests/list.t
  87. 38 0
      discount/tests/list3deep.t
  88. 12 0
      discount/tests/misc.t
  89. 44 0
      discount/tests/pandoc.t
  90. 17 0
      discount/tests/para.t
  91. 12 0
      discount/tests/paranoia.t
  92. 77 0
      discount/tests/peculiarities.t
  93. 20 0
      discount/tests/pseudo.t
  94. 27 0
      discount/tests/reddit.t
  95. 14 0
      discount/tests/reparse.t
  96. 91 0
      discount/tests/schiraldi.t
  97. 25 0
      discount/tests/smarty.t
  98. 29 0
      discount/tests/snakepit.t
  99. 16 0
      discount/tests/strikethrough.t
  100. 34 0
      discount/tests/style.t

+ 47 - 0
discount/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.

+ 35 - 0
discount/CREDITS

@@ -0,0 +1,35 @@
+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 ([email protected])
+
+
+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.
+John J. Foerch  --  bug reports about incorrect `&ndash;` and `&mdash;`
+                    translations.
+
+		    

+ 61 - 0
discount/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);
+}

+ 41 - 0
discount/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''

+ 135 - 0
discount/Makefile

@@ -0,0 +1,135 @@
+CC=gcc -I. -L.
+CFLAGS=-g
+AR=/mingw/bin/ar
+RANLIB=/mingw/bin/ranlib
+
+BINDIR=/usr/local/bin
+MANDIR=/usr/local/share/man
+LIBDIR=/usr/local/lib
+INCDIR=/usr/local/include
+
+PGMS=markdown
+SAMPLE_PGMS=mkd2html makepage
+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 
+TESTFRAMEWORK=echo cols
+
+MAN3PAGES=mkd-callbacks.3 mkd-functions.3 markdown.3 mkd-line.3
+
+all: $(PGMS) $(SAMPLE_PGMS) $(TESTFRAMEWORK)
+
+install: $(PGMS) $(DESTDIR)/$(BINDIR) $(DESTDIR)/$(LIBDIR) $(DESTDIR)/$(INCDIR)
+	/bin/install -s -m 755 $(PGMS) $(DESTDIR)/$(BINDIR)
+	./librarian.sh install libmarkdown VERSION $(DESTDIR)/$(LIBDIR)
+	/bin/install -m 444 mkdio.h $(DESTDIR)/$(INCDIR)
+
+install.everything: install install.samples install.man
+
+install.samples: $(SAMPLE_PGMS) install $(DESTDIR)/$(BINDIR)
+	/bin/install -s -m 755 $(SAMPLE_PGMS) $(DESTDIR)/$(BINDIR)
+	/g/c/discount-2.1.2/config.md $(DESTDIR)/$(MANDIR)/man1
+	/bin/install -m 444 theme.1 makepage.1 mkd2html.1 $(DESTDIR)/$(MANDIR)/man1
+
+install.man:
+	/g/c/discount-2.1.2/config.md $(DESTDIR)/$(MANDIR)/man3
+	/bin/install -m 444 $(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
+	/g/c/discount-2.1.2/config.md $(DESTDIR)/$(MANDIR)/man7
+	/bin/install -m 444 markdown.7 mkd-extensions.7 $(DESTDIR)/$(MANDIR)/man7
+	/g/c/discount-2.1.2/config.md $(DESTDIR)/$(MANDIR)/man1
+	/bin/install -m 444 markdown.1 $(DESTDIR)/$(MANDIR)/man1
+
+install.everything: install install.man
+
+$(DESTDIR)/$(BINDIR):
+	/g/c/discount-2.1.2/config.md $(DESTDIR)/$(BINDIR)
+
+$(DESTDIR)/$(INCDIR):
+	/g/c/discount-2.1.2/config.md $(DESTDIR)/$(INCDIR)
+
+$(DESTDIR)/$(LIBDIR):
+	/g/c/discount-2.1.2/config.md $(DESTDIR)/$(LIBDIR)
+
+version.o: version.c VERSION
+	$(CC) -DVERSION=\"`cat VERSION`\" -c version.c
+
+tags.o: tags.c blocktags
+
+blocktags: mktags
+	./mktags > blocktags
+
+# example programs
+theme:  theme.o $(MKDLIB) mkdio.h
+	$(CC) -o theme theme.o -lmarkdown 
+
+
+mkd2html:  mkd2html.o $(MKDLIB) mkdio.h
+	$(CC) -o mkd2html mkd2html.o -lmarkdown 
+
+markdown: main.o pgm_options.o $(MKDLIB)
+	$(CC) $(CFLAGS) -o markdown main.o pgm_options.o -lmarkdown 
+	
+makepage:  makepage.c pgm_options.o $(MKDLIB) mkdio.h
+	$(CC) $(CFLAGS) -o makepage makepage.c pgm_options.o -lmarkdown 
+
+pgm_options.o: pgm_options.c mkdio.h config.h
+	$(CC) -I. -c pgm_options.c
+
+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) $(TESTFRAMEWORK) verify
+	@for x in tests/*.t; do \
+	    HERE=`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) $(TESTFRAMEWORK) $(SAMPLE_PGMS) *.o
+	rm -f $(MKDLIB) `./librarian.sh files $(MKDLIB) VERSION`
+
+distclean spotless: clean
+	rm -f Makefile version.c mkdio.h config.cmd config.sub config.h config.mak config.log config.md
+
+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
+pgm_options.o: pgm_options.c pgm_options.h 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

+ 135 - 0
discount/Makefile.in

@@ -0,0 +1,135 @@
+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
+@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@
+TESTFRAMEWORK=echo cols
+
+MAN3PAGES=mkd-callbacks.3 mkd-functions.3 markdown.3 mkd-line.3
+
+all: $(PGMS) $(SAMPLE_PGMS) $(TESTFRAMEWORK)
+
+install: $(PGMS) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(INCDIR)
+	@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 $(DESTDIR)$(BINDIR)
+	@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
+
+$(DESTDIR)$(BINDIR):
+	@INSTALL_DIR@ $(DESTDIR)$(BINDIR)
+
+$(DESTDIR)$(INCDIR):
+	@INSTALL_DIR@ $(DESTDIR)$(INCDIR)
+
+$(DESTDIR)$(LIBDIR):
+	@INSTALL_DIR@ $(DESTDIR)$(LIBDIR)
+
+version.o: version.c VERSION
+	$(CC) -DVERSION=\"`cat VERSION`\" -c version.c
+
+tags.o: tags.c blocktags
+
+blocktags: mktags
+	./mktags > blocktags
+
+# example programs
+@THEME@theme:  theme.o $(MKDLIB) mkdio.h
+@THEME@	$(CC) -o theme theme.o -lmarkdown @LIBS@
+
+
+mkd2html:  mkd2html.o $(MKDLIB) mkdio.h
+	$(CC) -o mkd2html mkd2html.o -lmarkdown @LIBS@
+
+markdown: main.o pgm_options.o $(MKDLIB)
+	$(CC) $(CFLAGS) -o markdown main.o pgm_options.o -lmarkdown @LIBS@
+	
+makepage:  makepage.c pgm_options.o $(MKDLIB) mkdio.h
+	$(CC) $(CFLAGS) -o makepage makepage.c pgm_options.o -lmarkdown @LIBS@
+
+pgm_options.o: pgm_options.c mkdio.h config.h
+	$(CC) -I. -c pgm_options.c
+
+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) $(TESTFRAMEWORK) 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) $(TESTFRAMEWORK) $(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
+pgm_options.o: pgm_options.c pgm_options.h 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

+ 40 - 0
discount/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.

+ 169 - 0
discount/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.

+ 332 - 0
discount/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.

+ 543 - 0
discount/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 &lt;
+and
+.BR &amp; .
+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 &amp; .
+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 &lsquo;text&rsquo; ).
+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 ( &hellip; )
+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 &copy; ,
+the Copyright symbol entity.)
+.TP
+Fractions
+A small set of plain-text shorthands for fractions is recognized.
+.B 1/4
+becomes
+.BR &frac14; ,
+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''.

+ 37 - 0
discount/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'

+ 16 - 0
discount/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 <[email protected]>;
+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

+ 1 - 0
discount/VERSION

@@ -0,0 +1 @@
+2.1.3

+ 111 - 0
discount/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");
+    }
+}

+ 29 - 0
discount/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*/

+ 43 - 0
discount/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);
+}

+ 34 - 0
discount/blocktags

@@ -0,0 +1,34 @@
+static struct kw blocktags[] = {
+   { "P", 1, 0 },
+   { "BR", 2, 1 },
+   { "DL", 2, 0 },
+   { "H1", 2, 0 },
+   { "H2", 2, 0 },
+   { "H3", 2, 0 },
+   { "H4", 2, 0 },
+   { "H5", 2, 0 },
+   { "H6", 2, 0 },
+   { "HR", 2, 1 },
+   { "OL", 2, 0 },
+   { "UL", 2, 0 },
+   { "BDO", 3, 0 },
+   { "DFN", 3, 0 },
+   { "DIV", 3, 0 },
+   { "MAP", 3, 0 },
+   { "PRE", 3, 0 },
+   { "WBR", 3, 0 },
+   { "XMP", 3, 0 },
+   { "NOBR", 4, 0 },
+   { "STYLE", 5, 0 },
+   { "TABLE", 5, 0 },
+   { "CENTER", 6, 0 },
+   { "IFRAME", 6, 0 },
+   { "OBJECT", 6, 0 },
+   { "SCRIPT", 6, 0 },
+   { "ADDRESS", 7, 0 },
+   { "LISTING", 7, 0 },
+   { "PLAINTEXT", 9, 0 },
+   { "BLOCKQUOTE", 10, 0 },
+};
+
+#define NR_blocktags 30

+ 2 - 0
discount/config.cmd

@@ -0,0 +1,2 @@
+#! /bin/sh
+CC='gcc'  ./configure.sh 

+ 27 - 0
discount/config.h

@@ -0,0 +1,27 @@
+/*
+ * configuration for markdown, generated Mon Dec  5 22:48:15 GMT 2011
+ * by mingo2@mingov
+ */
+#ifndef __AC_MARKDOWN_D
+#define __AC_MARKDOWN_D 1
+
+
+#define OS_MINGW32_NT 1
+#define USE_DISCOUNT_DL 1
+#define DWORD unsigned long
+#define WORD unsigned short
+#define BYTE unsigned char
+#define HAVE_SRAND 1
+#define INITRNG(x) srand((unsigned int)x)
+#define HAVE_BZERO 1
+#define HAVE_RAND 1
+#define COINTOSS() (rand()&1)
+#define HAVE_STRCASECMP 1
+#define HAVE_STRNCASECMP 1
+#define HAVE_GETCWD 1
+#define TABSTOP 4
+#define HAVE_MALLOC_H 1
+#define PATH_FIND "/bin/find"
+#define PATH_SED "/bin/sed"
+
+#endif/* __AC_MARKDOWN_D */

+ 2 - 0
discount/config.mak

@@ -0,0 +1,2 @@
+HAVE_FIND = 1
+HAVE_SED = 1

+ 5 - 0
discount/config.md

@@ -0,0 +1,5 @@
+#! /bin/sh
+# script generated Mon Dec 5 22:48:16 GMT 2011 by configure.sh
+
+test -d "$1" || mkdir -p "$1"
+exit 0

+ 35 - 0
discount/config.sub

@@ -0,0 +1,35 @@
+s;@CPP@;/mingw/bin/cpp;g
+s;@CPPFLAGS@;;g
+s;@INSTALL@;/bin/install;g
+s;@INSTALL_PROGRAM@;/bin/install -s -m 755;g
+s;@INSTALL_DATA@;/bin/install -m 444;g
+s;@INSTALL_DIR@;/g/c/discount-2.1.2/config.md;g
+s;@CC@;gcc;g
+s;@AR@;/mingw/bin/ar;g
+s;@RANLIB@;/mingw/bin/ranlib;g
+s:@DWORD@:unsigned long:g
+s:@WORD@:unsigned short:g
+s:@BYTE@:unsigned char:g
+s;@THEME@;;g
+s;@TABSTOP@;4;g
+s;@AMALLOC@;;g
+s;@LIBS@;;g
+s;@CONFIGURE_FILES@;config.cmd config.sub config.h config.mak config.log config.md;g
+s;@GENERATED_FILES@;Makefile version.c mkdio.h;g
+s;@CFLAGS@;-g;g
+s;@LDFLAGS@;-g;g
+s;@srcdir@;/g/c/discount-2.1.2;g
+s;@prefix@;/usr/local;g
+s;@exedir@;/usr/local/bin;g
+s;@sbindir@;/usr/local/sbin;g
+s;@libdir@;/usr/local/lib;g
+s;@libexec@;/usr/local/lib;g
+s;@confdir@;/etc;g
+s;@mandir@;/usr/local/share/man;g
+s;@FIND@;/bin/find;g
+s;@LN_S@;;g
+s;@LN@;/bin/ln;g
+s;@AR@;/mingw/bin/ar;g
+s;@RANLIB@;/mingw/bin/ranlib;g
+s;@LD_LIBRARY_PATH@;HERE;g
+s;@SED@;/bin/sed;g

+ 1695 - 0
discount/configure.inc

@@ -0,0 +1,1695 @@
+#   @(#) 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
+
+__remove() {
+    if [ \( -x "$1" \) -a "$__MACOS_DSYM" ]; then
+	rm -rf "$1".dSYM
+    fi
+    rm -f "$@"
+}
+
+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 ngc$$.o "$@" $AC_LIBS 2>ngc$$.err; then
+	__remove ngc$$.o ngc$$.err
+	TLOG " (found)"
+	return 0
+    fi
+    __remove ngc$$.o
+    TLOG " (not found)"
+    echo "test failed:  command was $AC_CC -c -o ngc$$.o" "$@" $AC_LIBS
+    echo "output:"
+    cat ngc$$.err
+    __remove ngc$$.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 */" > ngc$$.c
+    for hdr in $*; do
+	echo "#include <$hdr>" >> ngc$$.c
+    done
+    echo "main() { }" >> ngc$$.c
+
+    LOGN "looking for header $hdr"
+
+    if __MAKEDOTO ngc$$.c; then
+	AC_DEFINE 'HAVE_'`echo $hdr | $AC_UPPERCASE | tr './' '_'` 1
+	rc=0
+    else
+	rc=1
+    fi
+    __remove 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
+    __remove ngc$$.c
+
+    while [ "$1" ]; do
+	echo "#include <$1>" >> ngc$$.c
+	shift
+    done
+
+    cat >> ngc$$.c << EOF
+main()
+{
+
+    $F;
+}
+EOF
+
+    LOGN "looking for the $B function"
+
+    if $AC_CC -o ngc$$ ngc$$.c $LIBS; then
+	AC_DEFINE `echo ${2:-HAVE_$B} | $AC_UPPERCASE` 1
+	TLOG " (found)"
+	rc=0
+    else
+	echo "offending command was:"
+	cat ngc$$.c
+	echo "$AC_CC -o ngc$$ ngc$$.c $LIBS"
+	TLOG " (not found)"
+	rc=1
+    fi
+    __remove ngc$$ ngc$$.c
+    return $rc
+}
+
+
+#
+# check to see if some structure exists
+#
+# usage: AC_CHECK_STRUCT structure {include ...}
+#
+AC_CHECK_STRUCT () {
+    struct=$1
+    shift
+
+    __remove ngc$$.c
+
+    for include in $*; do
+	echo "#include <$include>" >> ngc$$.c
+    done
+
+    cat >> ngc$$.c << EOF
+main()
+{
+    struct $struct foo;
+}
+EOF
+
+    LOGN "looking for struct $struct"
+
+    if __MAKEDOTO ngc$$.c; then
+	AC_DEFINE HAVE_STRUCT_`echo ${struct} | $AC_UPPERCASE`
+	rc=0
+    else
+	rc=1
+    fi
+    __remove ngc$$.c
+    return $rc
+}
+
+
+#
+# check to see if some type exists
+#
+# usage: AC_CHECK_TYPE type {include ...}
+#
+AC_CHECK_TYPE () {
+    type=$1
+    shift
+
+    __remove ngc$$.c
+
+    for include in $*; do
+	echo "#include <$include>" >> ngc$$.c
+    done
+
+    cat >> ngc$$.c << EOF
+main()
+{
+    $type foo;
+}
+EOF
+
+    LOGN "looking for $type type"
+
+    if __MAKEDOTO ngc$$.c; then
+	AC_DEFINE HAVE_TYPE_`echo ${type} | $AC_UPPERCASE`
+	rc=0
+    else
+	rc=1
+    fi
+    __remove 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
+
+    __remove ngc$$.c
+
+    for include in $*;do
+	echo "#include <$include>" >> ngc$$.c
+    done
+
+    cat >> ngc$$.c << EOF
+main()
+{
+    struct $struct foo;
+
+    foo.$field;
+}
+EOF
+
+    LOGN "checking that struct $struct has a $field field"
+
+    if __MAKEDOTO ngc$$.c; then
+	AC_DEFINE HAVE_`echo ${struct}_$field | $AC_UPPERCASE`
+	rc=0
+    else
+	rc=1
+    fi
+    __remove ngc$$.c
+    return $rc
+}
+
+
+#
+# check that the C compiler works
+#
+AC_PROG_CC () {
+    test "$AC_CC" && return 0
+
+    cat > 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"
+
+    unset __MACOS_DSYM
+    $AC_CC -o ngc$$ ngc$$.c
+    status=$?
+    test -d ngc$$.dSYM && __MACOS_DSYM=1
+
+    TLOGN " ($AC_CC)"
+
+    if [ $status -eq 0 ]; then
+	if $AC_CC -x c /dev/null -dM -E 2>&1 | grep '__clang__' >/dev/null; then
+	    TLOG " yuck, you're using clang"
+	    IS_BROKEN_CC=T
+	elif $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 ngc$$.o 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 ngc$$ 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"
+
+    __remove ngc$$ ngc$$.c 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 > ngc$$.c << EOF
+const char me=1;
+EOF
+    LOGN "checking for \"const\" keyword"
+
+    if __MAKEDOTO ngc$$.c; then
+	rc=0
+    else
+	AC_DEFINE 'const' '/**/'
+	rc=1
+    fi
+    __remove ngc$$.c
+    return $rc
+}
+
+
+#
+# AC_C_VOLATILE checks to see if the compiler supports the volatile keyword
+#
+AC_C_VOLATILE () {
+    echo 'f() { volatile char me=1; }' > ngc$$.c
+    LOGN "checking for \"volatile\" keyword"
+
+    if __MAKEDOTO ngc$$.c; then
+	rc=0
+    else
+	AC_DEFINE 'volatile' '/**/'
+	rc=1
+    fi
+    __remove ngc$$.c
+    return $rc
+}
+
+
+#
+# AC_C_INLINE checks to see if compiler supports the inline keyword
+#
+AC_C_INLINE() {
+    echo 'inline int foo() { return 1; }' > ngc$$.c
+    LOGN 'Checking for "inline" keyword'
+    if __MAKEDOTO ngc$$.c; then
+	rc=0
+    else
+	AC_DEFINE inline '/**/'
+	rc=1
+    fi
+    __remove ngc$$.c
+    return $rc
+}
+
+
+#
+# AC_SCALAR_TYPES checks to see if the compiler can generate 2 and 4 byte ints.
+#
+AC_SCALAR_TYPES () {
+    cat > ngc$$.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);
+}
+
+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 ngc$$.c -o ngc$$; then
+	while [ "$1" ]; do
+	    case "$1" in
+	    sub)if ./ngc$$ sub >> $__cwd/config.sub; then
+		    rc=0
+		fi;;
+	    *)  if ./ngc$$ >> $__cwd/config.h; then
+		    rc=0
+		fi ;;
+	    esac
+	    shift
+	done
+	if [ "$rc" != 0 ]; then
+	    if ./ngc$$ >> $__cwd/config.h; then
+		rc=1
+	    fi
+	fi
+    fi
+    __remove ngc$$ ngc$$.c
+    case "$rc" in
+    0) TLOG "" ;;
+    *) AC_FAIL " ** FAILED **" ;;
+    esac
+}
+
+
+#
+# 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
+	if AC_PROG_LN_S ; then
+	    __dolink=$PROG_LN_S
+	elif AC_PROG ln; then
+	    __dolink=$PROG_LN
+	elif AC_PROG cp; then
+	    __dolink=$PROG_CP
+	else
+	    __dolink=:
+	fi
+	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
+	${__dolink} \$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 > ngc$$.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 ngc$$ ngc$$.c ; then
+	if ./flock ; then
+	    LOG " (good)"
+	    HAS_FLOCK=1
+	    AC_DEFINE HAS_FLOCK
+	else
+	    LOG " (bad)"
+	fi
+    else
+	LOG " (not found)"
+    fi
+
+    __remove ngc$$ ngc$$.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 > 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 ngc$$ ngc$$.c; then
+	TLOG " (found)"
+    elif $AC_CC -o ngc$$ ngc$$.c -lresolv; then
+	TLOG " (found, needs -lresolv)"
+	AC_LIBS="$AC_LIBS -lresolv"
+    elif $AC_CC -DBIND_8_COMPAT -o ngc$$ ngc$$.c; then
+	TLOG " (found, needs BIND_8_COMPAT)"
+	AC_DEFINE BIND_8_COMPAT 1
+    elif $AC_CC -DBIND_8_COMPAT -o ngc$$ 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
+    __remove ngc$$ ngc$$.c
+    return $__ACR_rc
+}
+
+
+#
+# AC_CHECK_ALLOCA looks for alloca
+#
+AC_CHECK_ALLOCA () {
+
+    AC_PROG_CC
+    AC_CHECK_HEADERS stdlib.h
+
+    cat - > 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 ngc$$.c -o ngc$$; then
+	AC_DEFINE 'HAVE_ALLOCA_H' 1
+	status=0
+	TLOG " (found in alloca.h)"
+    elif $AC_CC ngc$$.c -o ngc$$; then
+	TLOG " (found)"
+	status=0
+    else
+	TLOG " (not found)"
+	status=1
+    fi
+    __remove ngc$$ ngc$$.c
+    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 > 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 ngc$$ ngc$$.c $LIBS; then
+	if ./ngc$$; then
+	    TLOG "(found)"
+	    AC_DEFINE 'HAVE_BASENAME' 1
+	    AC_CHECK_HEADERS libgen.h
+	else
+	    TLOG "(broken)"
+	fi
+    else
+	TLOG "(not found)"
+    fi
+    __remove ngc$$ 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;" > ngc$$.c 
+
+    if $AC_CC -c -fPIC -o ngc$$ ngc$$.c $LIBS; then
+	AC_CFLAGS="$AC_CFLAGS -fPIC"
+        LOG "(-fPIC)"
+	__rc=0
+    else
+        LOG "(none)"
+	__rc=1
+    fi
+    __remove ngc$$ 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_FAIL "Need a C compiler to build shared libraries"
+    AC_PROG_LN_S || AC_FAIL "Need to be able to make symbolic links for shared libraries"
+    AC_PROG_INSTALL || AC_FAIL "Need an install program to install shared libraries"
+    LOGN "checking whether the C compiler can build shared libraries "
+
+    echo "int some_variable = 0;" > ngc$$.c 
+
+    if $AC_CC $AC_PICFLAG -shared -o ngc$$.so 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 ngc$$.so 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 ngc$$.so 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
+
+    __remove ngc$$.so 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 > ngc$$.c << EOF
+#define FOO BAR
+
+FOO
+EOF
+
+    if $1 $2 ngc$$.c > ngc$$.o; then
+	if grep -v '#define' ngc$$.o | grep -s BAR >/dev/null; then
+	    echo "CPP=[$1], CPPFLAGS=[$2]"
+	    AC_SUB 'CPP' "$1"
+	    AC_SUB 'CPPFLAGS' "$2"
+	    rm ngc$$.c ngc$$.o
+	    return 0
+	fi
+    fi
+    rm ngc$$.c 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
+

+ 149 - 0
discount/configure.sh

@@ -0,0 +1,149 @@
+#! /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
+--with-id-anchor	Use id= anchors for table-of-contents links
+--with-github-tags	Allow `_` and `-` in <> tags
+--with-fenced-code	Allow fenced code blocks
+--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
+
+test "$WITH_FENCED_CODE" && AC_DEFINE "WITH_FENCED_CODE" 1
+test "$WITH_ID_ANCHOR" && AC_DEFINE 'WITH_ID_ANCHOR' 1
+test "$WITH_GITHUB_TAGS" && AC_DEFINE 'WITH_GITHUB_TAGS' 1
+
+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_C_INLINE
+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

+ 85 - 0
discount/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;
+}

+ 77 - 0
discount/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*/

+ 58 - 0
discount/discount.cbp

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="discount" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="Debug">
+				<Option output="discount" prefix_auto="1" extension_auto="1" />
+				<Option object_output="obj/Debug/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Option use_console_runner="0" />
+				<Compiler>
+					<Add option="-Wall" />
+					<Add option="-g" />
+				</Compiler>
+			</Target>
+			<Target title="Release">
+				<Option output="discount" prefix_auto="1" extension_auto="1" />
+				<Option object_output="obj/Release/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Option use_console_runner="0" />
+				<Compiler>
+					<Add option="-O2" />
+					<Add option="-Wall" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+				</Linker>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add option="-Wall" />
+			<Add option="-g" />
+			<Add directory="../discount-2.1.2" />
+		</Compiler>
+		<Linker>
+			<Add library="discount" />
+			<Add directory="../discount-2.1.2" />
+		</Linker>
+		<Unit filename="main.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="mkdio.h" />
+		<Unit filename="pgm_options.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="pgm_options.h" />
+		<Extensions>
+			<code_completion />
+			<debugger />
+			<envvars />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>

+ 49 - 0
discount/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;
+}

+ 152 - 0
discount/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 > 1 )
+	    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;
+}

+ 188 - 0
discount/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 */

+ 84 - 0
discount/flags.c

@@ -0,0 +1,84 @@
+#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" },
+    { MKD_EXTRA_FOOTNOTE, "FOOTNOTE" },
+};
+#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);
+}

+ 1781 - 0
discount/generate.c

@@ -0,0 +1,1781 @@
+/* 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 inline 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 inline 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 inline char*
+cursor(MMIOT *f)
+{
+    return T(f->in) + f->isp;
+}
+
+
+static inline int
+isthisspace(MMIOT *f, int i)
+{
+    int c = peek(f, i);
+
+    return isspace(c) || (c == EOF);
+}
+
+
+static inline int
+isthisalnum(MMIOT *f, int i)
+{
+    int c = peek(f, i);
+
+    return (c != EOF) && isalnum(c);
+}
+
+
+static inline 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
+QstringSTD(char *s, MMIOT *f)
+{
+    while (*s)
+	Qchar(*s++, f);
+}
+
+typedef void   (*mkd_qstring_t)(char*, MMIOT*);
+
+static void
+(*Qstring)(char *s, MMIOT *f) = &QstringSTD;
+
+mkd_qstring_t
+mkd_e_qstring(mkd_qstring_t qstring_func){
+    mkd_qstring_t old = Qstring;
+    Qstring = qstring_func;
+    return old;
+}
+
+/* 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;
+    sub.ref_prefix = f->ref_prefix;
+
+    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("&amp;", f);
+	else if ( c == '<' )
+	    Qstring("&lt;", 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 */
+
+
+/* helper function for php markdown extra footnotes; allow the user to
+ * define a prefix tag instead of just `fn`
+ */
+static char *
+p_or_nothing(p)
+MMIOT *p;
+{
+    return p->ref_prefix ? p->ref_prefix : "fn";
+}
+
+
+/* php markdown extra/daring fireball style print footnotes
+ */
+static int
+extra_linky(MMIOT *f, Cstring text, Footnote *ref)
+{
+    if ( ref->flags & REFERENCED )
+	return 0;
+
+    if ( f->flags & IS_LABEL )
+    	___mkd_reparse(T(text), S(text), linkt.flags, f);
+    else {
+	ref->flags |= REFERENCED;
+	ref->refnumber = ++ f->reference;
+	Qprintf(f, "<sup id=\"%sref:%d\"><a href=\"#%s:%d\" rel=\"footnote\">%d</a></sup>",
+		p_or_nothing(f), ref->refnumber,
+		p_or_nothing(f), ref->refnumber, ref->refnumber);
+    }
+    return 1;
+} /* extra_linky */
+
+
+/* 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 || (ref == 0) )
+	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;
+    int extra_footnote = 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 ( (f->flags & MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' )
+		    extra_footnote = 1;
+	    }
+
+	    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) ) {
+		    if ( extra_footnote )
+			status = extra_linky(f,name,ref);
+		    else
+			status = linkyformat(f, name, image, ref);
+		}
+		else if ( f->flags & IS_LABEL )
+		    status = linkyformat(f, name, image, 0);
+	    }
+	}
+    }
+
+    DELETE(name);
+    ___mkd_freefootnote(&key);
+
+    if ( status == 0 )
+	mmiotseek(f, start);
+
+    return status;
+}
+
+
+/* write a character to output, doing text escapes ( & -> &amp;,
+ *                                          > -> &gt; < -> &lt; )
+ */
+static void
+cputc(int c, MMIOT *f)
+{
+    switch (c) {
+    case '&':   Qstring("&amp;", f); break;
+    case '>':   Qstring("&gt;", f); break;
+    case '<':   Qstring("&lt;", 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 &lt; and &amp;
+ */
+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;
+#if WITH_GITHUB_TAGS
+	else if ( ! (c == '/' || c == '-' || c == '_' || isalnum(c) ) )
+#else
+	else if ( ! (c == '/' || isalnum(c) ) )
+#endif
+	    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",  2 },
+    { '-',  "--",       "ndash",  1 },
+    { '.',  "...",      "hellip", 2 },
+    { '.',  ". . .",    "hellip", 4 },
+    { '(',  "(c)",      "copy",   2 },
+    { '(',  "(r)",      "reg",    2 },
+    { '(',  "(tm)",     "trade",  3 },
+    { '3',  "<3/4>",    "frac34", 2 },
+    { '3',  "<3/4ths>", "frac34", 2 },
+    { '1',  "<1/2>",    "frac12", 2 },
+    { '1',  "<1/4>",    "frac14", 2 },
+    { '1',  "<1/4th>",  "frac14", 2 },
+    { '&',  "&#0;",      0,       3 },
+} ;
+#define 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("&ldquo;", f);
+			    ___mkd_reparse(cursor(f)+1, j-2, 0, f);
+			    Qstring("&rdquo;", 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, int allow_space, spanhandler spanner)
+{
+    int endticks, size;
+    int tick = nrticks(0, tickchar, f);
+
+    if ( !allow_space && isspace(peek(f,tick)) )
+	return 0;
+
+    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("&gt;", f);
+		    else
+			Qchar(c, f);
+		    break;
+
+	case '"':   if ( tag_text(f) )
+			Qstring("&quot;", 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,0, delspan) )
+			Qchar(c, f);
+		    break;
+
+	case '`':   if ( tag_text(f) || !tickhandler(f,c,1,1,codespan) )
+			Qchar(c, f);
+		    break;
+
+	case '\\':  switch ( c = pull(f) ) {
+		    case '&':   Qstring("&amp;", f);
+				break;
+		    case '<':   Qstring("&lt;", f);
+				break;
+		    case '^':   if ( f->flags & (MKD_STRICT|MKD_NOSUPERSCRIPT) ) {
+				    Qchar('\\', f);
+				    shift(f,-1);
+				    break;
+				}
+				Qchar(c, f);
+				break;
+
+		    case ':': case '|':
+				if ( f->flags & MKD_NOTABLES ) {
+				    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("&lt;", f);
+		    break;
+
+	case '&':   j = (peek(f,1) == '#' ) ? 2 : 1;
+		    while ( isthisalnum(f,j) )
+			++j;
+
+		    if ( peek(f,j) != ';' )
+			Qstring("&amp;", 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)
+{
+#if WITH_ID_ANCHOR
+    Qprintf(f, "<h%d", pp->hnumber);
+    if ( f->flags & MKD_TOC ) {
+	Qstring(" id=\"", f);
+	mkd_string_to_anchor(T(pp->text->text),
+			     S(pp->text->text),
+			     (mkd_sta_function_t)Qchar, f, 1);
+	Qchar('"', f);
+    }
+    Qchar('>', f);
+#else
+    if ( f->flags & MKD_TOC ) {
+	Qstring("<a name=\"", f);
+	mkd_string_to_anchor(T(pp->text->text),
+			     S(pp->text->text),
+			     (mkd_sta_function_t)Qchar, f, 1);
+	Qstring("\"></a>\n", f);
+    }
+    Qprintf(f, "<h%d>", pp->hnumber);
+#endif
+    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 = p->dle,
+	colno = 0;
+
+
+    ___mkd_tidy(&p->text);
+    if ( T(p->text)[S(p->text)-1] == '|' )
+	--S(p->text);
+
+    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] != '|') ) {
+		if ( T(p->text)[idx] == '\\' )
+		    ++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 hcols,start, with_border = 0;
+    char *p;
+    enum e_alignments it;
+
+    hdr = pp->text;
+    dash= hdr->next;
+    body= dash->next;
+
+    if ( T(hdr->text)[hdr->dle] == '|' ) {
+	/* trim leading pipe off all lines
+	 */
+	Line *r;
+	for ( r = pp->text; r; r = r->next )
+	    r->dle ++;
+    }
+
+    /*Check if we want a border on the table*/
+    for(p=T(dash->text); *p; ++p){
+        if(*p == '='){
+            with_border = 1;
+            break;
+        }
+    }
+
+    /* figure out cell alignments */
+
+    CREATE(align);
+
+    for (p=T(dash->text), start=dash->dle; start < S(dash->text); ) {
+	char first, last;
+	int end;
+
+	last=first=0;
+	for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) {
+	    if ( p[end] == '\\' )
+		++ end;
+	    else if ( !isspace(p[end]) ) {
+		if ( !first) first = p[end];
+		last = p[end];
+	    }
+	}
+	it = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT)
+			      : (( last == ':') ? a_RIGHT : a_NONE );
+
+	EXPAND(align) = it;
+	start = 1+end;
+    }
+
+    if(with_border) Qstring("<table border='1' bordercolor='#cccccc'>\n", f);
+    else 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;
+}
+
+
+/* dump out a list of footnotes
+ */
+static void
+mkd_extra_footnotes(MMIOT *m)
+{
+    int j, i;
+    Footnote *t;
+
+    if ( m->reference == 0 )
+	return;
+
+    Csprintf(&m->out, "\n<div class=\"footnotes\">\n<hr/>\n<ol>\n");
+
+    for ( i=1; i <= m->reference; i++ ) {
+	for ( j=0; j < S(*m->footnotes); j++ ) {
+	    t = &T(*m->footnotes)[j];
+	    if ( (t->refnumber == i) && (t->flags & REFERENCED) ) {
+		Csprintf(&m->out, "<li id=\"%s:%d\">\n<p>",
+			    p_or_nothing(m), t->refnumber);
+		Csreparse(&m->out, T(t->title), S(t->title), 0);
+		Csprintf(&m->out, "<a href=\"#%sref:%d\" rev=\"footnote\">&#8617;</a>",
+			    p_or_nothing(m), t->refnumber);
+		Csprintf(&m->out, "</p></li>\n");
+	    }
+	}
+    }
+    Csprintf(&m->out, "</ol>\n</div>\n");
+}
+
+
+/* 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);
+	    if ( p->ctx->flags & MKD_EXTRA_FOOTNOTE )
+		mkd_extra_footnotes(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;
+}

+ 22 - 0
discount/html5.c

@@ -0,0 +1,22 @@
+/* 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_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();
+}

+ 116 - 0
discount/libdiscount.cbp

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="libdiscount" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="Debug">
+				<Option output="libdiscount" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="" />
+				<Option object_output="obj/Debug/" />
+				<Option type="2" />
+				<Option compiler="gcc" />
+				<Option createDefFile="1" />
+				<Compiler>
+					<Add option="-Wall" />
+					<Add option="-g" />
+				</Compiler>
+			</Target>
+			<Target title="Release">
+				<Option output="libdiscount" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="" />
+				<Option object_output="obj/Release/" />
+				<Option type="2" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-O2" />
+					<Add option="-Wall" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+				</Linker>
+			</Target>
+			<Target title="Release win32">
+				<Option output="libdiscount-win32" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="" />
+				<Option object_output="obj/Release-win32/" />
+				<Option type="2" />
+				<Option compiler="mingw32_compiler" />
+				<Compiler>
+					<Add option="-O2" />
+					<Add option="-Wall" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+				</Linker>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add directory="G:/c/discount-2.1.2/" />
+		</Compiler>
+		<Unit filename="Csio.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="basename.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="css.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="docheader.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="dumptree.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="emmatch.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="flags.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="generate.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="html5.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="markdown.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="markdown.h" />
+		<Unit filename="mkdio.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="mkdio.h" />
+		<Unit filename="pgm_options.h" />
+		<Unit filename="resource.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="setup.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="tags.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="toc.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="version.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="xml.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="xmlpage.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Extensions>
+			<code_completion />
+			<debugger />
+			<envvars />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>

+ 19 - 0
discount/librarian.sh

@@ -0,0 +1,19 @@
+#! /bin/sh
+#
+#  Build static libraries, hiding (some) ickiness from the makefile
+
+ACTION=$1; shift
+LIBRARY=$1; shift
+VERSION=$1; shift
+
+case "$ACTION" in
+make)   /mingw/bin/ar crv $LIBRARY.a "$@"
+	/mingw/bin/ranlib $LIBRARY.a
+	rm -f $LIBRARY
+	/bin/ln $LIBRARY.a $LIBRARY
+	;;
+files)  echo "${LIBRARY}.a"
+	;;
+install)/bin/install -m 644 ${LIBRARY}.a $1
+	;;
+esac

+ 208 - 0
discount/main.c

@@ -0,0 +1,208 @@
+/*
+ * 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 <stdarg.h>
+
+#include "config.h"
+#include "amalloc.h"
+#include "pgm_options.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";
+
+char *
+e_flags(const char *text, const int size, void *context)
+{
+    return (char*)context;
+}
+
+
+void
+complain(char *fmt, ...)
+{
+    va_list ptr;
+
+    fprintf(stderr, "%s: ", pgm);
+    va_start(ptr, fmt);
+    vfprintf(stderr, fmt, ptr);
+    va_end(ptr);
+    fputc('\n', stderr);
+    fflush(stderr);
+}
+
+static mkd_qstring_t old_qstring;
+static void
+MyQstring(char *s, MMIOT *f)
+{
+    if(strcmp(s, "<table>\n") == 0) s = "<table border='1'>\n";
+    else if(strcmp(s, "<p>") == 0) s = "<p class='pc'>";
+    old_qstring(s,f);
+}
+
+int
+main(int argc, char **argv)
+{
+    int opt;
+    int rc;
+    mkd_flag_t flags = 0;
+    int debug = 0;
+    int toc = 0;
+    int version = 0;
+    int with_html5 = 0;
+    int use_mkd_line = 0;
+    char *extra_footnote_prefix = 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:C: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':   if ( strcmp(optarg, "?") == 0 ) {
+			show_flags(0);
+			exit(0);
+		    }
+		    else
+			flags = strtol(optarg, 0, 0);
+		    break;
+	case 'f':   if ( strcmp(optarg, "?") == 0 ) {
+			show_flags(1);
+			exit(0);
+		    }
+		    else if ( !set_flag(&flags, optarg) )
+			complain("unknown option <%s>", optarg);
+		    break;
+	case 't':   text = optarg;
+		    use_mkd_line = 1;
+		    break;
+	case 'T':   toc = 1;
+		    break;
+	case 's':   text = optarg;
+		    break;
+	case 'C':   extra_footnote_prefix = optarg;
+		    break;
+	case 'o':   if ( ofile ) {
+			complain("Too many -o options");
+			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;
+
+    old_qstring = mkd_e_qstring(MyQstring);
+
+    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 ( extra_footnote_prefix )
+	    mkd_ref_prefix(doc, extra_footnote_prefix);
+
+	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);
+	    }
+	}
+    }
+    mkd_deallocate_tags();
+    adump();
+    exit( (rc == 0) ? 0 : errno );
+}

+ 9 - 0
discount/main.cpp

@@ -0,0 +1,9 @@
+#include <iostream>
+
+using namespace std;
+
+int main()
+{
+    cout << "Hello world!" << endl;
+    return 0;
+}

+ 45 - 0
discount/makepage.1

@@ -0,0 +1,45 @@
+.\"     %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 Fl V
+.Op Fl F Pa bitmap
+.Op Fl f Ar flags
+.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
+The 
+.Fl F ,
+.Fl f , 
+and
+.Fl V
+flags are identical to the same options in 
+.Xr markdown 1 .
+.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 [email protected]

+ 86 - 0
discount/makepage.c

@@ -0,0 +1,86 @@
+/*
+ * makepage: Use mkd_xhtmlpage() to convert markdown input to a
+ *           fully-formed xhtml page.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <mkdio.h>
+#include "config.h"
+#include "pgm_options.h"
+
+#ifndef HAVE_BASENAME
+char*
+basename(char *p)
+{
+    char *ret = strrchr(p, '/');
+
+    return ret ? (1+ret) : p;
+}
+#endif
+
+char *pgm = "makepage";
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+    MMIOT *doc;
+    char *q;
+    int version = 0;
+    int opt;
+    mkd_flag_t flags = 0;
+
+    if ( (q = getenv("MARKDOWN_FLAGS")) )
+	flags = strtol(q, 0, 0);
+
+    opterr = 1;
+
+    while ( (opt=getopt(argc, argv, "F:f:V")) != EOF ) {
+	switch (opt) {
+	case 'V':   version++;
+		    break;
+	case 'F':   if ( strcmp(optarg, "?") == 0 ) {
+			show_flags(0);
+			exit(0);
+		    }
+		    else
+			flags = strtol(optarg, 0, 0);
+		    break;
+	case 'f':   if ( strcmp(optarg, "?") == 0 ) {
+			show_flags(1);
+			exit(0);
+		    }
+		    else if ( !set_flag(&flags, optarg) )
+			fprintf(stderr, "unknown option <%s>\n", optarg);
+		    break;
+	default:    fprintf(stderr, "usage: %s [-V] [-F bitmap] [-f {+-}flags]"
+				    " [file]\n", pgm);
+		    exit(1);
+	}
+    }
+    
+    argc -= optind;
+    argv += optind;
+    
+    if ( version ) {
+	printf("%s: discount %s", pgm, markdown_version);
+	if ( version > 1 )
+	    mkd_flags_are(stdout, flags, 0);
+	putchar('\n');
+	exit(0);
+    }    
+    
+    if ( (argc > 1) && !freopen(argv[1], "r", stdin) ) {
+	perror(argv[1]);
+	exit(1);
+    }
+
+    if ( (doc = mkd_in(stdin, flags)) == 0 ) {
+	perror( (argc > 1) ? argv[1] : "stdin" );
+	exit(1);
+    }
+
+    exit(mkd_xhtmlpage(doc, flags, stdout));
+}

+ 171 - 0
discount/markdown.1

@@ -0,0 +1,171 @@
+.\"     %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 C Ar prefix
+.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 beginning with / will be prefixed with
+.Ar url-base
+in the output.
+.It Fl C
+When processing markdown extra-style footnotes, use the
+given prefix instead of the default of
+.Ar fn .
+.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.
+.It Ar footnote
+Allow markdown extra-style footnotes.
+.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 [email protected]

+ 137 - 0
discount/markdown.3

@@ -0,0 +1,137 @@
+.\"
+.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.
+.It Ar MKD_EXTRA_FOOTNOTE
+Enable markdown extra-style footnotes.
+.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.

+ 1020 - 0
discount/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. `&lt;`, and
+`&amp;`.
+.Pp
+Ampersands in particular are bedeviling for web writers. If you want to
+write about 'AT&T', you need to write '`AT&amp;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&amp;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 `&amp;`.
+.Pp
+So, if you want to include a copyright symbol in your article, you can write:
+.Bd -literal -offset indent
+    &copy;
+.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&amp;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 &lt; 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">
+            &copy; 2004 Foo Corporation
+        </div>
+.Ed
+.Pp
+will turn into:
+.Bd -literal -offset indent
+    <pre><code>&lt;div class="footer"&gt;
+        &amp;copy; 2004 Foo Corporation
+    &lt;/div&gt;
+    </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>&lt;blink&gt;</code> tags.</p>
+.Ed
+.Pp
+You can write this:
+.Bd -literal -offset indent
+    `&#8212;` is the decimal-encoded equivalent of `&mdash;`.
+.Ed
+.Pp
+to produce:
+.Bd -literal -offset indent
+    <p><code>&amp;#8212;</code> is the decimal-encoded
+    equivalent of <code>&amp;mdash;</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
+    <[email protected]>
+.Ed
+.Pp
+into something like this:
+.Bd -literal -offset indent
+    <a href="&#x6D;&#x61;i&#x6C;&#x74;&#x6F;:&#x61;&#x64;&#x64;&#x72;&#x65;
+    &#115;&#115;&#64;&#101;&#120;&#x61;&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;
+    &#109;">&#x61;&#x64;&#x64;&#x72;&#x65;&#115;&#115;&#64;&#101;&#120;&#x61;
+    &#109;&#x70;&#x6C;e&#x2E;&#99;&#111;&#109;</a>
+.Ed
+.Pp
+which will render in a browser as a clickable link to
+.Qq [email protected] .
+.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/

+ 1302 - 0
discount/markdown.c

@@ -0,0 +1,1302 @@
+/* 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;
+
+static Paragraph *Pp(ParagraphRoot *, Line *, int);
+static Paragraph *compile(Line *, int, MMIOT *);
+
+/* 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 inline 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;
+    }
+}
+
+#define UNCHECK(l) ((l)->flags &= ~CHECKED)
+
+/*
+ * walk a line, seeing if it's any of half a dozen interesting regular
+ * types.
+ */
+static void
+checkline(Line *l)
+{
+    int eol, i;
+    int dashes = 0, spaces = 0,
+	equals = 0, underscores = 0,
+	stars = 0, tildes = 0;
+
+    l->flags |= CHECKED;
+    l->kind = chk_text;
+    l->count = 0;
+
+    if (l->dle >= 4) { l->kind=chk_code; return; }
+
+    for ( eol = S(l->text); eol > l->dle && isspace(T(l->text)[eol-1]); --eol )
+	;
+
+    for (i=l->dle; i<eol; i++) {
+	register int c = T(l->text)[i];
+
+	if ( c != ' ' ) l->count++;
+
+	switch (c) {
+	case '-':  dashes = 1; break;
+	case ' ':  spaces = 1; break;
+	case '=':  equals = 1; break;
+	case '_':  underscores = 1; break;
+	case '*':  stars = 1; break;
+	case '~':  tildes = 1; break;
+	default:   return;
+	}
+    }
+
+    if ( dashes + equals + underscores + stars + tildes > 1 )
+	return;
+
+    if ( spaces ) {
+	if ( (underscores || stars || dashes) )
+	    l->kind = chk_hr;
+	return;
+    }
+
+    if ( stars || underscores ) { l->kind = chk_hr; }
+    else if ( dashes ) { l->kind = chk_dash; }
+    else if ( tildes ) { l->kind = chk_tilde; }
+    else if ( equals ) { l->kind = chk_equal; }
+}
+
+
+
+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;
+}
+
+
+/* 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 inline int
+isquote(Line *t)
+{
+    return (t->dle < 4 && T(t->text)[t->dle] == '>');
+}
+
+
+static inline int
+iscode(Line *t)
+{
+    return (t->dle >= 4);
+}
+
+
+static inline int
+ishr(Line *t)
+{
+    if ( ! (t->flags & CHECKED) )
+	checkline(t);
+
+    if ( t->count > 2 )
+	return t->kind == chk_hr || t->kind == chk_dash || t->kind == chk_equal;
+    return 0;
+}
+
+
+static int
+issetext(Line *t, int *htyp)
+{
+    Line *n;
+
+    /* check for setext-style HEADER
+     *                        ======
+     */
+
+    if ( (n = t->next) ) {
+	if ( !(n->flags & CHECKED) )
+	    checkline(n);
+
+	if ( n->kind == chk_dash || n->kind == chk_equal ) {
+	    *htyp = SETEXT;
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+
+static int
+ishdr(Line *t, int *htyp)
+{
+    /* ANY leading `#`'s make this into an ETX header
+     */
+    if ( (t->dle == 0) && (S(t->text) > 1) && (T(t->text)[0] == '#') ) {
+	*htyp = ETX;
+	return 1;
+    }
+
+    /* And if not, maybe it's a SETEXT header instead
+     */
+    return issetext(t, htyp);
+}
+
+
+static inline int
+end_of_block(Line *t)
+{
+    int dummy;
+
+    if ( !t )
+	return 0;
+
+    return ( (S(t->text) <= t->dle) || ishr(t) || ishdr(t, &dummy) );
+}
+
+
+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
+
+    if ( t && t->next && T(t->text)[0] != '='
+		      && T(t->text)[S(t->text)-1] != '=') {
+	Line *x;
+
+	if ( iscode(t) || end_of_block(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 ( end_of_block(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);
+	    UNCHECK(p);
+
+	    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;
+}
+
+
+#ifdef WITH_FENCED_CODE
+static int
+iscodefence(Line *r, int size)
+{
+    if ( !(r->flags & CHECKED) )
+	checkline(r);
+
+    return (r->kind == chk_tilde) && (r->count >= size);
+}
+
+static Paragraph *
+fencedcodeblock(ParagraphRoot *d, Line **ptr)
+{
+    Line *first, *r;
+    Paragraph *ret;
+
+    first = (*ptr);
+
+    /* don't allow zero-length code fences
+     */
+    if ( (first->next == 0) || iscodefence(first->next, first->count) )
+	return 0;
+
+    /* find the closing fence, discard the fences,
+     * return a Paragraph with the contents
+     */
+    for ( r = first; r && r->next; r = r->next )
+	if ( iscodefence(r->next, first->count) ) {
+	    (*ptr) = r->next->next;
+	    ret = Pp(d, first->next, CODE);
+	    ___mkd_freeLine(first);
+	    ___mkd_freeLine(r->next);
+	    r->next = 0;
+	    return ret;
+	}
+    return 0;
+}
+#endif
+
+
+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 ( end_of_block(t) || isquote(t) )
+	return 1;
+
+    /* HORRIBLE STANDARDS KLUDGES:
+     * 1. non-toplevel paragraphs absorb adjacent code blocks
+     * 2. Toplevel paragraphs eat absorb adjacent list items,
+     *    but sublevel blocks behave properly.
+     * (What this means is that we only need to check for code
+     *  blocks at toplevel, and only check for list items at
+     *  nested levels.)
+     */
+    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%
+ */
+#define iscsschar(c) (isalpha(c) || (c == '-') || (c == '_') )
+
+static int
+isdivmarker(Line *p, int start, DWORD flags)
+{
+    char *s;
+    int last, i;
+
+    if ( flags & (MKD_NODIVQUOTE|MKD_STRICT) )
+	return 0;
+
+    last= S(p->text) - (1 + start);
+    s   = T(p->text) + start;
+
+    if ( (last <= 0) || (*s != '%') || (s[last] != '%') )
+	return 0;
+
+    i = szmarkerclass(s+1);
+
+    if ( !iscsschar(s[i+1]) )
+	return 0;
+    while ( ++i < last )
+	if ( !(isdigit(s[i]) || iscsschar(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);
+	    UNCHECK(t);
+	    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;
+}
+
+
+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);
+	UNCHECK(t);
+	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);
+		UNCHECK(q);
+		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->flags = 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);
+
+    if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) {
+	while ( j < S(p->text) )
+	    EXPAND(foot->title) = T(p->text)[j++];
+	goto skip_to_end;
+    }
+
+    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);
+    }
+
+skip_to_end:
+    ___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);
+}
+
+
+static int
+first_nonblank_before(Line *j, int dle)
+{
+    return (j->dle < dle) ? j->dle : dle;
+}
+
+
+static int
+actually_a_table(MMIOT *f, Line *pp)
+{
+    Line *r;
+    int j;
+    int c;
+
+    /* tables need to be turned on */
+    if ( f->flags & (MKD_STRICT|MKD_NOTABLES) )
+	return 0;
+
+    /* tables need three lines */
+    if ( !(pp && pp->next && pp->next->next) ) {
+	return 0;
+    }
+
+    /* all lines must contain |'s */
+    for (r = pp; r; r = r->next )
+	if ( !(r->flags & PIPECHAR) ) {
+	    return 0;
+	}
+
+    /* if the header has a leading |, all lines must have leading |'s */
+    if ( T(pp->text)[pp->dle] == '|' ) {
+	for ( r = pp; r; r = r->next )
+	    if ( T(r->text)[first_nonblank_before(r,pp->dle)] != '|' ) {
+		return 0;
+	    }
+    }
+
+    /* second line must be only whitespace, -, |, or : */
+    r = pp->next;
+
+    for ( j=r->dle; j < S(r->text); ++j ) {
+	c = T(r->text)[j];
+
+	if ( !(isspace(c)||(c=='-')||(c=='=')||(c==':')||(c=='|')) ) {
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+
+/*
+ * 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, &para);
+
+    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);
+	}
+#if WITH_FENCED_CODE
+	else if ( iscodefence(ptr,3) && (p=fencedcodeblock(&d, &ptr)) )
+	    /* yay, it's already done */ ;
+#endif
+	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 {
+	    p = Pp(&d, ptr, MARKUP);
+	    ptr = textblock(p, toplevel, f->flags);
+	    /* tables are a special kind of paragraph */
+	    if ( actually_a_table(f, p->text) )
+		p->typ = TABLE;
+	}
+
+	if ( (para||toplevel) && !p->align )
+	    p->align = PARA;
+
+	blocks++;
+	para = toplevel || (blocks > 1);
+	ptr = consume(ptr, &para);
+
+	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->ref_prefix= doc->ref_prefix;
+    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;
+}
+

+ 193 - 0
discount/markdown.h

@@ -0,0 +1,193 @@
+#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? */
+    int refnumber;
+    int flags;
+#define EXTRA_BOOKMARK	0x01
+#define REFERENCED	0x02
+} 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;			/* leading indent on the line */
+    int flags;			/* special attributes for this line */
+#define PIPECHAR	0x01		/* line contains a | */
+#define CHECKED		0x02
+
+    enum { chk_text, chk_code,
+	   chk_hr, chk_dash,
+	   chk_tilde, chk_equal } kind;
+    int count;
+} 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;
+    int reference;
+    char *ref_prefix;
+    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 MKD_EXTRA_FOOTNOTE 0x00200000
+#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) */
+    char *ref_prefix;
+    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 *);
+
+typedef int (*mkd_sta_function_t)(const int,const void*);
+extern void mkd_string_to_anchor(char*,int, mkd_sta_function_t, void*, int);
+
+extern Document *mkd_in(FILE *, DWORD);
+extern Document *mkd_string(const char*,int, DWORD);
+
+extern void mkd_initialize();
+extern void mkd_shlib_destructor();
+
+extern void mkd_ref_prefix(Document*, char*);
+
+/* 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 *);
+
+extern int mkd_generatehtml_str(Document *p,void *out,void (*mywrite)(char const *,int size,void*));
+extern int markdown_str(Document *document, void *out,void (*)(char const *,int size,void*), int flags);
+
+
+#endif/*_MARKDOWN_D*/

+ 49 - 0
discount/markdowncpp.cbp

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="markdowncpp" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="Debug">
+				<Option output="markdowncpp" prefix_auto="1" extension_auto="1" />
+				<Option object_output="obj/Debug/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+			</Target>
+			<Target title="Release">
+				<Option output="markdowncpp" prefix_auto="1" extension_auto="1" />
+				<Option object_output="obj/Release/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-O2" />
+					<Add option="-DTEST_MARKDOWN_LIBRARY=1" />
+					<Add directory="../discount-2.1.2" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+					<Add directory="../discount-2.1.2" />
+				</Linker>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add option="-Wall" />
+			<Add option="-fexceptions" />
+		</Compiler>
+		<Linker>
+			<Add library="discount" />
+		</Linker>
+		<Unit filename="markdowncxx.cpp" />
+		<Unit filename="markdowncxx.h" />
+		<Extensions>
+			<code_completion />
+			<debugger />
+			<envvars />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>

+ 46 - 0
discount/markdowncxx.cpp

@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <string>
+#include <iostream>
+extern "C" {
+#include "markdown.h"
+}
+
+static void mywrite(char const *s,int len,void *str_ptr)
+{
+	std::string *cxx_str=static_cast<std::string *>(str_ptr);
+	cxx_str->append(s,len);
+}
+
+void markdown2html(std::string const &in,std::string &out)
+{
+	markdown_str(mkd_string(const_cast<char *>(in.c_str()),in.size(),0),&out,mywrite,0);
+}
+
+void markdown2html(const char *in, int len, std::string &out)
+{
+	markdown_str(mkd_string(in, len, 0), &out,mywrite,0);
+}
+
+void markdown2html(const char *in, std::string &out)
+{
+	markdown2html(in, strlen(in), out);
+}
+
+#ifdef TEST_MARKDOWN_LIBRARY
+int main()
+{
+	std::string test;
+	test.reserve(32000);
+	int c;
+	while((c=getchar())!=EOF) {
+		test+=(char)c;
+	}
+
+	std::string out;
+	out.reserve(32000);
+	markdown2html(test,out);
+	std::cout<<out;
+	return 0;
+}
+#endif

+ 9 - 0
discount/markdowncxx.h

@@ -0,0 +1,9 @@
+#ifndef MARKDOWNCXX_H
+#define MARKDOWNCXX_H
+#include <string>
+
+void markdown2html(std::string const &in,std::string &out);
+void markdown2html(const char *in, int len, std::string &out);
+void markdown2html(const char *in, std::string &out);
+
+#endif

+ 71 - 0
discount/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.

+ 205 - 0
discount/mkd-extensions.7

@@ -0,0 +1,205 @@
+.\"
+.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
+Alternatively, definition list items are defined as
+.nf
+tag
+: description
+.fi
+(This is the format that
+.Ar "PHP Markdown Extra"
+uses.)
+.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.
+.Ss markdown extra-style footnotes
+.Ar "PHP Markdown Extra"
+footnotes are supported.   If a footnote link begins with a 
+.Ar ^ ,
+the first use of that footnote will generate a link down to the 
+bottom of the rendered document, which will contain a numbered footnote
+with a link back to where the footnote was called.
+.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 .
+.Pp
+.%T http://daringfireball.net/projects/markdown
+.Pp
+.%T http://michelf.com/projects/php-markdown

+ 186 - 0
discount/mkd-functions.3

@@ -0,0 +1,186 @@
+.\"
+.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 function
+.Fn mkd_compile
+returns 1 in the case of success, or 0 if the document is already compiled.
+The function
+.Fn mkd_generatecss
+returns the number of bytes written in the case of success, or EOF if an error
+occurred.  
+The function
+.Fn mkd_generatehtml
+returns 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.

+ 41 - 0
discount/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.

+ 52 - 0
discount/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 [email protected]

+ 185 - 0
discount/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);
+}

+ 400 - 0
discount/mkdio.c

@@ -0,0 +1,400 @@
+/*
+ * 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, expanding tabs and
+ * noting the presence of special characters as we go.
+ */
+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 >= ' ' ) {
+	    if ( c == '|' )
+		p->flags |= PIPECHAR;
+	    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 {
+    const 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(const 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, mkd_sta_function_t outchar,
+				       void *out, int labelformat)
+{
+    unsigned char c;
+
+    int i, size;
+    char *line;
+
+    size = mkd_line(s, len, &line, IS_LABEL);
+
+    if ( labelformat && (size>0) && !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;
+}
+
+
+/* set the href prefix for markdown extra style footnotes
+ */
+void
+mkd_ref_prefix(Document *f, char *data)
+{
+    if ( f )
+	f->ref_prefix = data;
+}
+
+int
+mkd_generatehtml_str(Document *p,void *out,void (*mywrite)(char const *,int size,void*))
+{
+    char *doc;
+    int szdoc;
+
+    if ( (szdoc = mkd_document(p, &doc)) != EOF ) {
+	if ( p->ctx->flags & MKD_CDATA ) {
+	    char c;
+
+	    while ( szdoc-- > 0 ) {
+		if ( !isascii(c = *doc++) )
+		    continue;
+		switch (c) {
+		case '<': mywrite("&lt;",4,out);   break;
+		case '>': mywrite("&gt;",4,out);   break;
+		case '&': mywrite("&amp;",5,out);  break;
+		case '"': mywrite("&quot;",6,out); break;
+		case '\'':mywrite("&apos;",6,out); break;
+		default:  mywrite(&c,1,out); break;
+		}
+	    }
+	}
+	else{
+		mywrite(doc,szdoc,out);
+	}
+	mywrite("\n",1, out);
+	return 0;
+    }
+    return -1;
+}
+
+int
+markdown_str(Document *document, void *out,void (*mywrite)(char const *,int size,void*),int flags)
+{
+    if ( mkd_compile(document, flags) ) {
+	mkd_generatehtml_str(document,out,mywrite);
+	mkd_cleanup(document);
+	return 0;
+    }
+    return -1;
+}

+ 111 - 0
discount/mkdio.h

@@ -0,0 +1,111 @@
+#ifndef _MKDIO_D
+#define _MKDIO_D
+
+#include <stdio.h>
+
+typedef void MMIOT;
+
+typedef unsigned long mkd_flag_t;
+
+/* line builder for markdown()
+ */
+MMIOT *mkd_in(FILE*,mkd_flag_t);		/* assemble input from a file */
+MMIOT *mkd_string(const char*,int,mkd_flag_t);	/* assemble input from a buffer */
+
+void mkd_basename(MMIOT*,char*);
+
+void mkd_initialize();
+void mkd_with_html5_tags();
+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);
+typedef int (*mkd_sta_function_t)(const int,const void*);
+void mkd_string_to_anchor(char *, int, mkd_sta_function_t, 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*);
+typedef void   (*mkd_qstring_t)(char*, MMIOT*);
+
+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 *);
+mkd_qstring_t mkd_e_qstring(mkd_qstring_t);
+
+/* version#.
+ */
+extern char markdown_version[];
+void mkd_mmiot_flags(FILE *, MMIOT *, int);
+void mkd_flags_are(FILE*, mkd_flag_t, int);
+
+void mkd_ref_prefix(MMIOT*, char*);
+
+
+/* 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_NOEXT	MKD_NO_EXT	/* ^^^ (aliased for user convenience) */
+#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_EXTRA_FOOTNOTE 0x00200000	/* enable markdown extra-style footnotes */
+#define MKD_EMBED	MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT
+
+/* special flags for mkd_in() and mkd_string()
+ */
+
+
+#endif/*_MKDIO_D*/

+ 111 - 0
discount/mkdio.h.in

@@ -0,0 +1,111 @@
+#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(const char*,int,mkd_flag_t);	/* assemble input from a buffer */
+
+void mkd_basename(MMIOT*,char*);
+
+void mkd_initialize();
+void mkd_with_html5_tags();
+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);
+typedef int (*mkd_sta_function_t)(const int,const void*);
+void mkd_string_to_anchor(char *, int, mkd_sta_function_t, 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*);
+typedef void   (*mkd_qstring_t)(char*, MMIOT*);
+
+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 *);
+mkd_qstring_t mkd_e_qstring(mkd_qstring_t);
+
+/* version#.
+ */
+extern char markdown_version[];
+void mkd_mmiot_flags(FILE *, MMIOT *, int);
+void mkd_flags_are(FILE*, mkd_flag_t, int);
+
+void mkd_ref_prefix(MMIOT*, char*);
+
+
+/* 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_NOEXT	MKD_NO_EXT	/* ^^^ (aliased for user convenience) */
+#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_EXTRA_FOOTNOTE 0x00200000	/* enable markdown extra-style footnotes */
+#define MKD_EMBED	MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT
+
+/* special flags for mkd_in() and mkd_string()
+ */
+
+
+#endif/*_MKDIO_D*/

+ 91 - 0
discount/mktags.c

@@ -0,0 +1,91 @@
+/* block-level tags for passing html blocks through the blender
+ */
+#include <stdio.h>
+
+#define __WITHOUT_AMALLOC 1
+#include "cstring.h"
+#include "tags.h"
+
+STRING(struct kw) blocktags;
+
+
+/* define a html block tag
+ */
+static void
+define_one_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*);
+
+
+/* load in the standard collection of html tags that markdown supports
+ */
+int
+main()
+{
+    int i;
+
+#define KW(x)	define_one_tag(x, 0)
+#define SC(x)	define_one_tag(x, 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");
+
+    qsort(T(blocktags), S(blocktags), sizeof(struct kw), (stfu)casort);
+
+    printf("static struct kw blocktags[] = {\n");
+    for (i=0; i < S(blocktags); i++)
+	printf("   { \"%s\", %d, %d },\n", T(blocktags)[i].id, T(blocktags)[i].size, T(blocktags)[i].selfclose );
+    printf("};\n\n");
+    printf("#define NR_blocktags %d\n", S(blocktags));
+    exit(0);
+}

+ 135 - 0
discount/pgm_options.c

@@ -0,0 +1,135 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007-2011 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 <stdarg.h>
+
+#include "config.h"
+#include "amalloc.h"
+
+#if HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+static struct _opt {
+    char *name;
+    char *desc;
+    int off;
+    int skip;
+    int sayenable;
+    mkd_flag_t flag;
+} opts[] = {
+    { "tabstop",       "default (4-space) tabstops", 0, 0, 1, MKD_TABSTOP  },
+    { "image",         "images",                     1, 0, 1, MKD_NOIMAGE  },
+    { "links",         "links",                      1, 0, 1, MKD_NOLINKS  },
+    { "relax",         "emphasis inside words",      1, 1, 1, MKD_STRICT   },
+    { "strict",        "emphasis inside words",      0, 0, 1, MKD_STRICT   },
+    { "tables",        "tables",                     1, 0, 1, MKD_NOTABLES },
+    { "header",        "pandoc-style headers",       1, 0, 1, MKD_NOHEADER },
+    { "html",          "raw html",                   1, 0, 1, MKD_NOHTML   },
+    { "ext",           "extended protocols",         1, 0, 1, MKD_NO_EXT   },
+    { "cdata",         "generate cdata",             0, 0, 0, MKD_CDATA    },
+    { "smarty",        "smartypants",                1, 0, 1, MKD_NOPANTS  },
+    { "pants",         "smartypants",                1, 1, 1, MKD_NOPANTS  },
+    { "toc",           "tables of contents",         0, 0, 1, MKD_TOC      },
+    { "autolink",      "autolinking",                0, 0, 1, MKD_AUTOLINK },
+    { "safelink",      "safe links",                 0, 0, 1, MKD_SAFELINK },
+    { "strikethrough", "strikethrough",              1, 0, 1, MKD_NOSTRIKETHROUGH },
+    { "del",           "strikethrough",              1, 1, 1, MKD_NOSTRIKETHROUGH },
+    { "superscript",   "superscript",                1, 0, 1, MKD_NOSUPERSCRIPT },
+    { "emphasis",      "emphasis inside words",      0, 0, 1, MKD_NORELAXED },
+    { "divquote",      ">%class% blockquotes",       1, 0, 1, MKD_NODIVQUOTE },
+    { "alphalist",     "alpha lists",                1, 0, 1, MKD_NOALPHALIST },
+    { "definitionlist","definition lists",           1, 0, 1, MKD_NODLIST },
+    { "1.0",           "markdown 1.0 compatibility", 0, 0, 1, MKD_1_COMPAT },
+    { "footnotes",     "markdown extra footnotes",   0, 0, 1, MKD_EXTRA_FOOTNOTE },
+    { "footnote",      "markdown extra footnotes",   0, 1, 1, MKD_EXTRA_FOOTNOTE },
+} ;
+
+#define NR(x)	(sizeof x / sizeof x[0])
+
+
+int
+sort_by_name(struct _opt *a, struct _opt *b)
+{
+    return strcmp(a->name,b->name);
+}
+
+int
+sort_by_flag(struct _opt *a, struct _opt *b)
+{
+    return a->flag - b->flag;
+}
+
+
+void
+show_flags(int byname)
+{
+    int i;
+
+    if ( byname ) {
+	qsort(opts, NR(opts), sizeof(opts[0]), sort_by_name);
+    
+	for (i=0; i < NR(opts); i++)
+	    if ( ! opts[i].skip )
+		fprintf(stderr, "%16s : %s\n", opts[i].name, opts[i].desc);
+    }
+    else {
+	qsort(opts, NR(opts), sizeof(opts[0]), sort_by_flag);
+	
+	for (i=0; i < NR(opts); i++)
+	    if ( ! opts[i].skip ) {
+		fprintf(stderr, "%08lx : ", (long)opts[i].flag);
+		if ( opts[i].sayenable )
+		    fprintf(stderr, opts[i].off ? "disable " : "enable ");
+		fprintf(stderr, "%s\n", opts[i].desc);
+	    }
+    }
+}
+    
+
+int
+set_flag(mkd_flag_t *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
+	    return 0;
+    }
+    return 1;
+}

+ 9 - 0
discount/pgm_options.h

@@ -0,0 +1,9 @@
+#ifndef PGM_OPTIONS_D
+#define PGM_OPTIONS_D
+
+#include <mkdio.h>
+
+int set_flag(mkd_flag_t *flags, char *optionstring);
+void show_flags(int byname);
+
+#endif/*PGM_OPTIONS_D*/

+ 157 - 0
discount/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);
+    }
+}

+ 39 - 0
discount/setup.c

@@ -0,0 +1,39 @@
+/* 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_initrng = 1;
+
+void
+mkd_initialize()
+{
+
+    if ( need_to_initrng ) {
+	need_to_initrng = 0;
+	INITRNG(time(0));
+    }
+}
+
+
+void
+mkd_shlib_destructor()
+{
+    mkd_deallocate_tags();
+}
+

+ 91 - 0
discount/tags.c

@@ -0,0 +1,91 @@
+/* block-level tags for passing html blocks through the blender
+ */
+#define __WITHOUT_AMALLOC 1
+#include "cstring.h"
+#include "tags.h"
+
+STRING(struct kw) extratags;
+
+/* the standard collection of tags are built and sorted when
+ * discount is configured, so all we need to do is pull them
+ * in and use them.
+ *
+ * Additional tags still need to be allocated, sorted, and deallocated.
+ */
+#include "blocktags"
+
+
+/* define an additional html block tag
+ */
+void
+mkd_define_tag(char *id, int selfclose)
+{
+    struct kw *p;
+
+    /* only add the new tag if it doesn't exist in
+     * either the standard or extra tag tables.
+     */
+    if ( !(p = mkd_search_tags(id, strlen(id))) ) {
+	p = &EXPAND(extratags);
+	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 extra html block tags for later searching
+ */
+void
+mkd_sort_tags()
+{
+    qsort(T(extratags), S(extratags), 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;
+    struct kw *ret;
+    
+    key.id = pat;
+    key.size = len;
+    
+    if ( (ret=bsearch(&key,blocktags,NR_blocktags,sizeof key,(stfu)casort)) )
+	return ret;
+
+    if ( S(extratags) )
+	return bsearch(&key,T(extratags),S(extratags),sizeof key,(stfu)casort);
+    
+    return 0;
+}
+
+
+/* destroy the extratags list (for shared libraries)
+ */
+void
+mkd_deallocate_tags()
+{
+    if ( S(extratags) > 0 )
+	DELETE(extratags);
+} /* mkd_deallocate_tags */

+ 19 - 0
discount/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

+ 27 - 0
discount/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

+ 27 - 0
discount/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 '<[email protected]>' '<[email protected]>' '<a href='
+match '<[email protected].>' '<[email protected].>' '<a href='
+try 'invalid <orc@>' '<orc@>' '<p>&lt;orc@></p>'
+try 'invalid <@pell>' '<@pell>' '<p>&lt;@pell></p>'
+try 'invalid <orc@pell>' '<orc@pell>' '<p>&lt;orc@pell></p>'
+try 'invalid <[email protected]>' '<[email protected]>' '<p>&lt;[email protected]></p>'
+try 'invalid <orc@pell.>' '<orc@pell.>' '<p>&lt;orc@pell.></p>'
+match '<mailto:orc@pell>' '<mailto:orc@pell>' '<a href='
+match '<mailto:[email protected]>' '<mailto:[email protected]>' '<a href='
+match '<mailto:orc@>' '<mailto:orc@>' '<a href='
+match '<mailto:@pell>' '<mailto:@pell>' '<a href='
+
+summary $0
+exit $rc

+ 16 - 0
discount/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

+ 17 - 0
discount/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

+ 13 - 0
discount/tests/chrome.text

@@ -0,0 +1,13 @@
+->###chrome with my markdown###<-
+
+1. `(c)`  -> `&copy;`  (c)
+2. `(r)`  -> `&reg;`   (r)
+3. `(tm)` -> `&trade;` (tm)
+4. `...`  -> `&hellip;` ...
+5. `--`   -> `&emdash;` --
+6. `-`    -> `&ndash;`  - (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. `` (`) ``
+

+ 91 - 0
discount/tests/code.t

@@ -0,0 +1,91 @@
+. 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>&gt;</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>'
+
+if ./markdown -V | grep FENCED-CODE >/dev/null; then
+
+try 'fenced code block' \
+'~~~
+code!
+~~~' \
+    '<pre><code>code!
+</code></pre>'
+
+try 'fenced code block in list' \
+'1. ~~~
+code block
+~~~' \
+'<ol>
+<li><pre><code>code block
+</code></pre></li>
+</ol>'
+
+try 'fenced code block in blockquote' \
+'>~~~
+code
+~~~' \
+'<blockquote><pre><code>code
+</code></pre></blockquote>'
+
+try 'unterminated fenced code block' \
+'~~~
+code' \
+'<p>~~~
+code</p>'
+
+try 'fenced code block with tildes' \
+'~~~~~
+~~~
+code with tildes
+~~~
+~~~~~' \
+'<pre><code>~~~
+code with tildes
+~~~
+</code></pre>'
+
+try 'paragraph with trailing fenced block' \
+'text text text
+text text text
+~~~
+code code code?
+~~~' \
+'<p>text text text
+text text text
+~~~
+code code code?
+~~~</p>'
+
+fi
+
+summary $0
+exit $rc

+ 29 - 0
discount/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

+ 31 - 0
discount/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>&lt;/table</p>'
+try 'unclosed style block' '<style>' '<p><style></p>'
+
+summary $0
+exit $rc

+ 49 - 0
discount/tests/div.t

@@ -0,0 +1,49 @@
+. 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>'
+
+try '%class% with _' '>%class:this_that%' '<div class="this_that"></div>'
+try '%class% with -' '>%class:this-that%' '<div class="this-that"></div>'
+try 'illegal %class%' '>%class:0zip%' '<blockquote><p>%class:0zip%</p></blockquote>'
+
+summary $0
+exit $rc

+ 91 - 0
discount/tests/dl.t

@@ -0,0 +1,91 @@
+. 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=
+    is an ugly
+=test=
+    eh?</p>'
+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

+ 9 - 0
discount/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!"
+

+ 19 - 0
discount/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

+ 35 - 0
discount/tests/extrafootnotes.t

@@ -0,0 +1,35 @@
+. tests/functions.sh
+
+title "markdown extra-style footnotes"
+
+rc=0
+MARKDOWN_FLAGS=
+
+FOOTIE='I haz a footnote[^1]
+[^1]: yes?'
+
+try -ffootnote 'footnotes (-ffootnote)' "$FOOTIE" \
+'<p>I haz a footnote<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup></p>
+<div class="footnotes">
+<hr/>
+<ol>
+<li id="fn:1">
+<p>yes?<a href="#fnref:1" rev="footnote">&#8617;</a></p></li>
+</ol>
+</div>'
+
+try -ffootnote -Cfoot 'footnotes (-ffootnote -Cfoot)' "$FOOTIE" \
+'<p>I haz a footnote<sup id="footref:1"><a href="#foot:1" rel="footnote">1</a></sup></p>
+<div class="footnotes">
+<hr/>
+<ol>
+<li id="foot:1">
+<p>yes?<a href="#footref:1" rev="footnote">&#8617;</a></p></li>
+</ol>
+</div>'
+
+try -fnofootnote 'footnotes (-fnofootnote)' "$FOOTIE" \
+'<p>I haz a footnote<a href="yes?">^1</a></p>'
+
+summary $0
+exit $rc

+ 33 - 0
discount/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

+ 16 - 0
discount/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

+ 85 - 0
discount/tests/functions.sh

@@ -0,0 +1,85 @@
+__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
+    while [ "$1" ]; do
+	case "$1" in
+	-*) FLAGS="$FLAGS $1"
+	    shift ;;
+	*) break ;;
+	esac
+    done
+
+    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 "source:"
+	./echo "$2" | sed -e 's/^/	/'
+	./echo "diff:"
+	(./echo "$3"  >> $$.w
+	./echo "$Q"  >> $$.g
+	diff  $$.w $$.g ) | sed -e 's/^/	/'
+	rm -f $$.w $$.g
+	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
+}

+ 26 - 0
discount/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

+ 141 - 0
discount/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>&lt;b>hi!&lt;/b></p>'
+try -fnohtml 'malformed tag injection' '<x <script>' '<p>&lt;x &lt;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>&lt;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

+ 17 - 0
discount/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

+ 14 - 0
discount/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] [

+ 130 - 0
discount/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&amp;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&lt;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&lt;he&lt;he" /></p>'
+
+try 'image label contains >' \
+    '![he>he>he](url)' \
+    '<p><img src="url" alt="he&gt;he&gt;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

+ 21 - 0
discount/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

+ 155 - 0
discount/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

+ 38 - 0
discount/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

+ 12 - 0
discount/tests/misc.t

@@ -0,0 +1,12 @@
+. tests/functions.sh
+
+title "misc"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'single paragraph' 'AAA' '<p>AAA</p>'
+try '< -> &lt;' '<' '<p>&lt;</p>'
+
+summary $0
+exit $rc

+ 44 - 0
discount/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

+ 17 - 0
discount/tests/para.t

@@ -0,0 +1,17 @@
+. tests/functions.sh
+
+title "paragraph blocking"
+
+rc=0
+MARKDOWN_FLAGS=
+
+try 'paragraph followed by code' \
+    'a
+    b' \
+    '<p>a
+    b</p>'
+
+try 'single-line paragraph' 'a' '<p>a</p>'
+
+summary $0
+exit $rc

+ 12 - 0
discount/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

+ 77 - 0
discount/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

+ 20 - 0
discount/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

+ 27 - 0
discount/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] &lt;]:-( x &mdash;> [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

+ 14 - 0
discount/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

+ 91 - 0
discount/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&amp;b=c">http://foo.bar?a&amp;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&amp;b=c">http://foo.bar?a&amp;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;&amp;version=31;">http://www.biblegateway.com/passage/?search=Matthew%205:29-30;&amp;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="\&quot;=\&quot;\&quot;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

+ 25 - 0
discount/tests/smarty.t

@@ -0,0 +1,25 @@
+. tests/functions.sh
+
+title "smarty pants"
+
+rc=0
+MARKDOWN_FLAGS=0x0; export MARKDOWN_FLAGS
+
+try '(c) -> &copy;' '(c)' '<p>&copy;</p>'
+try '(r) -> &reg;' '(r)' '<p>&reg;</p>'
+try '(tm) -> &trade;' '(tm)' '<p>&trade;</p>'
+try '... -> &hellip;' '...' '<p>&hellip;</p>'
+
+try '"?--" -> &ndash;' '?--' '<p>?&ndash;</p>'
+try '"?---" -> &mdash;' '?---' '<p>?&mdash;</p>'
+
+try '"--" -> &ndash;' 'regular --' '<p>regular &ndash;</p>'
+try 'A-B -> A-B' 'A-B' '<p>A-B</p>'
+try '"fancy" -> &ldquo;fancy&rdquo;' '"fancy"' '<p>&ldquo;fancy&rdquo;</p>'
+try "'fancy'" "'fancy'" '<p>&lsquo;fancy&rsquo;</p>'
+try "don<b>'t -> don<b>&rsquo;t" "don<b>'t" '<p>don<b>&rsquo;t</p>'
+try "don't -> don&rsquo;t" "don't" '<p>don&rsquo;t</p>'
+try "it's -> it&rsquo;s" "it's" '<p>it&rsquo;s</p>'
+
+summary $0
+exit $rc

+ 29 - 0
discount/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](&lt;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

+ 16 - 0
discount/tests/strikethrough.t

@@ -0,0 +1,16 @@
+. 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>'
+try 'tildes wrapped in spaces' '~~~ ~~~' '<p>~~~ ~~~</p>'
+
+summary $0
+exit $rc

+ 34 - 0
discount/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>&lt;style>foo</p>'
+
+summary $0
+exit $rc

Some files were not shown because too many files changed in this diff