[couchdb/el5/master] Remove bundled mochiweb

Peter Lemenkov peter at fedoraproject.org
Thu Aug 5 09:25:10 UTC 2010


commit 7e3c2fd643bd275cb25d49f48df1af0acbbe2eca
Author: Peter Lemenkov <peter at fedoraproject.org>
Date:   Tue Jun 15 05:06:48 2010 +0000

    Remove bundled mochiweb

 couchdb-0001-Force-init-script-installation.patch  |    4 +-
 ...db-0002-Install-into-erllibdir-by-default.patch |    4 +-
 ...-0003-Remove-bundled-erlang-oauth-library.patch |  938 +++-
 ...b-0004-Remove-bundled-erlang-etap-library.patch | 1846 +++++-
 couchdb-0005-Remove-bundled-mochiweb-library.patch | 7205 ++++++++++++++++++++
 couchdb-0006-Remove-pid-file-after-stop.patch      |   28 +
 couchdb-0007-Fix-for-system-wide-mochiweb.patch    |   60 +
 couchdb.spec                                       |   53 +-
 8 files changed, 10110 insertions(+), 28 deletions(-)
---
diff --git a/couchdb-0001-Force-init-script-installation.patch b/couchdb-0001-Force-init-script-installation.patch
index d0c12a0..5fbf961 100644
--- a/couchdb-0001-Force-init-script-installation.patch
+++ b/couchdb-0001-Force-init-script-installation.patch
@@ -1,7 +1,7 @@
-From c63c6662fe59e5c515075b2b9511ccf0438aabc5 Mon Sep 17 00:00:00 2001
+From 4180fb6424162cfb8ee49f108c86fc408ddf640d Mon Sep 17 00:00:00 2001
 From: Peter Lemenkov <lemenkov at gmail.com>
 Date: Thu, 27 May 2010 15:17:20 +0400
-Subject: [PATCH 1/4] Force init script installation
+Subject: [PATCH 1/7] Force init script installation
 
 ---
  configure.ac |   16 +++-------------
diff --git a/couchdb-0002-Install-into-erllibdir-by-default.patch b/couchdb-0002-Install-into-erllibdir-by-default.patch
index 350b838..4636da8 100644
--- a/couchdb-0002-Install-into-erllibdir-by-default.patch
+++ b/couchdb-0002-Install-into-erllibdir-by-default.patch
@@ -1,7 +1,7 @@
-From 63afe8dc321ed5c418e28b12fc831f19bcf532e7 Mon Sep 17 00:00:00 2001
+From e3f247d873c8245c05f573295c1a2e021614f0af Mon Sep 17 00:00:00 2001
 From: Peter Lemenkov <lemenkov at gmail.com>
 Date: Thu, 27 May 2010 15:18:29 +0400
-Subject: [PATCH 2/4] Install into erllibdir by default
+Subject: [PATCH 2/7] Install into erllibdir by default
 
 ---
  configure |    4 ++--
diff --git a/couchdb-0003-Remove-bundled-erlang-oauth-library.patch b/couchdb-0003-Remove-bundled-erlang-oauth-library.patch
index 036a597..ed04057 100644
--- a/couchdb-0003-Remove-bundled-erlang-oauth-library.patch
+++ b/couchdb-0003-Remove-bundled-erlang-oauth-library.patch
@@ -1,14 +1,35 @@
-From 42a4f19bde9c94685a88b3f3c979de07e39f56ab Mon Sep 17 00:00:00 2001
+From 8d31e5ff75531deecbf9279fe0e819e9c4bdae5c Mon Sep 17 00:00:00 2001
 From: Peter Lemenkov <lemenkov at gmail.com>
 Date: Thu, 27 May 2010 15:19:51 +0400
-Subject: [PATCH 3/4] Remove bundled erlang-oauth library
+Subject: [PATCH 3/7] Remove bundled erlang-oauth library
 
 ---
- configure       |    3 ---
- configure.ac    |    1 -
- src/Makefile.am |    2 +-
- src/Makefile.in |    2 +-
- 4 files changed, 2 insertions(+), 6 deletions(-)
+ configure                            |    3 -
+ configure.ac                         |    1 -
+ src/Makefile.am                      |    2 +-
+ src/Makefile.in                      |    2 +-
+ src/erlang-oauth/Makefile.am         |   50 ----
+ src/erlang-oauth/Makefile.in         |  476 ----------------------------------
+ src/erlang-oauth/oauth.app.in        |   20 --
+ src/erlang-oauth/oauth.erl           |  107 --------
+ src/erlang-oauth/oauth_hmac_sha1.erl |   11 -
+ src/erlang-oauth/oauth_http.erl      |   22 --
+ src/erlang-oauth/oauth_plaintext.erl |   10 -
+ src/erlang-oauth/oauth_rsa_sha1.erl  |   30 ---
+ src/erlang-oauth/oauth_unix.erl      |   16 --
+ src/erlang-oauth/oauth_uri.erl       |   88 -------
+ test/etap/test_util.erl.in           |    2 +-
+ 15 files changed, 3 insertions(+), 837 deletions(-)
+ delete mode 100644 src/erlang-oauth/Makefile.am
+ delete mode 100644 src/erlang-oauth/Makefile.in
+ delete mode 100644 src/erlang-oauth/oauth.app.in
+ delete mode 100644 src/erlang-oauth/oauth.erl
+ delete mode 100644 src/erlang-oauth/oauth_hmac_sha1.erl
+ delete mode 100644 src/erlang-oauth/oauth_http.erl
+ delete mode 100644 src/erlang-oauth/oauth_plaintext.erl
+ delete mode 100644 src/erlang-oauth/oauth_rsa_sha1.erl
+ delete mode 100644 src/erlang-oauth/oauth_unix.erl
+ delete mode 100644 src/erlang-oauth/oauth_uri.erl
 
 diff --git a/configure b/configure
 index 5309998..39dda91 100755
@@ -66,6 +87,909 @@ index 54fdb7b..480da98 100644
  all: all-recursive
  
  .SUFFIXES:
+diff --git a/src/erlang-oauth/Makefile.am b/src/erlang-oauth/Makefile.am
+deleted file mode 100644
+index 95e0898..0000000
+--- a/src/erlang-oauth/Makefile.am
++++ /dev/null
+@@ -1,50 +0,0 @@
+-## Licensed under the Apache License, Version 2.0 (the "License"); you may not
+-## use this file except in compliance with the License.  You may obtain a copy
+-## of the License at
+-##
+-##   http://www.apache.org/licenses/LICENSE-2.0
+-##
+-## Unless required by applicable law or agreed to in writing, software
+-## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+-## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+-## License for the specific language governing permissions and limitations under
+-## the License.
+-
+-oauthebindir = $(localerlanglibdir)/erlang-oauth/ebin
+-
+-# Removed oauth_rsa_sha1.erl until we require R12B5 or
+-# we add a ./configure option to enable it.
+-
+-oauth_file_collection = \
+-    oauth.app.in \
+-    oauth.erl \
+-    oauth_hmac_sha1.erl \
+-    oauth_http.erl \
+-    oauth_plaintext.erl \
+-    oauth_unix.erl \
+-    oauth_uri.erl
+-
+-oauthebin_make_generated_file_list = \
+-    oauth.app \
+-    oauth.beam \
+-    oauth_hmac_sha1.beam \
+-    oauth_http.beam \
+-    oauth_plaintext.beam \
+-    oauth_unix.beam \
+-    oauth_uri.beam
+-
+-oauthebin_DATA = \
+-    $(oauthebin_make_generated_file_list)
+-
+-EXTRA_DIST = \
+-    $(oauth_file_collection) \
+-    oauth_rsa_sha1.erl
+-
+-CLEANFILES = \
+-    $(oauthebin_make_generated_file_list)
+-
+-%.app: %.app.in
+-	cp $< $@
+-
+-%.beam: %.erl
+-	$(ERLC) $(ERLC_FLAGS) $<
+diff --git a/src/erlang-oauth/Makefile.in b/src/erlang-oauth/Makefile.in
+deleted file mode 100644
+index 748abe9..0000000
+--- a/src/erlang-oauth/Makefile.in
++++ /dev/null
+@@ -1,476 +0,0 @@
+-# Makefile.in generated by automake 1.11 from Makefile.am.
+-# @configure_input@
+-
+-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+-# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+-# Inc.
+-# This Makefile.in is free software; the Free Software Foundation
+-# gives unlimited permission to copy and/or distribute it,
+-# with or without modifications, as long as this notice is preserved.
+-
+-# This program is distributed in the hope that it will be useful,
+-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+-# PARTICULAR PURPOSE.
+-
+- at SET_MAKE@
+-
+-VPATH = @srcdir@
+-pkgdatadir = $(datadir)/@PACKAGE@
+-pkgincludedir = $(includedir)/@PACKAGE@
+-pkglibdir = $(libdir)/@PACKAGE@
+-pkglibexecdir = $(libexecdir)/@PACKAGE@
+-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+-install_sh_DATA = $(install_sh) -c -m 644
+-install_sh_PROGRAM = $(install_sh) -c
+-install_sh_SCRIPT = $(install_sh) -c
+-INSTALL_HEADER = $(INSTALL_DATA)
+-transform = $(program_transform_name)
+-NORMAL_INSTALL = :
+-PRE_INSTALL = :
+-POST_INSTALL = :
+-NORMAL_UNINSTALL = :
+-PRE_UNINSTALL = :
+-POST_UNINSTALL = :
+-build_triplet = @build@
+-host_triplet = @host@
+-subdir = src/erlang-oauth
+-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+-am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \
+-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/m4/ac_check_icu.m4 \
+-	$(top_srcdir)/configure.ac
+-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+-	$(ACLOCAL_M4)
+-mkinstalldirs = $(install_sh) -d
+-CONFIG_HEADER = $(top_builddir)/config.h
+-CONFIG_CLEAN_FILES =
+-CONFIG_CLEAN_VPATH_FILES =
+-SOURCES =
+-DIST_SOURCES =
+-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+-am__vpath_adj = case $$p in \
+-    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+-    *) f=$$p;; \
+-  esac;
+-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+-am__install_max = 40
+-am__nobase_strip_setup = \
+-  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+-am__nobase_strip = \
+-  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+-am__nobase_list = $(am__nobase_strip_setup); \
+-  for p in $$list; do echo "$$p $$p"; done | \
+-  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+-  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+-    if (++n[$$2] == $(am__install_max)) \
+-      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+-    END { for (dir in files) print dir, files[dir] }'
+-am__base_list = \
+-  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+-  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+-am__installdirs = "$(DESTDIR)$(oauthebindir)"
+-DATA = $(oauthebin_DATA)
+-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+-ACLOCAL = @ACLOCAL@
+-AMTAR = @AMTAR@
+-AR = @AR@
+-AUTOCONF = @AUTOCONF@
+-AUTOHEADER = @AUTOHEADER@
+-AUTOMAKE = @AUTOMAKE@
+-AWK = @AWK@
+-CC = @CC@
+-CCDEPMODE = @CCDEPMODE@
+-CFLAGS = @CFLAGS@
+-CPP = @CPP@
+-CPPFLAGS = @CPPFLAGS@
+-CURL_CFLAGS = @CURL_CFLAGS@
+-CURL_CONFIG = @CURL_CONFIG@
+-CURL_LDFLAGS = @CURL_LDFLAGS@
+-CURL_LIBS = @CURL_LIBS@
+-CYGPATH_W = @CYGPATH_W@
+-DEFS = @DEFS@
+-DEPDIR = @DEPDIR@
+-DSYMUTIL = @DSYMUTIL@
+-DUMPBIN = @DUMPBIN@
+-ECHO_C = @ECHO_C@
+-ECHO_N = @ECHO_N@
+-ECHO_T = @ECHO_T@
+-EGREP = @EGREP@
+-ERL = @ERL@
+-ERLC = @ERLC@
+-ERLC_FLAGS = @ERLC_FLAGS@
+-EXEEXT = @EXEEXT@
+-FGREP = @FGREP@
+-FLAGS = @FLAGS@
+-GREP = @GREP@
+-HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@
+-ICU_CFLAGS = @ICU_CFLAGS@
+-ICU_CONFIG = @ICU_CONFIG@
+-ICU_CXXFLAGS = @ICU_CXXFLAGS@
+-ICU_LIBS = @ICU_LIBS@
+-ICU_LOCAL_BIN = @ICU_LOCAL_BIN@
+-ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@
+-ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@
+-INSTALL = @INSTALL@
+-INSTALL_DATA = @INSTALL_DATA@
+-INSTALL_PROGRAM = @INSTALL_PROGRAM@
+-INSTALL_SCRIPT = @INSTALL_SCRIPT@
+-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+-JSLIB = @JSLIB@
+-LD = @LD@
+-LDFLAGS = @LDFLAGS@
+-LIBOBJS = @LIBOBJS@
+-LIBS = @LIBS@
+-LIBTOOL = @LIBTOOL@
+-LIPO = @LIPO@
+-LN_S = @LN_S@
+-LTLIBOBJS = @LTLIBOBJS@
+-MAKEINFO = @MAKEINFO@
+-MKDIR_P = @MKDIR_P@
+-NM = @NM@
+-NMEDIT = @NMEDIT@
+-OBJDUMP = @OBJDUMP@
+-OBJEXT = @OBJEXT@
+-OTOOL = @OTOOL@
+-OTOOL64 = @OTOOL64@
+-PACKAGE = @PACKAGE@
+-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+-PACKAGE_NAME = @PACKAGE_NAME@
+-PACKAGE_STRING = @PACKAGE_STRING@
+-PACKAGE_TARNAME = @PACKAGE_TARNAME@
+-PACKAGE_URL = @PACKAGE_URL@
+-PACKAGE_VERSION = @PACKAGE_VERSION@
+-PATH_SEPARATOR = @PATH_SEPARATOR@
+-RANLIB = @RANLIB@
+-SED = @SED@
+-SET_MAKE = @SET_MAKE@
+-SHELL = @SHELL@
+-STRIP = @STRIP@
+-VERSION = @VERSION@
+-abs_builddir = @abs_builddir@
+-abs_srcdir = @abs_srcdir@
+-abs_top_builddir = @abs_top_builddir@
+-abs_top_srcdir = @abs_top_srcdir@
+-ac_ct_CC = @ac_ct_CC@
+-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+-am__include = @am__include@
+-am__leading_dot = @am__leading_dot@
+-am__quote = @am__quote@
+-am__tar = @am__tar@
+-am__untar = @am__untar@
+-bindir = @bindir@
+-bug_uri = @bug_uri@
+-build = @build@
+-build_alias = @build_alias@
+-build_cpu = @build_cpu@
+-build_os = @build_os@
+-build_vendor = @build_vendor@
+-builddir = @builddir@
+-datadir = @datadir@
+-datarootdir = @datarootdir@
+-docdir = @docdir@
+-dvidir = @dvidir@
+-exec_prefix = @exec_prefix@
+-host = @host@
+-host_alias = @host_alias@
+-host_cpu = @host_cpu@
+-host_os = @host_os@
+-host_vendor = @host_vendor@
+-htmldir = @htmldir@
+-includedir = @includedir@
+-infodir = @infodir@
+-initdir = @initdir@
+-install_sh = @install_sh@
+-launchddir = @launchddir@
+-libdir = @libdir@
+-libexecdir = @libexecdir@
+-localconfdir = @localconfdir@
+-localdatadir = @localdatadir@
+-localdocdir = @localdocdir@
+-localedir = @localedir@
+-localerlanglibdir = @localerlanglibdir@
+-locallibbindir = @locallibbindir@
+-locallibdir = @locallibdir@
+-localstatedir = @localstatedir@
+-localstatelibdir = @localstatelibdir@
+-localstatelogdir = @localstatelogdir@
+-localstaterundir = @localstaterundir@
+-lt_ECHO = @lt_ECHO@
+-mandir = @mandir@
+-mkdir_p = @mkdir_p@
+-oldincludedir = @oldincludedir@
+-package_author_address = @package_author_address@
+-package_author_name = @package_author_name@
+-package_identifier = @package_identifier@
+-package_name = @package_name@
+-package_tarname = @package_tarname@
+-pdfdir = @pdfdir@
+-prefix = @prefix@
+-program_transform_name = @program_transform_name@
+-psdir = @psdir@
+-sbindir = @sbindir@
+-sharedstatedir = @sharedstatedir@
+-srcdir = @srcdir@
+-sysconfdir = @sysconfdir@
+-target_alias = @target_alias@
+-top_build_prefix = @top_build_prefix@
+-top_builddir = @top_builddir@
+-top_srcdir = @top_srcdir@
+-version = @version@
+-version_major = @version_major@
+-version_minor = @version_minor@
+-version_release = @version_release@
+-version_revision = @version_revision@
+-version_stage = @version_stage@
+-oauthebindir = $(localerlanglibdir)/erlang-oauth/ebin
+-
+-# Removed oauth_rsa_sha1.erl until we require R12B5 or
+-# we add a ./configure option to enable it.
+-oauth_file_collection = \
+-    oauth.app.in \
+-    oauth.erl \
+-    oauth_hmac_sha1.erl \
+-    oauth_http.erl \
+-    oauth_plaintext.erl \
+-    oauth_unix.erl \
+-    oauth_uri.erl
+-
+-oauthebin_make_generated_file_list = \
+-    oauth.app \
+-    oauth.beam \
+-    oauth_hmac_sha1.beam \
+-    oauth_http.beam \
+-    oauth_plaintext.beam \
+-    oauth_unix.beam \
+-    oauth_uri.beam
+-
+-oauthebin_DATA = \
+-    $(oauthebin_make_generated_file_list)
+-
+-EXTRA_DIST = \
+-    $(oauth_file_collection) \
+-    oauth_rsa_sha1.erl
+-
+-CLEANFILES = \
+-    $(oauthebin_make_generated_file_list)
+-
+-all: all-am
+-
+-.SUFFIXES:
+-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+-	@for dep in $?; do \
+-	  case '$(am__configure_deps)' in \
+-	    *$$dep*) \
+-	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+-	        && { if test -f $@; then exit 0; else break; fi; }; \
+-	      exit 1;; \
+-	  esac; \
+-	done; \
+-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/erlang-oauth/Makefile'; \
+-	$(am__cd) $(top_srcdir) && \
+-	  $(AUTOMAKE) --foreign src/erlang-oauth/Makefile
+-.PRECIOUS: Makefile
+-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+-	@case '$?' in \
+-	  *config.status*) \
+-	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+-	  *) \
+-	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+-	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+-	esac;
+-
+-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-
+-$(top_srcdir)/configure:  $(am__configure_deps)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-$(am__aclocal_m4_deps):
+-
+-mostlyclean-libtool:
+-	-rm -f *.lo
+-
+-clean-libtool:
+-	-rm -rf .libs _libs
+-install-oauthebinDATA: $(oauthebin_DATA)
+-	@$(NORMAL_INSTALL)
+-	test -z "$(oauthebindir)" || $(MKDIR_P) "$(DESTDIR)$(oauthebindir)"
+-	@list='$(oauthebin_DATA)'; test -n "$(oauthebindir)" || list=; \
+-	for p in $$list; do \
+-	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+-	  echo "$$d$$p"; \
+-	done | $(am__base_list) | \
+-	while read files; do \
+-	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(oauthebindir)'"; \
+-	  $(INSTALL_DATA) $$files "$(DESTDIR)$(oauthebindir)" || exit $$?; \
+-	done
+-
+-uninstall-oauthebinDATA:
+-	@$(NORMAL_UNINSTALL)
+-	@list='$(oauthebin_DATA)'; test -n "$(oauthebindir)" || list=; \
+-	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+-	test -n "$$files" || exit 0; \
+-	echo " ( cd '$(DESTDIR)$(oauthebindir)' && rm -f" $$files ")"; \
+-	cd "$(DESTDIR)$(oauthebindir)" && rm -f $$files
+-tags: TAGS
+-TAGS:
+-
+-ctags: CTAGS
+-CTAGS:
+-
+-
+-distdir: $(DISTFILES)
+-	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+-	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+-	list='$(DISTFILES)'; \
+-	  dist_files=`for file in $$list; do echo $$file; done | \
+-	  sed -e "s|^$$srcdirstrip/||;t" \
+-	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+-	case $$dist_files in \
+-	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+-			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+-			   sort -u` ;; \
+-	esac; \
+-	for file in $$dist_files; do \
+-	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+-	  if test -d $$d/$$file; then \
+-	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+-	    if test -d "$(distdir)/$$file"; then \
+-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+-	    fi; \
+-	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+-	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+-	    fi; \
+-	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+-	  else \
+-	    test -f "$(distdir)/$$file" \
+-	    || cp -p $$d/$$file "$(distdir)/$$file" \
+-	    || exit 1; \
+-	  fi; \
+-	done
+-check-am: all-am
+-check: check-am
+-all-am: Makefile $(DATA)
+-installdirs:
+-	for dir in "$(DESTDIR)$(oauthebindir)"; do \
+-	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+-	done
+-install: install-am
+-install-exec: install-exec-am
+-install-data: install-data-am
+-uninstall: uninstall-am
+-
+-install-am: all-am
+-	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+-
+-installcheck: installcheck-am
+-install-strip:
+-	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+-	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+-	  `test -z '$(STRIP)' || \
+-	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+-mostlyclean-generic:
+-
+-clean-generic:
+-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+-
+-distclean-generic:
+-	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+-	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+-
+-maintainer-clean-generic:
+-	@echo "This command is intended for maintainers to use"
+-	@echo "it deletes files that may require special tools to rebuild."
+-clean: clean-am
+-
+-clean-am: clean-generic clean-libtool mostlyclean-am
+-
+-distclean: distclean-am
+-	-rm -f Makefile
+-distclean-am: clean-am distclean-generic
+-
+-dvi: dvi-am
+-
+-dvi-am:
+-
+-html: html-am
+-
+-html-am:
+-
+-info: info-am
+-
+-info-am:
+-
+-install-data-am: install-oauthebinDATA
+-
+-install-dvi: install-dvi-am
+-
+-install-dvi-am:
+-
+-install-exec-am:
+-
+-install-html: install-html-am
+-
+-install-html-am:
+-
+-install-info: install-info-am
+-
+-install-info-am:
+-
+-install-man:
+-
+-install-pdf: install-pdf-am
+-
+-install-pdf-am:
+-
+-install-ps: install-ps-am
+-
+-install-ps-am:
+-
+-installcheck-am:
+-
+-maintainer-clean: maintainer-clean-am
+-	-rm -f Makefile
+-maintainer-clean-am: distclean-am maintainer-clean-generic
+-
+-mostlyclean: mostlyclean-am
+-
+-mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+-
+-pdf: pdf-am
+-
+-pdf-am:
+-
+-ps: ps-am
+-
+-ps-am:
+-
+-uninstall-am: uninstall-oauthebinDATA
+-
+-.MAKE: install-am install-strip
+-
+-.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+-	distclean distclean-generic distclean-libtool distdir dvi \
+-	dvi-am html html-am info info-am install install-am \
+-	install-data install-data-am install-dvi install-dvi-am \
+-	install-exec install-exec-am install-html install-html-am \
+-	install-info install-info-am install-man install-oauthebinDATA \
+-	install-pdf install-pdf-am install-ps install-ps-am \
+-	install-strip installcheck installcheck-am installdirs \
+-	maintainer-clean maintainer-clean-generic mostlyclean \
+-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+-	uninstall uninstall-am uninstall-oauthebinDATA
+-
+-
+-%.app: %.app.in
+-	cp $< $@
+-
+-%.beam: %.erl
+-	$(ERLC) $(ERLC_FLAGS) $<
+-
+-# Tell versions [3.59,3.63) of GNU make to not export all variables.
+-# Otherwise a system limit (for SysV at least) may be exceeded.
+-.NOEXPORT:
+diff --git a/src/erlang-oauth/oauth.app.in b/src/erlang-oauth/oauth.app.in
+deleted file mode 100644
+index 6357b9b..0000000
+--- a/src/erlang-oauth/oauth.app.in
++++ /dev/null
+@@ -1,20 +0,0 @@
+-{application, oauth, [
+-  {description, "Erlang OAuth implementation"},
+-  {vsn, "dev"},
+-  {modules, [
+-    oauth,
+-    oauth_hmac_sha1,
+-    oauth_http,
+-    oauth_plaintext,
+-    oauth_rsa_sha1,
+-    oauth_unix,
+-    oauth_uri
+-  ]},
+-  {registered, []},
+-  {applications, [
+-    kernel,
+-    stdlib,
+-    crypto,
+-    inets
+-  ]}
+-]}.
+diff --git a/src/erlang-oauth/oauth.erl b/src/erlang-oauth/oauth.erl
+deleted file mode 100644
+index 866655c..0000000
+--- a/src/erlang-oauth/oauth.erl
++++ /dev/null
+@@ -1,107 +0,0 @@
+--module(oauth).
+-
+--export(
+-  [ get/5
+-  , header/1
+-  , post/5
+-  , signature/5
+-  , signature_base_string/3
+-  , signed_params/6
+-  , token/1
+-  , token_secret/1
+-  , uri/2
+-  , verify/6
+-  ]).
+-
+-
+-get(URL, ExtraParams, Consumer, Token, TokenSecret) ->
+-  SignedParams = signed_params("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
+-  oauth_http:get(uri(URL, SignedParams)).
+-
+-post(URL, ExtraParams, Consumer, Token, TokenSecret) ->
+-  SignedParams = signed_params("POST", URL, ExtraParams, Consumer, Token, TokenSecret),
+-  oauth_http:post(URL, oauth_uri:params_to_string(SignedParams)).
+-
+-uri(Base, []) ->
+-  Base;
+-uri(Base, Params) ->
+-  lists:concat([Base, "?", oauth_uri:params_to_string(Params)]).
+-
+-header(Params) ->
+-  {"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}.
+-
+-token(Params) ->
+-  proplists:get_value("oauth_token", Params).
+-
+-token_secret(Params) ->
+-  proplists:get_value("oauth_token_secret", Params).
+-
+-verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
+-  case signature_method(Consumer) of
+-    plaintext ->
+-      oauth_plaintext:verify(Signature, consumer_secret(Consumer), TokenSecret);
+-    hmac_sha1 ->
+-      BaseString = signature_base_string(HttpMethod, URL, Params),
+-      oauth_hmac_sha1:verify(Signature, BaseString, consumer_secret(Consumer), TokenSecret);
+-    rsa_sha1 ->
+-      BaseString = signature_base_string(HttpMethod, URL, Params),
+-      oauth_rsa_sha1:verify(Signature, BaseString, consumer_secret(Consumer))
+-  end.
+-
+-signed_params(HttpMethod, URL, ExtraParams, Consumer, Token, TokenSecret) ->
+-  Params = token_param(Token, params(Consumer, ExtraParams)),
+-  [{"oauth_signature", signature(HttpMethod, URL, Params, Consumer, TokenSecret)}|Params].
+-
+-signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
+-  case signature_method(Consumer) of
+-    plaintext ->
+-      oauth_plaintext:signature(consumer_secret(Consumer), TokenSecret);
+-    hmac_sha1 ->
+-      BaseString = signature_base_string(HttpMethod, URL, Params),
+-      oauth_hmac_sha1:signature(BaseString, consumer_secret(Consumer), TokenSecret);
+-    rsa_sha1 ->
+-      BaseString = signature_base_string(HttpMethod, URL, Params),
+-      oauth_rsa_sha1:signature(BaseString, consumer_secret(Consumer))
+-  end.
+-
+-signature_base_string(HttpMethod, URL, Params) ->
+-  NormalizedURL = oauth_uri:normalize(URL),
+-  NormalizedParams = oauth_uri:params_to_string(lists:sort(Params)),
+-  oauth_uri:calate("&", [HttpMethod, NormalizedURL, NormalizedParams]).
+-
+-token_param("", Params) ->
+-  Params;
+-token_param(Token, Params) ->
+-  [{"oauth_token", Token}|Params].
+-
+-params(Consumer, Params) ->
+-  Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth
+-  params(Consumer, oauth_unix:timestamp(), Nonce, Params).
+-
+-params(Consumer, Timestamp, Nonce, Params) ->
+-  [ {"oauth_version", "1.0"}
+-  , {"oauth_nonce", Nonce}
+-  , {"oauth_timestamp", integer_to_list(Timestamp)}
+-  , {"oauth_signature_method", signature_method_string(Consumer)}
+-  , {"oauth_consumer_key", consumer_key(Consumer)}
+-  | Params
+-  ].
+-
+-signature_method_string(Consumer) ->
+-  case signature_method(Consumer) of
+-    plaintext ->
+-      "PLAINTEXT";
+-    hmac_sha1 ->
+-      "HMAC-SHA1";
+-    rsa_sha1 ->
+-      "RSA-SHA1"
+-  end.
+-
+-signature_method(_Consumer={_, _, Method}) ->
+-  Method.
+-
+-consumer_secret(_Consumer={_, Secret, _}) ->
+-  Secret.
+-
+-consumer_key(_Consumer={Key, _, _}) ->
+-  Key.
+diff --git a/src/erlang-oauth/oauth_hmac_sha1.erl b/src/erlang-oauth/oauth_hmac_sha1.erl
+deleted file mode 100644
+index 79d59f3..0000000
+--- a/src/erlang-oauth/oauth_hmac_sha1.erl
++++ /dev/null
+@@ -1,11 +0,0 @@
+--module(oauth_hmac_sha1).
+-
+--export([signature/3, verify/4]).
+-
+-
+-signature(BaseString, CS, TS) ->
+-  Key = oauth_uri:calate("&", [CS, TS]),
+-  base64:encode_to_string(crypto:sha_mac(Key, BaseString)).
+-
+-verify(Signature, BaseString, CS, TS) ->
+-  couch_util:verify(signature(BaseString, CS, TS), Signature).
+diff --git a/src/erlang-oauth/oauth_http.erl b/src/erlang-oauth/oauth_http.erl
+deleted file mode 100644
+index bf5a4ba..0000000
+--- a/src/erlang-oauth/oauth_http.erl
++++ /dev/null
+@@ -1,22 +0,0 @@
+--module(oauth_http).
+-
+--export([get/1, post/2, response_params/1, response_body/1, response_code/1]).
+-
+-
+-get(URL) ->
+-  request(get, {URL, []}).
+-
+-post(URL, Data) ->
+-  request(post, {URL, [], "application/x-www-form-urlencoded", Data}).
+-
+-request(Method, Request) ->
+-  http:request(Method, Request, [{autoredirect, false}], []).
+-
+-response_params(Response) ->
+-  oauth_uri:params_from_string(response_body(Response)).
+-
+-response_body({{_, _, _}, _, Body}) ->
+-  Body.
+-
+-response_code({{_, Code, _}, _, _}) ->
+-  Code.
+diff --git a/src/erlang-oauth/oauth_plaintext.erl b/src/erlang-oauth/oauth_plaintext.erl
+deleted file mode 100644
+index 41a1e9b..0000000
+--- a/src/erlang-oauth/oauth_plaintext.erl
++++ /dev/null
+@@ -1,10 +0,0 @@
+--module(oauth_plaintext).
+-
+--export([signature/2, verify/3]).
+-
+-
+-signature(CS, TS) ->
+-  oauth_uri:calate("&", [CS, TS]).
+-
+-verify(Signature, CS, TS) ->
+-  couch_util:verify(signature(CS, TS), Signature).
+diff --git a/src/erlang-oauth/oauth_rsa_sha1.erl b/src/erlang-oauth/oauth_rsa_sha1.erl
+deleted file mode 100644
+index 6f4828e..0000000
+--- a/src/erlang-oauth/oauth_rsa_sha1.erl
++++ /dev/null
+@@ -1,30 +0,0 @@
+--module(oauth_rsa_sha1).
+-
+--export([signature/2, verify/3]).
+-
+--include_lib("public_key/include/public_key.hrl").
+-
+-
+-signature(BaseString, PrivateKeyPath) ->
+-  {ok, [Info]} = public_key:pem_to_der(PrivateKeyPath),
+-  {ok, PrivateKey} = public_key:decode_private_key(Info),
+-  base64:encode_to_string(public_key:sign(list_to_binary(BaseString), PrivateKey)).
+-
+-verify(Signature, BaseString, PublicKey) ->
+-  public_key:verify_signature(to_binary(BaseString), sha, base64:decode(Signature), public_key(PublicKey)).
+-
+-to_binary(Term) when is_list(Term) ->
+-  list_to_binary(Term);
+-to_binary(Term) when is_binary(Term) ->
+-  Term.
+-
+-public_key(Path) when is_list(Path) ->
+-  {ok, [{cert, DerCert, not_encrypted}]} = public_key:pem_to_der(Path),
+-  {ok, Cert} = public_key:pkix_decode_cert(DerCert, otp),
+-  public_key(Cert);
+-public_key(#'OTPCertificate'{tbsCertificate=Cert}) ->
+-  public_key(Cert);
+-public_key(#'OTPTBSCertificate'{subjectPublicKeyInfo=Info}) ->
+-  public_key(Info);
+-public_key(#'OTPSubjectPublicKeyInfo'{subjectPublicKey=Key}) ->
+-  Key.
+diff --git a/src/erlang-oauth/oauth_unix.erl b/src/erlang-oauth/oauth_unix.erl
+deleted file mode 100644
+index 73ca314..0000000
+--- a/src/erlang-oauth/oauth_unix.erl
++++ /dev/null
+@@ -1,16 +0,0 @@
+--module(oauth_unix).
+-
+--export([timestamp/0]).
+-
+-
+-timestamp() ->
+-  timestamp(calendar:universal_time()).
+-
+-timestamp(DateTime) ->
+-  seconds(DateTime) - epoch().
+-
+-epoch() ->
+-  seconds({{1970,1,1},{00,00,00}}).
+-
+-seconds(DateTime) ->
+-  calendar:datetime_to_gregorian_seconds(DateTime).
+diff --git a/src/erlang-oauth/oauth_uri.erl b/src/erlang-oauth/oauth_uri.erl
+deleted file mode 100644
+index fb27ae7..0000000
+--- a/src/erlang-oauth/oauth_uri.erl
++++ /dev/null
+@@ -1,88 +0,0 @@
+--module(oauth_uri).
+-
+--export([normalize/1, calate/2, encode/1]).
+--export([params_from_string/1, params_to_string/1,
+-  params_from_header_string/1, params_to_header_string/1]).
+-
+--import(lists, [concat/1]).
+-
+--define(is_uppercase_alpha(C), C >= $A, C =< $Z).
+--define(is_lowercase_alpha(C), C >= $a, C =< $z).
+--define(is_alpha(C), ?is_uppercase_alpha(C); ?is_lowercase_alpha(C)).
+--define(is_digit(C), C >= $0, C =< $9).
+--define(is_alphanumeric(C), ?is_alpha(C); ?is_digit(C)).
+--define(is_unreserved(C), ?is_alphanumeric(C); C =:= $-; C =:= $_; C =:= $.; C =:= $~).
+--define(is_hex(C), ?is_digit(C); C >= $A, C =< $F).
+-
+-
+-normalize(URI) ->
+-  case http_uri:parse(URI) of
+-    {Scheme, UserInfo, Host, Port, Path, _Query} ->
+-      normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]);
+-    Else ->
+-      Else
+-  end.
+-
+-normalize(http, UserInfo, Host, 80, Acc) ->
+-  normalize(http, UserInfo, [Host|Acc]);
+-normalize(https, UserInfo, Host, 443, Acc) ->
+-  normalize(https, UserInfo, [Host|Acc]);
+-normalize(Scheme, UserInfo, Host, Port, Acc) ->
+-  normalize(Scheme, UserInfo, [Host, ":", Port|Acc]).
+-
+-normalize(Scheme, [], Acc) ->
+-  concat([Scheme, "://"|Acc]);
+-normalize(Scheme, UserInfo, Acc) ->
+-  concat([Scheme, "://", UserInfo, "@"|Acc]).
+-
+-params_to_header_string(Params) ->
+-  intercalate(", ", [concat([encode(K), "=\"", encode(V), "\""]) || {K, V} <- Params]).
+-
+-params_from_header_string(String) ->
+-  [param_from_header_string(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""].
+-
+-param_from_header_string(Param) ->
+-  [Key, QuotedValue] = string:tokens(Param, "="),
+-  Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2),
+-  {decode(Key), decode(Value)}.
+-
+-params_from_string(Params) ->
+-  [param_from_string(Param) || Param <- string:tokens(Params, "&")].
+-
+-param_from_string(Param) ->
+-  list_to_tuple([decode(Value) || Value <- string:tokens(Param, "=")]).
+-
+-params_to_string(Params) ->
+-  intercalate("&", [calate("=", [K, V]) || {K, V} <- Params]).
+-
+-calate(Sep, Xs) ->
+-  intercalate(Sep, [encode(X) || X <- Xs]).
+-
+-intercalate(Sep, Xs) ->
+-  concat(intersperse(Sep, Xs)).
+-
+-intersperse(_, []) -> [];
+-intersperse(_, [X]) -> [X];
+-intersperse(Sep, [X|Xs]) ->
+-  [X, Sep|intersperse(Sep, Xs)].
+-
+-decode(Chars) ->
+-  decode(Chars, []).
+-
+-decode([], Decoded) ->
+-  lists:reverse(Decoded);
+-decode([$%,A,B|Etc], Decoded) when ?is_hex(A), ?is_hex(B) ->
+-  decode(Etc, [erlang:list_to_integer([A,B], 16)|Decoded]);
+-decode([C|Etc], Decoded) when ?is_unreserved(C) ->
+-  decode(Etc, [C|Decoded]).
+-
+-encode(Chars) ->
+-  encode(Chars, []).
+-
+-encode([], Encoded) ->
+-  lists:flatten(lists:reverse(Encoded));
+-encode([C|Etc], Encoded) when ?is_unreserved(C) ->
+-  encode(Etc, [C|Encoded]);
+-encode([C|Etc], Encoded) ->
+-  Value = io_lib:format("%~2.1.0s", [erlang:integer_to_list(C, 16)]),
+-  encode(Etc, [Value|Encoded]).
+diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in
+index 4c42edb..79b0417 100644
+--- a/test/etap/test_util.erl.in
++++ b/test/etap/test_util.erl.in
+@@ -22,7 +22,7 @@ builddir() ->
+     "@abs_top_builddir@".
+ 
+ init_code_path() ->
+-    Paths = ["etap", "couchdb", "erlang-oauth", "ibrowse", "mochiweb"],
++    Paths = ["etap", "couchdb", "ibrowse", "mochiweb"],
+     lists:foreach(fun(Name) ->
+         code:add_pathz(filename:join([builddir(), "src", Name]))
+     end, Paths).
 -- 
 1.6.6.1
 
diff --git a/couchdb-0004-Remove-bundled-erlang-etap-library.patch b/couchdb-0004-Remove-bundled-erlang-etap-library.patch
index bfdce85..68f3b4d 100644
--- a/couchdb-0004-Remove-bundled-erlang-etap-library.patch
+++ b/couchdb-0004-Remove-bundled-erlang-etap-library.patch
@@ -1,14 +1,37 @@
-From c2946e7f2cd60daf7cd75491caf5108161db45be Mon Sep 17 00:00:00 2001
+From 4312a2e8f45586ea2d6f2794854100da6f4092b7 Mon Sep 17 00:00:00 2001
 From: Peter Lemenkov <lemenkov at gmail.com>
 Date: Thu, 27 May 2010 15:22:37 +0400
-Subject: [PATCH 4/4] Remove bundled erlang-etap library
+Subject: [PATCH 4/7] Remove bundled erlang-etap library
 
 ---
- configure       |    3 ---
- configure.ac    |    1 -
- src/Makefile.am |    2 +-
- src/Makefile.in |    2 +-
- 4 files changed, 2 insertions(+), 6 deletions(-)
+ configure                     |    3 -
+ configure.ac                  |    1 -
+ src/Makefile.am               |    2 +-
+ src/Makefile.in               |    2 +-
+ src/etap/Makefile.am          |   44 ----
+ src/etap/Makefile.in          |  467 -----------------------------------------
+ src/etap/etap.erl             |  416 ------------------------------------
+ src/etap/etap_application.erl |   72 -------
+ src/etap/etap_can.erl         |   79 -------
+ src/etap/etap_exception.erl   |   66 ------
+ src/etap/etap_process.erl     |   42 ----
+ src/etap/etap_report.erl      |  343 ------------------------------
+ src/etap/etap_request.erl     |   89 --------
+ src/etap/etap_string.erl      |   47 ----
+ src/etap/etap_web.erl         |   65 ------
+ test/etap/test_util.erl.in    |    2 +-
+ 16 files changed, 3 insertions(+), 1737 deletions(-)
+ delete mode 100644 src/etap/Makefile.am
+ delete mode 100644 src/etap/Makefile.in
+ delete mode 100644 src/etap/etap.erl
+ delete mode 100644 src/etap/etap_application.erl
+ delete mode 100644 src/etap/etap_can.erl
+ delete mode 100644 src/etap/etap_exception.erl
+ delete mode 100644 src/etap/etap_process.erl
+ delete mode 100644 src/etap/etap_report.erl
+ delete mode 100644 src/etap/etap_request.erl
+ delete mode 100644 src/etap/etap_string.erl
+ delete mode 100644 src/etap/etap_web.erl
 
 diff --git a/configure b/configure
 index 39dda91..ca7732f 100755
@@ -66,6 +89,1815 @@ index 480da98..2422e39 100644
  all: all-recursive
  
  .SUFFIXES:
+diff --git a/src/etap/Makefile.am b/src/etap/Makefile.am
+deleted file mode 100644
+index 732347b..0000000
+--- a/src/etap/Makefile.am
++++ /dev/null
+@@ -1,44 +0,0 @@
+-## Licensed under the Apache License, Version 2.0 (the "License"); you may not
+-## use this file except in compliance with the License.  You may obtain a copy
+-## of the License at
+-##
+-##   http://www.apache.org/licenses/LICENSE-2.0
+-##
+-## Unless required by applicable law or agreed to in writing, software
+-## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+-## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+-## License for the specific language governing permissions and limitations under
+-## the License.
+-
+-etapebindir = $(localerlanglibdir)/etap/ebin
+-
+-etap_file_collection = \
+-    etap.erl \
+-   	etap_application.erl \
+-    etap_can.erl \
+-    etap_exception.erl \
+-    etap_process.erl \
+-	etap_report.erl \
+-	etap_request.erl \
+-	etap_string.erl \
+-   	etap_web.erl
+-
+-etapebin_make_generated_file_list = \
+-    etap.beam \
+-   	etap_application.beam \
+-    etap_can.beam \
+-    etap_exception.beam \
+-    etap_process.beam \
+-	etap_report.beam \
+-	etap_request.beam \
+-	etap_string.beam \
+-   	etap_web.beam
+-
+-etapebin_DATA = $(etapebin_make_generated_file_list)
+-
+-EXTRA_DIST =  $(etap_file_collection)
+-
+-CLEANFILES = $(etapebin_make_generated_file_list)
+-
+-%.beam: %.erl
+-	$(ERLC) $(ERLC_FLAGS) $<
+diff --git a/src/etap/Makefile.in b/src/etap/Makefile.in
+deleted file mode 100644
+index 2c752b3..0000000
+--- a/src/etap/Makefile.in
++++ /dev/null
+@@ -1,467 +0,0 @@
+-# Makefile.in generated by automake 1.11 from Makefile.am.
+-# @configure_input@
+-
+-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+-# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+-# Inc.
+-# This Makefile.in is free software; the Free Software Foundation
+-# gives unlimited permission to copy and/or distribute it,
+-# with or without modifications, as long as this notice is preserved.
+-
+-# This program is distributed in the hope that it will be useful,
+-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+-# PARTICULAR PURPOSE.
+-
+- at SET_MAKE@
+-
+-VPATH = @srcdir@
+-pkgdatadir = $(datadir)/@PACKAGE@
+-pkgincludedir = $(includedir)/@PACKAGE@
+-pkglibdir = $(libdir)/@PACKAGE@
+-pkglibexecdir = $(libexecdir)/@PACKAGE@
+-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+-install_sh_DATA = $(install_sh) -c -m 644
+-install_sh_PROGRAM = $(install_sh) -c
+-install_sh_SCRIPT = $(install_sh) -c
+-INSTALL_HEADER = $(INSTALL_DATA)
+-transform = $(program_transform_name)
+-NORMAL_INSTALL = :
+-PRE_INSTALL = :
+-POST_INSTALL = :
+-NORMAL_UNINSTALL = :
+-PRE_UNINSTALL = :
+-POST_UNINSTALL = :
+-build_triplet = @build@
+-host_triplet = @host@
+-subdir = src/etap
+-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+-am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \
+-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/m4/ac_check_icu.m4 \
+-	$(top_srcdir)/configure.ac
+-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+-	$(ACLOCAL_M4)
+-mkinstalldirs = $(install_sh) -d
+-CONFIG_HEADER = $(top_builddir)/config.h
+-CONFIG_CLEAN_FILES =
+-CONFIG_CLEAN_VPATH_FILES =
+-SOURCES =
+-DIST_SOURCES =
+-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+-am__vpath_adj = case $$p in \
+-    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+-    *) f=$$p;; \
+-  esac;
+-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+-am__install_max = 40
+-am__nobase_strip_setup = \
+-  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+-am__nobase_strip = \
+-  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+-am__nobase_list = $(am__nobase_strip_setup); \
+-  for p in $$list; do echo "$$p $$p"; done | \
+-  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+-  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+-    if (++n[$$2] == $(am__install_max)) \
+-      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+-    END { for (dir in files) print dir, files[dir] }'
+-am__base_list = \
+-  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+-  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+-am__installdirs = "$(DESTDIR)$(etapebindir)"
+-DATA = $(etapebin_DATA)
+-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+-ACLOCAL = @ACLOCAL@
+-AMTAR = @AMTAR@
+-AR = @AR@
+-AUTOCONF = @AUTOCONF@
+-AUTOHEADER = @AUTOHEADER@
+-AUTOMAKE = @AUTOMAKE@
+-AWK = @AWK@
+-CC = @CC@
+-CCDEPMODE = @CCDEPMODE@
+-CFLAGS = @CFLAGS@
+-CPP = @CPP@
+-CPPFLAGS = @CPPFLAGS@
+-CURL_CFLAGS = @CURL_CFLAGS@
+-CURL_CONFIG = @CURL_CONFIG@
+-CURL_LDFLAGS = @CURL_LDFLAGS@
+-CURL_LIBS = @CURL_LIBS@
+-CYGPATH_W = @CYGPATH_W@
+-DEFS = @DEFS@
+-DEPDIR = @DEPDIR@
+-DSYMUTIL = @DSYMUTIL@
+-DUMPBIN = @DUMPBIN@
+-ECHO_C = @ECHO_C@
+-ECHO_N = @ECHO_N@
+-ECHO_T = @ECHO_T@
+-EGREP = @EGREP@
+-ERL = @ERL@
+-ERLC = @ERLC@
+-ERLC_FLAGS = @ERLC_FLAGS@
+-EXEEXT = @EXEEXT@
+-FGREP = @FGREP@
+-FLAGS = @FLAGS@
+-GREP = @GREP@
+-HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@
+-ICU_CFLAGS = @ICU_CFLAGS@
+-ICU_CONFIG = @ICU_CONFIG@
+-ICU_CXXFLAGS = @ICU_CXXFLAGS@
+-ICU_LIBS = @ICU_LIBS@
+-ICU_LOCAL_BIN = @ICU_LOCAL_BIN@
+-ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@
+-ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@
+-INSTALL = @INSTALL@
+-INSTALL_DATA = @INSTALL_DATA@
+-INSTALL_PROGRAM = @INSTALL_PROGRAM@
+-INSTALL_SCRIPT = @INSTALL_SCRIPT@
+-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+-JSLIB = @JSLIB@
+-LD = @LD@
+-LDFLAGS = @LDFLAGS@
+-LIBOBJS = @LIBOBJS@
+-LIBS = @LIBS@
+-LIBTOOL = @LIBTOOL@
+-LIPO = @LIPO@
+-LN_S = @LN_S@
+-LTLIBOBJS = @LTLIBOBJS@
+-MAKEINFO = @MAKEINFO@
+-MKDIR_P = @MKDIR_P@
+-NM = @NM@
+-NMEDIT = @NMEDIT@
+-OBJDUMP = @OBJDUMP@
+-OBJEXT = @OBJEXT@
+-OTOOL = @OTOOL@
+-OTOOL64 = @OTOOL64@
+-PACKAGE = @PACKAGE@
+-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+-PACKAGE_NAME = @PACKAGE_NAME@
+-PACKAGE_STRING = @PACKAGE_STRING@
+-PACKAGE_TARNAME = @PACKAGE_TARNAME@
+-PACKAGE_URL = @PACKAGE_URL@
+-PACKAGE_VERSION = @PACKAGE_VERSION@
+-PATH_SEPARATOR = @PATH_SEPARATOR@
+-RANLIB = @RANLIB@
+-SED = @SED@
+-SET_MAKE = @SET_MAKE@
+-SHELL = @SHELL@
+-STRIP = @STRIP@
+-VERSION = @VERSION@
+-abs_builddir = @abs_builddir@
+-abs_srcdir = @abs_srcdir@
+-abs_top_builddir = @abs_top_builddir@
+-abs_top_srcdir = @abs_top_srcdir@
+-ac_ct_CC = @ac_ct_CC@
+-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+-am__include = @am__include@
+-am__leading_dot = @am__leading_dot@
+-am__quote = @am__quote@
+-am__tar = @am__tar@
+-am__untar = @am__untar@
+-bindir = @bindir@
+-bug_uri = @bug_uri@
+-build = @build@
+-build_alias = @build_alias@
+-build_cpu = @build_cpu@
+-build_os = @build_os@
+-build_vendor = @build_vendor@
+-builddir = @builddir@
+-datadir = @datadir@
+-datarootdir = @datarootdir@
+-docdir = @docdir@
+-dvidir = @dvidir@
+-exec_prefix = @exec_prefix@
+-host = @host@
+-host_alias = @host_alias@
+-host_cpu = @host_cpu@
+-host_os = @host_os@
+-host_vendor = @host_vendor@
+-htmldir = @htmldir@
+-includedir = @includedir@
+-infodir = @infodir@
+-initdir = @initdir@
+-install_sh = @install_sh@
+-launchddir = @launchddir@
+-libdir = @libdir@
+-libexecdir = @libexecdir@
+-localconfdir = @localconfdir@
+-localdatadir = @localdatadir@
+-localdocdir = @localdocdir@
+-localedir = @localedir@
+-localerlanglibdir = @localerlanglibdir@
+-locallibbindir = @locallibbindir@
+-locallibdir = @locallibdir@
+-localstatedir = @localstatedir@
+-localstatelibdir = @localstatelibdir@
+-localstatelogdir = @localstatelogdir@
+-localstaterundir = @localstaterundir@
+-lt_ECHO = @lt_ECHO@
+-mandir = @mandir@
+-mkdir_p = @mkdir_p@
+-oldincludedir = @oldincludedir@
+-package_author_address = @package_author_address@
+-package_author_name = @package_author_name@
+-package_identifier = @package_identifier@
+-package_name = @package_name@
+-package_tarname = @package_tarname@
+-pdfdir = @pdfdir@
+-prefix = @prefix@
+-program_transform_name = @program_transform_name@
+-psdir = @psdir@
+-sbindir = @sbindir@
+-sharedstatedir = @sharedstatedir@
+-srcdir = @srcdir@
+-sysconfdir = @sysconfdir@
+-target_alias = @target_alias@
+-top_build_prefix = @top_build_prefix@
+-top_builddir = @top_builddir@
+-top_srcdir = @top_srcdir@
+-version = @version@
+-version_major = @version_major@
+-version_minor = @version_minor@
+-version_release = @version_release@
+-version_revision = @version_revision@
+-version_stage = @version_stage@
+-etapebindir = $(localerlanglibdir)/etap/ebin
+-etap_file_collection = \
+-    etap.erl \
+-   	etap_application.erl \
+-    etap_can.erl \
+-    etap_exception.erl \
+-    etap_process.erl \
+-	etap_report.erl \
+-	etap_request.erl \
+-	etap_string.erl \
+-   	etap_web.erl
+-
+-etapebin_make_generated_file_list = \
+-    etap.beam \
+-   	etap_application.beam \
+-    etap_can.beam \
+-    etap_exception.beam \
+-    etap_process.beam \
+-	etap_report.beam \
+-	etap_request.beam \
+-	etap_string.beam \
+-   	etap_web.beam
+-
+-etapebin_DATA = $(etapebin_make_generated_file_list)
+-EXTRA_DIST = $(etap_file_collection)
+-CLEANFILES = $(etapebin_make_generated_file_list)
+-all: all-am
+-
+-.SUFFIXES:
+-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+-	@for dep in $?; do \
+-	  case '$(am__configure_deps)' in \
+-	    *$$dep*) \
+-	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+-	        && { if test -f $@; then exit 0; else break; fi; }; \
+-	      exit 1;; \
+-	  esac; \
+-	done; \
+-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/etap/Makefile'; \
+-	$(am__cd) $(top_srcdir) && \
+-	  $(AUTOMAKE) --foreign src/etap/Makefile
+-.PRECIOUS: Makefile
+-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+-	@case '$?' in \
+-	  *config.status*) \
+-	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+-	  *) \
+-	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+-	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+-	esac;
+-
+-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-
+-$(top_srcdir)/configure:  $(am__configure_deps)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-$(am__aclocal_m4_deps):
+-
+-mostlyclean-libtool:
+-	-rm -f *.lo
+-
+-clean-libtool:
+-	-rm -rf .libs _libs
+-install-etapebinDATA: $(etapebin_DATA)
+-	@$(NORMAL_INSTALL)
+-	test -z "$(etapebindir)" || $(MKDIR_P) "$(DESTDIR)$(etapebindir)"
+-	@list='$(etapebin_DATA)'; test -n "$(etapebindir)" || list=; \
+-	for p in $$list; do \
+-	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+-	  echo "$$d$$p"; \
+-	done | $(am__base_list) | \
+-	while read files; do \
+-	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(etapebindir)'"; \
+-	  $(INSTALL_DATA) $$files "$(DESTDIR)$(etapebindir)" || exit $$?; \
+-	done
+-
+-uninstall-etapebinDATA:
+-	@$(NORMAL_UNINSTALL)
+-	@list='$(etapebin_DATA)'; test -n "$(etapebindir)" || list=; \
+-	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+-	test -n "$$files" || exit 0; \
+-	echo " ( cd '$(DESTDIR)$(etapebindir)' && rm -f" $$files ")"; \
+-	cd "$(DESTDIR)$(etapebindir)" && rm -f $$files
+-tags: TAGS
+-TAGS:
+-
+-ctags: CTAGS
+-CTAGS:
+-
+-
+-distdir: $(DISTFILES)
+-	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+-	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+-	list='$(DISTFILES)'; \
+-	  dist_files=`for file in $$list; do echo $$file; done | \
+-	  sed -e "s|^$$srcdirstrip/||;t" \
+-	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+-	case $$dist_files in \
+-	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+-			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+-			   sort -u` ;; \
+-	esac; \
+-	for file in $$dist_files; do \
+-	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+-	  if test -d $$d/$$file; then \
+-	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+-	    if test -d "$(distdir)/$$file"; then \
+-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+-	    fi; \
+-	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+-	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+-	    fi; \
+-	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+-	  else \
+-	    test -f "$(distdir)/$$file" \
+-	    || cp -p $$d/$$file "$(distdir)/$$file" \
+-	    || exit 1; \
+-	  fi; \
+-	done
+-check-am: all-am
+-check: check-am
+-all-am: Makefile $(DATA)
+-installdirs:
+-	for dir in "$(DESTDIR)$(etapebindir)"; do \
+-	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+-	done
+-install: install-am
+-install-exec: install-exec-am
+-install-data: install-data-am
+-uninstall: uninstall-am
+-
+-install-am: all-am
+-	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+-
+-installcheck: installcheck-am
+-install-strip:
+-	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+-	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+-	  `test -z '$(STRIP)' || \
+-	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+-mostlyclean-generic:
+-
+-clean-generic:
+-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+-
+-distclean-generic:
+-	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+-	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+-
+-maintainer-clean-generic:
+-	@echo "This command is intended for maintainers to use"
+-	@echo "it deletes files that may require special tools to rebuild."
+-clean: clean-am
+-
+-clean-am: clean-generic clean-libtool mostlyclean-am
+-
+-distclean: distclean-am
+-	-rm -f Makefile
+-distclean-am: clean-am distclean-generic
+-
+-dvi: dvi-am
+-
+-dvi-am:
+-
+-html: html-am
+-
+-html-am:
+-
+-info: info-am
+-
+-info-am:
+-
+-install-data-am: install-etapebinDATA
+-
+-install-dvi: install-dvi-am
+-
+-install-dvi-am:
+-
+-install-exec-am:
+-
+-install-html: install-html-am
+-
+-install-html-am:
+-
+-install-info: install-info-am
+-
+-install-info-am:
+-
+-install-man:
+-
+-install-pdf: install-pdf-am
+-
+-install-pdf-am:
+-
+-install-ps: install-ps-am
+-
+-install-ps-am:
+-
+-installcheck-am:
+-
+-maintainer-clean: maintainer-clean-am
+-	-rm -f Makefile
+-maintainer-clean-am: distclean-am maintainer-clean-generic
+-
+-mostlyclean: mostlyclean-am
+-
+-mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+-
+-pdf: pdf-am
+-
+-pdf-am:
+-
+-ps: ps-am
+-
+-ps-am:
+-
+-uninstall-am: uninstall-etapebinDATA
+-
+-.MAKE: install-am install-strip
+-
+-.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+-	distclean distclean-generic distclean-libtool distdir dvi \
+-	dvi-am html html-am info info-am install install-am \
+-	install-data install-data-am install-dvi install-dvi-am \
+-	install-etapebinDATA install-exec install-exec-am install-html \
+-	install-html-am install-info install-info-am install-man \
+-	install-pdf install-pdf-am install-ps install-ps-am \
+-	install-strip installcheck installcheck-am installdirs \
+-	maintainer-clean maintainer-clean-generic mostlyclean \
+-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+-	uninstall uninstall-am uninstall-etapebinDATA
+-
+-
+-%.beam: %.erl
+-	$(ERLC) $(ERLC_FLAGS) $<
+-
+-# Tell versions [3.59,3.63) of GNU make to not export all variables.
+-# Otherwise a system limit (for SysV at least) may be exceeded.
+-.NOEXPORT:
+diff --git a/src/etap/etap.erl b/src/etap/etap.erl
+deleted file mode 100644
+index 5ad5dba..0000000
+--- a/src/etap/etap.erl
++++ /dev/null
+@@ -1,416 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%% 
+-%% @author Nick Gerakines <nick at gerakines.net> [http://socklabs.com/]
+-%% @author Jeremy Wall <jeremy at marzhillstudios.com>
+-%% @version 0.3.4
+-%% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines
+-%% @reference http://testanything.org/wiki/index.php/Main_Page
+-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol
+-%% @todo Finish implementing the skip directive.
+-%% @todo Document the messages handled by this receive loop.
+-%% @todo Explain in documentation why we use a process to handle test input.
+-%% @doc etap is a TAP testing module for Erlang components and applications.
+-%% This module allows developers to test their software using the TAP method.
+-%% 
+-%% <blockquote cite="http://en.wikipedia.org/wiki/Test_Anything_Protocol"><p>
+-%% TAP, the Test Anything Protocol, is a simple text-based interface between
+-%% testing modules in a test harness. TAP started life as part of the test
+-%% harness for Perl but now has implementations in C/C++, Python, PHP, Perl
+-%% and probably others by the time you read this.
+-%% </p></blockquote>
+-%% 
+-%% The testing process begins by defining a plan using etap:plan/1, running
+-%% a number of etap tests and then calling eta:end_tests/0. Please refer to
+-%% the Erlang modules in the t directory of this project for example tests.
+--module(etap).
+--export([
+-    ensure_test_server/0, start_etap_server/0, test_server/1,
+-    diag/1, diag/2, plan/1, end_tests/0, not_ok/2, ok/2, is/3, isnt/3,
+-    any/3, none/3, fun_is/3, is_greater/3, skip/1, skip/2,
+-    ensure_coverage_starts/0, ensure_coverage_ends/0, coverage_report/0,
+-    datetime/1, skip/3, bail/0, bail/1
+-]).
+--record(test_state, {planned = 0, count = 0, pass = 0, fail = 0, skip = 0, skip_reason = ""}).
+--vsn("0.3.4").
+-
+-%% @spec plan(N) -> Result
+-%%       N = unknown | skip | {skip, string()} | integer()
+-%%       Result = ok
+-%% @doc Create a test plan and boot strap the test server.
+-plan(unknown) ->
+-    ensure_coverage_starts(),
+-    ensure_test_server(),
+-    etap_server ! {self(), plan, unknown},
+-    ok;
+-plan(skip) ->
+-    io:format("1..0 # skip~n");
+-plan({skip, Reason}) ->
+-    io:format("1..0 # skip ~s~n", [Reason]);
+-plan(N) when is_integer(N), N > 0 ->
+-    ensure_coverage_starts(),
+-    ensure_test_server(),
+-    etap_server ! {self(), plan, N},
+-    ok.
+-
+-%% @spec end_tests() -> ok
+-%% @doc End the current test plan and output test results.
+-%% @todo This should probably be done in the test_server process.
+-end_tests() ->
+-    ensure_coverage_ends(),
+-    etap_server ! {self(), state},
+-    State = receive X -> X end,
+-    if
+-        State#test_state.planned == -1 ->
+-            io:format("1..~p~n", [State#test_state.count]);
+-        true ->
+-            ok
+-    end,
+-    case whereis(etap_server) of
+-        undefined -> ok;
+-        _ -> etap_server ! done, ok
+-    end.
+-
+-%% @private
+-ensure_coverage_starts() ->
+-    case os:getenv("COVER") of
+-        false -> ok;
+-        _ ->
+-            BeamDir = case os:getenv("COVER_BIN") of false -> "ebin"; X -> X end,
+-            cover:compile_beam_directory(BeamDir)
+-    end.
+-
+-%% @private
+-%% @doc Attempts to write out any collected coverage data to the cover/
+-%% directory. This function should not be called externally, but it could be.
+-ensure_coverage_ends() ->
+-    case os:getenv("COVER") of
+-        false -> ok;
+-        _ ->
+-            filelib:ensure_dir("cover/"),
+-            Name = lists:flatten([
+-                io_lib:format("~.16b", [X]) || X <- binary_to_list(erlang:md5(
+-                     term_to_binary({make_ref(), now()})
+-                ))
+-            ]),
+-            cover:export("cover/" ++ Name ++ ".coverdata")
+-    end.
+-
+-%% @spec coverage_report() -> ok
+-%% @doc Use the cover module's covreage report builder to create code coverage
+-%% reports from recently created coverdata files.
+-coverage_report() ->
+-    [cover:import(File) || File <- filelib:wildcard("cover/*.coverdata")],
+-    lists:foreach(
+-        fun(Mod) ->
+-            cover:analyse_to_file(Mod, atom_to_list(Mod) ++ "_coverage.txt", [])
+-        end,
+-        cover:imported_modules()
+-    ),
+-    ok.
+-
+-bail() ->
+-    bail("").
+-
+-bail(Reason) ->
+-    etap_server ! {self(), diag, "Bail out! " ++ Reason},
+-    ensure_coverage_ends(),
+-    etap_server ! done, ok,
+-    ok.
+-
+-
+-%% @spec diag(S) -> ok
+-%%       S = string()
+-%% @doc Print a debug/status message related to the test suite.
+-diag(S) -> etap_server ! {self(), diag, "# " ++ S}, ok.
+-
+-%% @spec diag(Format, Data) -> ok
+-%%      Format = atom() | string() | binary()
+-%%      Data = [term()]
+-%%      UnicodeList = [Unicode]
+-%%      Unicode = int()
+-%% @doc Print a debug/status message related to the test suite.
+-%% Function arguments are passed through io_lib:format/2.
+-diag(Format, Data) -> diag(io_lib:format(Format, Data)).
+-
+-%% @spec ok(Expr, Desc) -> Result
+-%%       Expr = true | false
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Assert that a statement is true.
+-ok(Expr, Desc) -> mk_tap(Expr == true, Desc).
+-
+-%% @spec not_ok(Expr, Desc) -> Result
+-%%       Expr = true | false
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Assert that a statement is false.
+-not_ok(Expr, Desc) -> mk_tap(Expr == false, Desc).
+-
+-%% @spec is(Got, Expected, Desc) -> Result
+-%%       Got = any()
+-%%       Expected = any()
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Assert that two values are the same.
+-is(Got, Expected, Desc) ->
+-    case mk_tap(Got == Expected, Desc) of
+-        false ->
+-            etap_server ! {self(), diag, "    ---"},
+-            etap_server ! {self(), diag, io_lib:format("    description: ~p", [Desc])},
+-            etap_server ! {self(), diag, io_lib:format("    found:       ~p", [Got])},
+-            etap_server ! {self(), diag, io_lib:format("    wanted:      ~p", [Expected])},
+-            etap_server ! {self(), diag, "    ..."},
+-            false;
+-        true -> true
+-    end.
+-
+-%% @spec isnt(Got, Expected, Desc) -> Result
+-%%       Got = any()
+-%%       Expected = any()
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Assert that two values are not the same.
+-isnt(Got, Expected, Desc) -> mk_tap(Got /= Expected, Desc).
+-
+-%% @spec is_greater(ValueA, ValueB, Desc) -> Result
+-%%       ValueA = number()
+-%%       ValueB = number()
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Assert that an integer is greater than another.
+-is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) ->
+-    mk_tap(ValueA > ValueB, Desc).
+-
+-%% @spec any(Got, Items, Desc) -> Result
+-%%       Got = any()
+-%%       Items = [any()]
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Assert that an item is in a list.
+-any(Got, Items, Desc) ->
+-    is(lists:member(Got, Items), true, Desc).
+-
+-%% @spec none(Got, Items, Desc) -> Result
+-%%       Got = any()
+-%%       Items = [any()]
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Assert that an item is not in a list.
+-none(Got, Items, Desc) ->
+-    is(lists:member(Got, Items), false, Desc).
+-
+-%% @spec fun_is(Fun, Expected, Desc) -> Result
+-%%       Fun = function()
+-%%       Expected = any()
+-%%       Desc = string()
+-%%       Result = true | false
+-%% @doc Use an anonymous function to assert a pattern match.
+-fun_is(Fun, Expected, Desc) when is_function(Fun) ->
+-    is(Fun(Expected), true, Desc).
+-
+-%% @equiv skip(TestFun, "")
+-skip(TestFun) when is_function(TestFun) ->
+-    skip(TestFun, "").
+-
+-%% @spec skip(TestFun, Reason) -> ok
+-%%       TestFun = function()
+-%%       Reason = string()
+-%% @doc Skip a test.
+-skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
+-    begin_skip(Reason),
+-    catch TestFun(),
+-    end_skip(),
+-    ok.
+-
+-%% @spec skip(Q, TestFun, Reason) -> ok
+-%%       Q = true | false | function() 
+-%%       TestFun = function()
+-%%       Reason = string()
+-%% @doc Skips a test conditionally. The first argument to this function can
+-%% either be the 'true' or 'false' atoms or a function that returns 'true' or
+-%% 'false'.
+-skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason) ->
+-    case QFun() of
+-        true -> begin_skip(Reason), TestFun(), end_skip();
+-        _ -> TestFun()
+-    end,
+-    ok;
+-
+-skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true ->
+-    begin_skip(Reason),
+-    TestFun(),
+-    end_skip(),
+-    ok;
+-
+-skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
+-    TestFun(),
+-    ok.
+-
+-%% @private
+-begin_skip(Reason) ->
+-    etap_server ! {self(), begin_skip, Reason}.
+-
+-%% @private
+-end_skip() ->
+-    etap_server ! {self(), end_skip}.
+-
+-% ---
+-% Internal / Private functions
+-
+-%% @private
+-%% @doc Start the etap_server process if it is not running already.
+-ensure_test_server() ->
+-    case whereis(etap_server) of
+-        undefined ->
+-            proc_lib:start(?MODULE, start_etap_server,[]);
+-        _ ->
+-            diag("The test server is already running.")
+-    end.
+-
+-%% @private
+-%% @doc Start the etap_server loop and register itself as the etap_server
+-%% process.
+-start_etap_server() ->
+-    catch register(etap_server, self()),
+-    proc_lib:init_ack(ok),
+-    etap:test_server(#test_state{
+-        planned = 0,
+-        count = 0,
+-        pass = 0,
+-        fail = 0,
+-        skip = 0,
+-        skip_reason = ""
+-    }).
+-
+-
+-%% @private
+-%% @doc The main etap_server receive/run loop. The etap_server receive loop
+-%% responds to seven messages apperatining to failure or passing of tests.
+-%% It is also used to initiate the testing process with the {_, plan, _}
+-%% message that clears the current test state.
+-test_server(State) ->
+-    NewState = receive
+-        {_From, plan, unknown} ->
+-            io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
+-            io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]),
+-            State#test_state{
+-                planned = -1,
+-                count = 0,
+-                pass = 0,
+-                fail = 0,
+-                skip = 0,
+-                skip_reason = ""
+-            };
+-        {_From, plan, N} ->
+-            io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
+-            io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]),
+-            io:format("1..~p~n", [N]),
+-            State#test_state{
+-                planned = N,
+-                count = 0,
+-                pass = 0,
+-                fail = 0,
+-                skip = 0,
+-                skip_reason = ""
+-            };
+-        {_From, begin_skip, Reason} ->
+-            State#test_state{
+-                skip = 1,
+-                skip_reason = Reason
+-            };
+-        {_From, end_skip} ->
+-            State#test_state{
+-                skip = 0,
+-                skip_reason = ""
+-            };
+-        {_From, pass, Desc} ->
+-            FullMessage = skip_diag(
+-                " - " ++ Desc,
+-                State#test_state.skip,
+-                State#test_state.skip_reason
+-            ),
+-            io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
+-            State#test_state{
+-                count = State#test_state.count + 1,
+-                pass = State#test_state.pass + 1
+-            };
+-            
+-        {_From, fail, Desc} ->
+-            FullMessage = skip_diag(
+-                " - " ++ Desc,
+-                State#test_state.skip,
+-                State#test_state.skip_reason
+-            ),
+-            io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
+-            State#test_state{
+-                count = State#test_state.count + 1,
+-                fail = State#test_state.fail + 1
+-            };
+-        {From, state} ->
+-            From ! State,
+-            State;
+-        {_From, diag, Message} ->
+-            io:format("~s~n", [Message]),
+-            State;
+-        {From, count} ->
+-            From ! State#test_state.count,
+-            State;
+-        {From, is_skip} ->
+-            From ! State#test_state.skip,
+-            State;
+-        done ->
+-            exit(normal)
+-    end,
+-    test_server(NewState).
+-
+-%% @private
+-%% @doc Process the result of a test and send it to the etap_server process.
+-mk_tap(Result, Desc) ->
+-    IsSkip = lib:sendw(etap_server, is_skip),
+-    case [IsSkip, Result] of
+-        [_, true] ->
+-            etap_server ! {self(), pass, Desc},
+-            true;                        
+-        [1, _] ->                        
+-            etap_server ! {self(), pass, Desc},
+-            true;                        
+-        _ ->                             
+-            etap_server ! {self(), fail, Desc},
+-            false
+-    end.
+-
+-%% @private
+-%% @doc Format a date/time string.
+-datetime(DateTime) ->
+-    {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime,
+-    io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month, Day, Hour, Min, Sec]).
+-
+-%% @private
+-%% @doc Craft an output message taking skip/todo into consideration.
+-skip_diag(Message, 0, _) ->
+-    Message;
+-skip_diag(_Message, 1, "") ->
+-    " # SKIP";
+-skip_diag(_Message, 1, Reason) ->
+-    " # SKIP : " ++ Reason.
+diff --git a/src/etap/etap_application.erl b/src/etap/etap_application.erl
+deleted file mode 100644
+index 98b5275..0000000
+--- a/src/etap/etap_application.erl
++++ /dev/null
+@@ -1,72 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @author Nick Gerakines <nick at gerakines.net> [http://socklabs.com/]
+-%% @copyright 2008 Nick Gerakines
+-%% @reference http://testanything.org/wiki/index.php/Main_Page
+-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol
+-%% @todo Explain in documentation why we use a process to handle test input.
+-%% @todo Add test to verify the number of members in a pg2 group.
+-%% @doc Provide test functionality to the application and related behaviors.
+--module(etap_application).
+--export([
+-    start_ok/2, ensure_loaded/3, load_ok/2,
+-    pg2_group_exists/2, pg2_group_doesntexist/2
+-]).
+-
+-%% @spec load_ok(string(), string()) -> true | false
+-%% @doc Assert that an application can be loaded successfully.
+-load_ok(AppName, Desc) ->
+-    etap:ok(application:load(AppName) == ok, Desc).
+-
+-%% @spec start_ok(string(), string()) -> true | false
+-%% @doc Assert that an application can be started successfully.
+-start_ok(AppName, Desc) ->
+-    etap:ok(application:start(AppName) == ok, Desc).
+-
+-%% @spec ensure_loaded(string(), string(), string()) -> true | false
+-%% @doc Assert that an application has been loaded successfully.
+-ensure_loaded(AppName, AppVsn, Desc) ->
+-    etap:any(
+-        fun(Match) -> case Match of {AppName, _, AppVsn} -> true; _ -> false end end,
+-        application:loaded_applications(),
+-        Desc
+-    ).
+-
+-%% @spec pg2_group_exists(string(), string()) -> true | false
+-%% @doc Assert that a pg2 group exists.
+-pg2_group_exists(GroupName, Desc) ->
+-    etap:any(
+-        fun(Match) -> Match == GroupName end,
+-        pg2:which_groups(),
+-        Desc
+-    ).
+-
+-%% @spec pg2_group_doesntexist(string(), string()) -> true | false
+-%% @doc Assert that a pg2 group does not exists.
+-pg2_group_doesntexist(GroupName, Desc) ->
+-    etap:none(
+-        fun(Match) -> Match == GroupName end,
+-        pg2:which_groups(),
+-        Desc
+-    ).
+diff --git a/src/etap/etap_can.erl b/src/etap/etap_can.erl
+deleted file mode 100644
+index 552b717..0000000
+--- a/src/etap/etap_can.erl
++++ /dev/null
+@@ -1,79 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @reference http://testanything.org/wiki/index.php/Main_Page
+-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol
+-%% @doc Provide test functionality modules
+--module(etap_can).
+-
+--export([
+-    loaded_ok/2, can_ok/2, can_ok/3,
+-    has_attrib/2, is_attrib/3, is_behaviour/2
+-]).
+-
+-%% @spec loaded_ok(atom(), string()) -> true | false
+-%% @doc Assert that a module has been loaded successfully.
+-loaded_ok(M, Desc) when is_atom(M) ->
+-    etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc).
+-
+-%% @spec can_ok(atom(), atom()) -> true | false
+-%% @doc Assert that a module exports a given function.
+-can_ok(M, F) when is_atom(M), is_atom(F) ->
+-    Matches = [X || {X, _} <- M:module_info(exports), X == F],
+-    etap:ok(Matches > 0, lists:concat([M, " can ", F])).
+-
+-%% @spec can_ok(atom(), atom(), integer()) -> true | false
+-%% @doc Assert that a module exports a given function with a given arity.
+-can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) ->
+-    Matches = [X || X <- M:module_info(exports), X == {F, A}],
+-    etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])).
+-
+-%% @spec has_attrib(M, A) -> true | false
+-%%       M = atom()
+-%%       A = atom()
+-%% @doc Asserts that a module has a given attribute.
+-has_attrib(M, A) when is_atom(M), is_atom(A) ->
+-    etap:isnt(
+-        proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'),
+-        'asdlkjasdlkads',
+-        lists:concat([M, " has attribute ", A])
+-    ).
+-
+-%% @spec has_attrib(M, A. V) -> true | false
+-%%       M = atom()
+-%%       A = atom()
+-%%       V = any()
+-%% @doc Asserts that a module has a given attribute with a given value.
+-is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) ->
+-    etap:is(
+-        proplists:get_value(A, M:module_info(attributes)),
+-        [V],
+-        lists:concat([M, "'s ", A, " is ", V])
+-    ).
+-
+-%% @spec is_behavior(M, B) -> true | false
+-%%       M = atom()
+-%%       B = atom()
+-%% @doc Asserts that a given module has a specific behavior.
+-is_behaviour(M, B) when is_atom(M) andalso is_atom(B) ->
+-    is_attrib(M, behaviour, B).
+diff --git a/src/etap/etap_exception.erl b/src/etap/etap_exception.erl
+deleted file mode 100644
+index ba66072..0000000
+--- a/src/etap/etap_exception.erl
++++ /dev/null
+@@ -1,66 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @reference http://testanything.org/wiki/index.php/Main_Page
+-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol
+-%% @doc Adds exception based testing to the etap suite.
+--module(etap_exception).
+-
+--export([dies_ok/2, lives_ok/2, throws_ok/3]).
+-
+-% ---
+-% External / Public functions
+-
+-%% @doc Assert that an exception is raised when running a given function.
+-dies_ok(F, Desc) ->
+-    case (catch F()) of
+-        {'EXIT', _} -> etap:ok(true, Desc);
+-        _ -> etap:ok(false, Desc)
+-    end.
+-
+-%% @doc Assert that an exception is not raised when running a given function.
+-lives_ok(F, Desc) ->
+-    etap:is(try_this(F), success, Desc).
+-
+-%% @doc Assert that the exception thrown by a function matches the given exception.
+-throws_ok(F, Exception, Desc) ->
+-    try F() of
+-        _ -> etap:ok(nok, Desc)
+-    catch
+-        _:E ->
+-            etap:is(E, Exception, Desc)
+-    end.
+-
+-% ---
+-% Internal / Private functions
+-
+-%% @private
+-%% @doc Run a function and catch any exceptions.
+-try_this(F) when is_function(F, 0) ->
+-    try F() of
+-        _ -> success
+-    catch
+-        throw:E -> {throw, E};
+-        error:E -> {error, E};
+-        exit:E -> {exit, E}
+-    end.
+diff --git a/src/etap/etap_process.erl b/src/etap/etap_process.erl
+deleted file mode 100644
+index 69f5ba0..0000000
+--- a/src/etap/etap_process.erl
++++ /dev/null
+@@ -1,42 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @doc Adds process/pid testing to the etap suite.
+--module(etap_process).
+-
+--export([is_pid/2, is_alive/2, is_mfa/3]).
+-
+-% ---
+-% External / Public functions
+-
+-%% @doc Assert that a given variable is a pid.
+-is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc);
+-is_pid(_, Desc) -> etap:ok(false, Desc).
+-
+-%% @doc Assert that a given process/pid is alive.
+-is_alive(Pid, Desc) ->
+-    etap:ok(erlang:is_process_alive(Pid), Desc).
+-
+-%% @doc Assert that the current function of a pid is a given {M, F, A} tuple.
+-is_mfa(Pid, MFA, Desc) ->
+-    etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc).
+diff --git a/src/etap/etap_report.erl b/src/etap/etap_report.erl
+deleted file mode 100644
+index 6d692fb..0000000
+--- a/src/etap/etap_report.erl
++++ /dev/null
+@@ -1,343 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @doc A module for creating nice looking code coverage reports.
+--module(etap_report).
+--export([create/0]).
+-
+-%% @spec create() -> ok
+-%% @doc Create html code coverage reports for each module that code coverage
+-%% data exists for.
+-create() ->
+-    [cover:import(File) || File <- filelib:wildcard("cover/*.coverdata")],
+-    Modules = lists:foldl(
+-        fun(Module, Acc) ->
+-            [{Module, file_report(Module)} | Acc]
+-        end,
+-        [],
+-        cover:imported_modules()
+-    ),
+-    index(Modules).
+-
+-%% @private
+-index(Modules) ->
+-    {ok, IndexFD} = file:open("cover/index.html", [write]),
+-    io:format(IndexFD, "<html><head><style>
+-    table.percent_graph { height: 12px; border:1px solid #E2E6EF; empty-cells: show; }
+-    table.percent_graph td.covered { height: 10px; background: #00f000; }
+-    table.percent_graph td.uncovered { height: 10px; background: #e00000; }
+-    .odd { background-color: #ddd; }
+-    .even { background-color: #fff; }
+-    </style></head>", []),
+-    io:format(IndexFD, "<body>", []),
+-    lists:foldl(
+-        fun({Module, {Good, Bad, Source}}, LastRow) ->
+-            case {Good + Bad, Source} of
+-                {0, _} -> LastRow;
+-                {_, none} -> LastRow;
+-                _ ->
+-                    CovPer = round((Good / (Good + Bad)) * 100),
+-                    UnCovPer = round((Bad / (Good + Bad)) * 100),
+-                    RowClass = case LastRow of 1 -> "odd"; _ -> "even" end,
+-                    io:format(IndexFD, "<div class=\"~s\">", [RowClass]),
+-                    io:format(IndexFD, "<a href=\"~s\">~s</a>", [atom_to_list(Module) ++ "_report.html", atom_to_list(Module)]),
+-                    io:format(IndexFD, "
+-                    <table cellspacing='0' cellpadding='0' align='right'>
+-                      <tr>
+-                        <td><tt>~p%</tt>&nbsp;</td><td>
+-                          <table cellspacing='0' class='percent_graph' cellpadding='0' width='100'>
+-                          <tr><td class='covered' width='~p' /><td class='uncovered' width='~p' /></tr>
+-                          </table>
+-                        </td>
+-                      </tr>
+-                    </table>
+-                    ", [CovPer, CovPer, UnCovPer]),
+-                    io:format(IndexFD, "</div>", []),
+-                    case LastRow of
+-                        1 -> 0;
+-                        0 -> 1
+-                    end
+-            end
+-        end,
+-        0,
+-        lists:sort(Modules)
+-    ),
+-    {TotalGood, TotalBad} = lists:foldl(
+-        fun({_, {Good, Bad, Source}}, {TGood, TBad}) ->
+-            case Source of none -> {TGood, TBad}; _ -> {TGood + Good, TBad + Bad} end
+-        end,
+-        {0, 0},
+-        Modules
+-    ),
+-    io:format(IndexFD, "<p>Generated on ~s.</p>~n", [etap:datetime({date(), time()})]),
+-    case TotalGood + TotalBad of
+-        0 -> ok;
+-        _ ->
+-            TotalCovPer = round((TotalGood / (TotalGood + TotalBad)) * 100),
+-            TotalUnCovPer = round((TotalBad / (TotalGood + TotalBad)) * 100),
+-            io:format(IndexFD, "<div>", []),
+-            io:format(IndexFD, "Total 
+-            <table cellspacing='0' cellpadding='0' align='right'>
+-              <tr>
+-                <td><tt>~p%</tt>&nbsp;</td><td>
+-                  <table cellspacing='0' class='percent_graph' cellpadding='0' width='100'>
+-                  <tr><td class='covered' width='~p' /><td class='uncovered' width='~p' /></tr>
+-                  </table>
+-                </td>
+-              </tr>
+-            </table>
+-            ", [TotalCovPer, TotalCovPer, TotalUnCovPer]),
+-            io:format(IndexFD, "</div>", [])
+-    end,
+-    io:format(IndexFD, "</body></html>", []),
+-    file:close(IndexFD),
+-    ok.
+-
+-%% @private
+-file_report(Module) ->
+-    {ok, Data} = cover:analyse(Module, calls, line),
+-    Source = find_source(Module),
+-    {Good, Bad} = collect_coverage(Data, {0, 0}),
+-    case {Source, Good + Bad} of
+-        {none, _} -> ok;
+-        {_, 0} -> ok;
+-        _ ->
+-            {ok, SourceFD} = file:open(Source, [read]),
+-            {ok, WriteFD} = file:open("cover/" ++ atom_to_list(Module) ++ "_report.html", [write]),
+-            io:format(WriteFD, "~s", [header(Module, Good, Bad)]),
+-            output_lines(Data, WriteFD, SourceFD, 1),
+-            io:format(WriteFD, "~s", [footer()]),
+-            file:close(WriteFD),
+-            file:close(SourceFD),
+-            ok
+-    end,
+-    {Good, Bad, Source}.
+-
+-%% @private
+-collect_coverage([], Acc) -> Acc;
+-collect_coverage([{{_, _}, 0} | Data], {Good, Bad}) ->
+-    collect_coverage(Data, {Good, Bad + 1});
+-collect_coverage([_ | Data], {Good, Bad}) ->
+-    collect_coverage(Data, {Good + 1, Bad}).
+-
+-%% @private
+-output_lines(Data, WriteFD, SourceFD, LineNumber) ->
+-    {Match, NextData} = datas_match(Data, LineNumber),
+-    case io:get_line(SourceFD, '') of
+-        eof -> ok;
+-        Line = "%% @todo" ++ _ ->
+-            io:format(WriteFD, "~s", [out_line(LineNumber, highlight, Line)]),
+-            output_lines(NextData, WriteFD, SourceFD, LineNumber + 1);
+-        Line = "% " ++ _ ->
+-            io:format(WriteFD, "~s", [out_line(LineNumber, none, Line)]),
+-            output_lines(NextData, WriteFD, SourceFD, LineNumber + 1);
+-        Line ->
+-            case Match of
+-                {true, CC} ->
+-                    io:format(WriteFD, "~s", [out_line(LineNumber, CC, Line)]),
+-                    output_lines(NextData, WriteFD, SourceFD, LineNumber + 1);
+-                false ->
+-                    io:format(WriteFD, "~s", [out_line(LineNumber, none, Line)]),
+-                    output_lines(NextData, WriteFD, SourceFD, LineNumber + 1)
+-            end
+-    end.
+-
+-%% @private
+-out_line(Number, none, Line) ->
+-    PadNu = string:right(integer_to_list(Number), 5, $.),
+-    io_lib:format("<span class=\"marked\"><a name=\"line~p\"></a>~s ~s</span>", [Number, PadNu, Line]);
+-out_line(Number, highlight, Line) ->
+-    PadNu = string:right(integer_to_list(Number), 5, $.),
+-    io_lib:format("<span class=\"highlight\"><a name=\"line~p\"></a>~s ~s</span>", [Number, PadNu, Line]);
+-out_line(Number, 0, Line) ->
+-    PadNu = string:right(integer_to_list(Number), 5, $.),
+-    io_lib:format("<span class=\"uncovered\"><a name=\"line~p\"></a>~s ~s</span>", [Number, PadNu, Line]);
+-out_line(Number, _, Line) ->
+-    PadNu = string:right(integer_to_list(Number), 5, $.),
+-    io_lib:format("<span class=\"covered\"><a name=\"line~p\"></a>~s ~s</span>", [Number, PadNu, Line]).
+-
+-%% @private
+-datas_match([], _) -> {false, []};
+-datas_match([{{_, Line}, CC} | Datas], LineNumber) when Line == LineNumber -> {{true, CC}, Datas};
+-datas_match(Data, _) -> {false, Data}.
+-
+-%% @private
+-find_source(Module) when is_atom(Module) ->
+-    Root = filename:rootname(Module),
+-    Dir = filename:dirname(Root),
+-    XDir = case os:getenv("SRC") of false -> "src"; X -> X end,
+-    find_source([
+-        filename:join([Dir, Root ++ ".erl"]),
+-        filename:join([Dir, "..", "src", Root ++ ".erl"]),
+-        filename:join([Dir, "src", Root ++ ".erl"]),
+-        filename:join([Dir, "elibs", Root ++ ".erl"]),
+-        filename:join([Dir, "..", "elibs", Root ++ ".erl"]),
+-        filename:join([Dir, XDir, Root ++ ".erl"])
+-    ]);
+-find_source([]) -> none;
+-find_source([Test | Tests]) ->
+-    case filelib:is_file(Test) of
+-        true -> Test;
+-        false -> find_source(Tests)
+-    end.
+-
+-%% @private
+-header(Module, Good, Bad) ->
+-    io:format("Good ~p~n", [Good]),
+-    io:format("Bad ~p~n", [Bad]),
+-    CovPer = round((Good / (Good + Bad)) * 100),
+-    UnCovPer = round((Bad / (Good + Bad)) * 100),
+-    io:format("CovPer ~p~n", [CovPer]),
+-    io_lib:format("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
+-        <html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
+-          <head>
+-            <title>~s - C0 code coverage information</title>
+-            <style type='text/css'>body { background-color: rgb(240, 240, 245); }</style>
+-            <style type='text/css'>span.marked0 {
+-     background-color: rgb(185, 210, 200);
+-     display: block;
+-    }
+-    span.marked { display: block; background-color: #ffffff; }
+-    span.highlight { display: block; background-color: #fff9d7; }
+-    span.covered { display: block; background-color: #f7f7f7 ; }
+-    span.uncovered { display: block; background-color: #ffebe8 ; }
+-    span.overview {
+-     border-bottom: 1px solid #E2E6EF; 
+-    }
+-    div.overview {
+-     border-bottom: 1px solid #E2E6EF; 
+-    }
+-    body {
+-     font-family: verdana, arial, helvetica;
+-    }
+-    div.footer {
+-     font-size: 68%;
+-     margin-top: 1.5em;
+-    }
+-    h1, h2, h3, h4, h5, h6 {
+-     margin-bottom: 0.5em;
+-    }
+-    h5 {
+-     margin-top: 0.5em;
+-    }
+-    .hidden {
+-     display: none;
+-    }
+-    div.separator {
+-     height: 10px;
+-    }
+-    table.percent_graph {
+-     height: 12px;
+-     border: 1px solid #E2E6EF; 
+-     empty-cells: show;
+-    }
+-    table.percent_graph td.covered {
+-     height: 10px;
+-     background: #00f000;
+-    }
+-    table.percent_graph td.uncovered {
+-     height: 10px;
+-     background: #e00000;
+-    }
+-    table.percent_graph td.NA {
+-     height: 10px;
+-     background: #eaeaea;
+-    }
+-    table.report {
+-     border-collapse: collapse;
+-     width: 100%;
+-    }
+-    table.report td.heading {
+-     background: #dcecff;
+-     border: 1px solid #E2E6EF; 
+-     font-weight: bold;
+-     text-align: center;
+-    }
+-    table.report td.heading:hover {
+-     background: #c0ffc0;
+-    }
+-    table.report td.text {
+-     border: 1px solid #E2E6EF; 
+-    }
+-    table.report td.value {
+-     text-align: right;
+-     border: 1px solid #E2E6EF; 
+-    }
+-    table.report tr.light {
+-     background-color: rgb(240, 240, 245);
+-    }
+-    table.report tr.dark {
+-     background-color: rgb(230, 230, 235);
+-    }
+-    </style>
+-          </head>
+-          <body>
+-            <h3>C0 code coverage information</h3>
+-            <p>Generated on ~s with <a href='http://github.com/ngerakines/etap'>etap 0.3.4</a>.
+-            </p>            
+-        <table class='report'>
+-          <thead>
+-            <tr>
+-              <td class='heading'>Name</td>
+-              <td class='heading'>Total lines</td>
+-              <td class='heading'>Lines of code</td>
+-              <td class='heading'>Total coverage</td>
+-              <td class='heading'>Code coverage</td>
+-            </tr>
+-          </thead>
+-          <tbody>
+-            <tr class='light'>
+-
+-              <td>
+-                <a href='~s'>~s</a>
+-              </td>
+-              <td class='value'>
+-                <tt>??</tt>
+-              </td>
+-              <td class='value'>
+-                <tt>??</tt>
+-              </td>
+-              <td class='value'>
+-                <tt>??</tt>
+-              </td>
+-              <td>
+-                <table cellspacing='0' cellpadding='0' align='right'>
+-                  <tr>
+-                    <td><tt>~p%</tt>&nbsp;</td><td>
+-                      <table cellspacing='0' class='percent_graph' cellpadding='0' width='100'>
+-                      <tr><td class='covered' width='~p' /><td class='uncovered' width='~p' /></tr>
+-                      </table>
+-                    </td>
+-                  </tr>
+-                </table>
+-              </td>
+-            </tr>
+-          </tbody>
+-        </table><pre>", [Module, etap:datetime({date(), time()}), atom_to_list(Module) ++ "_report.html", Module, CovPer, CovPer, UnCovPer]).
+-
+-%% @private
+-footer() ->
+-    "</pre><hr /><p>Generated using <a href='http://github.com/ngerakines/etap'>etap 0.3.4</a>.</p>
+-          </body>
+-        </html>
+-    ".
+diff --git a/src/etap/etap_request.erl b/src/etap/etap_request.erl
+deleted file mode 100644
+index 9fd23ac..0000000
+--- a/src/etap/etap_request.erl
++++ /dev/null
+@@ -1,89 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @doc Provides test functionality against a specific web request. Many of
+-%% the exported methods can be used to build your own more complex tests.
+--module(etap_request, [Method, Url, InHeaders, InBody, Status, OutHeaders, OutBody]).
+-
+--export([status_is/2]).
+-
+--export([
+-    method/0, url/0, status/0, status_code/0, status_line/0, rheaders/0,
+-    has_rheader/1, rheader/1, rbody/0, header_is/3, body_is/2,
+-    body_has_string/2
+-]).
+-
+-% ---
+-% Tests
+-
+-%% @doc Assert that response status code is the given status code.
+-status_is(Code, Desc) ->
+-    etap:is(status_code(), Code, Desc).
+-
+-header_is(Name, Value, Desc) ->
+-    etap:is(rheader(Name), Value, Desc).
+-
+-body_is(Value, Desc) ->
+-    etap:is(rbody(), Value, Desc).
+-
+-body_has_string(String, Desc) when is_list(OutBody), is_list(String) ->
+-    etap_string:contains_ok(OutBody, String, Desc).
+-
+-% ---
+-% Accessor functions
+-
+-%% @doc Access a request's method.
+-method() -> Method.
+-
+-%% @doc Access a request's URL.
+-url() -> Url.
+-
+-%% @doc Access a request's status.
+-status() -> Status.
+-
+-%% @doc Access a request's status code.
+-status_code() ->
+-    {_, Code, _} = Status,
+-    Code.
+-
+-%% @doc Access a request's status line.
+-status_line() ->
+-    {_, _, Line} = Status,
+-    Line.
+-
+-%% @doc Access a request's headers.
+-rheaders() -> OutHeaders.
+-
+-%% @doc Dertermine if a specific request header exists.
+-has_rheader(Key) ->
+-    lists:keymember(Key, 1, OutHeaders).
+-
+-%% @doc Return a specific request header.
+-rheader(Key) ->
+-    case lists:keysearch(Key, 1, OutHeaders) of
+-        false -> undefined;
+-        {value, {Key, Value}} -> Value
+-    end.
+-
+-%% @doc Access the request's body.
+-rbody() -> OutBody.
+diff --git a/src/etap/etap_string.erl b/src/etap/etap_string.erl
+deleted file mode 100644
+index 67aa3d5..0000000
+--- a/src/etap/etap_string.erl
++++ /dev/null
+@@ -1,47 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @author Nick Gerakines <nick at gerakines.net> [http://socklabs.com/]
+-%% @copyright 2008 Nick Gerakines
+-%% @doc Provide testing functionality for strings.
+--module(etap_string).
+-
+--export([contains_ok/3, is_before/4]).
+-
+-%% @spec contains_ok(string(), string(), string()) -> true | false
+-%% @doc Assert that a string is contained in another string.
+-contains_ok(Source, String, Desc) ->
+-    etap:isnt(
+-        string:str(Source, String),
+-        0,
+-        Desc
+-    ).
+-
+-%% @spec is_before(string(), string(), string(), string()) -> true | false
+-%% @doc Assert that a string comes before another string within a larger body.
+-is_before(Source, StringA, StringB, Desc) ->
+-    etap:is_greater(
+-        string:str(Source, StringB),
+-        string:str(Source, StringA),
+-        Desc
+-    ).
+diff --git a/src/etap/etap_web.erl b/src/etap/etap_web.erl
+deleted file mode 100644
+index fb7aee1..0000000
+--- a/src/etap/etap_web.erl
++++ /dev/null
+@@ -1,65 +0,0 @@
+-%% Copyright (c) 2008-2009 Nick Gerakines <nick at gerakines.net>
+-%% 
+-%% 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, sublicense, 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:
+-%% 
+-%% The above copyright notice and this permission notice shall be
+-%% included in all copies or substantial portions of the Software.
+-%% 
+-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+-%% OTHER DEALINGS IN THE SOFTWARE.
+-%%
+-%% @author Nick Gerakines <nick at gerakines.net> [http://socklabs.com/]
+-%% @copyright 2008 Nick Gerakines
+-%% @todo Support cookies.
+-%% @doc Provide testing functionality for web requests.
+--module(etap_web).
+-
+--export([simple_200/2, simple_404/2, build_request/4]).
+-
+-%% @doc Fetch a url and verify that it returned a 200 status.
+-simple_200(Url, Desc) ->
+-    Request = build_request(get, Url, [], []),
+-    Request:status_is(200, Desc).
+-
+-%% @doc Fetch a url and verify that it returned a 404 status.
+-simple_404(Url, Desc) ->
+-    Request = build_request(get, Url, [], []),
+-    Request:status_is(404, Desc).
+-
+-%% @doc Create and return a request structure.
+-build_request(Method, Url, Headers, Body) 
+- when Method==options;Method==get;Method==head;Method==delete;Method==trace ->
+-     try http:request(Method, {Url, Headers}, [{autoredirect, false}], []) of
+-        {ok, {OutStatus, OutHeaders, OutBody}} ->
+-            etap_request:new(Method, Url, Headers, Body, OutStatus, OutHeaders, OutBody);
+-        _ -> error
+-    catch
+-        _:_ -> error
+-    end;
+-
+-%% @doc Create and return a request structure.
+-build_request(Method, Url, Headers, Body) when Method == post; Method == put ->
+-    ContentType = case lists:keysearch("Content-Type", 1, Headers) of
+-        {value, {"Content-Type", X}} -> X;
+-        _ -> []
+-    end,
+-    try http:request(Method, {Url, Headers, ContentType, Body}, [{autoredirect, false}], []) of
+-        {ok, {OutStatus, OutHeaders, OutBody}} ->
+-            etap_request:new(Method, Url, Headers, Body, OutStatus, OutHeaders, OutBody);
+-        _ -> error
+-    catch
+-        _:_ -> error
+-    end.
+diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in
+index 79b0417..c57d7a8 100644
+--- a/test/etap/test_util.erl.in
++++ b/test/etap/test_util.erl.in
+@@ -22,7 +22,7 @@ builddir() ->
+     "@abs_top_builddir@".
+ 
+ init_code_path() ->
+-    Paths = ["etap", "couchdb", "ibrowse", "mochiweb"],
++    Paths = ["couchdb", "ibrowse", "mochiweb"],
+     lists:foreach(fun(Name) ->
+         code:add_pathz(filename:join([builddir(), "src", Name]))
+     end, Paths).
 -- 
 1.6.6.1
 
diff --git a/couchdb-0005-Remove-bundled-mochiweb-library.patch b/couchdb-0005-Remove-bundled-mochiweb-library.patch
new file mode 100644
index 0000000..47421d6
--- /dev/null
+++ b/couchdb-0005-Remove-bundled-mochiweb-library.patch
@@ -0,0 +1,7205 @@
+From 29bc14d67186bc0b7fc82b31ca1bae9354a439a2 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov at gmail.com>
+Date: Mon, 7 Jun 2010 15:08:06 +0400
+Subject: [PATCH 5/7] Remove bundled mochiweb library
+
+---
+ configure                               |    3 -
+ configure.ac                            |    1 -
+ src/Makefile.am                         |    2 +-
+ src/Makefile.in                         |    2 +-
+ src/mochiweb/Makefile.am                |   80 ---
+ src/mochiweb/Makefile.in                |  506 ------------------
+ src/mochiweb/mochifmt.erl               |  426 ---------------
+ src/mochiweb/mochifmt_records.erl       |   30 -
+ src/mochiweb/mochifmt_std.erl           |   23 -
+ src/mochiweb/mochihex.erl               |   75 ---
+ src/mochiweb/mochijson.erl              |  525 ------------------
+ src/mochiweb/mochijson2.erl             |  621 ----------------------
+ src/mochiweb/mochinum.erl               |  289 ----------
+ src/mochiweb/mochiweb.app.in            |   32 --
+ src/mochiweb/mochiweb.erl               |  110 ----
+ src/mochiweb/mochiweb_app.erl           |   20 -
+ src/mochiweb/mochiweb_charref.erl       |  295 -----------
+ src/mochiweb/mochiweb_cookies.erl       |  257 ---------
+ src/mochiweb/mochiweb_echo.erl          |   31 --
+ src/mochiweb/mochiweb_headers.erl       |  186 -------
+ src/mochiweb/mochiweb_html.erl          |  880 -------------------------------
+ src/mochiweb/mochiweb_http.erl          |  142 -----
+ src/mochiweb/mochiweb_multipart.erl     |  429 ---------------
+ src/mochiweb/mochiweb_request.erl       |  790 ---------------------------
+ src/mochiweb/mochiweb_response.erl      |   56 --
+ src/mochiweb/mochiweb_skel.erl          |   71 ---
+ src/mochiweb/mochiweb_socket_server.erl |  248 ---------
+ src/mochiweb/mochiweb_sup.erl           |   34 --
+ src/mochiweb/mochiweb_util.erl          |  579 --------------------
+ src/mochiweb/reloader.erl               |  124 -----
+ test/etap/030-doc-from-json.t           |    2 +-
+ test/etap/031-doc-to-json.t             |    2 +-
+ test/etap/test_util.erl.in              |    2 +-
+ test/run_native_process.es              |    1 -
+ test/runner.sh                          |    2 +-
+ 35 files changed, 6 insertions(+), 6870 deletions(-)
+ delete mode 100644 src/mochiweb/Makefile.am
+ delete mode 100644 src/mochiweb/Makefile.in
+ delete mode 100644 src/mochiweb/mochifmt.erl
+ delete mode 100644 src/mochiweb/mochifmt_records.erl
+ delete mode 100644 src/mochiweb/mochifmt_std.erl
+ delete mode 100644 src/mochiweb/mochihex.erl
+ delete mode 100644 src/mochiweb/mochijson.erl
+ delete mode 100644 src/mochiweb/mochijson2.erl
+ delete mode 100644 src/mochiweb/mochinum.erl
+ delete mode 100644 src/mochiweb/mochiweb.app.in
+ delete mode 100644 src/mochiweb/mochiweb.erl
+ delete mode 100644 src/mochiweb/mochiweb_app.erl
+ delete mode 100644 src/mochiweb/mochiweb_charref.erl
+ delete mode 100644 src/mochiweb/mochiweb_cookies.erl
+ delete mode 100644 src/mochiweb/mochiweb_echo.erl
+ delete mode 100644 src/mochiweb/mochiweb_headers.erl
+ delete mode 100644 src/mochiweb/mochiweb_html.erl
+ delete mode 100644 src/mochiweb/mochiweb_http.erl
+ delete mode 100644 src/mochiweb/mochiweb_multipart.erl
+ delete mode 100644 src/mochiweb/mochiweb_request.erl
+ delete mode 100644 src/mochiweb/mochiweb_response.erl
+ delete mode 100644 src/mochiweb/mochiweb_skel.erl
+ delete mode 100644 src/mochiweb/mochiweb_socket_server.erl
+ delete mode 100644 src/mochiweb/mochiweb_sup.erl
+ delete mode 100644 src/mochiweb/mochiweb_util.erl
+ delete mode 100644 src/mochiweb/reloader.erl
+
+diff --git a/configure b/configure
+index ca7732f..2d728fe 100755
+--- a/configure
++++ b/configure
+@@ -12139,8 +12139,6 @@ ac_config_files="$ac_config_files src/couchdb/priv/Makefile"
+ 
+ ac_config_files="$ac_config_files src/ibrowse/Makefile"
+ 
+-ac_config_files="$ac_config_files src/mochiweb/Makefile"
+-
+ ac_config_files="$ac_config_files test/Makefile"
+ 
+ ac_config_files="$ac_config_files test/etap/Makefile"
+@@ -13157,7 +13155,6 @@ do
+     "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;;
+     "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;;
+     "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;;
+-    "src/mochiweb/Makefile") CONFIG_FILES="$CONFIG_FILES src/mochiweb/Makefile" ;;
+     "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
+     "test/etap/Makefile") CONFIG_FILES="$CONFIG_FILES test/etap/Makefile" ;;
+     "test/etap/test_util.erl") CONFIG_FILES="$CONFIG_FILES test/etap/test_util.erl" ;;
+diff --git a/configure.ac b/configure.ac
+index 0478b4f..a53df9b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -336,7 +336,6 @@ AC_CONFIG_FILES([src/couchdb/couch.app.tpl])
+ AC_CONFIG_FILES([src/couchdb/Makefile])
+ AC_CONFIG_FILES([src/couchdb/priv/Makefile])
+ AC_CONFIG_FILES([src/ibrowse/Makefile])
+-AC_CONFIG_FILES([src/mochiweb/Makefile])
+ AC_CONFIG_FILES([test/Makefile])
+ AC_CONFIG_FILES([test/etap/Makefile])
+ AC_CONFIG_FILES([test/etap/test_util.erl])
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 19a5d20..5a6646f 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -10,4 +10,4 @@
+ ## License for the specific language governing permissions and limitations under
+ ## the License.
+ 
+-SUBDIRS = couchdb ibrowse mochiweb
++SUBDIRS = couchdb ibrowse
+diff --git a/src/Makefile.in b/src/Makefile.in
+index 2422e39..d1e6ba5 100644
+--- a/src/Makefile.in
++++ b/src/Makefile.in
+@@ -239,7 +239,7 @@ version_minor = @version_minor@
+ version_release = @version_release@
+ version_revision = @version_revision@
+ version_stage = @version_stage@
+-SUBDIRS = couchdb ibrowse mochiweb
++SUBDIRS = couchdb ibrowse
+ all: all-recursive
+ 
+ .SUFFIXES:
+diff --git a/src/mochiweb/Makefile.am b/src/mochiweb/Makefile.am
+deleted file mode 100644
+index 608d4dc..0000000
+--- a/src/mochiweb/Makefile.am
++++ /dev/null
+@@ -1,80 +0,0 @@
+-## Licensed under the Apache License, Version 2.0 (the "License"); you may not
+-## use this file except in compliance with the License. You may obtain a copy of
+-## the License at
+-##
+-##   http://www.apache.org/licenses/LICENSE-2.0
+-##
+-## Unless required by applicable law or agreed to in writing, software
+-## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+-## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+-## License for the specific language governing permissions and limitations under
+-## the License.
+-
+-mochiwebebindir = $(localerlanglibdir)/mochiweb-r97/ebin
+-
+-mochiweb_file_collection = \
+-    mochifmt.erl \
+-    mochifmt_records.erl \
+-    mochifmt_std.erl \
+-    mochihex.erl \
+-    mochijson.erl \
+-    mochijson2.erl \
+-    mochinum.erl \
+-	mochiweb.app.in \
+-    mochiweb.erl \
+-    mochiweb_app.erl \
+-    mochiweb_charref.erl \
+-    mochiweb_cookies.erl \
+-    mochiweb_echo.erl \
+-    mochiweb_headers.erl \
+-    mochiweb_html.erl \
+-    mochiweb_http.erl \
+-    mochiweb_multipart.erl \
+-    mochiweb_request.erl \
+-    mochiweb_response.erl \
+-    mochiweb_skel.erl \
+-    mochiweb_socket_server.erl \
+-    mochiweb_sup.erl \
+-    mochiweb_util.erl \
+-    reloader.erl
+-
+-mochiwebebin_make_generated_file_list = \
+-    mochifmt.beam \
+-    mochifmt_records.beam \
+-    mochifmt_std.beam \
+-    mochihex.beam \
+-    mochijson.beam \
+-    mochijson2.beam \
+-    mochinum.beam \
+-	mochiweb.app \
+-    mochiweb.beam \
+-    mochiweb_app.beam \
+-    mochiweb_charref.beam \
+-    mochiweb_cookies.beam \
+-    mochiweb_echo.beam \
+-    mochiweb_headers.beam \
+-    mochiweb_html.beam \
+-    mochiweb_http.beam \
+-    mochiweb_multipart.beam \
+-    mochiweb_request.beam \
+-    mochiweb_response.beam \
+-    mochiweb_skel.beam \
+-    mochiweb_socket_server.beam \
+-    mochiweb_sup.beam \
+-    mochiweb_util.beam \
+-    reloader.beam
+-
+-mochiwebebin_DATA = \
+-    $(mochiwebebin_make_generated_file_list)
+-
+-EXTRA_DIST =  \
+-    $(mochiweb_file_collection)
+-
+-CLEANFILES = \
+-    $(mochiwebebin_make_generated_file_list)
+-
+-%.app: %.app.in
+-	cp $< $@
+-
+-%.beam: %.erl
+-	$(ERLC) $(ERLC_FLAGS) $<
+diff --git a/src/mochiweb/Makefile.in b/src/mochiweb/Makefile.in
+deleted file mode 100644
+index df1c548..0000000
+--- a/src/mochiweb/Makefile.in
++++ /dev/null
+@@ -1,506 +0,0 @@
+-# Makefile.in generated by automake 1.11 from Makefile.am.
+-# @configure_input@
+-
+-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+-# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+-# Inc.
+-# This Makefile.in is free software; the Free Software Foundation
+-# gives unlimited permission to copy and/or distribute it,
+-# with or without modifications, as long as this notice is preserved.
+-
+-# This program is distributed in the hope that it will be useful,
+-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+-# PARTICULAR PURPOSE.
+-
+- at SET_MAKE@
+-
+-VPATH = @srcdir@
+-pkgdatadir = $(datadir)/@PACKAGE@
+-pkgincludedir = $(includedir)/@PACKAGE@
+-pkglibdir = $(libdir)/@PACKAGE@
+-pkglibexecdir = $(libexecdir)/@PACKAGE@
+-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+-install_sh_DATA = $(install_sh) -c -m 644
+-install_sh_PROGRAM = $(install_sh) -c
+-install_sh_SCRIPT = $(install_sh) -c
+-INSTALL_HEADER = $(INSTALL_DATA)
+-transform = $(program_transform_name)
+-NORMAL_INSTALL = :
+-PRE_INSTALL = :
+-POST_INSTALL = :
+-NORMAL_UNINSTALL = :
+-PRE_UNINSTALL = :
+-POST_UNINSTALL = :
+-build_triplet = @build@
+-host_triplet = @host@
+-subdir = src/mochiweb
+-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+-am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \
+-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/m4/ac_check_icu.m4 \
+-	$(top_srcdir)/configure.ac
+-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+-	$(ACLOCAL_M4)
+-mkinstalldirs = $(install_sh) -d
+-CONFIG_HEADER = $(top_builddir)/config.h
+-CONFIG_CLEAN_FILES =
+-CONFIG_CLEAN_VPATH_FILES =
+-SOURCES =
+-DIST_SOURCES =
+-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+-am__vpath_adj = case $$p in \
+-    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+-    *) f=$$p;; \
+-  esac;
+-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+-am__install_max = 40
+-am__nobase_strip_setup = \
+-  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+-am__nobase_strip = \
+-  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+-am__nobase_list = $(am__nobase_strip_setup); \
+-  for p in $$list; do echo "$$p $$p"; done | \
+-  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+-  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+-    if (++n[$$2] == $(am__install_max)) \
+-      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+-    END { for (dir in files) print dir, files[dir] }'
+-am__base_list = \
+-  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+-  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+-am__installdirs = "$(DESTDIR)$(mochiwebebindir)"
+-DATA = $(mochiwebebin_DATA)
+-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+-ACLOCAL = @ACLOCAL@
+-AMTAR = @AMTAR@
+-AR = @AR@
+-AUTOCONF = @AUTOCONF@
+-AUTOHEADER = @AUTOHEADER@
+-AUTOMAKE = @AUTOMAKE@
+-AWK = @AWK@
+-CC = @CC@
+-CCDEPMODE = @CCDEPMODE@
+-CFLAGS = @CFLAGS@
+-CPP = @CPP@
+-CPPFLAGS = @CPPFLAGS@
+-CURL_CFLAGS = @CURL_CFLAGS@
+-CURL_CONFIG = @CURL_CONFIG@
+-CURL_LDFLAGS = @CURL_LDFLAGS@
+-CURL_LIBS = @CURL_LIBS@
+-CYGPATH_W = @CYGPATH_W@
+-DEFS = @DEFS@
+-DEPDIR = @DEPDIR@
+-DSYMUTIL = @DSYMUTIL@
+-DUMPBIN = @DUMPBIN@
+-ECHO_C = @ECHO_C@
+-ECHO_N = @ECHO_N@
+-ECHO_T = @ECHO_T@
+-EGREP = @EGREP@
+-ERL = @ERL@
+-ERLC = @ERLC@
+-ERLC_FLAGS = @ERLC_FLAGS@
+-EXEEXT = @EXEEXT@
+-FGREP = @FGREP@
+-FLAGS = @FLAGS@
+-GREP = @GREP@
+-HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@
+-ICU_CFLAGS = @ICU_CFLAGS@
+-ICU_CONFIG = @ICU_CONFIG@
+-ICU_CXXFLAGS = @ICU_CXXFLAGS@
+-ICU_LIBS = @ICU_LIBS@
+-ICU_LOCAL_BIN = @ICU_LOCAL_BIN@
+-ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@
+-ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@
+-INSTALL = @INSTALL@
+-INSTALL_DATA = @INSTALL_DATA@
+-INSTALL_PROGRAM = @INSTALL_PROGRAM@
+-INSTALL_SCRIPT = @INSTALL_SCRIPT@
+-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+-JSLIB = @JSLIB@
+-LD = @LD@
+-LDFLAGS = @LDFLAGS@
+-LIBOBJS = @LIBOBJS@
+-LIBS = @LIBS@
+-LIBTOOL = @LIBTOOL@
+-LIPO = @LIPO@
+-LN_S = @LN_S@
+-LTLIBOBJS = @LTLIBOBJS@
+-MAKEINFO = @MAKEINFO@
+-MKDIR_P = @MKDIR_P@
+-NM = @NM@
+-NMEDIT = @NMEDIT@
+-OBJDUMP = @OBJDUMP@
+-OBJEXT = @OBJEXT@
+-OTOOL = @OTOOL@
+-OTOOL64 = @OTOOL64@
+-PACKAGE = @PACKAGE@
+-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+-PACKAGE_NAME = @PACKAGE_NAME@
+-PACKAGE_STRING = @PACKAGE_STRING@
+-PACKAGE_TARNAME = @PACKAGE_TARNAME@
+-PACKAGE_URL = @PACKAGE_URL@
+-PACKAGE_VERSION = @PACKAGE_VERSION@
+-PATH_SEPARATOR = @PATH_SEPARATOR@
+-RANLIB = @RANLIB@
+-SED = @SED@
+-SET_MAKE = @SET_MAKE@
+-SHELL = @SHELL@
+-STRIP = @STRIP@
+-VERSION = @VERSION@
+-abs_builddir = @abs_builddir@
+-abs_srcdir = @abs_srcdir@
+-abs_top_builddir = @abs_top_builddir@
+-abs_top_srcdir = @abs_top_srcdir@
+-ac_ct_CC = @ac_ct_CC@
+-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+-am__include = @am__include@
+-am__leading_dot = @am__leading_dot@
+-am__quote = @am__quote@
+-am__tar = @am__tar@
+-am__untar = @am__untar@
+-bindir = @bindir@
+-bug_uri = @bug_uri@
+-build = @build@
+-build_alias = @build_alias@
+-build_cpu = @build_cpu@
+-build_os = @build_os@
+-build_vendor = @build_vendor@
+-builddir = @builddir@
+-datadir = @datadir@
+-datarootdir = @datarootdir@
+-docdir = @docdir@
+-dvidir = @dvidir@
+-exec_prefix = @exec_prefix@
+-host = @host@
+-host_alias = @host_alias@
+-host_cpu = @host_cpu@
+-host_os = @host_os@
+-host_vendor = @host_vendor@
+-htmldir = @htmldir@
+-includedir = @includedir@
+-infodir = @infodir@
+-initdir = @initdir@
+-install_sh = @install_sh@
+-launchddir = @launchddir@
+-libdir = @libdir@
+-libexecdir = @libexecdir@
+-localconfdir = @localconfdir@
+-localdatadir = @localdatadir@
+-localdocdir = @localdocdir@
+-localedir = @localedir@
+-localerlanglibdir = @localerlanglibdir@
+-locallibbindir = @locallibbindir@
+-locallibdir = @locallibdir@
+-localstatedir = @localstatedir@
+-localstatelibdir = @localstatelibdir@
+-localstatelogdir = @localstatelogdir@
+-localstaterundir = @localstaterundir@
+-lt_ECHO = @lt_ECHO@
+-mandir = @mandir@
+-mkdir_p = @mkdir_p@
+-oldincludedir = @oldincludedir@
+-package_author_address = @package_author_address@
+-package_author_name = @package_author_name@
+-package_identifier = @package_identifier@
+-package_name = @package_name@
+-package_tarname = @package_tarname@
+-pdfdir = @pdfdir@
+-prefix = @prefix@
+-program_transform_name = @program_transform_name@
+-psdir = @psdir@
+-sbindir = @sbindir@
+-sharedstatedir = @sharedstatedir@
+-srcdir = @srcdir@
+-sysconfdir = @sysconfdir@
+-target_alias = @target_alias@
+-top_build_prefix = @top_build_prefix@
+-top_builddir = @top_builddir@
+-top_srcdir = @top_srcdir@
+-version = @version@
+-version_major = @version_major@
+-version_minor = @version_minor@
+-version_release = @version_release@
+-version_revision = @version_revision@
+-version_stage = @version_stage@
+-mochiwebebindir = $(localerlanglibdir)/mochiweb-r97/ebin
+-mochiweb_file_collection = \
+-    mochifmt.erl \
+-    mochifmt_records.erl \
+-    mochifmt_std.erl \
+-    mochihex.erl \
+-    mochijson.erl \
+-    mochijson2.erl \
+-    mochinum.erl \
+-	mochiweb.app.in \
+-    mochiweb.erl \
+-    mochiweb_app.erl \
+-    mochiweb_charref.erl \
+-    mochiweb_cookies.erl \
+-    mochiweb_echo.erl \
+-    mochiweb_headers.erl \
+-    mochiweb_html.erl \
+-    mochiweb_http.erl \
+-    mochiweb_multipart.erl \
+-    mochiweb_request.erl \
+-    mochiweb_response.erl \
+-    mochiweb_skel.erl \
+-    mochiweb_socket_server.erl \
+-    mochiweb_sup.erl \
+-    mochiweb_util.erl \
+-    reloader.erl
+-
+-mochiwebebin_make_generated_file_list = \
+-    mochifmt.beam \
+-    mochifmt_records.beam \
+-    mochifmt_std.beam \
+-    mochihex.beam \
+-    mochijson.beam \
+-    mochijson2.beam \
+-    mochinum.beam \
+-	mochiweb.app \
+-    mochiweb.beam \
+-    mochiweb_app.beam \
+-    mochiweb_charref.beam \
+-    mochiweb_cookies.beam \
+-    mochiweb_echo.beam \
+-    mochiweb_headers.beam \
+-    mochiweb_html.beam \
+-    mochiweb_http.beam \
+-    mochiweb_multipart.beam \
+-    mochiweb_request.beam \
+-    mochiweb_response.beam \
+-    mochiweb_skel.beam \
+-    mochiweb_socket_server.beam \
+-    mochiweb_sup.beam \
+-    mochiweb_util.beam \
+-    reloader.beam
+-
+-mochiwebebin_DATA = \
+-    $(mochiwebebin_make_generated_file_list)
+-
+-EXTRA_DIST = \
+-    $(mochiweb_file_collection)
+-
+-CLEANFILES = \
+-    $(mochiwebebin_make_generated_file_list)
+-
+-all: all-am
+-
+-.SUFFIXES:
+-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+-	@for dep in $?; do \
+-	  case '$(am__configure_deps)' in \
+-	    *$$dep*) \
+-	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+-	        && { if test -f $@; then exit 0; else break; fi; }; \
+-	      exit 1;; \
+-	  esac; \
+-	done; \
+-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/mochiweb/Makefile'; \
+-	$(am__cd) $(top_srcdir) && \
+-	  $(AUTOMAKE) --foreign src/mochiweb/Makefile
+-.PRECIOUS: Makefile
+-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+-	@case '$?' in \
+-	  *config.status*) \
+-	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+-	  *) \
+-	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+-	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+-	esac;
+-
+-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-
+-$(top_srcdir)/configure:  $(am__configure_deps)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+-$(am__aclocal_m4_deps):
+-
+-mostlyclean-libtool:
+-	-rm -f *.lo
+-
+-clean-libtool:
+-	-rm -rf .libs _libs
+-install-mochiwebebinDATA: $(mochiwebebin_DATA)
+-	@$(NORMAL_INSTALL)
+-	test -z "$(mochiwebebindir)" || $(MKDIR_P) "$(DESTDIR)$(mochiwebebindir)"
+-	@list='$(mochiwebebin_DATA)'; test -n "$(mochiwebebindir)" || list=; \
+-	for p in $$list; do \
+-	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+-	  echo "$$d$$p"; \
+-	done | $(am__base_list) | \
+-	while read files; do \
+-	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(mochiwebebindir)'"; \
+-	  $(INSTALL_DATA) $$files "$(DESTDIR)$(mochiwebebindir)" || exit $$?; \
+-	done
+-
+-uninstall-mochiwebebinDATA:
+-	@$(NORMAL_UNINSTALL)
+-	@list='$(mochiwebebin_DATA)'; test -n "$(mochiwebebindir)" || list=; \
+-	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+-	test -n "$$files" || exit 0; \
+-	echo " ( cd '$(DESTDIR)$(mochiwebebindir)' && rm -f" $$files ")"; \
+-	cd "$(DESTDIR)$(mochiwebebindir)" && rm -f $$files
+-tags: TAGS
+-TAGS:
+-
+-ctags: CTAGS
+-CTAGS:
+-
+-
+-distdir: $(DISTFILES)
+-	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+-	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+-	list='$(DISTFILES)'; \
+-	  dist_files=`for file in $$list; do echo $$file; done | \
+-	  sed -e "s|^$$srcdirstrip/||;t" \
+-	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+-	case $$dist_files in \
+-	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+-			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+-			   sort -u` ;; \
+-	esac; \
+-	for file in $$dist_files; do \
+-	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+-	  if test -d $$d/$$file; then \
+-	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+-	    if test -d "$(distdir)/$$file"; then \
+-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+-	    fi; \
+-	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+-	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+-	    fi; \
+-	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+-	  else \
+-	    test -f "$(distdir)/$$file" \
+-	    || cp -p $$d/$$file "$(distdir)/$$file" \
+-	    || exit 1; \
+-	  fi; \
+-	done
+-check-am: all-am
+-check: check-am
+-all-am: Makefile $(DATA)
+-installdirs:
+-	for dir in "$(DESTDIR)$(mochiwebebindir)"; do \
+-	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+-	done
+-install: install-am
+-install-exec: install-exec-am
+-install-data: install-data-am
+-uninstall: uninstall-am
+-
+-install-am: all-am
+-	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+-
+-installcheck: installcheck-am
+-install-strip:
+-	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+-	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+-	  `test -z '$(STRIP)' || \
+-	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+-mostlyclean-generic:
+-
+-clean-generic:
+-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+-
+-distclean-generic:
+-	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+-	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+-
+-maintainer-clean-generic:
+-	@echo "This command is intended for maintainers to use"
+-	@echo "it deletes files that may require special tools to rebuild."
+-clean: clean-am
+-
+-clean-am: clean-generic clean-libtool mostlyclean-am
+-
+-distclean: distclean-am
+-	-rm -f Makefile
+-distclean-am: clean-am distclean-generic
+-
+-dvi: dvi-am
+-
+-dvi-am:
+-
+-html: html-am
+-
+-html-am:
+-
+-info: info-am
+-
+-info-am:
+-
+-install-data-am: install-mochiwebebinDATA
+-
+-install-dvi: install-dvi-am
+-
+-install-dvi-am:
+-
+-install-exec-am:
+-
+-install-html: install-html-am
+-
+-install-html-am:
+-
+-install-info: install-info-am
+-
+-install-info-am:
+-
+-install-man:
+-
+-install-pdf: install-pdf-am
+-
+-install-pdf-am:
+-
+-install-ps: install-ps-am
+-
+-install-ps-am:
+-
+-installcheck-am:
+-
+-maintainer-clean: maintainer-clean-am
+-	-rm -f Makefile
+-maintainer-clean-am: distclean-am maintainer-clean-generic
+-
+-mostlyclean: mostlyclean-am
+-
+-mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+-
+-pdf: pdf-am
+-
+-pdf-am:
+-
+-ps: ps-am
+-
+-ps-am:
+-
+-uninstall-am: uninstall-mochiwebebinDATA
+-
+-.MAKE: install-am install-strip
+-
+-.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+-	distclean distclean-generic distclean-libtool distdir dvi \
+-	dvi-am html html-am info info-am install install-am \
+-	install-data install-data-am install-dvi install-dvi-am \
+-	install-exec install-exec-am install-html install-html-am \
+-	install-info install-info-am install-man \
+-	install-mochiwebebinDATA install-pdf install-pdf-am install-ps \
+-	install-ps-am install-strip installcheck installcheck-am \
+-	installdirs maintainer-clean maintainer-clean-generic \
+-	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+-	ps ps-am uninstall uninstall-am uninstall-mochiwebebinDATA
+-
+-
+-%.app: %.app.in
+-	cp $< $@
+-
+-%.beam: %.erl
+-	$(ERLC) $(ERLC_FLAGS) $<
+-
+-# Tell versions [3.59,3.63) of GNU make to not export all variables.
+-# Otherwise a system limit (for SysV at least) may be exceeded.
+-.NOEXPORT:
+diff --git a/src/mochiweb/mochifmt.erl b/src/mochiweb/mochifmt.erl
+deleted file mode 100644
+index da0a133..0000000
+--- a/src/mochiweb/mochifmt.erl
++++ /dev/null
+@@ -1,426 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2008 Mochi Media, Inc.
+-
+-%% @doc String Formatting for Erlang, inspired by Python 2.6
+-%%      (<a href="http://www.python.org/dev/peps/pep-3101/">PEP 3101</a>).
+-%%
+--module(mochifmt).
+--author('bob at mochimedia.com').
+--export([format/2, format_field/2, convert_field/2, get_value/2, get_field/2]).
+--export([tokenize/1, format/3, get_field/3, format_field/3]).
+--export([bformat/2, bformat/3]).
+--export([f/2, f/3]).
+--export([test/0]).
+-
+--record(conversion, {length, precision, ctype, align, fill_char, sign}).
+-
+-%% @spec tokenize(S::string()) -> tokens()
+-%% @doc Tokenize a format string into mochifmt's internal format.
+-tokenize(S) ->
+-    {?MODULE, tokenize(S, "", [])}.
+-
+-%% @spec convert_field(Arg, Conversion::conversion()) -> term()
+-%% @doc Process Arg according to the given explicit conversion specifier.
+-convert_field(Arg, "") ->
+-    Arg;
+-convert_field(Arg, "r") ->
+-    repr(Arg);
+-convert_field(Arg, "s") ->
+-    str(Arg).
+-
+-%% @spec get_value(Key::string(), Args::args()) -> term()
+-%% @doc Get the Key from Args. If Args is a tuple then convert Key to
+-%%      an integer and get element(1 + Key, Args). If Args is a list and Key
+-%%      can be parsed as an integer then use lists:nth(1 + Key, Args),
+-%%      otherwise try and look for Key in Args as a proplist, converting
+-%%      Key to an atom or binary if necessary.
+-get_value(Key, Args) when is_tuple(Args) ->
+-    element(1 + list_to_integer(Key), Args);
+-get_value(Key, Args) when is_list(Args) ->
+-    try lists:nth(1 + list_to_integer(Key), Args)
+-    catch error:_ ->
+-            {_K, V} = proplist_lookup(Key, Args),
+-            V
+-    end.
+-
+-%% @spec get_field(Key::string(), Args) -> term()
+-%% @doc Consecutively call get_value/2 on parts of Key delimited by ".",
+-%%      replacing Args with the result of the previous get_value. This
+-%%      is used to implement formats such as {0.0}.
+-get_field(Key, Args) ->
+-    get_field(Key, Args, ?MODULE).
+-
+-%% @spec get_field(Key::string(), Args, Module) -> term()
+-%% @doc Consecutively call Module:get_value/2 on parts of Key delimited by ".",
+-%%      replacing Args with the result of the previous get_value. This
+-%%      is used to implement formats such as {0.0}.
+-get_field(Key, Args, Module) ->
+-    {Name, Next} = lists:splitwith(fun (C) -> C =/= $. end, Key),
+-    Res = try Module:get_value(Name, Args)
+-          catch error:undef -> get_value(Name, Args) end,
+-    case Next of
+-        "" ->
+-            Res;
+-        "." ++ S1 ->
+-            get_field(S1, Res, Module)
+-    end.
+-
+-%% @spec format(Format::string(), Args) -> iolist()
+-%% @doc Format Args with Format.
+-format(Format, Args) ->
+-    format(Format, Args, ?MODULE).
+-
+-%% @spec format(Format::string(), Args, Module) -> iolist()
+-%% @doc Format Args with Format using Module.
+-format({?MODULE, Parts}, Args, Module) ->
+-    format2(Parts, Args, Module, []);
+-format(S, Args, Module) ->
+-    format(tokenize(S), Args, Module).
+-
+-%% @spec format_field(Arg, Format) -> iolist()
+-%% @doc Format Arg with Format.
+-format_field(Arg, Format) ->
+-    format_field(Arg, Format, ?MODULE).
+-
+-%% @spec format_field(Arg, Format, _Module) -> iolist()
+-%% @doc Format Arg with Format.
+-format_field(Arg, Format, _Module) ->
+-    F = default_ctype(Arg, parse_std_conversion(Format)),
+-    fix_padding(fix_sign(convert2(Arg, F), F), F).
+-
+-%% @spec f(Format::string(), Args) -> string()
+-%% @doc Format Args with Format and return a string().
+-f(Format, Args) ->
+-    f(Format, Args, ?MODULE).
+-
+-%% @spec f(Format::string(), Args, Module) -> string()
+-%% @doc Format Args with Format using Module and return a string().
+-f(Format, Args, Module) ->
+-    case lists:member(${, Format) of
+-        true ->
+-            binary_to_list(bformat(Format, Args, Module));
+-        false ->
+-            Format
+-    end.
+-
+-%% @spec bformat(Format::string(), Args) -> binary()
+-%% @doc Format Args with Format and return a binary().
+-bformat(Format, Args) ->
+-    iolist_to_binary(format(Format, Args)).
+-
+-%% @spec bformat(Format::string(), Args, Module) -> binary()
+-%% @doc Format Args with Format using Module and return a binary().
+-bformat(Format, Args, Module) ->
+-    iolist_to_binary(format(Format, Args, Module)).
+-
+-%% @spec test() -> ok
+-%% @doc Run tests.
+-test() ->
+-    ok = test_tokenize(),
+-    ok = test_format(),
+-    ok = test_std(),
+-    ok = test_records(),
+-    ok.
+-
+-%% Internal API
+-
+-add_raw("", Acc) ->
+-    Acc;
+-add_raw(S, Acc) ->
+-    [{raw, lists:reverse(S)} | Acc].
+-
+-tokenize([], S, Acc) ->
+-    lists:reverse(add_raw(S, Acc));
+-tokenize("{{" ++ Rest, S, Acc) ->
+-    tokenize(Rest, "{" ++ S, Acc);
+-tokenize("{" ++ Rest, S, Acc) ->
+-    {Format, Rest1} = tokenize_format(Rest),
+-    tokenize(Rest1, "", [{format, make_format(Format)} | add_raw(S, Acc)]);
+-tokenize("}}" ++ Rest, S, Acc) ->
+-    tokenize(Rest, "}" ++ S, Acc);
+-tokenize([C | Rest], S, Acc) ->
+-    tokenize(Rest, [C | S], Acc).
+-
+-tokenize_format(S) ->
+-    tokenize_format(S, 1, []).
+-
+-tokenize_format("}" ++ Rest, 1, Acc) ->
+-    {lists:reverse(Acc), Rest};
+-tokenize_format("}" ++ Rest, N, Acc) ->
+-    tokenize_format(Rest, N - 1, "}" ++ Acc);
+-tokenize_format("{" ++ Rest, N, Acc) ->
+-    tokenize_format(Rest, 1 + N, "{" ++ Acc);
+-tokenize_format([C | Rest], N, Acc) ->
+-    tokenize_format(Rest, N, [C | Acc]).
+-
+-make_format(S) ->
+-    {Name0, Spec} = case lists:splitwith(fun (C) -> C =/= $: end, S) of
+-                        {_, ""} ->
+-                            {S, ""};
+-                        {SN, ":" ++ SS} ->
+-                            {SN, SS}
+-                    end,
+-    {Name, Transform} = case lists:splitwith(fun (C) -> C =/= $! end, Name0) of
+-                            {_, ""} ->
+-                                {Name0, ""};
+-                            {TN, "!" ++ TT} ->
+-                                {TN, TT}
+-                        end,
+-    {Name, Transform, Spec}.
+-
+-proplist_lookup(S, P) ->
+-    A = try list_to_existing_atom(S)
+-        catch error:_ -> make_ref() end,
+-    B = try list_to_binary(S)
+-        catch error:_ -> make_ref() end,
+-    proplist_lookup2({S, A, B}, P).
+-
+-proplist_lookup2({KS, KA, KB}, [{K, V} | _])
+-  when KS =:= K orelse KA =:= K orelse KB =:= K ->
+-    {K, V};
+-proplist_lookup2(Keys, [_ | Rest]) ->
+-    proplist_lookup2(Keys, Rest).
+-
+-format2([], _Args, _Module, Acc) ->
+-    lists:reverse(Acc);
+-format2([{raw, S} | Rest], Args, Module, Acc) ->
+-    format2(Rest, Args, Module, [S | Acc]);
+-format2([{format, {Key, Convert, Format0}} | Rest], Args, Module, Acc) ->
+-    Format = f(Format0, Args, Module),
+-    V = case Module of
+-            ?MODULE ->
+-                V0 = get_field(Key, Args),
+-                V1 = convert_field(V0, Convert),
+-                format_field(V1, Format);
+-            _ ->
+-                V0 = try Module:get_field(Key, Args)
+-                     catch error:undef -> get_field(Key, Args, Module) end,
+-                V1 = try Module:convert_field(V0, Convert)
+-                     catch error:undef -> convert_field(V0, Convert) end,
+-                try Module:format_field(V1, Format)
+-                catch error:undef -> format_field(V1, Format, Module) end
+-        end,
+-    format2(Rest, Args, Module, [V | Acc]).
+-
+-default_ctype(_Arg, C=#conversion{ctype=N}) when N =/= undefined ->
+-    C;
+-default_ctype(Arg, C) when is_integer(Arg) ->
+-    C#conversion{ctype=decimal};
+-default_ctype(Arg, C) when is_float(Arg) ->
+-    C#conversion{ctype=general};
+-default_ctype(_Arg, C) ->
+-    C#conversion{ctype=string}.
+-
+-fix_padding(Arg, #conversion{length=undefined}) ->
+-    Arg;
+-fix_padding(Arg, F=#conversion{length=Length, fill_char=Fill0, align=Align0,
+-                               ctype=Type}) ->
+-    Padding = Length - iolist_size(Arg),
+-    Fill = case Fill0 of
+-               undefined ->
+-                   $\s;
+-               _ ->
+-                   Fill0
+-           end,
+-    Align = case Align0 of
+-                undefined ->
+-                    case Type of
+-                        string ->
+-                            left;
+-                        _ ->
+-                            right
+-                    end;
+-                _ ->
+-                    Align0
+-            end,
+-    case Padding > 0 of
+-        true ->
+-            do_padding(Arg, Padding, Fill, Align, F);
+-        false ->
+-            Arg
+-    end.
+-
+-do_padding(Arg, Padding, Fill, right, _F) ->
+-    [lists:duplicate(Padding, Fill), Arg];
+-do_padding(Arg, Padding, Fill, center, _F) ->
+-    LPadding = lists:duplicate(Padding div 2, Fill),
+-    RPadding = case Padding band 1 of
+-                   1 ->
+-                       [Fill | LPadding];
+-                   _ ->
+-                       LPadding
+-               end,
+-    [LPadding, Arg, RPadding];
+-do_padding([$- | Arg], Padding, Fill, sign_right, _F) ->
+-    [[$- | lists:duplicate(Padding, Fill)], Arg];
+-do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=$-}) ->
+-    [lists:duplicate(Padding, Fill), Arg];
+-do_padding([S | Arg], Padding, Fill, sign_right, #conversion{sign=S}) ->
+-    [[S | lists:duplicate(Padding, Fill)], Arg];
+-do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=undefined}) ->
+-    [lists:duplicate(Padding, Fill), Arg];
+-do_padding(Arg, Padding, Fill, left, _F) ->
+-    [Arg | lists:duplicate(Padding, Fill)].
+-
+-fix_sign(Arg, #conversion{sign=$+}) when Arg >= 0 ->
+-    [$+, Arg];
+-fix_sign(Arg, #conversion{sign=$\s}) when Arg >= 0 ->
+-    [$\s, Arg];
+-fix_sign(Arg, _F) ->
+-    Arg.
+-
+-ctype($\%) -> percent;
+-ctype($s) -> string;
+-ctype($b) -> bin;
+-ctype($o) -> oct;
+-ctype($X) -> upper_hex;
+-ctype($x) -> hex;
+-ctype($c) -> char;
+-ctype($d) -> decimal;
+-ctype($g) -> general;
+-ctype($f) -> fixed;
+-ctype($e) -> exp.
+-
+-align($<) -> left;
+-align($>) -> right;
+-align($^) -> center;
+-align($=) -> sign_right.
+-
+-convert2(Arg, F=#conversion{ctype=percent}) ->
+-    [convert2(100.0 * Arg, F#conversion{ctype=fixed}), $\%];
+-convert2(Arg, #conversion{ctype=string}) ->
+-    str(Arg);
+-convert2(Arg, #conversion{ctype=bin}) ->
+-    erlang:integer_to_list(Arg, 2);
+-convert2(Arg, #conversion{ctype=oct}) ->
+-    erlang:integer_to_list(Arg, 8);
+-convert2(Arg, #conversion{ctype=upper_hex}) ->
+-    erlang:integer_to_list(Arg, 16);
+-convert2(Arg, #conversion{ctype=hex}) ->
+-    string:to_lower(erlang:integer_to_list(Arg, 16));
+-convert2(Arg, #conversion{ctype=char}) when Arg < 16#80 ->
+-    [Arg];
+-convert2(Arg, #conversion{ctype=char}) ->
+-    xmerl_ucs:to_utf8(Arg);
+-convert2(Arg, #conversion{ctype=decimal}) ->
+-    integer_to_list(Arg);
+-convert2(Arg, #conversion{ctype=general, precision=undefined}) ->
+-    try mochinum:digits(Arg)
+-    catch error:undef -> io_lib:format("~g", [Arg]) end;
+-convert2(Arg, #conversion{ctype=fixed, precision=undefined}) ->
+-    io_lib:format("~f", [Arg]);
+-convert2(Arg, #conversion{ctype=exp, precision=undefined}) ->
+-    io_lib:format("~e", [Arg]);
+-convert2(Arg, #conversion{ctype=general, precision=P}) ->
+-    io_lib:format("~." ++ integer_to_list(P) ++ "g", [Arg]);
+-convert2(Arg, #conversion{ctype=fixed, precision=P}) ->
+-    io_lib:format("~." ++ integer_to_list(P) ++ "f", [Arg]);
+-convert2(Arg, #conversion{ctype=exp, precision=P}) ->
+-    io_lib:format("~." ++ integer_to_list(P) ++ "e", [Arg]).
+-
+-str(A) when is_atom(A) ->
+-    atom_to_list(A);
+-str(I) when is_integer(I) ->
+-    integer_to_list(I);
+-str(F) when is_float(F) ->
+-    try mochinum:digits(F)
+-    catch error:undef -> io_lib:format("~g", [F]) end;
+-str(L) when is_list(L) ->
+-    L;
+-str(B) when is_binary(B) ->
+-    B;
+-str(P) ->
+-    repr(P).
+-
+-repr(P) when is_float(P) ->
+-    try mochinum:digits(P)
+-    catch error:undef -> float_to_list(P) end;
+-repr(P) ->
+-    io_lib:format("~p", [P]).
+-
+-parse_std_conversion(S) ->
+-    parse_std_conversion(S, #conversion{}).
+-
+-parse_std_conversion("", Acc) ->
+-    Acc;
+-parse_std_conversion([Fill, Align | Spec], Acc)
+-  when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ ->
+-    parse_std_conversion(Spec, Acc#conversion{fill_char=Fill,
+-                                              align=align(Align)});
+-parse_std_conversion([Align | Spec], Acc)
+-  when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ ->
+-    parse_std_conversion(Spec, Acc#conversion{align=align(Align)});
+-parse_std_conversion([Sign | Spec], Acc)
+-  when Sign =:= $+ orelse Sign =:= $- orelse Sign =:= $\s ->
+-    parse_std_conversion(Spec, Acc#conversion{sign=Sign});
+-parse_std_conversion("0" ++ Spec, Acc) ->
+-    Align = case Acc#conversion.align of
+-                undefined ->
+-                    sign_right;
+-                A ->
+-                    A
+-            end,
+-    parse_std_conversion(Spec, Acc#conversion{fill_char=$0, align=Align});
+-parse_std_conversion(Spec=[D|_], Acc) when D >= $0 andalso D =< $9 ->
+-    {W, Spec1} = lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec),
+-    parse_std_conversion(Spec1, Acc#conversion{length=list_to_integer(W)});
+-parse_std_conversion([$. | Spec], Acc) ->
+-    case lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec) of
+-        {"", Spec1} ->
+-            parse_std_conversion(Spec1, Acc);
+-        {P, Spec1} ->
+-            parse_std_conversion(Spec1,
+-                                 Acc#conversion{precision=list_to_integer(P)})
+-    end;
+-parse_std_conversion([Type], Acc) ->
+-    parse_std_conversion("", Acc#conversion{ctype=ctype(Type)}).
+-
+-test_tokenize() ->
+-    {?MODULE, [{raw, "ABC"}]} = tokenize("ABC"),
+-    {?MODULE, [{format, {"0", "", ""}}]} = tokenize("{0}"),
+-    {?MODULE, [{raw, "ABC"}, {format, {"1", "", ""}}, {raw, "DEF"}]} =
+-        tokenize("ABC{1}DEF"),
+-    ok.
+-
+-test_format() ->
+-    <<"  -4">> = bformat("{0:4}", [-4]),
+-    <<"   4">> = bformat("{0:4}", [4]),
+-    <<"   4">> = bformat("{0:{0}}", [4]),
+-    <<"4   ">> = bformat("{0:4}", ["4"]),
+-    <<"4   ">> = bformat("{0:{0}}", ["4"]),
+-    <<"1.2yoDEF">> = bformat("{2}{0}{1}{3}", {yo, "DE", 1.2, <<"F">>}),
+-    <<"cafebabe">> = bformat("{0:x}", {16#cafebabe}),
+-    <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}),
+-    <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}),
+-    <<"755">> = bformat("{0:o}", {8#755}),
+-    <<"a">> = bformat("{0:c}", {97}),
+-    %% Horizontal ellipsis
+-    <<226, 128, 166>> = bformat("{0:c}", {16#2026}),
+-    <<"11">> = bformat("{0:b}", {3}),
+-    <<"11">> = bformat("{0:b}", [3]),
+-    <<"11">> = bformat("{three:b}", [{three, 3}]),
+-    <<"11">> = bformat("{three:b}", [{"three", 3}]),
+-    <<"11">> = bformat("{three:b}", [{<<"three">>, 3}]),
+-    <<"\"foo\"">> = bformat("{0!r}", {"foo"}),
+-    <<"2008-5-4">> = bformat("{0.0}-{0.1}-{0.2}", {{2008,5,4}}),
+-    <<"2008-05-04">> = bformat("{0.0:04}-{0.1:02}-{0.2:02}", {{2008,5,4}}),
+-    <<"foo6bar-6">> = bformat("foo{1}{0}-{1}", {bar, 6}),
+-    <<"-'atom test'-">> = bformat("-{arg!r}-", [{arg, 'atom test'}]),
+-    <<"2008-05-04">> = bformat("{0.0:0{1.0}}-{0.1:0{1.1}}-{0.2:0{1.2}}",
+-                               {{2008,5,4}, {4, 2, 2}}),
+-    ok.
+-
+-test_std() ->
+-    M = mochifmt_std:new(),
+-    <<"01">> = bformat("{0}{1}", [0, 1], M),
+-    ok.
+-
+-test_records() ->
+-    M = mochifmt_records:new([{conversion, record_info(fields, conversion)}]),
+-    R = #conversion{length=long, precision=hard, sign=peace},
+-    long = M:get_value("length", R),
+-    hard = M:get_value("precision", R),
+-    peace = M:get_value("sign", R),
+-    <<"long hard">> = bformat("{length} {precision}", R, M),
+-    <<"long hard">> = bformat("{0.length} {0.precision}", [R], M),
+-    ok.
+diff --git a/src/mochiweb/mochifmt_records.erl b/src/mochiweb/mochifmt_records.erl
+deleted file mode 100644
+index 94c7797..0000000
+--- a/src/mochiweb/mochifmt_records.erl
++++ /dev/null
+@@ -1,30 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2008 Mochi Media, Inc.
+-
+-%% @doc Formatter that understands records.
+-%%
+-%% Usage:
+-%%
+-%%    1> M = mochifmt_records:new([{rec, record_info(fields, rec)}]),
+-%%    M:format("{0.bar}", [#rec{bar=foo}]).
+-%%    foo
+-
+--module(mochifmt_records, [Recs]).
+--author('bob at mochimedia.com').
+--export([get_value/2]).
+-
+-get_value(Key, Rec) when is_tuple(Rec) and is_atom(element(1, Rec)) ->
+-    try begin
+-            Atom = list_to_existing_atom(Key),
+-            {_, Fields} = proplists:lookup(element(1, Rec), Recs),
+-            element(get_rec_index(Atom, Fields, 2), Rec)
+-        end
+-    catch error:_ -> mochifmt:get_value(Key, Rec)
+-    end;
+-get_value(Key, Args) ->
+-    mochifmt:get_value(Key, Args).
+-
+-get_rec_index(Atom, [Atom | _], Index) ->
+-    Index;
+-get_rec_index(Atom, [_ | Rest], Index) ->
+-    get_rec_index(Atom, Rest, 1 + Index).
+diff --git a/src/mochiweb/mochifmt_std.erl b/src/mochiweb/mochifmt_std.erl
+deleted file mode 100644
+index 9442016..0000000
+--- a/src/mochiweb/mochifmt_std.erl
++++ /dev/null
+@@ -1,23 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2008 Mochi Media, Inc.
+-
+-%% @doc Template module for a mochifmt formatter.
+-
+--module(mochifmt_std, []).
+--author('bob at mochimedia.com').
+--export([format/2, get_value/2, format_field/2, get_field/2, convert_field/2]).
+-
+-format(Format, Args) ->
+-    mochifmt:format(Format, Args, THIS).
+-
+-get_field(Key, Args) ->
+-    mochifmt:get_field(Key, Args, THIS).
+-
+-convert_field(Key, Args) ->
+-    mochifmt:convert_field(Key, Args).
+-
+-get_value(Key, Args) ->
+-    mochifmt:get_value(Key, Args).
+-
+-format_field(Arg, Format) ->
+-    mochifmt:format_field(Arg, Format, THIS).
+diff --git a/src/mochiweb/mochihex.erl b/src/mochiweb/mochihex.erl
+deleted file mode 100644
+index 7fe6899..0000000
+--- a/src/mochiweb/mochihex.erl
++++ /dev/null
+@@ -1,75 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2006 Mochi Media, Inc.
+-
+-%% @doc Utilities for working with hexadecimal strings.
+-
+--module(mochihex).
+--author('bob at mochimedia.com').
+-
+--export([test/0, to_hex/1, to_bin/1, to_int/1, dehex/1, hexdigit/1]).
+-
+-%% @type iolist() = [char() | binary() | iolist()]
+-%% @type iodata() = iolist() | binary()
+-
+-%% @spec to_hex(integer | iolist()) -> string()
+-%% @doc Convert an iolist to a hexadecimal string.
+-to_hex(0) ->
+-    "0";
+-to_hex(I) when is_integer(I), I > 0 ->
+-    to_hex_int(I, []);
+-to_hex(B) ->
+-    to_hex(iolist_to_binary(B), []).
+-
+-%% @spec to_bin(string()) -> binary()
+-%% @doc Convert a hexadecimal string to a binary.
+-to_bin(L) ->
+-    to_bin(L, []).
+-
+-%% @spec to_int(string()) -> integer()
+-%% @doc Convert a hexadecimal string to an integer.
+-to_int(L) ->
+-    erlang:list_to_integer(L, 16).
+-
+-%% @spec dehex(char()) -> integer()
+-%% @doc Convert a hex digit to its integer value.
+-dehex(C) when C >= $0, C =< $9 ->
+-    C - $0;
+-dehex(C) when C >= $a, C =< $f ->
+-    C - $a + 10;
+-dehex(C) when C >= $A, C =< $F ->
+-    C - $A + 10.
+-
+-%% @spec hexdigit(integer()) -> char()
+-%% @doc Convert an integer less than 16 to a hex digit.
+-hexdigit(C) when C >= 0, C =< 9 ->
+-    C + $0;
+-hexdigit(C) when C =< 15 ->
+-    C + $a - 10.
+-
+-%% @spec test() -> ok
+-%% @doc Test this module.
+-test() ->
+-    "ff000ff1" = to_hex([255, 0, 15, 241]),
+-    <<255, 0, 15, 241>> = to_bin("ff000ff1"),
+-    16#ff000ff1 = to_int("ff000ff1"),
+-    "ff000ff1" = to_hex(16#ff000ff1),
+-    ok.
+-
+-
+-%% Internal API
+-
+-to_hex(<<>>, Acc) ->
+-    lists:reverse(Acc);
+-to_hex(<<C1:4, C2:4, Rest/binary>>, Acc) ->
+-    to_hex(Rest, [hexdigit(C2), hexdigit(C1) | Acc]).
+-
+-to_hex_int(0, Acc) ->
+-    Acc;
+-to_hex_int(I, Acc) ->
+-    to_hex_int(I bsr 4, [hexdigit(I band 15) | Acc]).
+-
+-to_bin([], Acc) ->
+-    iolist_to_binary(lists:reverse(Acc));
+-to_bin([C1, C2 | Rest], Acc) ->
+-    to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]).
+-
+diff --git a/src/mochiweb/mochijson.erl b/src/mochiweb/mochijson.erl
+deleted file mode 100644
+index 029642a..0000000
+--- a/src/mochiweb/mochijson.erl
++++ /dev/null
+@@ -1,525 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2006 Mochi Media, Inc.
+-
+-%% @doc Yet another JSON (RFC 4627) library for Erlang.
+--module(mochijson).
+--author('bob at mochimedia.com').
+--export([encoder/1, encode/1]).
+--export([decoder/1, decode/1]).
+--export([binary_encoder/1, binary_encode/1]).
+--export([binary_decoder/1, binary_decode/1]).
+--export([test/0]).
+-
+-% This is a macro to placate syntax highlighters..
+--define(Q, $\").
+--define(ADV_COL(S, N), S#decoder{column=N+S#decoder.column}).
+--define(INC_COL(S), S#decoder{column=1+S#decoder.column}).
+--define(INC_LINE(S), S#decoder{column=1, line=1+S#decoder.line}).
+-
+-%% @type iolist() = [char() | binary() | iolist()]
+-%% @type iodata() = iolist() | binary()
+-%% @type json_string() = atom | string() | binary()
+-%% @type json_number() = integer() | float()
+-%% @type json_array() = {array, [json_term()]}
+-%% @type json_object() = {struct, [{json_string(), json_term()}]}
+-%% @type json_term() = json_string() | json_number() | json_array() |
+-%%                     json_object()
+-%% @type encoding() = utf8 | unicode
+-%% @type encoder_option() = {input_encoding, encoding()} |
+-%%                          {handler, function()}
+-%% @type decoder_option() = {input_encoding, encoding()} |
+-%%                          {object_hook, function()}
+-%% @type bjson_string() = binary()
+-%% @type bjson_number() = integer() | float()
+-%% @type bjson_array() = [bjson_term()]
+-%% @type bjson_object() = {struct, [{bjson_string(), bjson_term()}]}
+-%% @type bjson_term() = bjson_string() | bjson_number() | bjson_array() |
+-%%                      bjson_object()
+-%% @type binary_encoder_option() = {handler, function()}
+-%% @type binary_decoder_option() = {object_hook, function()}
+-
+--record(encoder, {input_encoding=unicode,
+-                  handler=null}).
+-
+--record(decoder, {input_encoding=utf8,
+-                  object_hook=null,
+-                  line=1,
+-                  column=1,
+-                  state=null}).
+-
+-%% @spec encoder([encoder_option()]) -> function()
+-%% @doc Create an encoder/1 with the given options.
+-encoder(Options) ->
+-    State = parse_encoder_options(Options, #encoder{}),
+-    fun (O) -> json_encode(O, State) end.
+-
+-%% @spec encode(json_term()) -> iolist()
+-%% @doc Encode the given as JSON to an iolist.
+-encode(Any) ->
+-    json_encode(Any, #encoder{}).
+-
+-%% @spec decoder([decoder_option()]) -> function()
+-%% @doc Create a decoder/1 with the given options.
+-decoder(Options) ->
+-    State = parse_decoder_options(Options, #decoder{}),
+-    fun (O) -> json_decode(O, State) end.
+-
+-%% @spec decode(iolist()) -> json_term()
+-%% @doc Decode the given iolist to Erlang terms.
+-decode(S) ->
+-    json_decode(S, #decoder{}).
+-
+-%% @spec binary_decoder([binary_decoder_option()]) -> function()
+-%% @doc Create a binary_decoder/1 with the given options.
+-binary_decoder(Options) ->
+-    mochijson2:decoder(Options).
+-
+-%% @spec binary_encoder([binary_encoder_option()]) -> function()
+-%% @doc Create a binary_encoder/1 with the given options.
+-binary_encoder(Options) ->
+-    mochijson2:encoder(Options).
+-
+-%% @spec binary_encode(bjson_term()) -> iolist()
+-%% @doc Encode the given as JSON to an iolist, using lists for arrays and
+-%%      binaries for strings.
+-binary_encode(Any) ->
+-    mochijson2:encode(Any).
+-
+-%% @spec binary_decode(iolist()) -> bjson_term()
+-%% @doc Decode the given iolist to Erlang terms, using lists for arrays and
+-%%      binaries for strings.
+-binary_decode(S) ->
+-    mochijson2:decode(S).
+-
+-test() ->
+-    test_all(),
+-    mochijson2:test().
+-
+-%% Internal API
+-
+-parse_encoder_options([], State) ->
+-    State;
+-parse_encoder_options([{input_encoding, Encoding} | Rest], State) ->
+-    parse_encoder_options(Rest, State#encoder{input_encoding=Encoding});
+-parse_encoder_options([{handler, Handler} | Rest], State) ->
+-    parse_encoder_options(Rest, State#encoder{handler=Handler}).
+-
+-parse_decoder_options([], State) ->
+-    State;
+-parse_decoder_options([{input_encoding, Encoding} | Rest], State) ->
+-    parse_decoder_options(Rest, State#decoder{input_encoding=Encoding});
+-parse_decoder_options([{object_hook, Hook} | Rest], State) ->
+-    parse_decoder_options(Rest, State#decoder{object_hook=Hook}).
+-
+-json_encode(true, _State) ->
+-    "true";
+-json_encode(false, _State) ->
+-    "false";
+-json_encode(null, _State) ->
+-    "null";
+-json_encode(I, _State) when is_integer(I) ->
+-    integer_to_list(I);
+-json_encode(F, _State) when is_float(F) ->
+-    mochinum:digits(F);
+-json_encode(L, State) when is_list(L); is_binary(L); is_atom(L) ->
+-    json_encode_string(L, State);
+-json_encode({array, Props}, State) when is_list(Props) ->
+-    json_encode_array(Props, State);
+-json_encode({struct, Props}, State) when is_list(Props) ->
+-    json_encode_proplist(Props, State);
+-json_encode(Bad, #encoder{handler=null}) ->
+-    exit({json_encode, {bad_term, Bad}});
+-json_encode(Bad, State=#encoder{handler=Handler}) ->
+-    json_encode(Handler(Bad), State).
+-
+-json_encode_array([], _State) ->
+-    "[]";
+-json_encode_array(L, State) ->
+-    F = fun (O, Acc) ->
+-                [$,, json_encode(O, State) | Acc]
+-        end,
+-    [$, | Acc1] = lists:foldl(F, "[", L),
+-    lists:reverse([$\] | Acc1]).
+-
+-json_encode_proplist([], _State) ->
+-    "{}";
+-json_encode_proplist(Props, State) ->
+-    F = fun ({K, V}, Acc) ->
+-                KS = case K of
+-                         K when is_atom(K) ->
+-                             json_encode_string_utf8(atom_to_list(K));
+-                         K when is_integer(K) ->
+-                             json_encode_string(integer_to_list(K), State);
+-                         K when is_list(K); is_binary(K) ->
+-                             json_encode_string(K, State)
+-                     end,
+-                VS = json_encode(V, State),
+-                [$,, VS, $:, KS | Acc]
+-        end,
+-    [$, | Acc1] = lists:foldl(F, "{", Props),
+-    lists:reverse([$\} | Acc1]).
+-
+-json_encode_string(A, _State) when is_atom(A) ->
+-    json_encode_string_unicode(xmerl_ucs:from_utf8(atom_to_list(A)));
+-json_encode_string(B, _State) when is_binary(B) ->
+-    json_encode_string_unicode(xmerl_ucs:from_utf8(B));
+-json_encode_string(S, #encoder{input_encoding=utf8}) ->
+-    json_encode_string_utf8(S);
+-json_encode_string(S, #encoder{input_encoding=unicode}) ->
+-    json_encode_string_unicode(S).
+-
+-json_encode_string_utf8(S) ->
+-    [?Q | json_encode_string_utf8_1(S)].
+-
+-json_encode_string_utf8_1([C | Cs]) when C >= 0, C =< 16#7f ->
+-    NewC = case C of
+-               $\\ -> "\\\\";
+-               ?Q -> "\\\"";
+-               _ when C >= $\s, C < 16#7f -> C;
+-               $\t -> "\\t";
+-               $\n -> "\\n";
+-               $\r -> "\\r";
+-               $\f -> "\\f";
+-               $\b -> "\\b";
+-               _ when C >= 0, C =< 16#7f -> unihex(C);
+-               _ -> exit({json_encode, {bad_char, C}})
+-           end,
+-    [NewC | json_encode_string_utf8_1(Cs)];
+-json_encode_string_utf8_1(All=[C | _]) when C >= 16#80, C =< 16#10FFFF ->
+-    json_encode_string_unicode(xmerl_ucs:from_utf8(All));
+-json_encode_string_utf8_1([]) ->
+-    "\"".
+-
+-json_encode_string_unicode(S) ->
+-    [?Q | json_encode_string_unicode_1(S)].
+-
+-json_encode_string_unicode_1([C | Cs]) ->
+-    NewC = case C of
+-               $\\ -> "\\\\";
+-               ?Q -> "\\\"";
+-               _ when C >= $\s, C < 16#7f -> C;
+-               $\t -> "\\t";
+-               $\n -> "\\n";
+-               $\r -> "\\r";
+-               $\f -> "\\f";
+-               $\b -> "\\b";
+-               _ when C >= 0, C =< 16#10FFFF -> unihex(C);
+-               _ -> exit({json_encode, {bad_char, C}})
+-           end,
+-    [NewC | json_encode_string_unicode_1(Cs)];
+-json_encode_string_unicode_1([]) ->
+-    "\"".
+-
+-dehex(C) when C >= $0, C =< $9 ->
+-    C - $0;
+-dehex(C) when C >= $a, C =< $f ->
+-    C - $a + 10;
+-dehex(C) when C >= $A, C =< $F ->
+-    C - $A + 10.
+-
+-hexdigit(C) when C >= 0, C =< 9 ->
+-    C + $0;
+-hexdigit(C) when C =< 15 ->
+-    C + $a - 10.
+-
+-unihex(C) when C < 16#10000 ->
+-    <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
+-    Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
+-    [$\\, $u | Digits];
+-unihex(C) when C =< 16#10FFFF ->
+-    N = C - 16#10000,
+-    S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
+-    S2 = 16#dc00 bor (N band 16#3ff),
+-    [unihex(S1), unihex(S2)].
+-
+-json_decode(B, S) when is_binary(B) ->
+-    json_decode(binary_to_list(B), S);
+-json_decode(L, S) ->
+-    {Res, L1, S1} = decode1(L, S),
+-    {eof, [], _} = tokenize(L1, S1#decoder{state=trim}),
+-    Res.
+-
+-decode1(L, S=#decoder{state=null}) ->
+-    case tokenize(L, S#decoder{state=any}) of
+-        {{const, C}, L1, S1} ->
+-            {C, L1, S1};
+-        {start_array, L1, S1} ->
+-            decode_array(L1, S1#decoder{state=any}, []);
+-        {start_object, L1, S1} ->
+-            decode_object(L1, S1#decoder{state=key}, [])
+-    end.
+-
+-make_object(V, #decoder{object_hook=null}) ->
+-    V;
+-make_object(V, #decoder{object_hook=Hook}) ->
+-    Hook(V).
+-
+-decode_object(L, S=#decoder{state=key}, Acc) ->
+-    case tokenize(L, S) of
+-        {end_object, Rest, S1} ->
+-            V = make_object({struct, lists:reverse(Acc)}, S1),
+-            {V, Rest, S1#decoder{state=null}};
+-        {{const, K}, Rest, S1} when is_list(K) ->
+-            {colon, L2, S2} = tokenize(Rest, S1),
+-            {V, L3, S3} = decode1(L2, S2#decoder{state=null}),
+-            decode_object(L3, S3#decoder{state=comma}, [{K, V} | Acc])
+-    end;
+-decode_object(L, S=#decoder{state=comma}, Acc) ->
+-    case tokenize(L, S) of
+-        {end_object, Rest, S1} ->
+-            V = make_object({struct, lists:reverse(Acc)}, S1),
+-            {V, Rest, S1#decoder{state=null}};
+-        {comma, Rest, S1} ->
+-            decode_object(Rest, S1#decoder{state=key}, Acc)
+-    end.
+-
+-decode_array(L, S=#decoder{state=any}, Acc) ->
+-    case tokenize(L, S) of
+-        {end_array, Rest, S1} ->
+-            {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}};
+-        {start_array, Rest, S1} ->
+-            {Array, Rest1, S2} = decode_array(Rest, S1#decoder{state=any}, []),
+-            decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]);
+-        {start_object, Rest, S1} ->
+-            {Array, Rest1, S2} = decode_object(Rest, S1#decoder{state=key}, []),
+-            decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]);
+-        {{const, Const}, Rest, S1} ->
+-            decode_array(Rest, S1#decoder{state=comma}, [Const | Acc])
+-    end;
+-decode_array(L, S=#decoder{state=comma}, Acc) ->
+-    case tokenize(L, S) of
+-        {end_array, Rest, S1} ->
+-            {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}};
+-        {comma, Rest, S1} ->
+-            decode_array(Rest, S1#decoder{state=any}, Acc)
+-    end.
+-
+-tokenize_string(IoList=[C | _], S=#decoder{input_encoding=utf8}, Acc)
+-  when is_list(C); is_binary(C); C >= 16#7f ->
+-    List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)),
+-    tokenize_string(List, S#decoder{input_encoding=unicode}, Acc);
+-tokenize_string("\"" ++ Rest, S, Acc) ->
+-    {lists:reverse(Acc), Rest, ?INC_COL(S)};
+-tokenize_string("\\\"" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$\" | Acc]);
+-tokenize_string("\\\\" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$\\ | Acc]);
+-tokenize_string("\\/" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$/ | Acc]);
+-tokenize_string("\\b" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$\b | Acc]);
+-tokenize_string("\\f" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$\f | Acc]);
+-tokenize_string("\\n" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$\n | Acc]);
+-tokenize_string("\\r" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$\r | Acc]);
+-tokenize_string("\\t" ++ Rest, S, Acc) ->
+-    tokenize_string(Rest, ?ADV_COL(S, 2), [$\t | Acc]);
+-tokenize_string([$\\, $u, C3, C2, C1, C0 | Rest], S, Acc) ->
+-    % coalesce UTF-16 surrogate pair?
+-    C = dehex(C0) bor
+-        (dehex(C1) bsl 4) bor
+-        (dehex(C2) bsl 8) bor
+-        (dehex(C3) bsl 12),
+-    tokenize_string(Rest, ?ADV_COL(S, 6), [C | Acc]);
+-tokenize_string([C | Rest], S, Acc) when C >= $\s; C < 16#10FFFF ->
+-    tokenize_string(Rest, ?ADV_COL(S, 1), [C | Acc]).
+-
+-tokenize_number(IoList=[C | _], Mode, S=#decoder{input_encoding=utf8}, Acc)
+-  when is_list(C); is_binary(C); C >= 16#7f ->
+-    List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)),
+-    tokenize_number(List, Mode, S#decoder{input_encoding=unicode}, Acc);
+-tokenize_number([$- | Rest], sign, S, []) ->
+-    tokenize_number(Rest, int, ?INC_COL(S), [$-]);
+-tokenize_number(Rest, sign, S, []) ->
+-    tokenize_number(Rest, int, S, []);
+-tokenize_number([$0 | Rest], int, S, Acc) ->
+-    tokenize_number(Rest, frac, ?INC_COL(S), [$0 | Acc]);
+-tokenize_number([C | Rest], int, S, Acc) when C >= $1, C =< $9 ->
+-    tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]);
+-tokenize_number([C | Rest], int1, S, Acc) when C >= $0, C =< $9 ->
+-    tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]);
+-tokenize_number(Rest, int1, S, Acc) ->
+-    tokenize_number(Rest, frac, S, Acc);
+-tokenize_number([$., C | Rest], frac, S, Acc) when C >= $0, C =< $9 ->
+-    tokenize_number(Rest, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
+-tokenize_number([E | Rest], frac, S, Acc) when E == $e; E == $E ->
+-    tokenize_number(Rest, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
+-tokenize_number(Rest, frac, S, Acc) ->
+-    {{int, lists:reverse(Acc)}, Rest, S};
+-tokenize_number([C | Rest], frac1, S, Acc) when C >= $0, C =< $9 ->
+-    tokenize_number(Rest, frac1, ?INC_COL(S), [C | Acc]);
+-tokenize_number([E | Rest], frac1, S, Acc) when E == $e; E == $E ->
+-    tokenize_number(Rest, esign, ?INC_COL(S), [$e | Acc]);
+-tokenize_number(Rest, frac1, S, Acc) ->
+-    {{float, lists:reverse(Acc)}, Rest, S};
+-tokenize_number([C | Rest], esign, S, Acc) when C == $-; C == $+ ->
+-    tokenize_number(Rest, eint, ?INC_COL(S), [C | Acc]);
+-tokenize_number(Rest, esign, S, Acc) ->
+-    tokenize_number(Rest, eint, S, Acc);
+-tokenize_number([C | Rest], eint, S, Acc) when C >= $0, C =< $9 ->
+-    tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]);
+-tokenize_number([C | Rest], eint1, S, Acc) when C >= $0, C =< $9 ->
+-    tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]);
+-tokenize_number(Rest, eint1, S, Acc) ->
+-    {{float, lists:reverse(Acc)}, Rest, S}.
+-
+-tokenize([], S=#decoder{state=trim}) ->
+-    {eof, [], S};
+-tokenize([L | Rest], S) when is_list(L) ->
+-    tokenize(L ++ Rest, S);
+-tokenize([B | Rest], S) when is_binary(B) ->
+-    tokenize(xmerl_ucs:from_utf8(B) ++ Rest, S);
+-tokenize("\r\n" ++ Rest, S) ->
+-    tokenize(Rest, ?INC_LINE(S));
+-tokenize("\n" ++ Rest, S) ->
+-    tokenize(Rest, ?INC_LINE(S));
+-tokenize([C | Rest], S) when C == $\s; C == $\t ->
+-    tokenize(Rest, ?INC_COL(S));
+-tokenize("{" ++ Rest, S) ->
+-    {start_object, Rest, ?INC_COL(S)};
+-tokenize("}" ++ Rest, S) ->
+-    {end_object, Rest, ?INC_COL(S)};
+-tokenize("[" ++ Rest, S) ->
+-    {start_array, Rest, ?INC_COL(S)};
+-tokenize("]" ++ Rest, S) ->
+-    {end_array, Rest, ?INC_COL(S)};
+-tokenize("," ++ Rest, S) ->
+-    {comma, Rest, ?INC_COL(S)};
+-tokenize(":" ++ Rest, S) ->
+-    {colon, Rest, ?INC_COL(S)};
+-tokenize("null" ++ Rest, S) ->
+-    {{const, null}, Rest, ?ADV_COL(S, 4)};
+-tokenize("true" ++ Rest, S) ->
+-    {{const, true}, Rest, ?ADV_COL(S, 4)};
+-tokenize("false" ++ Rest, S) ->
+-    {{const, false}, Rest, ?ADV_COL(S, 5)};
+-tokenize("\"" ++ Rest, S) ->
+-    {String, Rest1, S1} = tokenize_string(Rest, ?INC_COL(S), []),
+-    {{const, String}, Rest1, S1};
+-tokenize(L=[C | _], S) when C >= $0, C =< $9; C == $- ->
+-    case tokenize_number(L, sign, S, []) of
+-        {{int, Int}, Rest, S1} ->
+-            {{const, list_to_integer(Int)}, Rest, S1};
+-        {{float, Float}, Rest, S1} ->
+-            {{const, list_to_float(Float)}, Rest, S1}
+-    end.
+-
+-%% testing constructs borrowed from the Yaws JSON implementation.
+-
+-%% Create an object from a list of Key/Value pairs.
+-
+-obj_new() ->
+-    {struct, []}.
+-
+-is_obj({struct, Props}) ->
+-    F = fun ({K, _}) when is_list(K) ->
+-                true;
+-            (_) ->
+-                false
+-        end,
+-    lists:all(F, Props).
+-
+-obj_from_list(Props) ->
+-    Obj = {struct, Props},
+-    case is_obj(Obj) of
+-        true -> Obj;
+-        false -> exit(json_bad_object)
+-    end.
+-
+-%% Test for equivalence of Erlang terms.
+-%% Due to arbitrary order of construction, equivalent objects might
+-%% compare unequal as erlang terms, so we need to carefully recurse
+-%% through aggregates (tuples and objects).
+-
+-equiv({struct, Props1}, {struct, Props2}) ->
+-    equiv_object(Props1, Props2);
+-equiv({array, L1}, {array, L2}) ->
+-    equiv_list(L1, L2);
+-equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
+-equiv(S1, S2) when is_list(S1), is_list(S2)     -> S1 == S2;
+-equiv(true, true) -> true;
+-equiv(false, false) -> true;
+-equiv(null, null) -> true.
+-
+-%% Object representation and traversal order is unknown.
+-%% Use the sledgehammer and sort property lists.
+-
+-equiv_object(Props1, Props2) ->
+-    L1 = lists:keysort(1, Props1),
+-    L2 = lists:keysort(1, Props2),
+-    Pairs = lists:zip(L1, L2),
+-    true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
+-        equiv(K1, K2) and equiv(V1, V2)
+-    end, Pairs).
+-
+-%% Recursively compare tuple elements for equivalence.
+-
+-equiv_list([], []) ->
+-    true;
+-equiv_list([V1 | L1], [V2 | L2]) ->
+-    case equiv(V1, V2) of
+-        true ->
+-            equiv_list(L1, L2);
+-        false ->
+-            false
+-    end.
+-
+-test_all() ->
+-    test_one(e2j_test_vec(utf8), 1).
+-
+-test_one([], _N) ->
+-    %% io:format("~p tests passed~n", [N-1]),
+-    ok;
+-test_one([{E, J} | Rest], N) ->
+-    %% io:format("[~p] ~p ~p~n", [N, E, J]),
+-    true = equiv(E, decode(J)),
+-    true = equiv(E, decode(encode(E))),
+-    test_one(Rest, 1+N).
+-
+-e2j_test_vec(utf8) ->
+-    [
+-    {1, "1"},
+-    {3.1416, "3.14160"}, % text representation may truncate, trail zeroes
+-    {-1, "-1"},
+-    {-3.1416, "-3.14160"},
+-    {12.0e10, "1.20000e+11"},
+-    {1.234E+10, "1.23400e+10"},
+-    {-1.234E-10, "-1.23400e-10"},
+-    {10.0, "1.0e+01"},
+-    {123.456, "1.23456E+2"},
+-    {10.0, "1e1"},
+-    {"foo", "\"foo\""},
+-    {"foo" ++ [5] ++ "bar", "\"foo\\u0005bar\""},
+-    {"", "\"\""},
+-    {"\"", "\"\\\"\""},
+-    {"\n\n\n", "\"\\n\\n\\n\""},
+-    {"\\", "\"\\\\\""},
+-    {"\" \b\f\r\n\t\"", "\"\\\" \\b\\f\\r\\n\\t\\\"\""},
+-    {obj_new(), "{}"},
+-    {obj_from_list([{"foo", "bar"}]), "{\"foo\":\"bar\"}"},
+-    {obj_from_list([{"foo", "bar"}, {"baz", 123}]),
+-     "{\"foo\":\"bar\",\"baz\":123}"},
+-    {{array, []}, "[]"},
+-    {{array, [{array, []}]}, "[[]]"},
+-    {{array, [1, "foo"]}, "[1,\"foo\"]"},
+-
+-    % json array in a json object
+-    {obj_from_list([{"foo", {array, [123]}}]),
+-     "{\"foo\":[123]}"},
+-
+-    % json object in a json object
+-    {obj_from_list([{"foo", obj_from_list([{"bar", true}])}]),
+-     "{\"foo\":{\"bar\":true}}"},
+-
+-    % fold evaluation order
+-    {obj_from_list([{"foo", {array, []}},
+-                     {"bar", obj_from_list([{"baz", true}])},
+-                     {"alice", "bob"}]),
+-     "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
+-
+-    % json object in a json array
+-    {{array, [-123, "foo", obj_from_list([{"bar", {array, []}}]), null]},
+-     "[-123,\"foo\",{\"bar\":[]},null]"}
+-    ].
+diff --git a/src/mochiweb/mochijson2.erl b/src/mochiweb/mochijson2.erl
+deleted file mode 100644
+index ee19458..0000000
+--- a/src/mochiweb/mochijson2.erl
++++ /dev/null
+@@ -1,621 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works
+-%%      with binaries as strings, arrays as lists (without an {array, _})
+-%%      wrapper and it only knows how to decode UTF-8 (and ASCII).
+-
+--module(mochijson2).
+--author('bob at mochimedia.com').
+--export([encoder/1, encode/1]).
+--export([decoder/1, decode/1]).
+--export([test/0]).
+-
+-% This is a macro to placate syntax highlighters..
+--define(Q, $\").
+--define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset,
+-                                 column=N+S#decoder.column}).
+--define(INC_COL(S), S#decoder{offset=1+S#decoder.offset,
+-                              column=1+S#decoder.column}).
+--define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset,
+-                               column=1,
+-                               line=1+S#decoder.line}).
+--define(INC_CHAR(S, C),
+-        case C of
+-            $\n ->
+-                S#decoder{column=1,
+-                          line=1+S#decoder.line,
+-                          offset=1+S#decoder.offset};
+-            _ ->
+-                S#decoder{column=1+S#decoder.column,
+-                          offset=1+S#decoder.offset}
+-        end).
+--define(IS_WHITESPACE(C),
+-        (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
+-
+-%% @type iolist() = [char() | binary() | iolist()]
+-%% @type iodata() = iolist() | binary()
+-%% @type json_string() = atom | binary()
+-%% @type json_number() = integer() | float()
+-%% @type json_array() = [json_term()]
+-%% @type json_object() = {struct, [{json_string(), json_term()}]}
+-%% @type json_term() = json_string() | json_number() | json_array() |
+-%%                     json_object()
+-
+--record(encoder, {handler=null}).
+-
+--record(decoder, {object_hook=null,
+-                  offset=0,
+-                  line=1,
+-                  column=1,
+-                  state=null}).
+-
+-%% @spec encoder([encoder_option()]) -> function()
+-%% @doc Create an encoder/1 with the given options.
+-encoder(Options) ->
+-    State = parse_encoder_options(Options, #encoder{}),
+-    fun (O) -> json_encode(O, State) end.
+-
+-%% @spec encode(json_term()) -> iolist()
+-%% @doc Encode the given as JSON to an iolist.
+-encode(Any) ->
+-    json_encode(Any, #encoder{}).
+-
+-%% @spec decoder([decoder_option()]) -> function()
+-%% @doc Create a decoder/1 with the given options.
+-decoder(Options) ->
+-    State = parse_decoder_options(Options, #decoder{}),
+-    fun (O) -> json_decode(O, State) end.
+-
+-%% @spec decode(iolist()) -> json_term()
+-%% @doc Decode the given iolist to Erlang terms.
+-decode(S) ->
+-    try json_decode(S, #decoder{})
+-    catch
+-        _:_ -> throw({invalid_json, S})
+-    end.
+-
+-test() ->
+-    test_all().
+-
+-%% Internal API
+-
+-parse_encoder_options([], State) ->
+-    State;
+-parse_encoder_options([{handler, Handler} | Rest], State) ->
+-    parse_encoder_options(Rest, State#encoder{handler=Handler}).
+-
+-parse_decoder_options([], State) ->
+-    State;
+-parse_decoder_options([{object_hook, Hook} | Rest], State) ->
+-    parse_decoder_options(Rest, State#decoder{object_hook=Hook}).
+-
+-json_encode(true, _State) ->
+-    <<"true">>;
+-json_encode(false, _State) ->
+-    <<"false">>;
+-json_encode(null, _State) ->
+-    <<"null">>;
+-json_encode(I, _State) when is_integer(I) ->
+-    integer_to_list(I);
+-json_encode(F, _State) when is_float(F) ->
+-    mochinum:digits(F);
+-json_encode(S, State) when is_binary(S); is_atom(S) ->
+-    json_encode_string(S, State);
+-json_encode(Array, State) when is_list(Array) ->
+-    json_encode_array(Array, State);
+-json_encode({Props}, State) when is_list(Props) ->
+-    json_encode_proplist(Props, State);
+-json_encode(Bad, #encoder{handler=null}) ->
+-    exit({json_encode, {bad_term, Bad}});
+-json_encode(Bad, State=#encoder{handler=Handler}) ->
+-    json_encode(Handler(Bad), State).
+-
+-json_encode_array([], _State) ->
+-    <<"[]">>;
+-json_encode_array(L, State) ->
+-    F = fun (O, Acc) ->
+-                [$,, json_encode(O, State) | Acc]
+-        end,
+-    [$, | Acc1] = lists:foldl(F, "[", L),
+-    lists:reverse([$\] | Acc1]).
+-
+-json_encode_proplist([], _State) ->
+-    <<"{}">>;
+-json_encode_proplist(Props, State) ->
+-    F = fun ({K, V}, Acc) ->
+-                KS = json_encode_string(K, State),
+-                VS = json_encode(V, State),
+-                [$,, VS, $:, KS | Acc]
+-        end,
+-    [$, | Acc1] = lists:foldl(F, "{", Props),
+-    lists:reverse([$\} | Acc1]).
+-
+-json_encode_string(A, _State) when is_atom(A) ->
+-    L = atom_to_list(A),
+-    case json_string_is_safe(L) of
+-        true ->
+-            [?Q, L, ?Q];
+-        false ->
+-            json_encode_string_unicode(xmerl_ucs:from_utf8(L), [?Q])
+-    end;
+-json_encode_string(B, _State) when is_binary(B) ->
+-    case json_bin_is_safe(B) of
+-        true ->
+-            [?Q, B, ?Q];
+-        false ->
+-            json_encode_string_unicode(xmerl_ucs:from_utf8(B), [?Q])
+-    end;
+-json_encode_string(I, _State) when is_integer(I) ->
+-    [?Q, integer_to_list(I), ?Q];
+-json_encode_string(L, _State) when is_list(L) ->
+-    case json_string_is_safe(L) of
+-        true ->
+-            [?Q, L, ?Q];
+-        false ->
+-            json_encode_string_unicode(L, [?Q])
+-    end.
+-
+-json_string_is_safe([]) ->
+-    true;
+-json_string_is_safe([C | Rest]) ->
+-    case C of
+-        ?Q ->
+-            false;
+-        $\\ ->
+-            false;
+-        $\b ->
+-            false;
+-        $\f ->
+-            false;
+-        $\n ->
+-            false;
+-        $\r ->
+-            false;
+-        $\t ->
+-            false;
+-        C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
+-            false;
+-        C when C < 16#7f ->
+-            json_string_is_safe(Rest);
+-        _ ->
+-            false
+-    end.
+-
+-json_bin_is_safe(<<>>) ->
+-    true;
+-json_bin_is_safe(<<C, Rest/binary>>) ->
+-    case C of
+-        ?Q ->
+-            false;
+-        $\\ ->
+-            false;
+-        $\b ->
+-            false;
+-        $\f ->
+-            false;
+-        $\n ->
+-            false;
+-        $\r ->
+-            false;
+-        $\t ->
+-            false;
+-        C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
+-            false;
+-        C when C < 16#7f ->
+-            json_bin_is_safe(Rest);
+-        _ ->
+-            false
+-    end.
+-
+-json_encode_string_unicode([], Acc) ->
+-    lists:reverse([$\" | Acc]);
+-json_encode_string_unicode([C | Cs], Acc) ->
+-    Acc1 = case C of
+-               ?Q ->
+-                   [?Q, $\\ | Acc];
+-               %% Escaping solidus is only useful when trying to protect
+-               %% against "</script>" injection attacks which are only
+-               %% possible when JSON is inserted into a HTML document
+-               %% in-line. mochijson2 does not protect you from this, so
+-               %% if you do insert directly into HTML then you need to
+-               %% uncomment the following case or escape the output of encode.
+-               %%
+-               %% $/ ->
+-               %%    [$/, $\\ | Acc];
+-               %%
+-               $\\ ->
+-                   [$\\, $\\ | Acc];
+-               $\b ->
+-                   [$b, $\\ | Acc];
+-               $\f ->
+-                   [$f, $\\ | Acc];
+-               $\n ->
+-                   [$n, $\\ | Acc];
+-               $\r ->
+-                   [$r, $\\ | Acc];
+-               $\t ->
+-                   [$t, $\\ | Acc];
+-               C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
+-                   [unihex(C) | Acc];
+-               C when C < 16#7f ->
+-                   [C | Acc];
+-               _ ->
+-                   exit({json_encode, {bad_char, C}})
+-           end,
+-    json_encode_string_unicode(Cs, Acc1).
+-
+-hexdigit(C) when C >= 0, C =< 9 ->
+-    C + $0;
+-hexdigit(C) when C =< 15 ->
+-    C + $a - 10.
+-
+-unihex(C) when C < 16#10000 ->
+-    <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
+-    Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
+-    [$\\, $u | Digits];
+-unihex(C) when C =< 16#10FFFF ->
+-    N = C - 16#10000,
+-    S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
+-    S2 = 16#dc00 bor (N band 16#3ff),
+-    [unihex(S1), unihex(S2)].
+-
+-json_decode(L, S) when is_list(L) ->
+-    json_decode(iolist_to_binary(L), S);
+-json_decode(B, S) ->
+-    {Res, S1} = decode1(B, S),
+-    {eof, _} = tokenize(B, S1#decoder{state=trim}),
+-    Res.
+-
+-decode1(B, S=#decoder{state=null}) ->
+-    case tokenize(B, S#decoder{state=any}) of
+-        {{const, C}, S1} ->
+-            {C, S1};
+-        {start_array, S1} ->
+-            decode_array(B, S1);
+-        {start_object, S1} ->
+-            decode_object(B, S1)
+-    end.
+-
+-make_object(V, #decoder{object_hook=null}) ->
+-    V;
+-make_object(V, #decoder{object_hook=Hook}) ->
+-    Hook(V).
+-
+-decode_object(B, S) ->
+-    decode_object(B, S#decoder{state=key}, []).
+-
+-decode_object(B, S=#decoder{state=key}, Acc) ->
+-    case tokenize(B, S) of
+-        {end_object, S1} ->
+-            V = make_object({lists:reverse(Acc)}, S1),
+-            {V, S1#decoder{state=null}};
+-        {{const, K}, S1} ->
+-            {colon, S2} = tokenize(B, S1),
+-            {V, S3} = decode1(B, S2#decoder{state=null}),
+-            decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc])
+-    end;
+-decode_object(B, S=#decoder{state=comma}, Acc) ->
+-    case tokenize(B, S) of
+-        {end_object, S1} ->
+-            V = make_object({lists:reverse(Acc)}, S1),
+-            {V, S1#decoder{state=null}};
+-        {comma, S1} ->
+-            decode_object(B, S1#decoder{state=key}, Acc)
+-    end.
+-
+-decode_array(B, S) ->
+-    decode_array(B, S#decoder{state=any}, []).
+-
+-decode_array(B, S=#decoder{state=any}, Acc) ->
+-    case tokenize(B, S) of
+-        {end_array, S1} ->
+-            {lists:reverse(Acc), S1#decoder{state=null}};
+-        {start_array, S1} ->
+-            {Array, S2} = decode_array(B, S1),
+-            decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
+-        {start_object, S1} ->
+-            {Array, S2} = decode_object(B, S1),
+-            decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
+-        {{const, Const}, S1} ->
+-            decode_array(B, S1#decoder{state=comma}, [Const | Acc])
+-    end;
+-decode_array(B, S=#decoder{state=comma}, Acc) ->
+-    case tokenize(B, S) of
+-        {end_array, S1} ->
+-            {lists:reverse(Acc), S1#decoder{state=null}};
+-        {comma, S1} ->
+-            decode_array(B, S1#decoder{state=any}, Acc)
+-    end.
+-
+-tokenize_string(B, S=#decoder{offset=O}) ->
+-    case tokenize_string_fast(B, O) of
+-        {escape, O1} ->
+-            Length = O1 - O,
+-            S1 = ?ADV_COL(S, Length),
+-            <<_:O/binary, Head:Length/binary, _/binary>> = B,
+-            tokenize_string(B, S1, lists:reverse(binary_to_list(Head)));
+-        O1 ->
+-            Length = O1 - O,
+-            <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B,
+-            {{const, String}, ?ADV_COL(S, Length + 1)}
+-    end.
+-
+-tokenize_string_fast(B, O) ->
+-    case B of
+-        <<_:O/binary, ?Q, _/binary>> ->
+-            O;
+-        <<_:O/binary, $\\, _/binary>> ->
+-            {escape, O};
+-        <<_:O/binary, C1, _/binary>> when C1 < 128 ->
+-            tokenize_string_fast(B, 1 + O);
+-        <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
+-                C2 >= 128, C2 =< 191 ->
+-            tokenize_string_fast(B, 2 + O);
+-        <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
+-                C2 >= 128, C2 =< 191,
+-                C3 >= 128, C3 =< 191 ->
+-            tokenize_string_fast(B, 3 + O);
+-        <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244,
+-                C2 >= 128, C2 =< 191,
+-                C3 >= 128, C3 =< 191,
+-                C4 >= 128, C4 =< 191 ->
+-            tokenize_string_fast(B, 4 + O);
+-        _ ->
+-            throw(invalid_utf8)
+-    end.
+-
+-tokenize_string(B, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, ?Q, _/binary>> ->
+-            {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)};
+-        <<_:O/binary, "\\\"", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]);
+-        <<_:O/binary, "\\\\", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]);
+-        <<_:O/binary, "\\/", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]);
+-        <<_:O/binary, "\\b", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]);
+-        <<_:O/binary, "\\f", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]);
+-        <<_:O/binary, "\\n", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]);
+-        <<_:O/binary, "\\r", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]);
+-        <<_:O/binary, "\\t", _/binary>> ->
+-            tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]);
+-        <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> ->
+-            C = erlang:list_to_integer([C3, C2, C1, C0], 16),
+-            if C > 16#D7FF, C < 16#DC00 ->
+-                %% coalesce UTF-16 surrogate pair
+-                <<"\\u", D3, D2, D1, D0, _/binary>> = Rest,
+-                D = erlang:list_to_integer([D3,D2,D1,D0], 16),
+-                [CodePoint] = xmerl_ucs:from_utf16be(<<C:16/big-unsigned-integer,
+-                    D:16/big-unsigned-integer>>),
+-                Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc),
+-                tokenize_string(B, ?ADV_COL(S, 12), Acc1);
+-            true ->
+-                Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc),
+-                tokenize_string(B, ?ADV_COL(S, 6), Acc1)
+-            end;
+-        <<_:O/binary, C, _/binary>> ->
+-            tokenize_string(B, ?INC_CHAR(S, C), [C | Acc])
+-    end.
+-
+-tokenize_number(B, S) ->
+-    case tokenize_number(B, sign, S, []) of
+-        {{int, Int}, S1} ->
+-            {{const, list_to_integer(Int)}, S1};
+-        {{float, Float}, S1} ->
+-            {{const, list_to_float(Float)}, S1}
+-    end.
+-
+-tokenize_number(B, sign, S=#decoder{offset=O}, []) ->
+-    case B of
+-        <<_:O/binary, $-, _/binary>> ->
+-            tokenize_number(B, int, ?INC_COL(S), [$-]);
+-        _ ->
+-            tokenize_number(B, int, S, [])
+-    end;
+-tokenize_number(B, int, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, $0, _/binary>> ->
+-            tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]);
+-        <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 ->
+-            tokenize_number(B, int1, ?INC_COL(S), [C | Acc])
+-    end;
+-tokenize_number(B, int1, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
+-            tokenize_number(B, int1, ?INC_COL(S), [C | Acc]);
+-        _ ->
+-            tokenize_number(B, frac, S, Acc)
+-    end;
+-tokenize_number(B, frac, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 ->
+-            tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
+-        <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
+-            tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
+-        _ ->
+-            {{int, lists:reverse(Acc)}, S}
+-    end;
+-tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
+-            tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]);
+-        <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
+-            tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]);
+-        _ ->
+-            {{float, lists:reverse(Acc)}, S}
+-    end;
+-tokenize_number(B, esign, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ ->
+-            tokenize_number(B, eint, ?INC_COL(S), [C | Acc]);
+-        _ ->
+-            tokenize_number(B, eint, S, Acc)
+-    end;
+-tokenize_number(B, eint, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
+-            tokenize_number(B, eint1, ?INC_COL(S), [C | Acc])
+-    end;
+-tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
+-            tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]);
+-        _ ->
+-            {{float, lists:reverse(Acc)}, S}
+-    end.
+-
+-tokenize(B, S=#decoder{offset=O}) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
+-            tokenize(B, ?INC_CHAR(S, C));
+-        <<_:O/binary, "{", _/binary>> ->
+-            {start_object, ?INC_COL(S)};
+-        <<_:O/binary, "}", _/binary>> ->
+-            {end_object, ?INC_COL(S)};
+-        <<_:O/binary, "[", _/binary>> ->
+-            {start_array, ?INC_COL(S)};
+-        <<_:O/binary, "]", _/binary>> ->
+-            {end_array, ?INC_COL(S)};
+-        <<_:O/binary, ",", _/binary>> ->
+-            {comma, ?INC_COL(S)};
+-        <<_:O/binary, ":", _/binary>> ->
+-            {colon, ?INC_COL(S)};
+-        <<_:O/binary, "null", _/binary>> ->
+-            {{const, null}, ?ADV_COL(S, 4)};
+-        <<_:O/binary, "true", _/binary>> ->
+-            {{const, true}, ?ADV_COL(S, 4)};
+-        <<_:O/binary, "false", _/binary>> ->
+-            {{const, false}, ?ADV_COL(S, 5)};
+-        <<_:O/binary, "\"", _/binary>> ->
+-            tokenize_string(B, ?INC_COL(S));
+-        <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9)
+-                                         orelse C =:= $- ->
+-            tokenize_number(B, S);
+-        <<_:O/binary>> ->
+-            trim = S#decoder.state,
+-            {eof, S}
+-    end.
+-
+-%% testing constructs borrowed from the Yaws JSON implementation.
+-
+-%% Create an object from a list of Key/Value pairs.
+-
+-obj_new() ->
+-    {[]}.
+-
+-is_obj({Props}) ->
+-    F = fun ({K, _}) when is_binary(K) ->
+-                true;
+-            (_) ->
+-                false
+-        end,
+-    lists:all(F, Props).
+-
+-obj_from_list(Props) ->
+-    Obj = {Props},
+-    case is_obj(Obj) of
+-        true -> Obj;
+-        false -> exit({json_bad_object, Obj})
+-    end.
+-
+-%% Test for equivalence of Erlang terms.
+-%% Due to arbitrary order of construction, equivalent objects might
+-%% compare unequal as erlang terms, so we need to carefully recurse
+-%% through aggregates (tuples and objects).
+-
+-equiv({Props1}, {Props2}) ->
+-    equiv_object(Props1, Props2);
+-equiv(L1, L2) when is_list(L1), is_list(L2) ->
+-    equiv_list(L1, L2);
+-equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
+-equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2;
+-equiv(true, true) -> true;
+-equiv(false, false) -> true;
+-equiv(null, null) -> true.
+-
+-%% Object representation and traversal order is unknown.
+-%% Use the sledgehammer and sort property lists.
+-
+-equiv_object(Props1, Props2) ->
+-    L1 = lists:keysort(1, Props1),
+-    L2 = lists:keysort(1, Props2),
+-    Pairs = lists:zip(L1, L2),
+-    true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
+-                             equiv(K1, K2) and equiv(V1, V2)
+-                     end, Pairs).
+-
+-%% Recursively compare tuple elements for equivalence.
+-
+-equiv_list([], []) ->
+-    true;
+-equiv_list([V1 | L1], [V2 | L2]) ->
+-    case equiv(V1, V2) of
+-        true ->
+-            equiv_list(L1, L2);
+-        false ->
+-            false
+-    end.
+-
+-test_all() ->
+-    [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>),
+-    <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]),
+-    test_one(e2j_test_vec(utf8), 1).
+-
+-test_one([], _N) ->
+-    %% io:format("~p tests passed~n", [N-1]),
+-    ok;
+-test_one([{E, J} | Rest], N) ->
+-    %% io:format("[~p] ~p ~p~n", [N, E, J]),
+-    true = equiv(E, decode(J)),
+-    true = equiv(E, decode(encode(E))),
+-    test_one(Rest, 1+N).
+-
+-e2j_test_vec(utf8) ->
+-    [
+-     {1, "1"},
+-     {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes
+-     {-1, "-1"},
+-     {-3.1416, "-3.14160"},
+-     {12.0e10, "1.20000e+11"},
+-     {1.234E+10, "1.23400e+10"},
+-     {-1.234E-10, "-1.23400e-10"},
+-     {10.0, "1.0e+01"},
+-     {123.456, "1.23456E+2"},
+-     {10.0, "1e1"},
+-     {<<"foo">>, "\"foo\""},
+-     {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""},
+-     {<<"">>, "\"\""},
+-     {<<"\n\n\n">>, "\"\\n\\n\\n\""},
+-     {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""},
+-     {obj_new(), "{}"},
+-     {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"},
+-     {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]),
+-      "{\"foo\":\"bar\",\"baz\":123}"},
+-     {[], "[]"},
+-     {[[]], "[[]]"},
+-     {[1, <<"foo">>], "[1,\"foo\"]"},
+-
+-     %% json array in a json object
+-     {obj_from_list([{<<"foo">>, [123]}]),
+-      "{\"foo\":[123]}"},
+-
+-     %% json object in a json object
+-     {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]),
+-      "{\"foo\":{\"bar\":true}}"},
+-
+-     %% fold evaluation order
+-     {obj_from_list([{<<"foo">>, []},
+-                     {<<"bar">>, obj_from_list([{<<"baz">>, true}])},
+-                     {<<"alice">>, <<"bob">>}]),
+-      "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
+-
+-     %% json object in a json array
+-     {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null],
+-      "[-123,\"foo\",{\"bar\":[]},null]"}
+-    ].
+diff --git a/src/mochiweb/mochinum.erl b/src/mochiweb/mochinum.erl
+deleted file mode 100644
+index 6a86604..0000000
+--- a/src/mochiweb/mochinum.erl
++++ /dev/null
+@@ -1,289 +0,0 @@
+-%% @copyright 2007 Mochi Media, Inc.
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-
+-%% @doc Useful numeric algorithms for floats that cover some deficiencies
+-%% in the math module. More interesting is digits/1, which implements
+-%% the algorithm from:
+-%% http://www.cs.indiana.edu/~burger/fp/index.html
+-%% See also "Printing Floating-Point Numbers Quickly and Accurately"
+-%% in Proceedings of the SIGPLAN '96 Conference on Programming Language
+-%% Design and Implementation.
+-
+--module(mochinum).
+--author("Bob Ippolito <bob at mochimedia.com>").
+--export([digits/1, frexp/1, int_pow/2, int_ceil/1, test/0]).
+-
+-%% IEEE 754 Float exponent bias
+--define(FLOAT_BIAS, 1022).
+--define(MIN_EXP, -1074).
+--define(BIG_POW, 4503599627370496).
+-
+-%% External API
+-
+-%% @spec digits(number()) -> string()
+-%% @doc  Returns a string that accurately represents the given integer or float
+-%%       using a conservative amount of digits. Great for generating
+-%%       human-readable output, or compact ASCII serializations for floats.
+-digits(N) when is_integer(N) ->
+-    integer_to_list(N);
+-digits(0.0) ->
+-    "0.0";
+-digits(Float) ->
+-    {Frac, Exp} = frexp(Float),
+-    Exp1 = Exp - 53,
+-    Frac1 = trunc(abs(Frac) * (1 bsl 53)),
+-    [Place | Digits] = digits1(Float, Exp1, Frac1),
+-    R = insert_decimal(Place, [$0 + D || D <- Digits]),
+-    case Float < 0 of
+-        true ->
+-            [$- | R];
+-        _ ->
+-            R
+-    end.
+-
+-%% @spec frexp(F::float()) -> {Frac::float(), Exp::float()}
+-%% @doc  Return the fractional and exponent part of an IEEE 754 double,
+-%%       equivalent to the libc function of the same name.
+-%%       F = Frac * pow(2, Exp).
+-frexp(F) ->
+-    frexp1(unpack(F)).
+-
+-%% @spec int_pow(X::integer(), N::integer()) -> Y::integer()
+-%% @doc  Moderately efficient way to exponentiate integers.
+-%%       int_pow(10, 2) = 100.
+-int_pow(_X, 0) ->
+-    1;
+-int_pow(X, N) when N > 0 ->
+-    int_pow(X, N, 1).
+-
+-%% @spec int_ceil(F::float()) -> integer()
+-%% @doc  Return the ceiling of F as an integer. The ceiling is defined as
+-%%       F when F == trunc(F);
+-%%       trunc(F) when F &lt; 0;
+-%%       trunc(F) + 1 when F &gt; 0.
+-int_ceil(X) ->
+-    T = trunc(X),
+-    case (X - T) of
+-        Neg when Neg < 0 -> T;
+-        Pos when Pos > 0 -> T + 1;
+-        _ -> T
+-    end.
+-
+-
+-%% Internal API
+-
+-int_pow(X, N, R) when N < 2 ->
+-    R * X;
+-int_pow(X, N, R) ->
+-    int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end).
+-
+-insert_decimal(0, S) ->
+-    "0." ++ S;
+-insert_decimal(Place, S) when Place > 0 ->
+-    L = length(S),
+-    case Place - L of
+-         0 ->
+-            S ++ ".0";
+-        N when N < 0 ->
+-            {S0, S1} = lists:split(L + N, S),
+-            S0 ++ "." ++ S1;
+-        N when N < 6 ->
+-            %% More places than digits
+-            S ++ lists:duplicate(N, $0) ++ ".0";
+-        _ ->
+-            insert_decimal_exp(Place, S)
+-    end;
+-insert_decimal(Place, S) when Place > -6 ->
+-    "0." ++ lists:duplicate(abs(Place), $0) ++ S;
+-insert_decimal(Place, S) ->
+-    insert_decimal_exp(Place, S).
+-
+-insert_decimal_exp(Place, S) ->
+-    [C | S0] = S,
+-    S1 = case S0 of
+-             [] ->
+-                 "0";
+-             _ ->
+-                 S0
+-         end,
+-    Exp = case Place < 0 of
+-              true ->
+-                  "e-";
+-              false ->
+-                  "e+"
+-          end,
+-    [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)).
+-
+-
+-digits1(Float, Exp, Frac) ->
+-    Round = ((Frac band 1) =:= 0),
+-    case Exp >= 0 of
+-        true ->
+-            BExp = 1 bsl Exp,
+-            case (Frac /= ?BIG_POW) of
+-                true ->
+-                    scale((Frac * BExp * 2), 2, BExp, BExp,
+-                          Round, Round, Float);
+-                false ->
+-                    scale((Frac * BExp * 4), 4, (BExp * 2), BExp,
+-                          Round, Round, Float)
+-            end;
+-        false ->
+-            case (Exp == ?MIN_EXP) orelse (Frac /= ?BIG_POW) of
+-                true ->
+-                    scale((Frac * 2), 1 bsl (1 - Exp), 1, 1,
+-                          Round, Round, Float);
+-                false ->
+-                    scale((Frac * 4), 1 bsl (2 - Exp), 2, 1,
+-                          Round, Round, Float)
+-            end
+-    end.
+-
+-scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) ->
+-    Est = int_ceil(math:log10(abs(Float)) - 1.0e-10),
+-    %% Note that the scheme implementation uses a 326 element look-up table
+-    %% for int_pow(10, N) where we do not.
+-    case Est >= 0 of
+-        true ->
+-            fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est,
+-                  LowOk, HighOk);
+-        false ->
+-            Scale = int_pow(10, -Est),
+-            fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est,
+-                  LowOk, HighOk)
+-    end.
+-
+-fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) ->
+-    TooLow = case HighOk of
+-                 true ->
+-                     (R + MPlus) >= S;
+-                 false ->
+-                     (R + MPlus) > S
+-             end,
+-    case TooLow of
+-        true ->
+-            [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)];
+-        false ->
+-            [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)]
+-    end.
+-
+-generate(R0, S, MPlus, MMinus, LowOk, HighOk) ->
+-    D = R0 div S,
+-    R = R0 rem S,
+-    TC1 = case LowOk of
+-              true ->
+-                  R =< MMinus;
+-              false ->
+-                  R < MMinus
+-          end,
+-    TC2 = case HighOk of
+-              true ->
+-                  (R + MPlus) >= S;
+-              false ->
+-                  (R + MPlus) > S
+-          end,
+-    case TC1 of
+-        false ->
+-            case TC2 of
+-                false ->
+-                    [D | generate(R * 10, S, MPlus * 10, MMinus * 10,
+-                                  LowOk, HighOk)];
+-                true ->
+-                    [D + 1]
+-            end;
+-        true ->
+-            case TC2 of
+-                false ->
+-                    [D];
+-                true ->
+-                    case R * 2 < S of
+-                        true ->
+-                            [D];
+-                        false ->
+-                            [D + 1]
+-                    end
+-            end
+-    end.
+-
+-unpack(Float) ->
+-    <<Sign:1, Exp:11, Frac:52>> = <<Float:64/float>>,
+-    {Sign, Exp, Frac}.
+-
+-frexp1({_Sign, 0, 0}) ->
+-    {0.0, 0};
+-frexp1({Sign, 0, Frac}) ->
+-    Exp = log2floor(Frac),
+-    <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, (Frac-1):52>>,
+-    {Frac1, -(?FLOAT_BIAS) - 52 + Exp};
+-frexp1({Sign, Exp, Frac}) ->
+-    <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, Frac:52>>,
+-    {Frac1, Exp - ?FLOAT_BIAS}.
+-
+-log2floor(Int) ->
+-    log2floor(Int, 0).
+-
+-log2floor(0, N) ->
+-    N;
+-log2floor(Int, N) ->
+-    log2floor(Int bsr 1, 1 + N).
+-
+-
+-test() ->
+-    ok = test_frexp(),
+-    ok = test_int_ceil(),
+-    ok = test_int_pow(),
+-    ok = test_digits(),
+-    ok.
+-
+-test_int_ceil() ->
+-    1 = int_ceil(0.0001),
+-    0 = int_ceil(0.0),
+-    1 = int_ceil(0.99),
+-    1 = int_ceil(1.0),
+-    -1 = int_ceil(-1.5),
+-    -2 = int_ceil(-2.0),
+-    ok.
+-
+-test_int_pow() ->
+-    1 = int_pow(1, 1),
+-    1 = int_pow(1, 0),
+-    1 = int_pow(10, 0),
+-    10 = int_pow(10, 1),
+-    100 = int_pow(10, 2),
+-    1000 = int_pow(10, 3),
+-    ok.
+-
+-test_digits() ->
+-    "0" = digits(0),
+-    "0.0" = digits(0.0),
+-    "1.0" = digits(1.0),
+-    "-1.0" = digits(-1.0),
+-    "0.1" = digits(0.1),
+-    "0.01" = digits(0.01),
+-    "0.001" = digits(0.001),
+-    ok.
+-
+-test_frexp() ->
+-    %% zero
+-    {0.0, 0} = frexp(0.0),
+-    %% one
+-    {0.5, 1} = frexp(1.0),
+-    %% negative one
+-    {-0.5, 1} = frexp(-1.0),
+-    %% small denormalized number
+-    %% 4.94065645841246544177e-324
+-    <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
+-    {0.5, -1073} = frexp(SmallDenorm),
+-    %% large denormalized number
+-    %% 2.22507385850720088902e-308
+-    <<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>,
+-    {0.99999999999999978, -1022} = frexp(BigDenorm),
+-    %% small normalized number
+-    %% 2.22507385850720138309e-308
+-    <<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>,
+-    {0.5, -1021} = frexp(SmallNorm),
+-    %% large normalized number
+-    %% 1.79769313486231570815e+308
+-    <<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>,
+-    {0.99999999999999989, 1024} = frexp(LargeNorm),
+-    ok.
+diff --git a/src/mochiweb/mochiweb.app.in b/src/mochiweb/mochiweb.app.in
+deleted file mode 100644
+index cd8dbb2..0000000
+--- a/src/mochiweb/mochiweb.app.in
++++ /dev/null
+@@ -1,32 +0,0 @@
+-{application, mochiweb,
+- [{description, "MochiMedia Web Server"},
+-  {vsn, "0.01"},
+-  {modules, [
+-        mochihex,
+-        mochijson,
+-        mochijson2,
+-        mochinum,
+-        mochiweb,
+-        mochiweb_app,
+-        mochiweb_charref,
+-        mochiweb_cookies,
+-        mochiweb_echo,
+-        mochiweb_headers,
+-        mochiweb_html,
+-        mochiweb_http,
+-        mochiweb_multipart,
+-        mochiweb_request,
+-        mochiweb_response,
+-        mochiweb_skel,
+-        mochiweb_socket_server,
+-        mochiweb_sup,
+-        mochiweb_util,
+-        reloader,
+-        mochifmt,
+-        mochifmt_std,
+-        mochifmt_records
+-	    ]},
+-  {registered, []},
+-  {mod, {mochiweb_app, []}},
+-  {env, []},
+-  {applications, [kernel, stdlib]}]}.
+diff --git a/src/mochiweb/mochiweb.erl b/src/mochiweb/mochiweb.erl
+deleted file mode 100644
+index 0f4d52a..0000000
+--- a/src/mochiweb/mochiweb.erl
++++ /dev/null
+@@ -1,110 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Start and stop the MochiWeb server.
+-
+--module(mochiweb).
+--author('bob at mochimedia.com').
+-
+--export([start/0, stop/0]).
+--export([new_request/1, new_response/1]).
+--export([all_loaded/0, all_loaded/1, reload/0]).
+--export([test/0]).
+-
+-%% @spec start() -> ok
+-%% @doc Start the MochiWeb server.
+-start() ->
+-    ensure_started(crypto),
+-    application:start(mochiweb).
+-
+-%% @spec stop() -> ok
+-%% @doc Stop the MochiWeb server.
+-stop() ->
+-    Res = application:stop(mochiweb),
+-    application:stop(crypto),
+-    Res.
+-
+-%% @spec test() -> ok
+-%% @doc Run all of the tests for MochiWeb.
+-test() ->
+-    mochiweb_util:test(),
+-    mochiweb_headers:test(),
+-    mochiweb_cookies:test(),
+-    mochihex:test(),
+-    mochinum:test(),
+-    mochijson:test(),
+-    mochiweb_charref:test(),
+-    mochiweb_html:test(),
+-    mochifmt:test(),
+-    test_request(),
+-    ok.
+-
+-reload() ->
+-    [c:l(Module) || Module <- all_loaded()].
+-
+-all_loaded() ->
+-    all_loaded(filename:dirname(code:which(?MODULE))).
+-
+-all_loaded(Base) when is_atom(Base) ->
+-    [];
+-all_loaded(Base) ->
+-    FullBase = Base ++ "/",
+-    F = fun ({_Module, Loaded}, Acc) when is_atom(Loaded) ->
+-                Acc;
+-            ({Module, Loaded}, Acc) ->
+-                case lists:prefix(FullBase, Loaded) of
+-                    true ->
+-                        [Module | Acc];
+-                    false ->
+-                        Acc
+-                end
+-        end,
+-    lists:foldl(F, [], code:all_loaded()).
+-
+-
+-%% @spec new_request({Socket, Request, Headers}) -> MochiWebRequest
+-%% @doc Return a mochiweb_request data structure.
+-new_request({Socket, {Method, {abs_path, Uri}, Version}, Headers}) ->
+-    mochiweb_request:new(Socket,
+-                         Method,
+-                         Uri,
+-                         Version,
+-                         mochiweb_headers:make(Headers));
+-% this case probably doesn't "exist".
+-new_request({Socket, {Method, {absoluteURI, _Protocol, _Host, _Port, Uri},
+-                      Version}, Headers}) ->
+-    mochiweb_request:new(Socket,
+-                         Method,
+-                         Uri,
+-                         Version,
+-                         mochiweb_headers:make(Headers));
+-%% Request-URI is "*"
+-%% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
+-new_request({Socket, {Method, '*'=Uri, Version}, Headers}) ->
+-    mochiweb_request:new(Socket,
+-                         Method,
+-                         Uri,
+-                         Version,
+-                         mochiweb_headers:make(Headers)).
+-
+-%% @spec new_response({Request, integer(), Headers}) -> MochiWebResponse
+-%% @doc Return a mochiweb_response data structure.
+-new_response({Request, Code, Headers}) ->
+-    mochiweb_response:new(Request,
+-                          Code,
+-                          mochiweb_headers:make(Headers)).
+-
+-%% Internal API
+-
+-test_request() ->
+-    R = mochiweb_request:new(z, z, "/foo/bar/baz%20wibble+quux?qs=2", z, []),
+-    "/foo/bar/baz wibble quux" = R:get(path),
+-    ok.
+-
+-ensure_started(App) ->
+-    case application:start(App) of
+-        ok ->
+-            ok;
+-        {error, {already_started, App}} ->
+-            ok
+-    end.
+diff --git a/src/mochiweb/mochiweb_app.erl b/src/mochiweb/mochiweb_app.erl
+deleted file mode 100644
+index 2b437f6..0000000
+--- a/src/mochiweb/mochiweb_app.erl
++++ /dev/null
+@@ -1,20 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Callbacks for the mochiweb application.
+-
+--module(mochiweb_app).
+--author('bob at mochimedia.com').
+-
+--behaviour(application).
+--export([start/2,stop/1]).
+-
+-%% @spec start(_Type, _StartArgs) -> ServerRet
+-%% @doc application start callback for mochiweb.
+-start(_Type, _StartArgs) ->
+-    mochiweb_sup:start_link().
+-
+-%% @spec stop(_State) -> ServerRet
+-%% @doc application stop callback for mochiweb.
+-stop(_State) ->
+-    ok.
+diff --git a/src/mochiweb/mochiweb_charref.erl b/src/mochiweb/mochiweb_charref.erl
+deleted file mode 100644
+index d037d2f..0000000
+--- a/src/mochiweb/mochiweb_charref.erl
++++ /dev/null
+@@ -1,295 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Converts HTML 4 charrefs and entities to codepoints.
+--module(mochiweb_charref).
+--export([charref/1, test/0]).
+-
+-%% External API.
+-
+-%% @spec charref(S) -> integer() | undefined
+-%% @doc Convert a decimal charref, hex charref, or html entity to a unicode
+-%%      codepoint, or return undefined on failure.
+-%%      The input should not include an ampersand or semicolon.
+-%%      charref("#38") = 38, charref("#x26") = 38, charref("amp") = 38.
+-charref(B) when is_binary(B) ->
+-    charref(binary_to_list(B));
+-charref([$#, C | L]) when C =:= $x orelse C =:= $X ->
+-    try erlang:list_to_integer(L, 16)
+-    catch
+-        error:badarg -> undefined
+-    end;
+-charref([$# | L]) ->
+-    try list_to_integer(L)
+-    catch
+-        error:badarg -> undefined
+-    end;
+-charref(L) ->
+-    entity(L).
+-
+-%% @spec test() -> ok
+-%% @doc Run tests for mochiweb_charref.
+-test() ->
+-    1234 = charref("#1234"),
+-    255 = charref("#xfF"),
+-    255 = charref("#XFf"),
+-    38 = charref("amp"),
+-    undefined = charref("not_an_entity"),
+-    ok.
+-
+-%% Internal API.
+-
+-entity("nbsp") -> 160;
+-entity("iexcl") -> 161;
+-entity("cent") -> 162;
+-entity("pound") -> 163;
+-entity("curren") -> 164;
+-entity("yen") -> 165;
+-entity("brvbar") -> 166;
+-entity("sect") -> 167;
+-entity("uml") -> 168;
+-entity("copy") -> 169;
+-entity("ordf") -> 170;
+-entity("laquo") -> 171;
+-entity("not") -> 172;
+-entity("shy") -> 173;
+-entity("reg") -> 174;
+-entity("macr") -> 175;
+-entity("deg") -> 176;
+-entity("plusmn") -> 177;
+-entity("sup2") -> 178;
+-entity("sup3") -> 179;
+-entity("acute") -> 180;
+-entity("micro") -> 181;
+-entity("para") -> 182;
+-entity("middot") -> 183;
+-entity("cedil") -> 184;
+-entity("sup1") -> 185;
+-entity("ordm") -> 186;
+-entity("raquo") -> 187;
+-entity("frac14") -> 188;
+-entity("frac12") -> 189;
+-entity("frac34") -> 190;
+-entity("iquest") -> 191;
+-entity("Agrave") -> 192;
+-entity("Aacute") -> 193;
+-entity("Acirc") -> 194;
+-entity("Atilde") -> 195;
+-entity("Auml") -> 196;
+-entity("Aring") -> 197;
+-entity("AElig") -> 198;
+-entity("Ccedil") -> 199;
+-entity("Egrave") -> 200;
+-entity("Eacute") -> 201;
+-entity("Ecirc") -> 202;
+-entity("Euml") -> 203;
+-entity("Igrave") -> 204;
+-entity("Iacute") -> 205;
+-entity("Icirc") -> 206;
+-entity("Iuml") -> 207;
+-entity("ETH") -> 208;
+-entity("Ntilde") -> 209;
+-entity("Ograve") -> 210;
+-entity("Oacute") -> 211;
+-entity("Ocirc") -> 212;
+-entity("Otilde") -> 213;
+-entity("Ouml") -> 214;
+-entity("times") -> 215;
+-entity("Oslash") -> 216;
+-entity("Ugrave") -> 217;
+-entity("Uacute") -> 218;
+-entity("Ucirc") -> 219;
+-entity("Uuml") -> 220;
+-entity("Yacute") -> 221;
+-entity("THORN") -> 222;
+-entity("szlig") -> 223;
+-entity("agrave") -> 224;
+-entity("aacute") -> 225;
+-entity("acirc") -> 226;
+-entity("atilde") -> 227;
+-entity("auml") -> 228;
+-entity("aring") -> 229;
+-entity("aelig") -> 230;
+-entity("ccedil") -> 231;
+-entity("egrave") -> 232;
+-entity("eacute") -> 233;
+-entity("ecirc") -> 234;
+-entity("euml") -> 235;
+-entity("igrave") -> 236;
+-entity("iacute") -> 237;
+-entity("icirc") -> 238;
+-entity("iuml") -> 239;
+-entity("eth") -> 240;
+-entity("ntilde") -> 241;
+-entity("ograve") -> 242;
+-entity("oacute") -> 243;
+-entity("ocirc") -> 244;
+-entity("otilde") -> 245;
+-entity("ouml") -> 246;
+-entity("divide") -> 247;
+-entity("oslash") -> 248;
+-entity("ugrave") -> 249;
+-entity("uacute") -> 250;
+-entity("ucirc") -> 251;
+-entity("uuml") -> 252;
+-entity("yacute") -> 253;
+-entity("thorn") -> 254;
+-entity("yuml") -> 255;
+-entity("fnof") -> 402;
+-entity("Alpha") -> 913;
+-entity("Beta") -> 914;
+-entity("Gamma") -> 915;
+-entity("Delta") -> 916;
+-entity("Epsilon") -> 917;
+-entity("Zeta") -> 918;
+-entity("Eta") -> 919;
+-entity("Theta") -> 920;
+-entity("Iota") -> 921;
+-entity("Kappa") -> 922;
+-entity("Lambda") -> 923;
+-entity("Mu") -> 924;
+-entity("Nu") -> 925;
+-entity("Xi") -> 926;
+-entity("Omicron") -> 927;
+-entity("Pi") -> 928;
+-entity("Rho") -> 929;
+-entity("Sigma") -> 931;
+-entity("Tau") -> 932;
+-entity("Upsilon") -> 933;
+-entity("Phi") -> 934;
+-entity("Chi") -> 935;
+-entity("Psi") -> 936;
+-entity("Omega") -> 937;
+-entity("alpha") -> 945;
+-entity("beta") -> 946;
+-entity("gamma") -> 947;
+-entity("delta") -> 948;
+-entity("epsilon") -> 949;
+-entity("zeta") -> 950;
+-entity("eta") -> 951;
+-entity("theta") -> 952;
+-entity("iota") -> 953;
+-entity("kappa") -> 954;
+-entity("lambda") -> 955;
+-entity("mu") -> 956;
+-entity("nu") -> 957;
+-entity("xi") -> 958;
+-entity("omicron") -> 959;
+-entity("pi") -> 960;
+-entity("rho") -> 961;
+-entity("sigmaf") -> 962;
+-entity("sigma") -> 963;
+-entity("tau") -> 964;
+-entity("upsilon") -> 965;
+-entity("phi") -> 966;
+-entity("chi") -> 967;
+-entity("psi") -> 968;
+-entity("omega") -> 969;
+-entity("thetasym") -> 977;
+-entity("upsih") -> 978;
+-entity("piv") -> 982;
+-entity("bull") -> 8226;
+-entity("hellip") -> 8230;
+-entity("prime") -> 8242;
+-entity("Prime") -> 8243;
+-entity("oline") -> 8254;
+-entity("frasl") -> 8260;
+-entity("weierp") -> 8472;
+-entity("image") -> 8465;
+-entity("real") -> 8476;
+-entity("trade") -> 8482;
+-entity("alefsym") -> 8501;
+-entity("larr") -> 8592;
+-entity("uarr") -> 8593;
+-entity("rarr") -> 8594;
+-entity("darr") -> 8595;
+-entity("harr") -> 8596;
+-entity("crarr") -> 8629;
+-entity("lArr") -> 8656;
+-entity("uArr") -> 8657;
+-entity("rArr") -> 8658;
+-entity("dArr") -> 8659;
+-entity("hArr") -> 8660;
+-entity("forall") -> 8704;
+-entity("part") -> 8706;
+-entity("exist") -> 8707;
+-entity("empty") -> 8709;
+-entity("nabla") -> 8711;
+-entity("isin") -> 8712;
+-entity("notin") -> 8713;
+-entity("ni") -> 8715;
+-entity("prod") -> 8719;
+-entity("sum") -> 8721;
+-entity("minus") -> 8722;
+-entity("lowast") -> 8727;
+-entity("radic") -> 8730;
+-entity("prop") -> 8733;
+-entity("infin") -> 8734;
+-entity("ang") -> 8736;
+-entity("and") -> 8743;
+-entity("or") -> 8744;
+-entity("cap") -> 8745;
+-entity("cup") -> 8746;
+-entity("int") -> 8747;
+-entity("there4") -> 8756;
+-entity("sim") -> 8764;
+-entity("cong") -> 8773;
+-entity("asymp") -> 8776;
+-entity("ne") -> 8800;
+-entity("equiv") -> 8801;
+-entity("le") -> 8804;
+-entity("ge") -> 8805;
+-entity("sub") -> 8834;
+-entity("sup") -> 8835;
+-entity("nsub") -> 8836;
+-entity("sube") -> 8838;
+-entity("supe") -> 8839;
+-entity("oplus") -> 8853;
+-entity("otimes") -> 8855;
+-entity("perp") -> 8869;
+-entity("sdot") -> 8901;
+-entity("lceil") -> 8968;
+-entity("rceil") -> 8969;
+-entity("lfloor") -> 8970;
+-entity("rfloor") -> 8971;
+-entity("lang") -> 9001;
+-entity("rang") -> 9002;
+-entity("loz") -> 9674;
+-entity("spades") -> 9824;
+-entity("clubs") -> 9827;
+-entity("hearts") -> 9829;
+-entity("diams") -> 9830;
+-entity("quot") -> 34;
+-entity("amp") -> 38;
+-entity("lt") -> 60;
+-entity("gt") -> 62;
+-entity("OElig") -> 338;
+-entity("oelig") -> 339;
+-entity("Scaron") -> 352;
+-entity("scaron") -> 353;
+-entity("Yuml") -> 376;
+-entity("circ") -> 710;
+-entity("tilde") -> 732;
+-entity("ensp") -> 8194;
+-entity("emsp") -> 8195;
+-entity("thinsp") -> 8201;
+-entity("zwnj") -> 8204;
+-entity("zwj") -> 8205;
+-entity("lrm") -> 8206;
+-entity("rlm") -> 8207;
+-entity("ndash") -> 8211;
+-entity("mdash") -> 8212;
+-entity("lsquo") -> 8216;
+-entity("rsquo") -> 8217;
+-entity("sbquo") -> 8218;
+-entity("ldquo") -> 8220;
+-entity("rdquo") -> 8221;
+-entity("bdquo") -> 8222;
+-entity("dagger") -> 8224;
+-entity("Dagger") -> 8225;
+-entity("permil") -> 8240;
+-entity("lsaquo") -> 8249;
+-entity("rsaquo") -> 8250;
+-entity("euro") -> 8364;
+-entity(_) -> undefined.
+-
+diff --git a/src/mochiweb/mochiweb_cookies.erl b/src/mochiweb/mochiweb_cookies.erl
+deleted file mode 100644
+index 61711ff..0000000
+--- a/src/mochiweb/mochiweb_cookies.erl
++++ /dev/null
+@@ -1,257 +0,0 @@
+-%% @author Emad El-Haraty <emad at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc HTTP Cookie parsing and generating (RFC 2109, RFC 2965).
+-
+--module(mochiweb_cookies).
+--export([parse_cookie/1, cookie/3, cookie/2, test/0]).
+-
+--define(QUOTE, $\").
+-
+--define(IS_WHITESPACE(C),
+-        (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
+-
+-%% RFC 2616 separators (called tspecials in RFC 2068)
+--define(IS_SEPARATOR(C),
+-        (C < 32 orelse
+-         C =:= $\s orelse C =:= $\t orelse
+-         C =:= $( orelse C =:= $) orelse C =:= $< orelse C =:= $> orelse
+-         C =:= $@ orelse C =:= $, orelse C =:= $; orelse C =:= $: orelse
+-         C =:= $\\ orelse C =:= $\" orelse C =:= $/ orelse
+-         C =:= $[ orelse C =:= $] orelse C =:= $? orelse C =:= $= orelse
+-         C =:= ${ orelse C =:= $})).
+-
+-%% @type proplist() = [{Key::string(), Value::string()}].
+-%% @type header() = {Name::string(), Value::string()}.
+-
+-%% @spec cookie(Key::string(), Value::string()) -> header()
+-%% @doc Short-hand for <code>cookie(Key, Value, [])</code>.
+-cookie(Key, Value) ->
+-    cookie(Key, Value, []).
+-
+-%% @spec cookie(Key::string(), Value::string(), Options::[Option]) -> header()
+-%% where Option = {max_age, integer()} | {local_time, {date(), time()}}
+-%%                | {domain, string()} | {path, string()}
+-%%                | {secure, true | false} | {http_only, true | false}
+-%%
+-%% @doc Generate a Set-Cookie header field tuple.
+-cookie(Key, Value, Options) ->
+-    Cookie = [any_to_list(Key), "=", quote(Value), "; Version=1"],
+-    %% Set-Cookie:
+-    %%    Comment, Domain, Max-Age, Path, Secure, Version
+-    %% Set-Cookie2:
+-    %%    Comment, CommentURL, Discard, Domain, Max-Age, Path, Port, Secure,
+-    %%    Version
+-    ExpiresPart =
+-        case proplists:get_value(max_age, Options) of
+-            undefined ->
+-                "";
+-            RawAge ->
+-                When = case proplists:get_value(local_time, Options) of
+-                           undefined ->
+-                               calendar:local_time();
+-                           LocalTime ->
+-                               LocalTime
+-                       end,
+-                Age = case RawAge < 0 of
+-                          true ->
+-                              0;
+-                          false ->
+-                              RawAge
+-                      end,
+-                ["; Expires=", age_to_cookie_date(Age, When),
+-                 "; Max-Age=", quote(Age)]
+-        end,
+-    SecurePart =
+-        case proplists:get_value(secure, Options) of
+-            true ->
+-                "; Secure";
+-            _ ->
+-                ""
+-        end,
+-    DomainPart =
+-        case proplists:get_value(domain, Options) of
+-            undefined ->
+-                "";
+-            Domain ->
+-                ["; Domain=", quote(Domain)]
+-        end,
+-    PathPart =
+-        case proplists:get_value(path, Options) of
+-            undefined ->
+-                "";
+-            Path ->
+-                ["; Path=", quote(Path)]
+-        end,
+-    HttpOnlyPart =
+-        case proplists:get_value(http_only, Options) of
+-            true ->
+-                "; HttpOnly";
+-            _ ->
+-                ""
+-        end,
+-    CookieParts = [Cookie, ExpiresPart, SecurePart, DomainPart, PathPart, HttpOnlyPart],
+-    {"Set-Cookie", lists:flatten(CookieParts)}.
+-
+-
+-%% Every major browser incorrectly handles quoted strings in a
+-%% different and (worse) incompatible manner.  Instead of wasting time
+-%% writing redundant code for each browser, we restrict cookies to
+-%% only contain characters that browsers handle compatibly.
+-%%
+-%% By replacing the definition of quote with this, we generate
+-%% RFC-compliant cookies:
+-%%
+-%%     quote(V) ->
+-%%         Fun = fun(?QUOTE, Acc) -> [$\\, ?QUOTE | Acc];
+-%%                  (Ch, Acc) -> [Ch | Acc]
+-%%               end,
+-%%         [?QUOTE | lists:foldr(Fun, [?QUOTE], V)].
+-
+-%% Convert to a string and raise an error if quoting is required.
+-quote(V0) ->
+-    V = any_to_list(V0),
+-    lists:all(fun(Ch) -> Ch =:= $/ orelse not ?IS_SEPARATOR(Ch) end, V)
+-        orelse erlang:error({cookie_quoting_required, V}),
+-    V.
+-
+-add_seconds(Secs, LocalTime) ->
+-    Greg = calendar:datetime_to_gregorian_seconds(LocalTime),
+-    calendar:gregorian_seconds_to_datetime(Greg + Secs).
+-
+-age_to_cookie_date(Age, LocalTime) ->
+-    httpd_util:rfc1123_date(add_seconds(Age, LocalTime)).
+-
+-%% @spec parse_cookie(string()) -> [{K::string(), V::string()}]
+-%% @doc Parse the contents of a Cookie header field, ignoring cookie
+-%% attributes, and return a simple property list.
+-parse_cookie("") ->
+-    [];
+-parse_cookie(Cookie) ->
+-    parse_cookie(Cookie, []).
+-
+-%% @spec test() -> ok
+-%% @doc Run tests for mochiweb_cookies.
+-test() ->
+-    parse_cookie_test(),
+-    cookie_test(),
+-    ok.
+-
+-%% Internal API
+-
+-parse_cookie([], Acc) ->
+-    lists:reverse(Acc);
+-parse_cookie(String, Acc) ->
+-    {{Token, Value}, Rest} = read_pair(String),
+-    Acc1 = case Token of
+-               "" ->
+-                   Acc;
+-               "$" ++ _ ->
+-                   Acc;
+-               _ ->
+-                   [{Token, Value} | Acc]
+-           end,
+-    parse_cookie(Rest, Acc1).
+-
+-read_pair(String) ->
+-    {Token, Rest} = read_token(skip_whitespace(String)),
+-    {Value, Rest1} = read_value(skip_whitespace(Rest)),
+-    {{Token, Value}, skip_past_separator(Rest1)}.
+-
+-read_value([$= | Value]) ->
+-    Value1 = skip_whitespace(Value),
+-    case Value1 of
+-        [?QUOTE | _] ->
+-            read_quoted(Value1);
+-        _ ->
+-            read_token(Value1)
+-    end;
+-read_value(String) ->
+-    {"", String}.
+-
+-read_quoted([?QUOTE | String]) ->
+-    read_quoted(String, []).
+-
+-read_quoted([], Acc) ->
+-    {lists:reverse(Acc), []};
+-read_quoted([?QUOTE | Rest], Acc) ->
+-    {lists:reverse(Acc), Rest};
+-read_quoted([$\\, Any | Rest], Acc) ->
+-    read_quoted(Rest, [Any | Acc]);
+-read_quoted([C | Rest], Acc) ->
+-    read_quoted(Rest, [C | Acc]).
+-
+-skip_whitespace(String) ->
+-    F = fun (C) -> ?IS_WHITESPACE(C) end,
+-    lists:dropwhile(F, String).
+-
+-read_token(String) ->
+-    F = fun (C) -> not ?IS_SEPARATOR(C) end,
+-    lists:splitwith(F, String).
+-
+-skip_past_separator([]) ->
+-    [];
+-skip_past_separator([$; | Rest]) ->
+-    Rest;
+-skip_past_separator([$, | Rest]) ->
+-    Rest;
+-skip_past_separator([_ | Rest]) ->
+-    skip_past_separator(Rest).
+-
+-parse_cookie_test() ->
+-    %% RFC example
+-    C1 = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\";
+-    Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\";
+-    Shipping=\"FedEx\"; $Path=\"/acme\"",
+-    [
+-     {"Customer","WILE_E_COYOTE"},
+-     {"Part_Number","Rocket_Launcher_0001"},
+-     {"Shipping","FedEx"}
+-    ] = parse_cookie(C1),
+-    %% Potential edge cases
+-    [{"foo", "x"}] = parse_cookie("foo=\"\\x\""),
+-    [] = parse_cookie("="),
+-    [{"foo", ""}, {"bar", ""}] = parse_cookie("  foo ; bar  "),
+-    [{"foo", ""}, {"bar", ""}] = parse_cookie("foo=;bar="),
+-    [{"foo", "\";"}, {"bar", ""}] = parse_cookie("foo = \"\\\";\";bar "),
+-    [{"foo", "\";bar"}] = parse_cookie("foo=\"\\\";bar").
+-
+-any_to_list(V) when is_list(V) ->
+-    V;
+-any_to_list(V) when is_atom(V) ->
+-    atom_to_list(V);
+-any_to_list(V) when is_binary(V) ->
+-    binary_to_list(V);
+-any_to_list(V) when is_integer(V) ->
+-    integer_to_list(V).
+-
+-
+-cookie_test() ->
+-    C1 = {"Set-Cookie",
+-          "Customer=WILE_E_COYOTE; "
+-          "Version=1; "
+-          "Path=/acme"},
+-    C1 = cookie("Customer", "WILE_E_COYOTE", [{path, "/acme"}]),
+-    C1 = cookie("Customer", "WILE_E_COYOTE",
+-                [{path, "/acme"}, {badoption, "negatory"}]),
+-    C1 = cookie('Customer', 'WILE_E_COYOTE', [{path, '/acme'}]),
+-    C1 = cookie(<<"Customer">>, <<"WILE_E_COYOTE">>, [{path, <<"/acme">>}]),
+-
+-    {"Set-Cookie","=NoKey; Version=1"} = cookie("", "NoKey", []),
+-
+-        LocalTime = calendar:universal_time_to_local_time({{2007, 5, 15}, {13, 45, 33}}),
+-    C2 = {"Set-Cookie",
+-          "Customer=WILE_E_COYOTE; "
+-          "Version=1; "
+-          "Expires=Tue, 15 May 2007 13:45:33 GMT; "
+-          "Max-Age=0"},
+-    C2 = cookie("Customer", "WILE_E_COYOTE",
+-                [{max_age, -111}, {local_time, LocalTime}]),
+-    C3 = {"Set-Cookie",
+-          "Customer=WILE_E_COYOTE; "
+-          "Version=1; "
+-          "Expires=Wed, 16 May 2007 13:45:50 GMT; "
+-          "Max-Age=86417"},
+-    C3 = cookie("Customer", "WILE_E_COYOTE",
+-                [{max_age, 86417}, {local_time, LocalTime}]),
+-    ok.
+diff --git a/src/mochiweb/mochiweb_echo.erl b/src/mochiweb/mochiweb_echo.erl
+deleted file mode 100644
+index f32d680..0000000
+--- a/src/mochiweb/mochiweb_echo.erl
++++ /dev/null
+@@ -1,31 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Simple and stupid echo server to demo mochiweb_socket_server.
+-
+--module(mochiweb_echo).
+--author('bob at mochimedia.com').
+--export([start/0, stop/0, loop/1]).
+-
+-stop() ->
+-    mochiweb_socket_server:stop(?MODULE).
+-
+-start() ->
+-    mochiweb_socket_server:start([{name, ?MODULE},
+-                                  {port, 6789},
+-                                  {ip, "127.0.0.1"},
+-                                  {max, 1},
+-                                  {loop, {?MODULE, loop}}]).
+-
+-loop(Socket) ->
+-    case gen_tcp:recv(Socket, 0, 30000) of
+-        {ok, Data} ->
+-            case gen_tcp:send(Socket, Data) of
+-                ok ->
+-                    loop(Socket);
+-                _ ->
+-                    exit(normal)
+-            end;
+-        _Other ->
+-            exit(normal)
+-    end.
+diff --git a/src/mochiweb/mochiweb_headers.erl b/src/mochiweb/mochiweb_headers.erl
+deleted file mode 100644
+index 6fcec7c..0000000
+--- a/src/mochiweb/mochiweb_headers.erl
++++ /dev/null
+@@ -1,186 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Case preserving (but case insensitive) HTTP Header dictionary.
+-
+--module(mochiweb_headers).
+--author('bob at mochimedia.com').
+--export([empty/0, from_list/1, insert/3, enter/3, get_value/2, lookup/2]).
+--export([delete_any/2, get_primary_value/2]).
+--export([default/3, enter_from_list/2, default_from_list/2]).
+--export([to_list/1, make/1]).
+--export([test/0]).
+-
+-%% @type headers().
+-%% @type key() = atom() | binary() | string().
+-%% @type value() = atom() | binary() | string() | integer().
+-
+-%% @spec test() -> ok
+-%% @doc Run tests for this module.
+-test() ->
+-    H = ?MODULE:make([{hdr, foo}, {"Hdr", "bar"}, {'Hdr', 2}]),
+-    [{hdr, "foo, bar, 2"}] = ?MODULE:to_list(H),
+-    H1 = ?MODULE:insert(taco, grande, H),
+-    [{hdr, "foo, bar, 2"}, {taco, "grande"}] = ?MODULE:to_list(H1),
+-    H2 = ?MODULE:make([{"Set-Cookie", "foo"}]),
+-    [{"Set-Cookie", "foo"}] = ?MODULE:to_list(H2),
+-    H3 = ?MODULE:insert("Set-Cookie", "bar", H2),
+-    [{"Set-Cookie", "foo"}, {"Set-Cookie", "bar"}] = ?MODULE:to_list(H3),
+-    "foo, bar" = ?MODULE:get_value("set-cookie", H3),
+-    {value, {"Set-Cookie", "foo, bar"}} = ?MODULE:lookup("set-cookie", H3),
+-    undefined = ?MODULE:get_value("shibby", H3),
+-    none = ?MODULE:lookup("shibby", H3),
+-    H4 = ?MODULE:insert("content-type",
+-                        "application/x-www-form-urlencoded; charset=utf8",
+-                        H3),
+-    "application/x-www-form-urlencoded" = ?MODULE:get_primary_value(
+-                                             "content-type", H4),
+-    H4 = ?MODULE:delete_any("nonexistent-header", H4),
+-    H3 = ?MODULE:delete_any("content-type", H4),
+-    ok.
+-
+-%% @spec empty() -> headers()
+-%% @doc Create an empty headers structure.
+-empty() ->
+-    gb_trees:empty().
+-
+-%% @spec make(headers() | [{key(), value()}]) -> headers()
+-%% @doc Construct a headers() from the given list.
+-make(L) when is_list(L) ->
+-    from_list(L);
+-%% assume a tuple is already mochiweb_headers.
+-make(T) when is_tuple(T) ->
+-    T.
+-
+-%% @spec from_list([{key(), value()}]) -> headers()
+-%% @doc Construct a headers() from the given list.
+-from_list(List) ->
+-    lists:foldl(fun ({K, V}, T) -> insert(K, V, T) end, empty(), List).
+-
+-%% @spec enter_from_list([{key(), value()}], headers()) -> headers()
+-%% @doc Insert pairs into the headers, replace any values for existing keys.
+-enter_from_list(List, T) ->
+-    lists:foldl(fun ({K, V}, T1) -> enter(K, V, T1) end, T, List).
+-
+-%% @spec default_from_list([{key(), value()}], headers()) -> headers()
+-%% @doc Insert pairs into the headers for keys that do not already exist.
+-default_from_list(List, T) ->
+-    lists:foldl(fun ({K, V}, T1) -> default(K, V, T1) end, T, List).
+-
+-%% @spec to_list(headers()) -> [{key(), string()}]
+-%% @doc Return the contents of the headers. The keys will be the exact key
+-%%      that was first inserted (e.g. may be an atom or binary, case is
+-%%      preserved).
+-to_list(T) ->
+-    F = fun ({K, {array, L}}, Acc) ->
+-                L1 = lists:reverse(L),
+-                lists:foldl(fun (V, Acc1) -> [{K, V} | Acc1] end, Acc, L1);
+-            (Pair, Acc) ->
+-                [Pair | Acc]
+-        end,
+-    lists:reverse(lists:foldl(F, [], gb_trees:values(T))).
+-
+-%% @spec get_value(key(), headers()) -> string() | undefined
+-%% @doc Return the value of the given header using a case insensitive search.
+-%%      undefined will be returned for keys that are not present.
+-get_value(K, T) ->
+-    case lookup(K, T) of
+-        {value, {_, V}} ->
+-            expand(V);
+-        none ->
+-            undefined
+-    end.
+-
+-%% @spec get_primary_value(key(), headers()) -> string() | undefined
+-%% @doc Return the value of the given header up to the first semicolon using
+-%%      a case insensitive search. undefined will be returned for keys
+-%%      that are not present.
+-get_primary_value(K, T) ->
+-    case get_value(K, T) of
+-        undefined ->
+-            undefined;
+-        V ->
+-            lists:takewhile(fun (C) -> C =/= $; end, V)
+-    end.
+-
+-%% @spec lookup(key(), headers()) -> {value, {key(), string()}} | none
+-%% @doc Return the case preserved key and value for the given header using
+-%%      a case insensitive search. none will be returned for keys that are
+-%%      not present.
+-lookup(K, T) ->
+-    case gb_trees:lookup(normalize(K), T) of
+-        {value, {K0, V}} ->
+-            {value, {K0, expand(V)}};
+-        none ->
+-            none
+-    end.
+-
+-%% @spec default(key(), value(), headers()) -> headers()
+-%% @doc Insert the pair into the headers if it does not already exist.
+-default(K, V, T) ->
+-    K1 = normalize(K),
+-    V1 = any_to_list(V),
+-    try gb_trees:insert(K1, {K, V1}, T)
+-    catch
+-        error:{key_exists, _} ->
+-            T
+-    end.
+-
+-%% @spec enter(key(), value(), headers()) -> headers()
+-%% @doc Insert the pair into the headers, replacing any pre-existing key.
+-enter(K, V, T) ->
+-    K1 = normalize(K),
+-    V1 = any_to_list(V),
+-    gb_trees:enter(K1, {K, V1}, T).
+-
+-%% @spec insert(key(), value(), headers()) -> headers()
+-%% @doc Insert the pair into the headers, merging with any pre-existing key.
+-%%      A merge is done with Value = V0 ++ ", " ++ V1.
+-insert(K, V, T) ->
+-    K1 = normalize(K),
+-    V1 = any_to_list(V),
+-    try gb_trees:insert(K1, {K, V1}, T)
+-    catch
+-        error:{key_exists, _} ->
+-            {K0, V0} = gb_trees:get(K1, T),
+-            V2 = merge(K1, V1, V0),
+-            gb_trees:update(K1, {K0, V2}, T)
+-    end.
+-
+-%% @spec delete_any(key(), headers()) -> headers()
+-%% @doc Delete the header corresponding to key if it is present.
+-delete_any(K, T) ->
+-    K1 = normalize(K),
+-    gb_trees:delete_any(K1, T).
+-
+-%% Internal API
+-
+-expand({array, L}) ->
+-    mochiweb_util:join(lists:reverse(L), ", ");
+-expand(V) ->
+-    V.
+-
+-merge("set-cookie", V1, {array, L}) ->
+-    {array, [V1 | L]};
+-merge("set-cookie", V1, V0) ->
+-    {array, [V1, V0]};
+-merge(_, V1, V0) ->
+-    V0 ++ ", " ++ V1.
+-
+-normalize(K) when is_list(K) ->
+-    string:to_lower(K);
+-normalize(K) when is_atom(K) ->
+-    normalize(atom_to_list(K));
+-normalize(K) when is_binary(K) ->
+-    normalize(binary_to_list(K)).
+-
+-any_to_list(V) when is_list(V) ->
+-    V;
+-any_to_list(V) when is_atom(V) ->
+-    atom_to_list(V);
+-any_to_list(V) when is_binary(V) ->
+-    binary_to_list(V);
+-any_to_list(V) when is_integer(V) ->
+-    integer_to_list(V).
+-
+-
+diff --git a/src/mochiweb/mochiweb_html.erl b/src/mochiweb/mochiweb_html.erl
+deleted file mode 100644
+index 0e030c1..0000000
+--- a/src/mochiweb/mochiweb_html.erl
++++ /dev/null
+@@ -1,880 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Loosely tokenizes and generates parse trees for HTML 4.
+--module(mochiweb_html).
+--export([tokens/1, parse/1, parse_tokens/1, to_tokens/1, escape/1,
+-         escape_attr/1, to_html/1, test/0]).
+-
+-% This is a macro to placate syntax highlighters..
+--define(QUOTE, $\").
+--define(SQUOTE, $\').
+--define(ADV_COL(S, N),
+-        S#decoder{column=N+S#decoder.column,
+-                  offset=N+S#decoder.offset}).
+--define(INC_COL(S),
+-        S#decoder{column=1+S#decoder.column,
+-                  offset=1+S#decoder.offset}).
+--define(INC_LINE(S),
+-        S#decoder{column=1,
+-                  line=1+S#decoder.line,
+-                  offset=1+S#decoder.offset}).
+--define(INC_CHAR(S, C),
+-        case C of
+-            $\n ->
+-                S#decoder{column=1,
+-                          line=1+S#decoder.line,
+-                          offset=1+S#decoder.offset};
+-            _ ->
+-                S#decoder{column=1+S#decoder.column,
+-                          offset=1+S#decoder.offset}
+-        end).
+-
+--define(IS_WHITESPACE(C),
+-        (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
+--define(IS_LITERAL_SAFE(C),
+-        ((C >= $A andalso C =< $Z) orelse (C >= $a andalso C =< $z)
+-         orelse (C >= $0 andalso C =< $9))).
+-
+--record(decoder, {line=1,
+-                  column=1,
+-                  offset=0}).
+-
+-%% @type html_node() = {string(), [html_attr()], [html_node() | string()]}
+-%% @type html_attr() = {string(), string()}
+-%% @type html_token() = html_data() | start_tag() | end_tag() | inline_html() | html_comment() | html_doctype()
+-%% @type html_data() = {data, string(), Whitespace::boolean()}
+-%% @type start_tag() = {start_tag, Name, [html_attr()], Singleton::boolean()}
+-%% @type end_tag() = {end_tag, Name}
+-%% @type html_comment() = {comment, Comment}
+-%% @type html_doctype() = {doctype, [Doctype]}
+-%% @type inline_html() = {'=', iolist()}
+-
+-%% External API.
+-
+-%% @spec parse(string() | binary()) -> html_node()
+-%% @doc tokenize and then transform the token stream into a HTML tree.
+-parse(Input) ->
+-    parse_tokens(tokens(Input)).
+-
+-%% @spec parse_tokens([html_token()]) -> html_node()
+-%% @doc Transform the output of tokens(Doc) into a HTML tree.
+-parse_tokens(Tokens) when is_list(Tokens) ->
+-    %% Skip over doctype, processing instructions
+-    F = fun (X) ->
+-                case X of
+-                    {start_tag, _, _, false} ->
+-                        false;
+-                    _ ->
+-                        true
+-                end
+-        end,
+-    [{start_tag, Tag, Attrs, false} | Rest] = lists:dropwhile(F, Tokens),
+-    {Tree, _} = tree(Rest, [norm({Tag, Attrs})]),
+-    Tree.
+-
+-%% @spec tokens(StringOrBinary) -> [html_token()]
+-%% @doc Transform the input UTF-8 HTML into a token stream.
+-tokens(Input) ->
+-    tokens(iolist_to_binary(Input), #decoder{}, []).
+-
+-%% @spec to_tokens(html_node()) -> [html_token()]
+-%% @doc Convert a html_node() tree to a list of tokens.
+-to_tokens({Tag0}) ->
+-    to_tokens({Tag0, [], []});
+-to_tokens(T={'=', _}) ->
+-    [T];
+-to_tokens(T={doctype, _}) ->
+-    [T];
+-to_tokens(T={comment, _}) ->
+-    [T];
+-to_tokens({Tag0, Acc}) ->
+-    to_tokens({Tag0, [], Acc});
+-to_tokens({Tag0, Attrs, Acc}) ->
+-    Tag = to_tag(Tag0),
+-    to_tokens([{Tag, Acc}], [{start_tag, Tag, Attrs, is_singleton(Tag)}]).
+-
+-%% @spec to_html([html_token()] | html_node()) -> iolist()
+-%% @doc Convert a list of html_token() to a HTML document.
+-to_html(Node) when is_tuple(Node) ->
+-    to_html(to_tokens(Node));
+-to_html(Tokens) when is_list(Tokens) ->
+-    to_html(Tokens, []).
+-
+-%% @spec escape(string() | atom() | binary()) -> binary()
+-%% @doc Escape a string such that it's safe for HTML (amp; lt; gt;).
+-escape(B) when is_binary(B) ->
+-    escape(binary_to_list(B), []);
+-escape(A) when is_atom(A) ->
+-    escape(atom_to_list(A), []);
+-escape(S) when is_list(S) ->
+-    escape(S, []).
+-
+-%% @spec escape_attr(string() | binary() | atom() | integer() | float()) -> binary()
+-%% @doc Escape a string such that it's safe for HTML attrs
+-%%      (amp; lt; gt; quot;).
+-escape_attr(B) when is_binary(B) ->
+-    escape_attr(binary_to_list(B), []);
+-escape_attr(A) when is_atom(A) ->
+-    escape_attr(atom_to_list(A), []);
+-escape_attr(S) when is_list(S) ->
+-    escape_attr(S, []);
+-escape_attr(I) when is_integer(I) ->
+-    escape_attr(integer_to_list(I), []);
+-escape_attr(F) when is_float(F) ->
+-    escape_attr(mochinum:digits(F), []).
+-
+-%% @spec test() -> ok
+-%% @doc Run tests for mochiweb_html.
+-test() ->
+-    test_destack(),
+-    test_tokens(),
+-    test_tokens2(),
+-    test_parse(),
+-    test_parse2(),
+-    test_parse_tokens(),
+-    test_escape(),
+-    test_escape_attr(),
+-    test_to_html(),
+-    ok.
+-
+-
+-%% Internal API
+-
+-test_to_html() ->
+-    Expect = <<"<html><head><title>hey!</title></head><body><p class=\"foo\">what's up<br /></p><div>sucka</div><!-- comment! --></body></html>">>,
+-    Expect = iolist_to_binary(
+-               to_html({html, [],
+-                        [{<<"head">>, [],
+-                          [{title, <<"hey!">>}]},
+-                         {body, [],
+-                          [{p, [{class, foo}], [<<"what's">>, <<" up">>, {br}]},
+-                           {'div', <<"sucka">>},
+-                           {comment, <<" comment! ">>}]}]})),
+-    Expect1 = <<"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">">>,
+-    Expect1 = iolist_to_binary(
+-                to_html({doctype,
+-                         [<<"html">>, <<"PUBLIC">>,
+-                          <<"-//W3C//DTD XHTML 1.0 Transitional//EN">>,
+-                          <<"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>]})),
+-    ok.
+-to_html([], Acc) ->
+-    lists:reverse(Acc);
+-to_html([{'=', Content} | Rest], Acc) ->
+-    to_html(Rest, [Content | Acc]);
+-to_html([{pi, Tag, Attrs} | Rest], Acc) ->
+-    Open = [<<"<?">>,
+-            Tag,
+-            attrs_to_html(Attrs, []),
+-            <<"?>">>],
+-    to_html(Rest, [Open | Acc]);
+-to_html([{comment, Comment} | Rest], Acc) ->
+-    to_html(Rest, [[<<"<!--">>, Comment, <<"-->">>] | Acc]);
+-to_html([{doctype, Parts} | Rest], Acc) ->
+-    Inside = doctype_to_html(Parts, Acc),
+-    to_html(Rest, [[<<"<!DOCTYPE">>, Inside, <<">">>] | Acc]);
+-to_html([{data, Data, _Whitespace} | Rest], Acc) ->
+-    to_html(Rest, [escape(Data) | Acc]);
+-to_html([{start_tag, Tag, Attrs, Singleton} | Rest], Acc) ->
+-    Open = [<<"<">>,
+-            Tag,
+-            attrs_to_html(Attrs, []),
+-            case Singleton of
+-                true -> <<" />">>;
+-                false -> <<">">>
+-            end],
+-    to_html(Rest, [Open | Acc]);
+-to_html([{end_tag, Tag} | Rest], Acc) ->
+-    to_html(Rest, [[<<"</">>, Tag, <<">">>] | Acc]).
+-
+-doctype_to_html([], Acc) ->
+-    lists:reverse(Acc);
+-doctype_to_html([Word | Rest], Acc) ->
+-    case lists:all(fun (C) -> ?IS_LITERAL_SAFE(C) end,
+-                   binary_to_list(iolist_to_binary(Word))) of
+-        true ->
+-            doctype_to_html(Rest, [[<<" ">>, Word] | Acc]);
+-        false ->
+-            doctype_to_html(Rest, [[<<" \"">>, escape_attr(Word), ?QUOTE] | Acc])
+-    end.
+-
+-attrs_to_html([], Acc) ->
+-    lists:reverse(Acc);
+-attrs_to_html([{K, V} | Rest], Acc) ->
+-    attrs_to_html(Rest,
+-                  [[<<" ">>, escape(K), <<"=\"">>,
+-                    escape_attr(V), <<"\"">>] | Acc]).
+-
+-test_escape() ->
+-    <<"&amp;quot;\"word &lt;&lt;up!&amp;quot;">> =
+-        escape(<<"&quot;\"word <<up!&quot;">>),
+-    ok.
+-
+-test_escape_attr() ->
+-    <<"&amp;quot;&quot;word &lt;&lt;up!&amp;quot;">> =
+-        escape_attr(<<"&quot;\"word <<up!&quot;">>),
+-    ok.
+-
+-escape([], Acc) ->
+-    list_to_binary(lists:reverse(Acc));
+-escape("<" ++ Rest, Acc) ->
+-    escape(Rest, lists:reverse("&lt;", Acc));
+-escape(">" ++ Rest, Acc) ->
+-    escape(Rest, lists:reverse("&gt;", Acc));
+-escape("&" ++ Rest, Acc) ->
+-    escape(Rest, lists:reverse("&amp;", Acc));
+-escape([C | Rest], Acc) ->
+-    escape(Rest, [C | Acc]).
+-
+-escape_attr([], Acc) ->
+-    list_to_binary(lists:reverse(Acc));
+-escape_attr("<" ++ Rest, Acc) ->
+-    escape_attr(Rest, lists:reverse("&lt;", Acc));
+-escape_attr(">" ++ Rest, Acc) ->
+-    escape_attr(Rest, lists:reverse("&gt;", Acc));
+-escape_attr("&" ++ Rest, Acc) ->
+-    escape_attr(Rest, lists:reverse("&amp;", Acc));
+-escape_attr([?QUOTE | Rest], Acc) ->
+-    escape_attr(Rest, lists:reverse("&quot;", Acc));
+-escape_attr([C | Rest], Acc) ->
+-    escape_attr(Rest, [C | Acc]).
+-
+-to_tag(A) when is_atom(A) ->
+-    norm(atom_to_list(A));
+-to_tag(L) ->
+-    norm(L).
+-
+-to_tokens([], Acc) ->
+-    lists:reverse(Acc);
+-to_tokens([{Tag, []} | Rest], Acc) ->
+-    to_tokens(Rest, [{end_tag, to_tag(Tag)} | Acc]);
+-to_tokens([{Tag0, [{T0} | R1]} | Rest], Acc) ->
+-    %% Allow {br}
+-    to_tokens([{Tag0, [{T0, [], []} | R1]} | Rest], Acc);
+-to_tokens([{Tag0, [T0={'=', _C0} | R1]} | Rest], Acc) ->
+-    %% Allow {'=', iolist()}
+-    to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
+-to_tokens([{Tag0, [T0={comment, _C0} | R1]} | Rest], Acc) ->
+-    %% Allow {comment, iolist()}
+-    to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
+-to_tokens([{Tag0, [{T0, A0=[{_, _} | _]} | R1]} | Rest], Acc) ->
+-    %% Allow {p, [{"class", "foo"}]}
+-    to_tokens([{Tag0, [{T0, A0, []} | R1]} | Rest], Acc);
+-to_tokens([{Tag0, [{T0, C0} | R1]} | Rest], Acc) ->
+-    %% Allow {p, "content"} and {p, <<"content">>}
+-    to_tokens([{Tag0, [{T0, [], C0} | R1]} | Rest], Acc);
+-to_tokens([{Tag0, [{T0, A1, C0} | R1]} | Rest], Acc) when is_binary(C0) ->
+-    %% Allow {"p", [{"class", "foo"}], <<"content">>}
+-    to_tokens([{Tag0, [{T0, A1, binary_to_list(C0)} | R1]} | Rest], Acc);
+-to_tokens([{Tag0, [{T0, A1, C0=[C | _]} | R1]} | Rest], Acc)
+-  when is_integer(C) ->
+-    %% Allow {"p", [{"class", "foo"}], "content"}
+-    to_tokens([{Tag0, [{T0, A1, [C0]} | R1]} | Rest], Acc);
+-to_tokens([{Tag0, [{T0, A1, C1} | R1]} | Rest], Acc) ->
+-    %% Native {"p", [{"class", "foo"}], ["content"]}
+-    Tag = to_tag(Tag0),
+-    T1 = to_tag(T0),
+-    case is_singleton(norm(T1)) of
+-        true ->
+-            to_tokens([{Tag, R1} | Rest], [{start_tag, T1, A1, true} | Acc]);
+-        false ->
+-            to_tokens([{T1, C1}, {Tag, R1} | Rest],
+-                      [{start_tag, T1, A1, false} | Acc])
+-    end;
+-to_tokens([{Tag0, [L | R1]} | Rest], Acc) when is_list(L) ->
+-    %% List text
+-    Tag = to_tag(Tag0),
+-    to_tokens([{Tag, R1} | Rest], [{data, iolist_to_binary(L), false} | Acc]);
+-to_tokens([{Tag0, [B | R1]} | Rest], Acc) when is_binary(B) ->
+-    %% Binary text
+-    Tag = to_tag(Tag0),
+-    to_tokens([{Tag, R1} | Rest], [{data, B, false} | Acc]).
+-
+-test_tokens() ->
+-    [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>},
+-                             {<<"wibble">>, <<"wibble">>},
+-                             {<<"alice">>, <<"bob">>}], true}] =
+-        tokens(<<"<foo bar=baz wibble='wibble' alice=\"bob\"/>">>),
+-    [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>},
+-                             {<<"wibble">>, <<"wibble">>},
+-                             {<<"alice">>, <<"bob">>}], true}] =
+-        tokens(<<"<foo bar=baz wibble='wibble' alice=bob/>">>),
+-    [{comment, <<"[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]">>}] =
+-        tokens(<<"<!--[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]-->">>),
+-    [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false},
+-     {data, <<" A= B <= C ">>, false},
+-     {end_tag, <<"script">>}] =
+-        tokens(<<"<script type=\"text/javascript\"> A= B <= C </script>">>),
+-    [{start_tag, <<"textarea">>, [], false},
+-     {data, <<"<html></body>">>, false},
+-     {end_tag, <<"textarea">>}] =
+-        tokens(<<"<textarea><html></body></textarea>">>),
+-    ok.
+-
+-tokens(B, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary>> ->
+-            lists:reverse(Acc);
+-        _ ->
+-            {Tag, S1} = tokenize(B, S),
+-            case parse_flag(Tag) of
+-                script ->
+-                    {Tag2, S2} = tokenize_script(B, S1),
+-                    tokens(B, S2, [Tag2, Tag | Acc]);
+-                textarea ->
+-                    {Tag2, S2} = tokenize_textarea(B, S1),
+-                    tokens(B, S2, [Tag2, Tag | Acc]);
+-                none ->
+-                    tokens(B, S1, [Tag | Acc])
+-            end
+-    end.
+-
+-parse_flag({start_tag, B, _, false}) ->
+-    case string:to_lower(binary_to_list(B)) of
+-        "script" ->
+-            script;
+-        "textarea" ->
+-            textarea;
+-        _ ->
+-            none
+-    end;
+-parse_flag(_) ->
+-    none.
+-
+-tokenize(B, S=#decoder{offset=O}) ->
+-    case B of
+-        <<_:O/binary, "<!--", _/binary>> ->
+-            tokenize_comment(B, ?ADV_COL(S, 4));
+-        <<_:O/binary, "<!DOCTYPE", _/binary>> ->
+-            tokenize_doctype(B, ?ADV_COL(S, 10));
+-        <<_:O/binary, "<![CDATA[", _/binary>> ->
+-            tokenize_cdata(B, ?ADV_COL(S, 9));
+-        <<_:O/binary, "<?", _/binary>> ->
+-            {Tag, S1} = tokenize_literal(B, ?ADV_COL(S, 2)),
+-            {Attrs, S2} = tokenize_attributes(B, S1),
+-            S3 = find_qgt(B, S2),
+-            {{pi, Tag, Attrs}, S3};
+-        <<_:O/binary, "&", _/binary>> ->
+-            tokenize_charref(B, ?INC_COL(S));
+-        <<_:O/binary, "</", _/binary>> ->
+-            {Tag, S1} = tokenize_literal(B, ?ADV_COL(S, 2)),
+-            {S2, _} = find_gt(B, S1),
+-            {{end_tag, Tag}, S2};
+-        <<_:O/binary, "<", C, _/binary>> when ?IS_WHITESPACE(C) ->
+-            %% This isn't really strict HTML
+-            tokenize_data(B, ?INC_COL(S));
+-        <<_:O/binary, "<", _/binary>> ->
+-            {Tag, S1} = tokenize_literal(B, ?INC_COL(S)),
+-            {Attrs, S2} = tokenize_attributes(B, S1),
+-            {S3, HasSlash} = find_gt(B, S2),
+-            Singleton = HasSlash orelse is_singleton(norm(binary_to_list(Tag))),
+-            {{start_tag, Tag, Attrs, Singleton}, S3};
+-        _ ->
+-            tokenize_data(B, S)
+-    end.
+-
+-test_parse() ->
+-    D0 = <<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">
+-<html>
+- <head>
+-   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">
+-   <title>Foo</title>
+-   <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/rel/dojo/resources/dojo.css\" media=\"screen\">
+-   <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/foo.css\" media=\"screen\">
+-   <!--[if lt IE 7]>
+-   <style type=\"text/css\">
+-     .no_ie { display: none; }
+-   </style>
+-   <![endif]-->
+-   <link rel=\"icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\">
+-   <link rel=\"shortcut icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\">
+- </head>
+- <body id=\"home\" class=\"tundra\"><![CDATA[&lt;<this<!-- is -->CDATA>&gt;]]></body>
+-</html>">>,
+-    Expect = {<<"html">>, [],
+-              [{<<"head">>, [],
+-                [{<<"meta">>,
+-                  [{<<"http-equiv">>,<<"Content-Type">>},
+-                   {<<"content">>,<<"text/html; charset=UTF-8">>}],
+-                  []},
+-                 {<<"title">>,[],[<<"Foo">>]},
+-                 {<<"link">>,
+-                  [{<<"rel">>,<<"stylesheet">>},
+-                   {<<"type">>,<<"text/css">>},
+-                   {<<"href">>,<<"/static/rel/dojo/resources/dojo.css">>},
+-                   {<<"media">>,<<"screen">>}],
+-                  []},
+-                 {<<"link">>,
+-                  [{<<"rel">>,<<"stylesheet">>},
+-                   {<<"type">>,<<"text/css">>},
+-                   {<<"href">>,<<"/static/foo.css">>},
+-                   {<<"media">>,<<"screen">>}],
+-                  []},
+-                 {comment,<<"[if lt IE 7]>\n   <style type=\"text/css\">\n     .no_ie { display: none; }\n   </style>\n   <![endif]">>},
+-                 {<<"link">>,
+-                  [{<<"rel">>,<<"icon">>},
+-                   {<<"href">>,<<"/static/images/favicon.ico">>},
+-                   {<<"type">>,<<"image/x-icon">>}],
+-                  []},
+-                 {<<"link">>,
+-                  [{<<"rel">>,<<"shortcut icon">>},
+-                   {<<"href">>,<<"/static/images/favicon.ico">>},
+-                   {<<"type">>,<<"image/x-icon">>}],
+-                  []}]},
+-               {<<"body">>,
+-                [{<<"id">>,<<"home">>},
+-                 {<<"class">>,<<"tundra">>}],
+-                [<<"&lt;<this<!-- is -->CDATA>&gt;">>]}]},
+-    Expect = parse(D0),
+-    ok.
+-
+-test_tokens2() ->
+-    D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org</link><description>Bob's Rants</description></channel>">>,
+-    Expect = [{start_tag,<<"channel">>,[],false},
+-              {start_tag,<<"title">>,[],false},
+-              {data,<<"from __future__ import *">>,false},
+-              {end_tag,<<"title">>},
+-              {start_tag,<<"link">>,[],true},
+-              {data,<<"http://bob.pythonmac.org">>,false},
+-              {end_tag,<<"link">>},
+-              {start_tag,<<"description">>,[],false},
+-              {data,<<"Bob's Rants">>,false},
+-              {end_tag,<<"description">>},
+-              {end_tag,<<"channel">>}],
+-    Expect = tokens(D0),
+-    ok.
+-
+-test_parse2() ->
+-    D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org<br>foo</link><description>Bob's Rants</description></channel>">>,
+-    Expect = {<<"channel">>,[],
+-              [{<<"title">>,[],[<<"from __future__ import *">>]},
+-               {<<"link">>,[],[
+-                               <<"http://bob.pythonmac.org">>,
+-                               {<<"br">>,[],[]},
+-                               <<"foo">>]},
+-               {<<"description">>,[],[<<"Bob's Rants">>]}]},
+-    Expect = parse(D0),
+-    ok.
+-
+-test_parse_tokens() ->
+-    D0 = [{doctype,[<<"HTML">>,<<"PUBLIC">>,<<"-//W3C//DTD HTML 4.01 Transitional//EN">>]},
+-          {data,<<"\n">>,true},
+-          {start_tag,<<"html">>,[],false}],
+-    {<<"html">>, [], []} = parse_tokens(D0),
+-    D1 = D0 ++ [{end_tag, <<"html">>}],
+-    {<<"html">>, [], []} = parse_tokens(D1),
+-    D2 = D0 ++ [{start_tag, <<"body">>, [], false}],
+-    {<<"html">>, [], [{<<"body">>, [], []}]} = parse_tokens(D2),
+-    D3 = D0 ++ [{start_tag, <<"head">>, [], false},
+-                {end_tag, <<"head">>},
+-                {start_tag, <<"body">>, [], false}],
+-    {<<"html">>, [], [{<<"head">>, [], []}, {<<"body">>, [], []}]} = parse_tokens(D3),
+-    D4 = D3 ++ [{data,<<"\n">>,true},
+-                {start_tag,<<"div">>,[{<<"class">>,<<"a">>}],false},
+-                {start_tag,<<"a">>,[{<<"name">>,<<"#anchor">>}],false},
+-                {end_tag,<<"a">>},
+-                {end_tag,<<"div">>},
+-                {start_tag,<<"div">>,[{<<"class">>,<<"b">>}],false},
+-                {start_tag,<<"div">>,[{<<"class">>,<<"c">>}],false},
+-                {end_tag,<<"div">>},
+-                {end_tag,<<"div">>}],
+-    {<<"html">>, [],
+-     [{<<"head">>, [], []},
+-      {<<"body">>, [],
+-       [{<<"div">>, [{<<"class">>, <<"a">>}], [{<<"a">>, [{<<"name">>, <<"#anchor">>}], []}]},
+-        {<<"div">>, [{<<"class">>, <<"b">>}], [{<<"div">>, [{<<"class">>, <<"c">>}], []}]}
+-       ]}]} = parse_tokens(D4),
+-    D5 = [{start_tag,<<"html">>,[],false},
+-          {data,<<"\n">>,true},
+-          {data,<<"boo">>,false},
+-          {data,<<"hoo">>,false},
+-          {data,<<"\n">>,true},
+-          {end_tag,<<"html">>}],
+-    {<<"html">>, [], [<<"\nboohoo\n">>]} = parse_tokens(D5),
+-    D6 = [{start_tag,<<"html">>,[],false},
+-          {data,<<"\n">>,true},
+-          {data,<<"\n">>,true},
+-          {end_tag,<<"html">>}],
+-    {<<"html">>, [], []} = parse_tokens(D6),
+-    D7 = [{start_tag,<<"html">>,[],false},
+-          {start_tag,<<"ul">>,[],false},
+-          {start_tag,<<"li">>,[],false},
+-          {data,<<"word">>,false},
+-          {start_tag,<<"li">>,[],false},
+-          {data,<<"up">>,false},
+-          {end_tag,<<"li">>},
+-          {start_tag,<<"li">>,[],false},
+-          {data,<<"fdsa">>,false},
+-          {start_tag,<<"br">>,[],true},
+-          {data,<<"asdf">>,false},
+-          {end_tag,<<"ul">>},
+-          {end_tag,<<"html">>}],
+-    {<<"html">>, [],
+-     [{<<"ul">>, [],
+-       [{<<"li">>, [], [<<"word">>]},
+-        {<<"li">>, [], [<<"up">>]},
+-        {<<"li">>, [], [<<"fdsa">>,{<<"br">>, [], []}, <<"asdf">>]}]}]} = parse_tokens(D7),
+-    ok.
+-
+-tree_data([{data, Data, Whitespace} | Rest], AllWhitespace, Acc) ->
+-    tree_data(Rest, (Whitespace andalso AllWhitespace), [Data | Acc]);
+-tree_data(Rest, AllWhitespace, Acc) ->
+-    {iolist_to_binary(lists:reverse(Acc)), AllWhitespace, Rest}.
+-
+-tree([], Stack) ->
+-    {destack(Stack), []};
+-tree([{end_tag, Tag} | Rest], Stack) ->
+-    case destack(norm(Tag), Stack) of
+-        S when is_list(S) ->
+-            tree(Rest, S);
+-        Result ->
+-            {Result, []}
+-    end;
+-tree([{start_tag, Tag, Attrs, true} | Rest], S) ->
+-    tree(Rest, append_stack_child(norm({Tag, Attrs}), S));
+-tree([{start_tag, Tag, Attrs, false} | Rest], S) ->
+-    tree(Rest, stack(norm({Tag, Attrs}), S));
+-tree([T={pi, _Tag, _Attrs} | Rest], S) ->
+-    tree(Rest, append_stack_child(T, S));
+-tree([T={comment, _Comment} | Rest], S) ->
+-    tree(Rest, append_stack_child(T, S));
+-tree(L=[{data, _Data, _Whitespace} | _], S) ->
+-    case tree_data(L, true, []) of
+-        {_, true, Rest} ->
+-            tree(Rest, S);
+-        {Data, false, Rest} ->
+-            tree(Rest, append_stack_child(Data, S))
+-    end.
+-
+-norm({Tag, Attrs}) ->
+-    {norm(Tag), [{norm(K), iolist_to_binary(V)} || {K, V} <- Attrs], []};
+-norm(Tag) when is_binary(Tag) ->
+-    Tag;
+-norm(Tag) ->
+-    list_to_binary(string:to_lower(Tag)).
+-
+-test_destack() ->
+-    {<<"a">>, [], []} =
+-        destack([{<<"a">>, [], []}]),
+-    {<<"a">>, [], [{<<"b">>, [], []}]} =
+-        destack([{<<"b">>, [], []}, {<<"a">>, [], []}]),
+-    {<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]} =
+-     destack([{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]),
+-    [{<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]}] =
+-     destack(<<"b">>,
+-             [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]),
+-    [{<<"b">>, [], [{<<"c">>, [], []}]}, {<<"a">>, [], []}] =
+-     destack(<<"c">>,
+-             [{<<"c">>, [], []}, {<<"b">>, [], []},{<<"a">>, [], []}]),
+-    ok.
+-
+-stack(T1={TN, _, _}, Stack=[{TN, _, _} | _Rest])
+-  when TN =:= <<"li">> orelse TN =:= <<"option">> ->
+-    [T1 | destack(TN, Stack)];
+-stack(T1={TN0, _, _}, Stack=[{TN1, _, _} | _Rest])
+-  when (TN0 =:= <<"dd">> orelse TN0 =:= <<"dt">>) andalso
+-       (TN1 =:= <<"dd">> orelse TN1 =:= <<"dt">>) ->
+-    [T1 | destack(TN1, Stack)];
+-stack(T1, Stack) ->
+-    [T1 | Stack].
+-
+-append_stack_child(StartTag, [{Name, Attrs, Acc} | Stack]) ->
+-    [{Name, Attrs, [StartTag | Acc]} | Stack].
+-
+-destack(TagName, Stack) when is_list(Stack) ->
+-    F = fun (X) ->
+-                case X of
+-                    {TagName, _, _} ->
+-                        false;
+-                    _ ->
+-                        true
+-                end
+-        end,
+-    case lists:splitwith(F, Stack) of
+-        {_, []} ->
+-            %% If we're parsing something like XML we might find
+-            %% a <link>tag</link> that is normally a singleton
+-            %% in HTML but isn't here
+-            case {is_singleton(TagName), Stack} of
+-                {true, [{T0, A0, Acc0} | Post0]} ->
+-                    case lists:splitwith(F, Acc0) of
+-                        {_, []} ->
+-                            %% Actually was a singleton
+-                            Stack;
+-                        {Pre, [{T1, A1, []} | Post1]} ->
+-                            [{T0, A0, [{T1, A1, lists:reverse(Pre)} | Post1]}
+-                             | Post0]
+-                    end;
+-                _ ->
+-                    %% No match, no state change
+-                    Stack
+-            end;
+-        {_Pre, [_T]} ->
+-            %% Unfurl the whole stack, we're done
+-            destack(Stack);
+-        {Pre, [T, {T0, A0, Acc0} | Post]} ->
+-            %% Unfurl up to the tag, then accumulate it
+-            [{T0, A0, [destack(Pre ++ [T]) | Acc0]} | Post]
+-    end.
+-
+-destack([{Tag, Attrs, Acc}]) ->
+-    {Tag, Attrs, lists:reverse(Acc)};
+-destack([{T1, A1, Acc1}, {T0, A0, Acc0} | Rest]) ->
+-    destack([{T0, A0, [{T1, A1, lists:reverse(Acc1)} | Acc0]} | Rest]).
+-
+-is_singleton(<<"br">>) -> true;
+-is_singleton(<<"hr">>) -> true;
+-is_singleton(<<"img">>) -> true;
+-is_singleton(<<"input">>) -> true;
+-is_singleton(<<"base">>) -> true;
+-is_singleton(<<"meta">>) -> true;
+-is_singleton(<<"link">>) -> true;
+-is_singleton(<<"area">>) -> true;
+-is_singleton(<<"param">>) -> true;
+-is_singleton(<<"col">>) -> true;
+-is_singleton(_) -> false.
+-
+-tokenize_data(B, S=#decoder{offset=O}) ->
+-    tokenize_data(B, S, O, true).
+-
+-tokenize_data(B, S=#decoder{offset=O}, Start, Whitespace) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when (C =/= $< andalso C =/= $&) ->
+-            tokenize_data(B, ?INC_CHAR(S, C), Start,
+-                          (Whitespace andalso ?IS_WHITESPACE(C)));
+-        _ ->
+-            Len = O - Start,
+-            <<_:Start/binary, Data:Len/binary, _/binary>> = B,
+-            {{data, Data, Whitespace}, S}
+-    end.
+-
+-tokenize_attributes(B, S) ->
+-    tokenize_attributes(B, S, []).
+-
+-tokenize_attributes(B, S=#decoder{offset=O}, Acc) ->
+-    case B of
+-        <<_:O/binary>> ->
+-            {lists:reverse(Acc), S};
+-        <<_:O/binary, C, _/binary>> when (C =:= $> orelse C =:= $/) ->
+-            {lists:reverse(Acc), S};
+-        <<_:O/binary, "?>", _/binary>> ->
+-            {lists:reverse(Acc), S};
+-        <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
+-            tokenize_attributes(B, ?INC_CHAR(S, C), Acc);
+-        _ ->
+-            {Attr, S1} = tokenize_literal(B, S),
+-            {Value, S2} = tokenize_attr_value(Attr, B, S1),
+-            tokenize_attributes(B, S2, [{Attr, Value} | Acc])
+-    end.
+-
+-tokenize_attr_value(Attr, B, S) ->
+-    S1 = skip_whitespace(B, S),
+-    O = S1#decoder.offset,
+-    case B of
+-        <<_:O/binary, "=", _/binary>> ->
+-            tokenize_word_or_literal(B, ?INC_COL(S1));
+-        _ ->
+-            {Attr, S1}
+-    end.
+-
+-skip_whitespace(B, S=#decoder{offset=O}) ->
+-    case B of
+-        <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
+-            skip_whitespace(B, ?INC_CHAR(S, C));
+-        _ ->
+-            S
+-    end.
+-
+-tokenize_literal(Bin, S) ->
+-    tokenize_literal(Bin, S, []).
+-
+-tokenize_literal(Bin, S=#decoder{offset=O}, Acc) ->
+-    case Bin of
+-        <<_:O/binary, $&, _/binary>> ->
+-            {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)),
+-            tokenize_literal(Bin, S1, [Data | Acc]);
+-        <<_:O/binary, C, _/binary>> when not (?IS_WHITESPACE(C)
+-                                              orelse C =:= $>
+-                                              orelse C =:= $/
+-                                              orelse C =:= $=) ->
+-            tokenize_literal(Bin, ?INC_COL(S), [C | Acc]);
+-        _ ->
+-            {iolist_to_binary(lists:reverse(Acc)), S}
+-    end.
+-
+-find_qgt(Bin, S=#decoder{offset=O}) ->
+-    case Bin of
+-        <<_:O/binary, "?>", _/binary>> ->
+-            ?ADV_COL(S, 2);
+-        <<_:O/binary, C, _/binary>> ->
+-            find_qgt(Bin, ?INC_CHAR(S, C));
+-        _ ->
+-            S
+-    end.
+-
+-find_gt(Bin, S) ->
+-    find_gt(Bin, S, false).
+-
+-find_gt(Bin, S=#decoder{offset=O}, HasSlash) ->
+-    case Bin of
+-        <<_:O/binary, $/, _/binary>> ->
+-            find_gt(Bin, ?INC_COL(S), true);
+-        <<_:O/binary, $>, _/binary>> ->
+-            {?INC_COL(S), HasSlash};
+-        <<_:O/binary, C, _/binary>> ->
+-            find_gt(Bin, ?INC_CHAR(S, C), HasSlash);
+-        _ ->
+-            {S, HasSlash}
+-    end.
+-
+-tokenize_charref(Bin, S=#decoder{offset=O}) ->
+-    tokenize_charref(Bin, S, O).
+-
+-tokenize_charref(Bin, S=#decoder{offset=O}, Start) ->
+-    case Bin of
+-        <<_:O/binary>> ->
+-            <<_:Start/binary, Raw/binary>> = Bin,
+-            {{data, Raw, false}, S};
+-        <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C)
+-                                         orelse C =:= ?SQUOTE
+-                                         orelse C =:= ?QUOTE
+-                                         orelse C =:= $/
+-                                         orelse C =:= $> ->
+-            Len = O - Start,
+-            <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
+-            {{data, Raw, false}, S};
+-        <<_:O/binary, $;, _/binary>> ->
+-            Len = O - Start,
+-            <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
+-            Data = case mochiweb_charref:charref(Raw) of
+-                       undefined ->
+-                           Start1 = Start - 1,
+-                           Len1 = Len + 2,
+-                           <<_:Start1/binary, R:Len1/binary, _/binary>> = Bin,
+-                           R;
+-                       Unichar ->
+-                           list_to_binary(xmerl_ucs:to_utf8(Unichar))
+-                   end,
+-            {{data, Data, false}, ?INC_COL(S)};
+-        _ ->
+-            tokenize_charref(Bin, ?INC_COL(S), Start)
+-    end.
+-
+-tokenize_doctype(Bin, S) ->
+-    tokenize_doctype(Bin, S, []).
+-
+-tokenize_doctype(Bin, S=#decoder{offset=O}, Acc) ->
+-    case Bin of
+-        <<_:O/binary>> ->
+-            {{doctype, lists:reverse(Acc)}, S};
+-        <<_:O/binary, $>, _/binary>> ->
+-            {{doctype, lists:reverse(Acc)}, ?INC_COL(S)};
+-        <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
+-            tokenize_doctype(Bin, ?INC_CHAR(S, C), Acc);
+-        _ ->
+-            {Word, S1} = tokenize_word_or_literal(Bin, S),
+-            tokenize_doctype(Bin, S1, [Word | Acc])
+-    end.
+-
+-tokenize_word_or_literal(Bin, S=#decoder{offset=O}) ->
+-    case Bin of
+-        <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
+-            {error, {whitespace, [C], S}};
+-        <<_:O/binary, C, _/binary>> when C =:= ?QUOTE orelse C =:= ?SQUOTE ->
+-            tokenize_word(Bin, ?INC_COL(S), C);
+-        _ ->
+-            tokenize_literal(Bin, S, [])
+-    end.
+-
+-tokenize_word(Bin, S, Quote) ->
+-    tokenize_word(Bin, S, Quote, []).
+-
+-tokenize_word(Bin, S=#decoder{offset=O}, Quote, Acc) ->
+-    case Bin of
+-        <<_:O/binary>> ->
+-            {iolist_to_binary(lists:reverse(Acc)), S};
+-        <<_:O/binary, Quote, _/binary>> ->
+-            {iolist_to_binary(lists:reverse(Acc)), ?INC_COL(S)};
+-        <<_:O/binary, $&, _/binary>> ->
+-            {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)),
+-            tokenize_word(Bin, S1, Quote, [Data | Acc]);
+-        <<_:O/binary, C, _/binary>> ->
+-            tokenize_word(Bin, ?INC_CHAR(S, C), Quote, [C | Acc])
+-    end.
+-
+-tokenize_cdata(Bin, S=#decoder{offset=O}) ->
+-    tokenize_cdata(Bin, S, O).
+-
+-tokenize_cdata(Bin, S=#decoder{offset=O}, Start) ->
+-    case Bin of
+-        <<_:O/binary, "]]>", _/binary>> ->
+-            Len = O - Start,
+-            <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
+-            {{data, Raw, false}, ?ADV_COL(S, 3)};
+-        <<_:O/binary, C, _/binary>> ->
+-            tokenize_cdata(Bin, ?INC_CHAR(S, C), Start);
+-        _ ->
+-            <<_:O/binary, Raw/binary>> = Bin,
+-            {{data, Raw, false}, S}
+-    end.
+-
+-tokenize_comment(Bin, S=#decoder{offset=O}) ->
+-    tokenize_comment(Bin, S, O).
+-
+-tokenize_comment(Bin, S=#decoder{offset=O}, Start) ->
+-    case Bin of
+-        <<_:O/binary, "-->", _/binary>> ->
+-            Len = O - Start,
+-            <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
+-            {{comment, Raw}, ?ADV_COL(S, 3)};
+-        <<_:O/binary, C, _/binary>> ->
+-            tokenize_comment(Bin, ?INC_CHAR(S, C), Start);
+-        <<_:Start/binary, Raw/binary>> ->
+-            {{comment, Raw}, S}
+-    end.
+-
+-tokenize_script(Bin, S=#decoder{offset=O}) ->
+-    tokenize_script(Bin, S, O).
+-
+-tokenize_script(Bin, S=#decoder{offset=O}, Start) ->
+-    case Bin of
+-        %% Just a look-ahead, we want the end_tag separately
+-        <<_:O/binary, $<, $/, SS, CC, RR, II, PP, TT, _/binary>>
+-        when (SS =:= $s orelse SS =:= $S) andalso
+-             (CC =:= $c orelse CC =:= $C) andalso
+-             (RR =:= $r orelse RR =:= $R) andalso
+-             (II =:= $i orelse II =:= $I) andalso
+-             (PP =:= $p orelse PP =:= $P) andalso
+-             (TT=:= $t orelse TT =:= $T) ->
+-            Len = O - Start,
+-            <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
+-            {{data, Raw, false}, S};
+-        <<_:O/binary, C, _/binary>> ->
+-            tokenize_script(Bin, ?INC_CHAR(S, C), Start);
+-        <<_:Start/binary, Raw/binary>> ->
+-            {{data, Raw, false}, S}
+-    end.
+-
+-tokenize_textarea(Bin, S=#decoder{offset=O}) ->
+-    tokenize_textarea(Bin, S, O).
+-
+-tokenize_textarea(Bin, S=#decoder{offset=O}, Start) ->
+-    case Bin of
+-        %% Just a look-ahead, we want the end_tag separately
+-        <<_:O/binary, $<, $/, TT, EE, XX, TT2, AA, RR, EE2, AA2, _/binary>>
+-        when (TT =:= $t orelse TT =:= $T) andalso
+-             (EE =:= $e orelse EE =:= $E) andalso
+-             (XX =:= $x orelse XX =:= $X) andalso
+-             (TT2 =:= $t orelse TT2 =:= $T) andalso
+-             (AA =:= $a orelse AA =:= $A) andalso
+-             (RR =:= $r orelse RR =:= $R) andalso
+-             (EE2 =:= $e orelse EE2 =:= $E) andalso
+-             (AA2 =:= $a orelse AA2 =:= $A) ->
+-            Len = O - Start,
+-            <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
+-            {{data, Raw, false}, S};
+-        <<_:O/binary, C, _/binary>> ->
+-            tokenize_textarea(Bin, ?INC_CHAR(S, C), Start);
+-        <<_:Start/binary, Raw/binary>> ->
+-            {{data, Raw, false}, S}
+-    end.
+diff --git a/src/mochiweb/mochiweb_http.erl b/src/mochiweb/mochiweb_http.erl
+deleted file mode 100644
+index 14a3657..0000000
+--- a/src/mochiweb/mochiweb_http.erl
++++ /dev/null
+@@ -1,142 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc HTTP server.
+-
+--module(mochiweb_http).
+--author('bob at mochimedia.com').
+--export([start/0, start/1, stop/0, stop/1]).
+--export([loop/2, default_body/1]).
+-
+--define(IDLE_TIMEOUT, 30000).
+-
+--define(MAX_HEADERS, 1000).
+--define(DEFAULTS, [{name, ?MODULE},
+-                   {port, 8888}]).
+-
+-set_default({Prop, Value}, PropList) ->
+-    case proplists:is_defined(Prop, PropList) of
+-        true ->
+-            PropList;
+-        false ->
+-            [{Prop, Value} | PropList]
+-    end.
+-
+-set_defaults(Defaults, PropList) ->
+-    lists:foldl(fun set_default/2, PropList, Defaults).
+-
+-parse_options(Options) ->
+-    {loop, HttpLoop} = proplists:lookup(loop, Options),
+-    Loop = fun (S) ->
+-                   ?MODULE:loop(S, HttpLoop)
+-           end,
+-    Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
+-    set_defaults(?DEFAULTS, Options1).
+-
+-stop() ->
+-    mochiweb_socket_server:stop(?MODULE).
+-
+-stop(Name) ->
+-    mochiweb_socket_server:stop(Name).
+-
+-start() ->
+-    start([{ip, "127.0.0.1"},
+-           {loop, {?MODULE, default_body}}]).
+-
+-start(Options) ->
+-    mochiweb_socket_server:start(parse_options(Options)).
+-
+-frm(Body) ->
+-    ["<html><head></head><body>"
+-     "<form method=\"POST\">"
+-     "<input type=\"hidden\" value=\"message\" name=\"hidden\"/>"
+-     "<input type=\"submit\" value=\"regular POST\">"
+-     "</form>"
+-     "<br />"
+-     "<form method=\"POST\" enctype=\"multipart/form-data\""
+-     " action=\"/multipart\">"
+-     "<input type=\"hidden\" value=\"multipart message\" name=\"hidden\"/>"
+-     "<input type=\"file\" name=\"file\"/>"
+-     "<input type=\"submit\" value=\"multipart POST\" />"
+-     "</form>"
+-     "<pre>", Body, "</pre>"
+-     "</body></html>"].
+-
+-default_body(Req, M, "/chunked") when M =:= 'GET'; M =:= 'HEAD' ->
+-    Res = Req:ok({"text/plain", [], chunked}),
+-    Res:write_chunk("First chunk\r\n"),
+-    timer:sleep(5000),
+-    Res:write_chunk("Last chunk\r\n"),
+-    Res:write_chunk("");
+-default_body(Req, M, _Path) when M =:= 'GET'; M =:= 'HEAD' ->
+-    Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()},
+-                                   {parse_cookie, Req:parse_cookie()},
+-                                   Req:dump()]]),
+-    Req:ok({"text/html",
+-            [mochiweb_cookies:cookie("mochiweb_http", "test_cookie")],
+-            frm(Body)});
+-default_body(Req, 'POST', "/multipart") ->
+-    Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()},
+-                                   {parse_cookie, Req:parse_cookie()},
+-                                   {body, Req:recv_body()},
+-                                   Req:dump()]]),
+-    Req:ok({"text/html", [], frm(Body)});
+-default_body(Req, 'POST', _Path) ->
+-    Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()},
+-                                   {parse_cookie, Req:parse_cookie()},
+-                                   {parse_post, Req:parse_post()},
+-                                   Req:dump()]]),
+-    Req:ok({"text/html", [], frm(Body)});
+-default_body(Req, _Method, _Path) ->
+-    Req:respond({501, [], []}).
+-
+-default_body(Req) ->
+-    default_body(Req, Req:get(method), Req:get(path)).
+-
+-loop(Socket, Body) ->
+-    inet:setopts(Socket, [{packet, http}]),
+-    request(Socket, Body).
+-
+-request(Socket, Body) ->
+-    case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of
+-        {ok, {http_request, Method, Path, Version}} ->
+-            headers(Socket, {Method, Path, Version}, [], Body, 0);
+-        {error, {http_error, "\r\n"}} ->
+-            request(Socket, Body);
+-        {error, {http_error, "\n"}} ->
+-            request(Socket, Body);
+-        _Other ->
+-            gen_tcp:close(Socket),
+-            exit(normal)
+-    end.
+-
+-headers(Socket, Request, Headers, _Body, ?MAX_HEADERS) ->
+-    %% Too many headers sent, bad request.
+-    inet:setopts(Socket, [{packet, raw}]),
+-    Req = mochiweb:new_request({Socket, Request,
+-                                lists:reverse(Headers)}),
+-    Req:respond({400, [], []}),
+-    gen_tcp:close(Socket),
+-    exit(normal);
+-headers(Socket, Request, Headers, Body, HeaderCount) ->
+-    case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of
+-        {ok, http_eoh} ->
+-            inet:setopts(Socket, [{packet, raw}]),
+-            Req = mochiweb:new_request({Socket, Request,
+-                                        lists:reverse(Headers)}),
+-            Body(Req),
+-            case Req:should_close() of
+-                true ->
+-                    gen_tcp:close(Socket),
+-                    exit(normal);
+-                false ->
+-                    Req:cleanup(),
+-                    ?MODULE:loop(Socket, Body)
+-            end;
+-        {ok, {http_header, _, Name, _, Value}} ->
+-            headers(Socket, Request, [{Name, Value} | Headers], Body,
+-                    1 + HeaderCount);
+-        _Other ->
+-            gen_tcp:close(Socket),
+-            exit(normal)
+-    end.
+diff --git a/src/mochiweb/mochiweb_multipart.erl b/src/mochiweb/mochiweb_multipart.erl
+deleted file mode 100644
+index 9eb4bad..0000000
+--- a/src/mochiweb/mochiweb_multipart.erl
++++ /dev/null
+@@ -1,429 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Utilities for parsing multipart/form-data.
+-
+--module(mochiweb_multipart).
+--author('bob at mochimedia.com').
+-
+--export([parse_form/1, parse_form/2]).
+--export([parse_multipart_request/2]).
+--export([test/0]).
+-
+--define(CHUNKSIZE, 4096).
+-
+--record(mp, {state, boundary, length, buffer, callback, req}).
+-
+-%% TODO: DOCUMENT THIS MODULE.
+-
+-parse_form(Req) ->
+-    parse_form(Req, fun default_file_handler/2).
+-
+-parse_form(Req, FileHandler) ->
+-    Callback = fun (Next) -> parse_form_outer(Next, FileHandler, []) end,
+-    {_, _, Res} = parse_multipart_request(Req, Callback),
+-    Res.
+-
+-parse_form_outer(eof, _, Acc) ->
+-    lists:reverse(Acc);
+-parse_form_outer({headers, H}, FileHandler, State) ->
+-    {"form-data", H1} = proplists:get_value("content-disposition", H),
+-    Name = proplists:get_value("name", H1),
+-    Filename = proplists:get_value("filename", H1),
+-    case Filename of
+-        undefined ->
+-            fun (Next) ->
+-                    parse_form_value(Next, {Name, []}, FileHandler, State)
+-            end;
+-        _ ->
+-            ContentType = proplists:get_value("content-type", H),
+-            Handler = FileHandler(Filename, ContentType),
+-            fun (Next) ->
+-                    parse_form_file(Next, {Name, Handler}, FileHandler, State)
+-            end
+-    end.
+-
+-parse_form_value(body_end, {Name, Acc}, FileHandler, State) ->
+-    Value = binary_to_list(iolist_to_binary(lists:reverse(Acc))),
+-    State1 = [{Name, Value} | State],
+-    fun (Next) -> parse_form_outer(Next, FileHandler, State1) end;
+-parse_form_value({body, Data}, {Name, Acc}, FileHandler, State) ->
+-    Acc1 = [Data | Acc],
+-    fun (Next) -> parse_form_value(Next, {Name, Acc1}, FileHandler, State) end.
+-
+-parse_form_file(body_end, {Name, Handler}, FileHandler, State) ->
+-    Value = Handler(eof),
+-    State1 = [{Name, Value} | State],
+-    fun (Next) -> parse_form_outer(Next, FileHandler, State1) end;
+-parse_form_file({body, Data}, {Name, Handler}, FileHandler, State) ->
+-    H1 = Handler(Data),
+-    fun (Next) -> parse_form_file(Next, {Name, H1}, FileHandler, State) end.
+-
+-default_file_handler(Filename, ContentType) ->
+-    default_file_handler_1(Filename, ContentType, []).
+-
+-default_file_handler_1(Filename, ContentType, Acc) ->
+-    fun(eof) ->
+-            Value = iolist_to_binary(lists:reverse(Acc)),
+-            {Filename, ContentType, Value};
+-       (Next) ->
+-            default_file_handler_1(Filename, ContentType, [Next | Acc])
+-    end.
+-
+-parse_multipart_request(Req, Callback) ->
+-    %% TODO: Support chunked?
+-    Length = list_to_integer(Req:get_header_value("content-length")),
+-    Boundary = iolist_to_binary(
+-                 get_boundary(Req:get_header_value("content-type"))),
+-    Prefix = <<"\r\n--", Boundary/binary>>,
+-    BS = size(Boundary),
+-    Chunk = read_chunk(Req, Length),
+-    Length1 = Length - size(Chunk),
+-    <<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk,
+-    feed_mp(headers, #mp{boundary=Prefix,
+-                         length=Length1,
+-                         buffer=Rest,
+-                         callback=Callback,
+-                         req=Req}).
+-
+-parse_headers(<<>>) ->
+-    [];
+-parse_headers(Binary) ->
+-    parse_headers(Binary, []).
+-
+-parse_headers(Binary, Acc) ->
+-    case find_in_binary(<<"\r\n">>, Binary) of
+-        {exact, N} ->
+-            <<Line:N/binary, "\r\n", Rest/binary>> = Binary,
+-            parse_headers(Rest, [split_header(Line) | Acc]);
+-        not_found ->
+-            lists:reverse([split_header(Binary) | Acc])
+-    end.
+-
+-split_header(Line) ->
+-    {Name, [$: | Value]} = lists:splitwith(fun (C) -> C =/= $: end,
+-                                           binary_to_list(Line)),
+-    {string:to_lower(string:strip(Name)),
+-     mochiweb_util:parse_header(Value)}.
+-
+-read_chunk(Req, Length) when Length > 0 ->
+-    case Length of
+-        Length when Length < ?CHUNKSIZE ->
+-            Req:recv(Length);
+-        _ ->
+-            Req:recv(?CHUNKSIZE)
+-    end.
+-
+-read_more(State=#mp{length=Length, buffer=Buffer, req=Req}) ->
+-    Data = read_chunk(Req, Length),
+-    Buffer1 = <<Buffer/binary, Data/binary>>,
+-    State#mp{length=Length - size(Data),
+-             buffer=Buffer1}.
+-
+-feed_mp(headers, State=#mp{buffer=Buffer, callback=Callback}) ->
+-    {State1, P} = case find_in_binary(<<"\r\n\r\n">>, Buffer) of
+-                      {exact, N} ->
+-                          {State, N};
+-                      _ ->
+-                          S1 = read_more(State),
+-                          %% Assume headers must be less than ?CHUNKSIZE
+-                          {exact, N} = find_in_binary(<<"\r\n\r\n">>,
+-                                                      S1#mp.buffer),
+-                          {S1, N}
+-                  end,
+-    <<Headers:P/binary, "\r\n\r\n", Rest/binary>> = State1#mp.buffer,
+-    NextCallback = Callback({headers, parse_headers(Headers)}),
+-    feed_mp(body, State1#mp{buffer=Rest,
+-                            callback=NextCallback});
+-feed_mp(body, State=#mp{boundary=Prefix, buffer=Buffer, callback=Callback}) ->
+-    case find_boundary(Prefix, Buffer) of
+-        {end_boundary, Start, Skip} ->
+-            <<Data:Start/binary, _:Skip/binary, Rest/binary>> = Buffer,
+-            C1 = Callback({body, Data}),
+-            C2 = C1(body_end),
+-            {State#mp.length, Rest, C2(eof)};
+-        {next_boundary, Start, Skip} ->
+-            <<Data:Start/binary, _:Skip/binary, Rest/binary>> = Buffer,
+-            C1 = Callback({body, Data}),
+-            feed_mp(headers, State#mp{callback=C1(body_end),
+-                                      buffer=Rest});
+-        {maybe, Start} ->
+-            <<Data:Start/binary, Rest/binary>> = Buffer,
+-            feed_mp(body, read_more(State#mp{callback=Callback({body, Data}),
+-                                             buffer=Rest}));
+-        not_found ->
+-            {Data, Rest} = {Buffer, <<>>},
+-            feed_mp(body, read_more(State#mp{callback=Callback({body, Data}),
+-                                             buffer=Rest}))
+-    end.
+-
+-get_boundary(ContentType) ->
+-    {"multipart/form-data", Opts} = mochiweb_util:parse_header(ContentType),
+-    case proplists:get_value("boundary", Opts) of
+-        S when is_list(S) ->
+-            S
+-    end.
+-
+-find_in_binary(B, Data) when size(B) > 0 ->
+-    case size(Data) - size(B) of
+-        Last when Last < 0 ->
+-            partial_find(B, Data, 0, size(Data));
+-        Last ->
+-            find_in_binary(B, size(B), Data, 0, Last)
+-    end.
+-
+-find_in_binary(B, BS, D, N, Last) when N =< Last->
+-    case D of
+-        <<_:N/binary, B:BS/binary, _/binary>> ->
+-            {exact, N};
+-        _ ->
+-            find_in_binary(B, BS, D, 1 + N, Last)
+-    end;
+-find_in_binary(B, BS, D, N, Last) when N =:= 1 + Last ->
+-    partial_find(B, D, N, BS - 1).
+-
+-partial_find(_B, _D, _N, 0) ->
+-    not_found;
+-partial_find(B, D, N, K) ->
+-    <<B1:K/binary, _/binary>> = B,
+-    case D of
+-        <<_Skip:N/binary, B1:K/binary>> ->
+-            {partial, N, K};
+-        _ ->
+-            partial_find(B, D, 1 + N, K - 1)
+-    end.
+-
+-find_boundary(Prefix, Data) ->
+-    case find_in_binary(Prefix, Data) of
+-        {exact, Skip} ->
+-            PrefixSkip = Skip + size(Prefix),
+-            case Data of
+-                <<_:PrefixSkip/binary, "\r\n", _/binary>> ->
+-                    {next_boundary, Skip, size(Prefix) + 2};
+-                <<_:PrefixSkip/binary, "--\r\n", _/binary>> ->
+-                    {end_boundary, Skip, size(Prefix) + 4};
+-                _ when size(Data) < PrefixSkip + 4 ->
+-                    %% Underflow
+-                    {maybe, Skip};
+-                _ ->
+-                    %% False positive
+-                    not_found
+-            end;
+-        {partial, Skip, Length} when (Skip + Length) =:= size(Data) ->
+-            %% Underflow
+-            {maybe, Skip};
+-        _ ->
+-            not_found
+-    end.
+-
+-with_socket_server(ServerFun, ClientFun) ->
+-    {ok, Server} = mochiweb_socket_server:start([{ip, "127.0.0.1"},
+-                                                 {port, 0},
+-                                                 {loop, ServerFun}]),
+-    Port = mochiweb_socket_server:get(Server, port),
+-    {ok, Client} = gen_tcp:connect("127.0.0.1", Port,
+-                                   [binary, {active, false}]),
+-    Res = (catch ClientFun(Client)),
+-    mochiweb_socket_server:stop(Server),
+-    Res.
+-
+-fake_request(Socket, ContentType, Length) ->
+-    mochiweb_request:new(Socket,
+-                         'POST',
+-                         "/multipart",
+-                         {1,1},
+-                         mochiweb_headers:make(
+-                           [{"content-type", ContentType},
+-                            {"content-length", Length}])).
+-
+-test_callback(Expect, [Expect | Rest]) ->
+-    case Rest of
+-        [] ->
+-            ok;
+-        _ ->
+-            fun (Next) -> test_callback(Next, Rest) end
+-    end.
+-
+-test_parse3() ->
+-    ContentType = "multipart/form-data; boundary=---------------------------7386909285754635891697677882",
+-    BinContent = <<"-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test_file.txt\"\r\nContent-Type: text/plain\r\n\r\nWoo multiline text file\n\nLa la la\r\n-----------------------------7386909285754635891697677882--\r\n">>,
+-    Expect = [{headers,
+-               [{"content-disposition",
+-                 {"form-data", [{"name", "hidden"}]}}]},
+-              {body, <<"multipart message">>},
+-              body_end,
+-              {headers,
+-               [{"content-disposition",
+-                 {"form-data", [{"name", "file"}, {"filename", "test_file.txt"}]}},
+-                {"content-type", {"text/plain", []}}]},
+-              {body, <<"Woo multiline text file\n\nLa la la">>},
+-              body_end,
+-              eof],
+-    TestCallback = fun (Next) -> test_callback(Next, Expect) end,
+-    ServerFun = fun (Socket) ->
+-                        case gen_tcp:send(Socket, BinContent) of
+-                            ok ->
+-                                exit(normal)
+-                        end
+-                end,
+-    ClientFun = fun (Socket) ->
+-                        Req = fake_request(Socket, ContentType,
+-                                           size(BinContent)),
+-                        Res = parse_multipart_request(Req, TestCallback),
+-                        {0, <<>>, ok} = Res,
+-                        ok
+-                end,
+-    ok = with_socket_server(ServerFun, ClientFun),
+-    ok.
+-
+-
+-test_parse2() ->
+-    ContentType = "multipart/form-data; boundary=---------------------------6072231407570234361599764024",
+-    BinContent = <<"-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----------------------------6072231407570234361599764024--\r\n">>,
+-    Expect = [{headers,
+-               [{"content-disposition",
+-                 {"form-data", [{"name", "hidden"}]}}]},
+-              {body, <<"multipart message">>},
+-              body_end,
+-              {headers,
+-               [{"content-disposition",
+-                 {"form-data", [{"name", "file"}, {"filename", ""}]}},
+-                {"content-type", {"application/octet-stream", []}}]},
+-              {body, <<>>},
+-              body_end,
+-              eof],
+-    TestCallback = fun (Next) -> test_callback(Next, Expect) end,
+-    ServerFun = fun (Socket) ->
+-                        case gen_tcp:send(Socket, BinContent) of
+-                            ok ->
+-                                exit(normal)
+-                        end
+-                end,
+-    ClientFun = fun (Socket) ->
+-                        Req = fake_request(Socket, ContentType,
+-                                           size(BinContent)),
+-                        Res = parse_multipart_request(Req, TestCallback),
+-                        {0, <<>>, ok} = Res,
+-                        ok
+-                end,
+-    ok = with_socket_server(ServerFun, ClientFun),
+-    ok.
+-
+-test_parse_form() ->
+-    ContentType = "multipart/form-data; boundary=AaB03x",
+-    "AaB03x" = get_boundary(ContentType),
+-    Content = mochiweb_util:join(
+-                ["--AaB03x",
+-                 "Content-Disposition: form-data; name=\"submit-name\"",
+-                 "",
+-                 "Larry",
+-                 "--AaB03x",
+-                 "Content-Disposition: form-data; name=\"files\";"
+-                 ++ "filename=\"file1.txt\"",
+-                 "Content-Type: text/plain",
+-                 "",
+-                 "... contents of file1.txt ...",
+-                 "--AaB03x--",
+-                 ""], "\r\n"),
+-    BinContent = iolist_to_binary(Content),
+-    ServerFun = fun (Socket) ->
+-                        case gen_tcp:send(Socket, BinContent) of
+-                            ok ->
+-                                exit(normal)
+-                        end
+-                end,
+-    ClientFun = fun (Socket) ->
+-                        Req = fake_request(Socket, ContentType,
+-                                           size(BinContent)),
+-                        Res = parse_form(Req),
+-                        [{"submit-name", "Larry"},
+-                         {"files", {"file1.txt", {"text/plain",[]},
+-                                    <<"... contents of file1.txt ...">>}
+-                         }] = Res,
+-                        ok
+-                end,
+-    ok = with_socket_server(ServerFun, ClientFun),
+-    ok.
+-
+-test_parse() ->
+-    ContentType = "multipart/form-data; boundary=AaB03x",
+-    "AaB03x" = get_boundary(ContentType),
+-    Content = mochiweb_util:join(
+-                ["--AaB03x",
+-                 "Content-Disposition: form-data; name=\"submit-name\"",
+-                 "",
+-                 "Larry",
+-                 "--AaB03x",
+-                 "Content-Disposition: form-data; name=\"files\";"
+-                 ++ "filename=\"file1.txt\"",
+-                 "Content-Type: text/plain",
+-                 "",
+-                 "... contents of file1.txt ...",
+-                 "--AaB03x--",
+-                 ""], "\r\n"),
+-    BinContent = iolist_to_binary(Content),
+-    Expect = [{headers,
+-               [{"content-disposition",
+-                 {"form-data", [{"name", "submit-name"}]}}]},
+-              {body, <<"Larry">>},
+-              body_end,
+-              {headers,
+-               [{"content-disposition",
+-                 {"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}},
+-                 {"content-type", {"text/plain", []}}]},
+-              {body, <<"... contents of file1.txt ...">>},
+-              body_end,
+-              eof],
+-    TestCallback = fun (Next) -> test_callback(Next, Expect) end,
+-    ServerFun = fun (Socket) ->
+-                        case gen_tcp:send(Socket, BinContent) of
+-                            ok ->
+-                                exit(normal)
+-                        end
+-                end,
+-    ClientFun = fun (Socket) ->
+-                        Req = fake_request(Socket, ContentType,
+-                                           size(BinContent)),
+-                        Res = parse_multipart_request(Req, TestCallback),
+-                        {0, <<>>, ok} = Res,
+-                        ok
+-                end,
+-    ok = with_socket_server(ServerFun, ClientFun),
+-    ok.
+-
+-test_find_boundary() ->
+-    B = <<"\r\n--X">>,
+-    {next_boundary, 0, 7} = find_boundary(B, <<"\r\n--X\r\nRest">>),
+-    {next_boundary, 1, 7} = find_boundary(B, <<"!\r\n--X\r\nRest">>),
+-    {end_boundary, 0, 9} = find_boundary(B, <<"\r\n--X--\r\nRest">>),
+-    {end_boundary, 1, 9} = find_boundary(B, <<"!\r\n--X--\r\nRest">>),
+-    not_found = find_boundary(B, <<"--X\r\nRest">>),
+-    {maybe, 0} = find_boundary(B, <<"\r\n--X\r">>),
+-    {maybe, 1} = find_boundary(B, <<"!\r\n--X\r">>),
+-    P = <<"\r\n-----------------------------16037454351082272548568224146">>,
+-    B0 = <<55,212,131,77,206,23,216,198,35,87,252,118,252,8,25,211,132,229,
+-          182,42,29,188,62,175,247,243,4,4,0,59, 13,10,45,45,45,45,45,45,45,
+-          45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,
+-          49,54,48,51,55,52,53,52,51,53,49>>,
+-    {maybe, 30} = find_boundary(P, B0),
+-    ok.
+-
+-test_find_in_binary() ->
+-    {exact, 0} = find_in_binary(<<"foo">>, <<"foobarbaz">>),
+-    {exact, 1} = find_in_binary(<<"oo">>, <<"foobarbaz">>),
+-    {exact, 8} = find_in_binary(<<"z">>, <<"foobarbaz">>),
+-    not_found = find_in_binary(<<"q">>, <<"foobarbaz">>),
+-    {partial, 7, 2} = find_in_binary(<<"azul">>, <<"foobarbaz">>),
+-    {exact, 0} = find_in_binary(<<"foobarbaz">>, <<"foobarbaz">>),
+-    {partial, 0, 3} = find_in_binary(<<"foobar">>, <<"foo">>),
+-    {partial, 1, 3} = find_in_binary(<<"foobar">>, <<"afoo">>),
+-    ok.
+-
+-test() ->
+-    test_find_in_binary(),
+-    test_find_boundary(),
+-    test_parse(),
+-    test_parse2(),
+-    test_parse3(),
+-    test_parse_form(),
+-    ok.
+diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl
+deleted file mode 100644
+index 64c4f58..0000000
+--- a/src/mochiweb/mochiweb_request.erl
++++ /dev/null
+@@ -1,790 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc MochiWeb HTTP Request abstraction.
+-
+--module(mochiweb_request, [Socket, Method, RawPath, Version, Headers]).
+--author('bob at mochimedia.com').
+-
+--include_lib("kernel/include/file.hrl").
+-
+--define(QUIP, "Any of you quaids got a smint?").
+--define(READ_SIZE, 8192).
+-
+--export([get_header_value/1, get_primary_header_value/1, get/1, dump/0]).
+--export([send/1, recv/1, recv/2, recv_body/0, recv_body/1, stream_body/3]).
+--export([start_response/1, start_response_length/1, start_raw_response/1]).
+--export([respond/1, ok/1]).
+--export([not_found/0, not_found/1]).
+--export([parse_post/0, parse_qs/0]).
+--export([should_close/0, cleanup/0]).
+--export([parse_cookie/0, get_cookie_value/1]).
+--export([serve_file/2, serve_file/3]).
+--export([test/0]).
+-
+--define(SAVE_QS, mochiweb_request_qs).
+--define(SAVE_PATH, mochiweb_request_path).
+--define(SAVE_RECV, mochiweb_request_recv).
+--define(SAVE_BODY, mochiweb_request_body).
+--define(SAVE_BODY_LENGTH, mochiweb_request_body_length).
+--define(SAVE_POST, mochiweb_request_post).
+--define(SAVE_COOKIE, mochiweb_request_cookie).
+--define(SAVE_FORCE_CLOSE, mochiweb_request_force_close).
+-
+-%% @type iolist() = [iolist() | binary() | char()].
+-%% @type iodata() = binary() | iolist().
+-%% @type key() = atom() | string() | binary()
+-%% @type value() = atom() | string() | binary() | integer()
+-%% @type headers(). A mochiweb_headers structure.
+-%% @type response(). A mochiweb_response parameterized module instance.
+-%% @type ioheaders() = headers() | [{key(), value()}].
+-
+-% 10 second default idle timeout
+--define(IDLE_TIMEOUT, 10000).
+-
+-% Maximum recv_body() length of 1MB
+--define(MAX_RECV_BODY, (1024*1024)).
+-
+-%% @spec get_header_value(K) -> undefined | Value
+-%% @doc Get the value of a given request header.
+-get_header_value(K) ->
+-    mochiweb_headers:get_value(K, Headers).
+-
+-get_primary_header_value(K) ->
+-    mochiweb_headers:get_primary_value(K, Headers).
+-
+-%% @type field() = socket | method | raw_path | version | headers | peer | path | body_length | range
+-
+-%% @spec get(field()) -> term()
+-%% @doc Return the internal representation of the given field.
+-get(socket) ->
+-    Socket;
+-get(method) ->
+-    Method;
+-get(raw_path) ->
+-    RawPath;
+-get(version) ->
+-    Version;
+-get(headers) ->
+-    Headers;
+-get(peer) ->
+-    case inet:peername(Socket) of
+-        {ok, {Addr={10, _, _, _}, _Port}} ->
+-            case get_header_value("x-forwarded-for") of
+-                undefined ->
+-                    inet_parse:ntoa(Addr);
+-                Hosts ->
+-                    string:strip(lists:last(string:tokens(Hosts, ",")))
+-            end;
+-        {ok, {{127, 0, 0, 1}, _Port}} ->
+-            case get_header_value("x-forwarded-for") of
+-                undefined ->
+-                    "127.0.0.1";
+-                Hosts ->
+-                    string:strip(lists:last(string:tokens(Hosts, ",")))
+-            end;
+-        {ok, {Addr, _Port}} ->
+-            inet_parse:ntoa(Addr)
+-    end;
+-get(path) ->
+-    case erlang:get(?SAVE_PATH) of
+-        undefined ->
+-            {Path0, _, _} = mochiweb_util:urlsplit_path(RawPath),
+-            Path = mochiweb_util:unquote(Path0),
+-            put(?SAVE_PATH, Path),
+-            Path;
+-        Cached ->
+-            Cached
+-    end;
+-get(body_length) ->
+-    erlang:get(?SAVE_BODY_LENGTH);
+-get(range) ->
+-    case get_header_value(range) of
+-        undefined ->
+-            undefined;
+-        RawRange ->
+-            parse_range_request(RawRange)
+-    end.
+-
+-%% @spec dump() -> {mochiweb_request, [{atom(), term()}]}
+-%% @doc Dump the internal representation to a "human readable" set of terms
+-%%      for debugging/inspection purposes.
+-dump() ->
+-    {?MODULE, [{method, Method},
+-               {version, Version},
+-               {raw_path, RawPath},
+-               {headers, mochiweb_headers:to_list(Headers)}]}.
+-
+-%% @spec send(iodata()) -> ok
+-%% @doc Send data over the socket.
+-send(Data) ->
+-    case gen_tcp:send(Socket, Data) of
+-        ok ->
+-            ok;
+-        _ ->
+-            exit(normal)
+-    end.
+-
+-%% @spec recv(integer()) -> binary()
+-%% @doc Receive Length bytes from the client as a binary, with the default
+-%%      idle timeout.
+-recv(Length) ->
+-    recv(Length, ?IDLE_TIMEOUT).
+-
+-%% @spec recv(integer(), integer()) -> binary()
+-%% @doc Receive Length bytes from the client as a binary, with the given
+-%%      Timeout in msec.
+-recv(Length, Timeout) ->
+-    case gen_tcp:recv(Socket, Length, Timeout) of
+-        {ok, Data} ->
+-            put(?SAVE_RECV, true),
+-            Data;
+-        _ ->
+-            exit(normal)
+-    end.
+-
+-%% @spec body_length() -> undefined | chunked | unknown_transfer_encoding | integer()
+-%% @doc  Infer body length from transfer-encoding and content-length headers.
+-body_length() ->
+-    case get_header_value("transfer-encoding") of
+-        undefined ->
+-            case get_header_value("content-length") of
+-                undefined ->
+-                    undefined;
+-                Length ->
+-                    list_to_integer(Length)
+-            end;
+-        "chunked" ->
+-            chunked;
+-        Unknown ->
+-            {unknown_transfer_encoding, Unknown}
+-    end.
+-
+-
+-%% @spec recv_body() -> binary()
+-%% @doc Receive the body of the HTTP request (defined by Content-Length).
+-%%      Will only receive up to the default max-body length of 1MB.
+-recv_body() ->
+-    recv_body(?MAX_RECV_BODY).
+-
+-%% @spec recv_body(integer()) -> binary()
+-%% @doc Receive the body of the HTTP request (defined by Content-Length).
+-%%      Will receive up to MaxBody bytes.
+-recv_body(MaxBody) ->
+-    % we could use a sane constant for max chunk size
+-    Body = stream_body(?MAX_RECV_BODY, fun
+-        ({0, _ChunkedFooter}, {_LengthAcc, BinAcc}) ->
+-            iolist_to_binary(lists:reverse(BinAcc));
+-        ({Length, Bin}, {LengthAcc, BinAcc}) ->
+-            NewLength = Length + LengthAcc,
+-            if NewLength > MaxBody ->
+-                exit({body_too_large, chunked});
+-            true ->
+-                {NewLength, [Bin | BinAcc]}
+-            end
+-        end, {0, []}, MaxBody),
+-    put(?SAVE_BODY, Body),
+-    Body.
+-
+-stream_body(MaxChunkSize, ChunkFun, FunState) ->
+-    stream_body(MaxChunkSize, ChunkFun, FunState, undefined).
+-
+-stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength) ->
+-
+-    case get_header_value("expect") of
+-        "100-continue" ->
+-            start_raw_response({100, gb_trees:empty()});
+-        _Else ->
+-            ok
+-    end,
+-    case body_length() of
+-        undefined ->
+-            undefined;
+-        {unknown_transfer_encoding, Unknown} ->
+-            exit({unknown_transfer_encoding, Unknown});
+-        chunked ->
+-            % In this case the MaxBody is actually used to
+-            % determine the maximum allowed size of a single
+-            % chunk.
+-            stream_chunked_body(MaxChunkSize, ChunkFun, FunState);
+-        0 ->
+-            <<>>;
+-        Length when is_integer(Length) ->
+-            case MaxBodyLength of
+-            MaxBodyLength when is_integer(MaxBodyLength), MaxBodyLength < Length ->
+-                exit({body_too_large, content_length});
+-            _ ->
+-                stream_unchunked_body(Length, MaxChunkSize, ChunkFun, FunState)
+-            end;
+-        Length ->
+-            exit({length_not_integer, Length})
+-    end.
+-
+-
+-%% @spec start_response({integer(), ioheaders()}) -> response()
+-%% @doc Start the HTTP response by sending the Code HTTP response and
+-%%      ResponseHeaders. The server will set header defaults such as Server
+-%%      and Date if not present in ResponseHeaders.
+-start_response({Code, ResponseHeaders}) ->
+-    HResponse = mochiweb_headers:make(ResponseHeaders),
+-    HResponse1 = mochiweb_headers:default_from_list(server_headers(),
+-                                                    HResponse),
+-    start_raw_response({Code, HResponse1}).
+-
+-%% @spec start_raw_response({integer(), headers()}) -> response()
+-%% @doc Start the HTTP response by sending the Code HTTP response and
+-%%      ResponseHeaders.
+-start_raw_response({Code, ResponseHeaders}) ->
+-    F = fun ({K, V}, Acc) ->
+-                [make_io(K), <<": ">>, V, <<"\r\n">> | Acc]
+-        end,
+-    End = lists:foldl(F, [<<"\r\n">>],
+-                      mochiweb_headers:to_list(ResponseHeaders)),
+-    send([make_version(Version), make_code(Code), <<"\r\n">> | End]),
+-    mochiweb:new_response({THIS, Code, ResponseHeaders}).
+-
+-
+-%% @spec start_response_length({integer(), ioheaders(), integer()}) -> response()
+-%% @doc Start the HTTP response by sending the Code HTTP response and
+-%%      ResponseHeaders including a Content-Length of Length. The server
+-%%      will set header defaults such as Server
+-%%      and Date if not present in ResponseHeaders.
+-start_response_length({Code, ResponseHeaders, Length}) ->
+-    HResponse = mochiweb_headers:make(ResponseHeaders),
+-    HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse),
+-    start_response({Code, HResponse1}).
+-
+-%% @spec respond({integer(), ioheaders(), iodata() | chunked | {file, IoDevice}}) -> response()
+-%% @doc Start the HTTP response with start_response, and send Body to the
+-%%      client (if the get(method) /= 'HEAD'). The Content-Length header
+-%%      will be set by the Body length, and the server will insert header
+-%%      defaults.
+-respond({Code, ResponseHeaders, {file, IoDevice}}) ->
+-    Length = iodevice_size(IoDevice),
+-    Response = start_response_length({Code, ResponseHeaders, Length}),
+-    case Method of
+-        'HEAD' ->
+-            ok;
+-        _ ->
+-            iodevice_stream(IoDevice)
+-    end,
+-    Response;
+-respond({Code, ResponseHeaders, chunked}) ->
+-    HResponse = mochiweb_headers:make(ResponseHeaders),
+-    HResponse1 = case Method of
+-                     'HEAD' ->
+-                         %% This is what Google does, http://www.google.com/
+-                         %% is chunked but HEAD gets Content-Length: 0.
+-                         %% The RFC is ambiguous so emulating Google is smart.
+-                         mochiweb_headers:enter("Content-Length", "0",
+-                                                HResponse);
+-                     _ when Version >= {1, 1} ->
+-                         %% Only use chunked encoding for HTTP/1.1
+-                         mochiweb_headers:enter("Transfer-Encoding", "chunked",
+-                                                HResponse);
+-                     _ ->
+-                         %% For pre-1.1 clients we send the data as-is
+-                         %% without a Content-Length header and without
+-                         %% chunk delimiters. Since the end of the document
+-                         %% is now ambiguous we must force a close.
+-                         put(?SAVE_FORCE_CLOSE, true),
+-                         HResponse
+-                 end,
+-    start_response({Code, HResponse1});
+-respond({Code, ResponseHeaders, Body}) ->
+-    Response = start_response_length({Code, ResponseHeaders, iolist_size(Body)}),
+-    case Method of
+-        'HEAD' ->
+-            ok;
+-        _ ->
+-            send(Body)
+-    end,
+-    Response.
+-
+-%% @spec not_found() -> response()
+-%% @doc Alias for <code>not_found([])</code>.
+-not_found() ->
+-    not_found([]).
+-
+-%% @spec not_found(ExtraHeaders) -> response()
+-%% @doc Alias for <code>respond({404, [{"Content-Type", "text/plain"}
+-%% | ExtraHeaders], &lt;&lt;"Not found."&gt;&gt;})</code>.
+-not_found(ExtraHeaders) ->
+-    respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders],
+-             <<"Not found.">>}).
+-
+-%% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}) ->
+-%%           response()
+-%% @doc respond({200, [{"Content-Type", ContentType} | Headers], Body}).
+-ok({ContentType, Body}) ->
+-    ok({ContentType, [], Body});
+-ok({ContentType, ResponseHeaders, Body}) ->
+-    HResponse = mochiweb_headers:make(ResponseHeaders),
+-    case THIS:get(range) of
+-        X when X =:= undefined; X =:= fail ->
+-            HResponse1 = mochiweb_headers:enter("Content-Type", ContentType, HResponse),
+-            respond({200, HResponse1, Body});
+-        Ranges ->
+-            {PartList, Size} = range_parts(Body, Ranges),
+-            case PartList of
+-                [] -> %% no valid ranges
+-                    HResponse1 = mochiweb_headers:enter("Content-Type",
+-                                                        ContentType,
+-                                                        HResponse),
+-                    %% could be 416, for now we'll just return 200
+-                    respond({200, HResponse1, Body});
+-                PartList ->
+-                    {RangeHeaders, RangeBody} =
+-                        parts_to_body(PartList, ContentType, Size),
+-                    HResponse1 = mochiweb_headers:enter_from_list(
+-                                   [{"Accept-Ranges", "bytes"} |
+-                                    RangeHeaders],
+-                                   HResponse),
+-                    respond({206, HResponse1, RangeBody})
+-            end
+-    end.
+-
+-%% @spec should_close() -> bool()
+-%% @doc Return true if the connection must be closed. If false, using
+-%%      Keep-Alive should be safe.
+-should_close() ->
+-    ForceClose = erlang:get(mochiweb_request_force_close) =/= undefined,
+-    DidNotRecv = erlang:get(mochiweb_request_recv) =:= undefined,
+-    ForceClose orelse Version < {1, 0}
+-        %% Connection: close
+-        orelse get_header_value("connection") =:= "close"
+-        %% HTTP 1.0 requires Connection: Keep-Alive
+-        orelse (Version =:= {1, 0}
+-                andalso get_header_value("connection") =/= "Keep-Alive")
+-        %% unread data left on the socket, can't safely continue
+-        orelse (DidNotRecv
+-                andalso get_header_value("content-length") =/= undefined
+-                andalso list_to_integer(get_header_value("content-length")) > 0)
+-        orelse (DidNotRecv
+-                andalso get_header_value("transfer-encoding") =:= "chunked").
+-
+-%% @spec cleanup() -> ok
+-%% @doc Clean up any junk in the process dictionary, required before continuing
+-%%      a Keep-Alive request.
+-cleanup() ->
+-    [erase(K) || K <- [?SAVE_QS,
+-                       ?SAVE_PATH,
+-                       ?SAVE_RECV,
+-                       ?SAVE_BODY,
+-                       ?SAVE_POST,
+-                       ?SAVE_COOKIE,
+-                       ?SAVE_FORCE_CLOSE]],
+-    ok.
+-
+-%% @spec parse_qs() -> [{Key::string(), Value::string()}]
+-%% @doc Parse the query string of the URL.
+-parse_qs() ->
+-    case erlang:get(?SAVE_QS) of
+-        undefined ->
+-            {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath),
+-            Parsed = mochiweb_util:parse_qs(QueryString),
+-            put(?SAVE_QS, Parsed),
+-            Parsed;
+-        Cached ->
+-            Cached
+-    end.
+-
+-%% @spec get_cookie_value(Key::string) -> string() | undefined
+-%% @doc Get the value of the given cookie.
+-get_cookie_value(Key) ->
+-    proplists:get_value(Key, parse_cookie()).
+-
+-%% @spec parse_cookie() -> [{Key::string(), Value::string()}]
+-%% @doc Parse the cookie header.
+-parse_cookie() ->
+-    case erlang:get(?SAVE_COOKIE) of
+-        undefined ->
+-            Cookies = case get_header_value("cookie") of
+-                          undefined ->
+-                              [];
+-                          Value ->
+-                              mochiweb_cookies:parse_cookie(Value)
+-                      end,
+-            put(?SAVE_COOKIE, Cookies),
+-            Cookies;
+-        Cached ->
+-            Cached
+-    end.
+-
+-%% @spec parse_post() -> [{Key::string(), Value::string()}]
+-%% @doc Parse an application/x-www-form-urlencoded form POST. This
+-%%      has the side-effect of calling recv_body().
+-parse_post() ->
+-    case erlang:get(?SAVE_POST) of
+-        undefined ->
+-            Parsed = case recv_body() of
+-                         undefined ->
+-                             [];
+-                         Binary ->
+-                             case get_primary_header_value("content-type") of
+-                                 "application/x-www-form-urlencoded" ++ _ ->
+-                                     mochiweb_util:parse_qs(Binary);
+-                                 _ ->
+-                                     []
+-                             end
+-                     end,
+-            put(?SAVE_POST, Parsed),
+-            Parsed;
+-        Cached ->
+-            Cached
+-    end.
+-
+-%% @spec stream_chunked_body(integer(), fun(), term()) -> term()
+-%% @doc The function is called for each chunk.
+-%%      Used internally by read_chunked_body.
+-stream_chunked_body(MaxChunkSize, Fun, FunState) ->
+-    case read_chunk_length() of
+-        0 ->
+-            Fun({0, read_chunk(0)}, FunState);
+-        Length when Length > MaxChunkSize ->
+-            NewState = read_sub_chunks(Length, MaxChunkSize, Fun, FunState),
+-            stream_chunked_body(MaxChunkSize, Fun, NewState);
+-        Length ->
+-            NewState = Fun({Length, read_chunk(Length)}, FunState),
+-            stream_chunked_body(MaxChunkSize, Fun, NewState)
+-    end.
+-
+-stream_unchunked_body(0, _MaxChunkSize, Fun, FunState) ->
+-    Fun({0, <<>>}, FunState);
+-stream_unchunked_body(Length, MaxChunkSize, Fun, FunState) when Length > MaxChunkSize ->
+-    Bin = recv(MaxChunkSize),
+-    NewState = Fun({MaxChunkSize, Bin}, FunState),
+-    stream_unchunked_body(Length - MaxChunkSize, MaxChunkSize, Fun, NewState);
+-stream_unchunked_body(Length, MaxChunkSize, Fun, FunState) ->
+-    Bin = recv(Length),
+-    NewState = Fun({Length, Bin}, FunState),
+-    stream_unchunked_body(0, MaxChunkSize, Fun, NewState).
+-
+-
+-%% @spec read_chunk_length() -> integer()
+-%% @doc Read the length of the next HTTP chunk.
+-read_chunk_length() ->
+-    inet:setopts(Socket, [{packet, line}]),
+-    case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of
+-        {ok, Header} ->
+-            inet:setopts(Socket, [{packet, raw}]),
+-            Splitter = fun (C) ->
+-                               C =/= $\r andalso C =/= $\n andalso C =/= $
+-                       end,
+-            {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)),
+-            mochihex:to_int(Hex);
+-        _ ->
+-            exit(normal)
+-    end.
+-
+-%% @spec read_chunk(integer()) -> Chunk::binary() | [Footer::binary()]
+-%% @doc Read in a HTTP chunk of the given length. If Length is 0, then read the
+-%%      HTTP footers (as a list of binaries, since they're nominal).
+-read_chunk(0) ->
+-    inet:setopts(Socket, [{packet, line}]),
+-    F = fun (F1, Acc) ->
+-                case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of
+-                    {ok, <<"\r\n">>} ->
+-                        Acc;
+-                    {ok, Footer} ->
+-                        F1(F1, [Footer | Acc]);
+-                    _ ->
+-                        exit(normal)
+-                end
+-        end,
+-    Footers = F(F, []),
+-    inet:setopts(Socket, [{packet, raw}]),
+-    Footers;
+-read_chunk(Length) ->
+-    case gen_tcp:recv(Socket, 2 + Length, ?IDLE_TIMEOUT) of
+-        {ok, <<Chunk:Length/binary, "\r\n">>} ->
+-            Chunk;
+-        _ ->
+-            exit(normal)
+-    end.
+-
+-read_sub_chunks(Length, MaxChunkSize, Fun, FunState) when Length > MaxChunkSize ->
+-    Bin = recv(MaxChunkSize),
+-    NewState = Fun({size(Bin), Bin}, FunState),
+-    read_sub_chunks(Length - MaxChunkSize, MaxChunkSize, Fun, NewState);
+-
+-read_sub_chunks(Length, _MaxChunkSize, Fun, FunState) ->
+-    Fun({Length, read_chunk(Length)}, FunState).
+-
+-%% @spec serve_file(Path, DocRoot) -> Response
+-%% @doc Serve a file relative to DocRoot.
+-serve_file(Path, DocRoot) ->
+-    serve_file(Path, DocRoot, []).
+-
+-%% @spec serve_file(Path, DocRoot, ExtraHeaders) -> Response
+-%% @doc Serve a file relative to DocRoot.
+-serve_file(Path, DocRoot, ExtraHeaders) ->
+-    case mochiweb_util:safe_relative_path(Path) of
+-        undefined ->
+-            not_found(ExtraHeaders);
+-        RelPath ->
+-            FullPath = filename:join([DocRoot, RelPath]),
+-            File = case filelib:is_dir(FullPath) of
+-                       true ->
+-                           filename:join([FullPath, "index.html"]);
+-                       false ->
+-                           FullPath
+-                   end,
+-            case file:read_file_info(File) of
+-                {ok, FileInfo} ->
+-                    LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime),
+-                    case get_header_value("if-modified-since") of
+-                        LastModified ->
+-                            respond({304, ExtraHeaders, ""});
+-                        _ ->
+-                            case file:open(File, [raw, binary]) of
+-                                {ok, IoDevice} ->
+-                                    ContentType = mochiweb_util:guess_mime(File),
+-                                    Res = ok({ContentType,
+-                                              [{"last-modified", LastModified}
+-                                               | ExtraHeaders],
+-                                              {file, IoDevice}}),
+-                                    file:close(IoDevice),
+-                                    Res;
+-                                _ ->
+-                                    not_found(ExtraHeaders)
+-                            end
+-                    end;
+-                {error, _} ->
+-                    not_found(ExtraHeaders)
+-            end
+-    end.
+-
+-
+-%% Internal API
+-
+-server_headers() ->
+-    [{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"},
+-     {"Date", httpd_util:rfc1123_date()}].
+-
+-make_io(Atom) when is_atom(Atom) ->
+-    atom_to_list(Atom);
+-make_io(Integer) when is_integer(Integer) ->
+-    integer_to_list(Integer);
+-make_io(Io) when is_list(Io); is_binary(Io) ->
+-    Io.
+-
+-make_code(X) when is_integer(X) ->
+-    [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]];
+-make_code(Io) when is_list(Io); is_binary(Io) ->
+-    Io.
+-
+-make_version({1, 0}) ->
+-    <<"HTTP/1.0 ">>;
+-make_version(_) ->
+-    <<"HTTP/1.1 ">>.
+-
+-iodevice_stream(IoDevice) ->
+-    case file:read(IoDevice, ?READ_SIZE) of
+-        eof ->
+-            ok;
+-        {ok, Data} ->
+-            ok = send(Data),
+-            iodevice_stream(IoDevice)
+-    end.
+-
+-
+-parts_to_body([{Start, End, Body}], ContentType, Size) ->
+-    %% return body for a range reponse with a single body
+-    HeaderList = [{"Content-Type", ContentType},
+-                  {"Content-Range",
+-                   ["bytes ",
+-                    make_io(Start), "-", make_io(End),
+-                    "/", make_io(Size)]}],
+-    {HeaderList, Body};
+-parts_to_body(BodyList, ContentType, Size) when is_list(BodyList) ->
+-    %% return
+-    %% header Content-Type: multipart/byteranges; boundary=441934886133bdee4
+-    %% and multipart body
+-    Boundary = mochihex:to_hex(crypto:rand_bytes(8)),
+-    HeaderList = [{"Content-Type",
+-                   ["multipart/byteranges; ",
+-                    "boundary=", Boundary]}],
+-    MultiPartBody = multipart_body(BodyList, ContentType, Boundary, Size),
+-
+-    {HeaderList, MultiPartBody}.
+-
+-multipart_body([], _ContentType, Boundary, _Size) ->
+-    ["--", Boundary, "--\r\n"];
+-multipart_body([{Start, End, Body} | BodyList], ContentType, Boundary, Size) ->
+-    ["--", Boundary, "\r\n",
+-     "Content-Type: ", ContentType, "\r\n",
+-     "Content-Range: ",
+-         "bytes ", make_io(Start), "-", make_io(End),
+-             "/", make_io(Size), "\r\n\r\n",
+-     Body, "\r\n"
+-     | multipart_body(BodyList, ContentType, Boundary, Size)].
+-
+-iodevice_size(IoDevice) ->
+-    {ok, Size} = file:position(IoDevice, eof),
+-    {ok, 0} = file:position(IoDevice, bof),
+-    Size.
+-
+-range_parts({file, IoDevice}, Ranges) ->
+-    Size = iodevice_size(IoDevice),
+-    F = fun (Spec, Acc) ->
+-                case range_skip_length(Spec, Size) of
+-                    invalid_range ->
+-                        Acc;
+-                    V ->
+-                        [V | Acc]
+-                end
+-        end,
+-    LocNums = lists:foldr(F, [], Ranges),
+-    {ok, Data} = file:pread(IoDevice, LocNums),
+-    Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) ->
+-                                   {Skip, Skip + Length - 1, PartialBody}
+-                           end,
+-                           LocNums, Data),
+-    {Bodies, Size};
+-
+-range_parts(Body0, Ranges) ->
+-    Body = iolist_to_binary(Body0),
+-    Size = size(Body),
+-    F = fun(Spec, Acc) ->
+-                case range_skip_length(Spec, Size) of
+-                    invalid_range ->
+-                        Acc;
+-                    {Skip, Length} ->
+-                        <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body,
+-                        [{Skip, Skip + Length - 1, PartialBody} | Acc]
+-                end
+-        end,
+-    {lists:foldr(F, [], Ranges), Size}.
+-
+-range_skip_length(Spec, Size) ->
+-    case Spec of
+-        {none, R} when R =< Size, R >= 0 ->
+-            {Size - R, R};
+-        {none, _OutOfRange} ->
+-            {0, Size};
+-        {R, none} when R >= 0, R < Size ->
+-            {R, Size - R};
+-        {_OutOfRange, none} ->
+-            invalid_range;
+-        {Start, End} when 0 =< Start, Start =< End, End < Size ->
+-            {Start, End - Start + 1};
+-        {_OutOfRange, _End} ->
+-            invalid_range
+-    end.
+-
+-parse_range_request(RawRange) when is_list(RawRange) ->
+-    try
+-        "bytes=" ++ RangeString = RawRange,
+-        Ranges = string:tokens(RangeString, ","),
+-        lists:map(fun ("-" ++ V)  ->
+-                          {none, list_to_integer(V)};
+-                      (R) ->
+-                          case string:tokens(R, "-") of
+-                              [S1, S2] ->
+-                                  {list_to_integer(S1), list_to_integer(S2)};
+-                              [S] ->
+-                                  {list_to_integer(S), none}
+-                          end
+-                  end,
+-                  Ranges)
+-    catch
+-        _:_ ->
+-            fail
+-    end.
+-
+-
+-test() ->
+-    ok = test_range(),
+-    ok.
+-
+-test_range() ->
+-    %% valid, single ranges
+-    io:format("Testing parse_range_request with valid single ranges~n"),
+-    io:format("1"),
+-    [{20, 30}] = parse_range_request("bytes=20-30"),
+-    io:format("2"),
+-    [{20, none}] = parse_range_request("bytes=20-"),
+-    io:format("3"),
+-    [{none, 20}] = parse_range_request("bytes=-20"),
+-    io:format(".. ok ~n"),
+-
+-
+-    %% invalid, single ranges
+-    io:format("Testing parse_range_request with invalid ranges~n"),
+-    io:format("1"),
+-    fail = parse_range_request(""),
+-    io:format("2"),
+-    fail = parse_range_request("garbage"),
+-    io:format("3"),
+-    fail = parse_range_request("bytes=-20-30"),
+-    io:format(".. ok ~n"),
+-
+-    %% valid, multiple range
+-    io:format("Testing parse_range_request with valid multiple ranges~n"),
+-    io:format("1"),
+-    [{20, 30}, {50, 100}, {110, 200}] =
+-        parse_range_request("bytes=20-30,50-100,110-200"),
+-    io:format("2"),
+-    [{20, none}, {50, 100}, {none, 200}] =
+-        parse_range_request("bytes=20-,50-100,-200"),
+-    io:format(".. ok~n"),
+-
+-    %% no ranges
+-    io:format("Testing out parse_range_request with no ranges~n"),
+-    io:format("1"),
+-    [] = parse_range_request("bytes="),
+-    io:format(".. ok~n"),
+-
+-    Body = <<"012345678901234567890123456789012345678901234567890123456789">>,
+-    BodySize = size(Body), %% 60
+-    BodySize = 60,
+-
+-    %% these values assume BodySize =:= 60
+-    io:format("Testing out range_skip_length on valid ranges~n"),
+-    io:format("1"),
+-    {1,9} = range_skip_length({1,9}, BodySize), %% 1-9
+-    io:format("2"),
+-    {10,10} = range_skip_length({10,19}, BodySize), %% 10-19
+-    io:format("3"),
+-    {40, 20} = range_skip_length({none, 20}, BodySize), %% -20
+-    io:format("4"),
+-    {30, 30} = range_skip_length({30, none}, BodySize), %% 30-
+-    io:format(".. ok ~n"),
+-
+-    %% valid edge cases for range_skip_length
+-    io:format("Testing out range_skip_length on valid edge case ranges~n"),
+-    io:format("1"),
+-    {BodySize, 0} = range_skip_length({none, 0}, BodySize),
+-    io:format("2"),
+-    {0, BodySize} = range_skip_length({none, BodySize}, BodySize),
+-    io:format("3"),
+-    {0, BodySize} = range_skip_length({0, none}, BodySize),
+-    BodySizeLess1 = BodySize - 1,
+-    io:format("4"),
+-    {BodySizeLess1, 1} = range_skip_length({BodySize - 1, none}, BodySize),
+-
+-    %% out of range, return whole thing
+-    io:format("5"),
+-    {0, BodySize} = range_skip_length({none, BodySize + 1}, BodySize),
+-    io:format("6"),
+-    {0, BodySize} = range_skip_length({none, -1}, BodySize),
+-    io:format(".. ok ~n"),
+-
+-    %% invalid ranges
+-    io:format("Testing out range_skip_length on invalid ranges~n"),
+-    io:format("1"),
+-    invalid_range = range_skip_length({-1, 30}, BodySize),
+-    io:format("2"),
+-    invalid_range = range_skip_length({0, BodySize + 1}, BodySize),
+-    io:format("3"),
+-    invalid_range = range_skip_length({-1, BodySize + 1}, BodySize),
+-    io:format("4"),
+-    invalid_range = range_skip_length({BodySize, 40}, BodySize),
+-    io:format("5"),
+-    invalid_range = range_skip_length({-1, none}, BodySize),
+-    io:format("6"),
+-    invalid_range = range_skip_length({BodySize, none}, BodySize),
+-    io:format(".. ok ~n"),
+-    ok.
+-
+diff --git a/src/mochiweb/mochiweb_response.erl b/src/mochiweb/mochiweb_response.erl
+deleted file mode 100644
+index 6285c4c..0000000
+--- a/src/mochiweb/mochiweb_response.erl
++++ /dev/null
+@@ -1,56 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Response abstraction.
+-
+--module(mochiweb_response, [Request, Code, Headers]).
+--author('bob at mochimedia.com').
+-
+--define(QUIP, "Any of you quaids got a smint?").
+-
+--export([get_header_value/1, get/1, dump/0]).
+--export([send/1, write_chunk/1]).
+-
+-%% @spec get_header_value(string() | atom() | binary()) -> string() | undefined
+-%% @doc Get the value of the given response header.
+-get_header_value(K) ->
+-    mochiweb_headers:get_value(K, Headers).
+-
+-%% @spec get(request | code | headers) -> term()
+-%% @doc Return the internal representation of the given field.
+-get(request) ->
+-    Request;
+-get(code) ->
+-    Code;
+-get(headers) ->
+-    Headers.
+-
+-%% @spec dump() -> {mochiweb_request, [{atom(), term()}]}
+-%% @doc Dump the internal representation to a "human readable" set of terms
+-%%      for debugging/inspection purposes.
+-dump() ->
+-    [{request, Request:dump()},
+-     {code, Code},
+-     {headers, mochiweb_headers:to_list(Headers)}].
+-
+-%% @spec send(iodata()) -> ok
+-%% @doc Send data over the socket if the method is not HEAD.
+-send(Data) ->
+-    case Request:get(method) of
+-        'HEAD' ->
+-            ok;
+-        _ ->
+-            Request:send(Data)
+-    end.
+-
+-%% @spec write_chunk(iodata()) -> ok
+-%% @doc Write a chunk of a HTTP chunked response. If Data is zero length,
+-%%      then the chunked response will be finished.
+-write_chunk(Data) ->
+-    case Request:get(version) of
+-        Version when Version >= {1, 1} ->
+-            Length = iolist_size(Data),
+-            send([io_lib:format("~.16b\r\n", [Length]), Data, <<"\r\n">>]);
+-        _ ->
+-            send(Data)
+-    end.
+diff --git a/src/mochiweb/mochiweb_skel.erl b/src/mochiweb/mochiweb_skel.erl
+deleted file mode 100644
+index 098951b..0000000
+--- a/src/mochiweb/mochiweb_skel.erl
++++ /dev/null
+@@ -1,71 +0,0 @@
+--module(mochiweb_skel).
+--export([skelcopy/2]).
+-
+--include_lib("kernel/include/file.hrl").
+-
+-%% External API
+-
+-skelcopy(DestDir, Name) ->
+-    ok = ensuredir(DestDir),
+-    LDst = case length(filename:dirname(DestDir)) of
+-               1 -> %% handle case when dirname returns "/"
+-                   0;
+-               N ->
+-                   N + 1
+-           end,
+-    skelcopy(src(), DestDir, Name, LDst),
+-    ok = file:make_symlink(
+-        filename:join(filename:dirname(code:which(?MODULE)), ".."),
+-        filename:join([DestDir, Name, "deps", "mochiweb-src"])).
+-
+-
+-%% Internal API
+-
+-src() ->
+-    Dir = filename:dirname(code:which(?MODULE)),
+-    filename:join(Dir, "../priv/skel").
+-
+-skel() ->
+-    "skel".
+-
+-skelcopy(Src, DestDir, Name, LDst) ->
+-    {ok, Dest, _} = regexp:gsub(filename:basename(Src), skel(), Name),
+-    case file:read_file_info(Src) of
+-        {ok, #file_info{type=directory, mode=Mode}} ->
+-            Dir = DestDir ++ "/" ++ Dest,
+-            EDst = lists:nthtail(LDst, Dir),
+-            ok = ensuredir(Dir),
+-            ok = file:write_file_info(Dir, #file_info{mode=Mode}),
+-            {ok, Files} = file:list_dir(Src),
+-            io:format("~s/~n", [EDst]),
+-            lists:foreach(fun ("." ++ _) -> ok;
+-                              (F) ->
+-                                  skelcopy(filename:join(Src, F),
+-                                           Dir,
+-                                           Name,
+-                                           LDst)
+-                          end,
+-                          Files),
+-            ok;
+-        {ok, #file_info{type=regular, mode=Mode}} ->
+-            OutFile = filename:join(DestDir, Dest),
+-            {ok, B} = file:read_file(Src),
+-            {ok, S, _} = regexp:gsub(binary_to_list(B), skel(), Name),
+-            ok = file:write_file(OutFile, list_to_binary(S)),
+-            ok = file:write_file_info(OutFile, #file_info{mode=Mode}),
+-            io:format("    ~s~n", [filename:basename(Src)]),
+-            ok;
+-        {ok, _} ->
+-            io:format("ignored source file: ~p~n", [Src]),
+-            ok
+-    end.
+-
+-ensuredir(Dir) ->
+-    case file:make_dir(Dir) of
+-        ok ->
+-            ok;
+-        {error, eexist} ->
+-            ok;
+-        E ->
+-            E
+-    end.
+diff --git a/src/mochiweb/mochiweb_socket_server.erl b/src/mochiweb/mochiweb_socket_server.erl
+deleted file mode 100644
+index a483c3d..0000000
+--- a/src/mochiweb/mochiweb_socket_server.erl
++++ /dev/null
+@@ -1,248 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc MochiWeb socket server.
+-
+--module(mochiweb_socket_server).
+--author('bob at mochimedia.com').
+--behaviour(gen_server).
+-
+--export([start/1, stop/1]).
+--export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3,
+-         handle_info/2]).
+--export([get/2]).
+-
+--export([acceptor_loop/1]).
+-
+--record(mochiweb_socket_server,
+-        {port,
+-         loop,
+-         name=undefined,
+-         max=2048,
+-         ip=any,
+-         listen=null,
+-         acceptor=null,
+-         backlog=30}).
+-
+-start(State=#mochiweb_socket_server{}) ->
+-    start_server(State);
+-start(Options) ->
+-    start(parse_options(Options)).
+-
+-get(Name, Property) ->
+-    gen_server:call(Name, {get, Property}).
+-
+-stop(Name) when is_atom(Name) ->
+-    gen_server:cast(Name, stop);
+-stop(Pid) when is_pid(Pid) ->
+-    gen_server:cast(Pid, stop);
+-stop({local, Name}) ->
+-    stop(Name);
+-stop({global, Name}) ->
+-    stop(Name);
+-stop(Options) ->
+-    State = parse_options(Options),
+-    stop(State#mochiweb_socket_server.name).
+-
+-%% Internal API
+-
+-parse_options(Options) ->
+-    parse_options(Options, #mochiweb_socket_server{}).
+-
+-parse_options([], State) ->
+-    State;
+-parse_options([{name, L} | Rest], State) when is_list(L) ->
+-    Name = {local, list_to_atom(L)},
+-    parse_options(Rest, State#mochiweb_socket_server{name=Name});
+-parse_options([{name, A} | Rest], State) when is_atom(A) ->
+-    Name = {local, A},
+-    parse_options(Rest, State#mochiweb_socket_server{name=Name});
+-parse_options([{name, Name} | Rest], State) ->
+-    parse_options(Rest, State#mochiweb_socket_server{name=Name});
+-parse_options([{port, L} | Rest], State) when is_list(L) ->
+-    Port = list_to_integer(L),
+-    parse_options(Rest, State#mochiweb_socket_server{port=Port});
+-parse_options([{port, Port} | Rest], State) ->
+-    parse_options(Rest, State#mochiweb_socket_server{port=Port});
+-parse_options([{ip, Ip} | Rest], State) ->
+-    ParsedIp = case Ip of
+-                   any ->
+-                       any;
+-                   Ip when is_tuple(Ip) ->
+-                       Ip;
+-                   Ip when is_list(Ip) ->
+-                       {ok, IpTuple} = inet_parse:address(Ip),
+-                       IpTuple
+-               end,
+-    parse_options(Rest, State#mochiweb_socket_server{ip=ParsedIp});
+-parse_options([{loop, Loop} | Rest], State) ->
+-    parse_options(Rest, State#mochiweb_socket_server{loop=Loop});
+-parse_options([{backlog, Backlog} | Rest], State) ->
+-    parse_options(Rest, State#mochiweb_socket_server{backlog=Backlog});
+-parse_options([{max, Max} | Rest], State) ->
+-    MaxInt = case Max of
+-                 Max when is_list(Max) ->
+-                     list_to_integer(Max);
+-                 Max when is_integer(Max) ->
+-                     Max
+-             end,
+-    parse_options(Rest, State#mochiweb_socket_server{max=MaxInt}).
+-
+-start_server(State=#mochiweb_socket_server{name=Name}) ->
+-    case Name of
+-        undefined ->
+-            gen_server:start_link(?MODULE, State, []);
+-        _ ->
+-            gen_server:start_link(Name, ?MODULE, State, [])
+-    end.
+-
+-ipv6_supported() ->
+-    case (catch inet:getaddr("localhost", inet6)) of
+-        {ok, _Addr} ->
+-            true;
+-        {error, _} ->
+-            false
+-    end.
+-
+-init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog}) ->
+-    process_flag(trap_exit, true),
+-    BaseOpts = [binary,
+-                {reuseaddr, true},
+-                {packet, 0},
+-                {backlog, Backlog},
+-                {recbuf, 8192},
+-                {active, false},
+-                {nodelay, true}],
+-    Opts = case Ip of
+-        any ->
+-            case ipv6_supported() of % IPv4, and IPv6 if supported
+-                true -> [inet, inet6 | BaseOpts];
+-                _ -> BaseOpts
+-            end;
+-        {_, _, _, _} -> % IPv4
+-            [inet, {ip, Ip} | BaseOpts];
+-        {_, _, _, _, _, _, _, _} -> % IPv6
+-            [inet6, {ip, Ip} | BaseOpts]
+-    end,
+-    case gen_tcp_listen(Port, Opts, State) of
+-        {stop, eacces} ->
+-            case Port < 1024 of
+-                true ->
+-                    case fdsrv:start() of
+-                        {ok, _} ->
+-                            case fdsrv:bind_socket(tcp, Port) of
+-                                {ok, Fd} ->
+-                                    gen_tcp_listen(Port, [{fd, Fd} | Opts], State);
+-                                _ ->
+-                                    {stop, fdsrv_bind_failed}
+-                            end;
+-                        _ ->
+-                            {stop, fdsrv_start_failed}
+-                    end;
+-                false ->
+-                    {stop, eacces}
+-            end;
+-        Other ->
+-            Other
+-    end.
+-
+-gen_tcp_listen(Port, Opts, State) ->
+-    case gen_tcp:listen(Port, Opts) of
+-        {ok, Listen} ->
+-            {ok, ListenPort} = inet:port(Listen),
+-            {ok, new_acceptor(State#mochiweb_socket_server{listen=Listen,
+-                                                           port=ListenPort})};
+-        {error, Reason} ->
+-            {stop, Reason}
+-    end.
+-
+-new_acceptor(State=#mochiweb_socket_server{max=0}) ->
+-    io:format("Not accepting new connections~n"),
+-    State#mochiweb_socket_server{acceptor=null};
+-new_acceptor(State=#mochiweb_socket_server{listen=Listen,loop=Loop}) ->
+-    Pid = proc_lib:spawn_link(?MODULE, acceptor_loop,
+-                              [{self(), Listen, Loop}]),
+-    State#mochiweb_socket_server{acceptor=Pid}.
+-
+-call_loop({M, F}, Socket) ->
+-    M:F(Socket);
+-call_loop(Loop, Socket) ->
+-    Loop(Socket).
+-
+-acceptor_loop({Server, Listen, Loop}) ->
+-    case catch gen_tcp:accept(Listen) of
+-        {ok, Socket} ->
+-            gen_server:cast(Server, {accepted, self()}),
+-            call_loop(Loop, Socket);
+-        {error, closed} ->
+-            exit({error, closed});
+-        Other ->
+-            error_logger:error_report(
+-              [{application, mochiweb},
+-               "Accept failed error",
+-               lists:flatten(io_lib:format("~p", [Other]))]),
+-            exit({error, accept_failed})
+-    end.
+-
+-
+-do_get(port, #mochiweb_socket_server{port=Port}) ->
+-    Port.
+-
+-handle_call({get, Property}, _From, State) ->
+-    Res = do_get(Property, State),
+-    {reply, Res, State};
+-handle_call(_Message, _From, State) ->
+-    Res = error,
+-    {reply, Res, State}.
+-
+-handle_cast({accepted, Pid},
+-            State=#mochiweb_socket_server{acceptor=Pid, max=Max}) ->
+-    % io:format("accepted ~p~n", [Pid]),
+-    State1 = State#mochiweb_socket_server{max=Max - 1},
+-    {noreply, new_acceptor(State1)};
+-handle_cast(stop, State) ->
+-    {stop, normal, State}.
+-
+-terminate(_Reason, #mochiweb_socket_server{listen=Listen, port=Port}) ->
+-    gen_tcp:close(Listen),
+-    case Port < 1024 of
+-        true ->
+-            catch fdsrv:stop(),
+-            ok;
+-        false ->
+-            ok
+-    end.
+-
+-code_change(_OldVsn, State, _Extra) ->
+-    State.
+-
+-handle_info({'EXIT', Pid, normal},
+-            State=#mochiweb_socket_server{acceptor=Pid}) ->
+-    % io:format("normal acceptor down~n"),
+-    {noreply, new_acceptor(State)};
+-handle_info({'EXIT', Pid, Reason},
+-            State=#mochiweb_socket_server{acceptor=Pid}) ->
+-    error_logger:error_report({?MODULE, ?LINE,
+-                               {acceptor_error, Reason}}),
+-    timer:sleep(100),
+-    {noreply, new_acceptor(State)};
+-handle_info({'EXIT', _LoopPid, Reason},
+-            State=#mochiweb_socket_server{acceptor=Pid, max=Max}) ->
+-    case Reason of
+-        normal ->
+-            ok;
+-        _ ->
+-            error_logger:error_report({?MODULE, ?LINE,
+-                                       {child_error, Reason}})
+-    end,
+-    State1 = State#mochiweb_socket_server{max=Max + 1},
+-    State2 = case Pid of
+-                 null ->
+-                     new_acceptor(State1);
+-                 _ ->
+-                     State1
+-             end,
+-    {noreply, State2};
+-handle_info(Info, State) ->
+-    error_logger:info_report([{'INFO', Info}, {'State', State}]),
+-    {noreply, State}.
+diff --git a/src/mochiweb/mochiweb_sup.erl b/src/mochiweb/mochiweb_sup.erl
+deleted file mode 100644
+index 5cb525b..0000000
+--- a/src/mochiweb/mochiweb_sup.erl
++++ /dev/null
+@@ -1,34 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Supervisor for the mochiweb application.
+-
+--module(mochiweb_sup).
+--author('bob at mochimedia.com').
+-
+--behaviour(supervisor).
+-
+-%% External exports
+--export([start_link/0, upgrade/0]).
+-
+-%% supervisor callbacks
+--export([init/1]).
+-
+-%% @spec start_link() -> ServerRet
+-%% @doc API for starting the supervisor.
+-start_link() ->
+-    supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+-
+-%% @spec upgrade() -> ok
+-%% @doc Add processes if necessary.
+-upgrade() ->
+-    {ok, {_, Specs}} = init([]),
+-    [supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
+-    ok.
+-
+-%% @spec init([]) -> SupervisorTree
+-%% @doc supervisor callback, ensures yaws is in embedded mode and then
+-%%      returns the supervisor tree.
+-init([]) ->
+-    Processes = [],
+-    {ok, {{one_for_one, 10, 10}, Processes}}.
+diff --git a/src/mochiweb/mochiweb_util.erl b/src/mochiweb/mochiweb_util.erl
+deleted file mode 100644
+index 7bf18d1..0000000
+--- a/src/mochiweb/mochiweb_util.erl
++++ /dev/null
+@@ -1,579 +0,0 @@
+-%% @author Bob Ippolito <bob at mochimedia.com>
+-%% @copyright 2007 Mochi Media, Inc.
+-
+-%% @doc Utilities for parsing and quoting.
+-
+--module(mochiweb_util).
+--author('bob at mochimedia.com').
+--export([join/2, quote_plus/1, urlencode/1, parse_qs/1, unquote/1]).
+--export([path_split/1]).
+--export([urlsplit/1, urlsplit_path/1, urlunsplit/1, urlunsplit_path/1]).
+--export([guess_mime/1, parse_header/1]).
+--export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2]).
+--export([record_to_proplist/2, record_to_proplist/3]).
+--export([safe_relative_path/1, partition/2]).
+--export([test/0]).
+-
+--define(PERCENT, 37).  % $\%
+--define(FULLSTOP, 46). % $\.
+--define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse
+-                    (C >= $a andalso C =< $f) orelse
+-                    (C >= $A andalso C =< $F))).
+--define(QS_SAFE(C), ((C >= $a andalso C =< $z) orelse
+-                     (C >= $A andalso C =< $Z) orelse
+-                     (C >= $0 andalso C =< $9) orelse
+-                     (C =:= ?FULLSTOP orelse C =:= $- orelse C =:= $~ orelse
+-                      C =:= $_))).
+-
+-hexdigit(C) when C < 10 -> $0 + C;
+-hexdigit(C) when C < 16 -> $A + (C - 10).
+-
+-unhexdigit(C) when C >= $0, C =< $9 -> C - $0;
+-unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10;
+-unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10.
+-
+-%% @spec partition(String, Sep) -> {String, [], []} | {Prefix, Sep, Postfix}
+-%% @doc Inspired by Python 2.5's str.partition:
+-%%      partition("foo/bar", "/") = {"foo", "/", "bar"},
+-%%      partition("foo", "/") = {"foo", "", ""}.
+-partition(String, Sep) ->
+-    case partition(String, Sep, []) of
+-        undefined ->
+-            {String, "", ""};
+-        Result ->
+-            Result
+-    end.
+-
+-partition("", _Sep, _Acc) ->
+-    undefined;
+-partition(S, Sep, Acc) ->
+-    case partition2(S, Sep) of
+-        undefined ->
+-            [C | Rest] = S,
+-            partition(Rest, Sep, [C | Acc]);
+-        Rest ->
+-            {lists:reverse(Acc), Sep, Rest}
+-    end.
+-
+-partition2(Rest, "") ->
+-    Rest;
+-partition2([C | R1], [C | R2]) ->
+-    partition2(R1, R2);
+-partition2(_S, _Sep) ->
+-    undefined.
+-
+-
+-
+-%% @spec safe_relative_path(string()) -> string() | undefined
+-%% @doc Return the reduced version of a relative path or undefined if it
+-%%      is not safe. safe relative paths can be joined with an absolute path
+-%%      and will result in a subdirectory of the absolute path.
+-safe_relative_path("/" ++ _) ->
+-    undefined;
+-safe_relative_path(P) ->
+-    safe_relative_path(P, []).
+-
+-safe_relative_path("", Acc) ->
+-    case Acc of
+-        [] ->
+-            "";
+-        _ ->
+-            join(lists:reverse(Acc), "/")
+-    end;
+-safe_relative_path(P, Acc) ->
+-    case partition(P, "/") of
+-        {"", "/", _} ->
+-            %% /foo or foo//bar
+-            undefined;
+-        {"..", _, _} when Acc =:= [] ->
+-            undefined;
+-        {"..", _, Rest} ->
+-            safe_relative_path(Rest, tl(Acc));
+-        {Part, "/", ""} ->
+-            safe_relative_path("", ["", Part | Acc]);
+-        {Part, _, Rest} ->
+-            safe_relative_path(Rest, [Part | Acc])
+-    end.
+-
+-%% @spec shell_quote(string()) -> string()
+-%% @doc Quote a string according to UNIX shell quoting rules, returns a string
+-%%      surrounded by double quotes.
+-shell_quote(L) ->
+-    shell_quote(L, [$\"]).
+-
+-%% @spec cmd_port([string()], Options) -> port()
+-%% @doc open_port({spawn, mochiweb_util:cmd_string(Argv)}, Options).
+-cmd_port(Argv, Options) ->
+-    open_port({spawn, cmd_string(Argv)}, Options).
+-
+-%% @spec cmd([string()]) -> string()
+-%% @doc os:cmd(cmd_string(Argv)).
+-cmd(Argv) ->
+-    os:cmd(cmd_string(Argv)).
+-
+-%% @spec cmd_string([string()]) -> string()
+-%% @doc Create a shell quoted command string from a list of arguments.
+-cmd_string(Argv) ->
+-    join([shell_quote(X) || X <- Argv], " ").
+-
+-%% @spec join([string()], Separator) -> string()
+-%% @doc Join a list of strings together with the given separator
+-%%      string or char.
+-join([], _Separator) ->
+-    [];
+-join([S], _Separator) ->
+-    lists:flatten(S);
+-join(Strings, Separator) ->
+-    lists:flatten(revjoin(lists:reverse(Strings), Separator, [])).
+-
+-revjoin([], _Separator, Acc) ->
+-    Acc;
+-revjoin([S | Rest], Separator, []) ->
+-    revjoin(Rest, Separator, [S]);
+-revjoin([S | Rest], Separator, Acc) ->
+-    revjoin(Rest, Separator, [S, Separator | Acc]).
+-
+-%% @spec quote_plus(atom() | integer() | float() | string() | binary()) -> string()
+-%% @doc URL safe encoding of the given term.
+-quote_plus(Atom) when is_atom(Atom) ->
+-    quote_plus(atom_to_list(Atom));
+-quote_plus(Int) when is_integer(Int) ->
+-    quote_plus(integer_to_list(Int));
+-quote_plus(Binary) when is_binary(Binary) ->
+-    quote_plus(binary_to_list(Binary));
+-quote_plus(Float) when is_float(Float) ->
+-    quote_plus(mochinum:digits(Float));
+-quote_plus(String) ->
+-    quote_plus(String, []).
+-
+-quote_plus([], Acc) ->
+-    lists:reverse(Acc);
+-quote_plus([C | Rest], Acc) when ?QS_SAFE(C) ->
+-    quote_plus(Rest, [C | Acc]);
+-quote_plus([$\s | Rest], Acc) ->
+-    quote_plus(Rest, [$+ | Acc]);
+-quote_plus([C | Rest], Acc) ->
+-    <<Hi:4, Lo:4>> = <<C>>,
+-    quote_plus(Rest, [hexdigit(Lo), hexdigit(Hi), ?PERCENT | Acc]).
+-
+-%% @spec urlencode([{Key, Value}]) -> string()
+-%% @doc URL encode the property list.
+-urlencode(Props) ->
+-    RevPairs = lists:foldl(fun ({K, V}, Acc) ->
+-                                   [[quote_plus(K), $=, quote_plus(V)] | Acc]
+-                           end, [], Props),
+-    lists:flatten(revjoin(RevPairs, $&, [])).
+-
+-%% @spec parse_qs(string() | binary()) -> [{Key, Value}]
+-%% @doc Parse a query string or application/x-www-form-urlencoded.
+-parse_qs(Binary) when is_binary(Binary) ->
+-    parse_qs(binary_to_list(Binary));
+-parse_qs(String) ->
+-    parse_qs(String, []).
+-
+-parse_qs([], Acc) ->
+-    lists:reverse(Acc);
+-parse_qs(String, Acc) ->
+-    {Key, Rest} = parse_qs_key(String),
+-    {Value, Rest1} = parse_qs_value(Rest),
+-    parse_qs(Rest1, [{Key, Value} | Acc]).
+-
+-parse_qs_key(String) ->
+-    parse_qs_key(String, []).
+-
+-parse_qs_key([], Acc) ->
+-    {qs_revdecode(Acc), ""};
+-parse_qs_key([$= | Rest], Acc) ->
+-    {qs_revdecode(Acc), Rest};
+-parse_qs_key(Rest=[$; | _], Acc) ->
+-    {qs_revdecode(Acc), Rest};
+-parse_qs_key(Rest=[$& | _], Acc) ->
+-    {qs_revdecode(Acc), Rest};
+-parse_qs_key([C | Rest], Acc) ->
+-    parse_qs_key(Rest, [C | Acc]).
+-
+-parse_qs_value(String) ->
+-    parse_qs_value(String, []).
+-
+-parse_qs_value([], Acc) ->
+-    {qs_revdecode(Acc), ""};
+-parse_qs_value([$; | Rest], Acc) ->
+-    {qs_revdecode(Acc), Rest};
+-parse_qs_value([$& | Rest], Acc) ->
+-    {qs_revdecode(Acc), Rest};
+-parse_qs_value([C | Rest], Acc) ->
+-    parse_qs_value(Rest, [C | Acc]).
+-
+-%% @spec unquote(string() | binary()) -> string()
+-%% @doc Unquote a URL encoded string.
+-unquote(Binary) when is_binary(Binary) ->
+-    unquote(binary_to_list(Binary));
+-unquote(String) ->
+-    qs_revdecode(lists:reverse(String)).
+-
+-qs_revdecode(S) ->
+-    qs_revdecode(S, []).
+-
+-qs_revdecode([], Acc) ->
+-    Acc;
+-qs_revdecode([$+ | Rest], Acc) ->
+-    qs_revdecode(Rest, [$\s | Acc]);
+-qs_revdecode([Lo, Hi, ?PERCENT | Rest], Acc) when ?IS_HEX(Lo), ?IS_HEX(Hi) ->
+-    qs_revdecode(Rest, [(unhexdigit(Lo) bor (unhexdigit(Hi) bsl 4)) | Acc]);
+-qs_revdecode([C | Rest], Acc) ->
+-    qs_revdecode(Rest, [C | Acc]).
+-
+-%% @spec urlsplit(Url) -> {Scheme, Netloc, Path, Query, Fragment}
+-%% @doc Return a 5-tuple, does not expand % escapes. Only supports HTTP style
+-%%      URLs.
+-urlsplit(Url) ->
+-    {Scheme, Url1} = urlsplit_scheme(Url),
+-    {Netloc, Url2} = urlsplit_netloc(Url1),
+-    {Path, Query, Fragment} = urlsplit_path(Url2),
+-    {Scheme, Netloc, Path, Query, Fragment}.
+-
+-urlsplit_scheme(Url) ->
+-    urlsplit_scheme(Url, []).
+-
+-urlsplit_scheme([], Acc) ->
+-    {"", lists:reverse(Acc)};
+-urlsplit_scheme(":" ++ Rest, Acc) ->
+-    {string:to_lower(lists:reverse(Acc)), Rest};
+-urlsplit_scheme([C | Rest], Acc) ->
+-    urlsplit_scheme(Rest, [C | Acc]).
+-
+-urlsplit_netloc("//" ++ Rest) ->
+-    urlsplit_netloc(Rest, []);
+-urlsplit_netloc(Path) ->
+-    {"", Path}.
+-
+-urlsplit_netloc(Rest=[C | _], Acc) when C =:= $/; C =:= $?; C =:= $# ->
+-    {lists:reverse(Acc), Rest};
+-urlsplit_netloc([C | Rest], Acc) ->
+-    urlsplit_netloc(Rest, [C | Acc]).
+-
+-
+-%% @spec path_split(string()) -> {Part, Rest}
+-%% @doc Split a path starting from the left, as in URL traversal.
+-%%      path_split("foo/bar") = {"foo", "bar"},
+-%%      path_split("/foo/bar") = {"", "foo/bar"}.
+-path_split(S) ->
+-    path_split(S, []).
+-
+-path_split("", Acc) ->
+-    {lists:reverse(Acc), ""};
+-path_split("/" ++ Rest, Acc) ->
+-    {lists:reverse(Acc), Rest};
+-path_split([C | Rest], Acc) ->
+-    path_split(Rest, [C | Acc]).
+-
+-
+-%% @spec urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> string()
+-%% @doc Assemble a URL from the 5-tuple. Path must be absolute.
+-urlunsplit({Scheme, Netloc, Path, Query, Fragment}) ->
+-    lists:flatten([case Scheme of "" -> "";  _ -> [Scheme, "://"] end,
+-                   Netloc,
+-                   urlunsplit_path({Path, Query, Fragment})]).
+-
+-%% @spec urlunsplit_path({Path, Query, Fragment}) -> string()
+-%% @doc Assemble a URL path from the 3-tuple.
+-urlunsplit_path({Path, Query, Fragment}) ->
+-    lists:flatten([Path,
+-                   case Query of "" -> ""; _ -> [$? | Query] end,
+-                   case Fragment of "" -> ""; _ -> [$# | Fragment] end]).
+-
+-%% @spec urlsplit_path(Url) -> {Path, Query, Fragment}
+-%% @doc Return a 3-tuple, does not expand % escapes. Only supports HTTP style
+-%%      paths.
+-urlsplit_path(Path) ->
+-    urlsplit_path(Path, []).
+-
+-urlsplit_path("", Acc) ->
+-    {lists:reverse(Acc), "", ""};
+-urlsplit_path("?" ++ Rest, Acc) ->
+-    {Query, Fragment} = urlsplit_query(Rest),
+-    {lists:reverse(Acc), Query, Fragment};
+-urlsplit_path("#" ++ Rest, Acc) ->
+-    {lists:reverse(Acc), "", Rest};
+-urlsplit_path([C | Rest], Acc) ->
+-    urlsplit_path(Rest, [C | Acc]).
+-
+-urlsplit_query(Query) ->
+-    urlsplit_query(Query, []).
+-
+-urlsplit_query("", Acc) ->
+-    {lists:reverse(Acc), ""};
+-urlsplit_query("#" ++ Rest, Acc) ->
+-    {lists:reverse(Acc), Rest};
+-urlsplit_query([C | Rest], Acc) ->
+-    urlsplit_query(Rest, [C | Acc]).
+-
+-%% @spec guess_mime(string()) -> string()
+-%% @doc  Guess the mime type of a file by the extension of its filename.
+-guess_mime(File) ->
+-    case filename:extension(File) of
+-        ".html" ->
+-            "text/html";
+-        ".xhtml" ->
+-            "application/xhtml+xml";
+-        ".xml" ->
+-            "application/xml";
+-        ".css" ->
+-            "text/css";
+-        ".js" ->
+-            "application/x-javascript";
+-        ".jpg" ->
+-            "image/jpeg";
+-        ".gif" ->
+-            "image/gif";
+-        ".png" ->
+-            "image/png";
+-        ".swf" ->
+-            "application/x-shockwave-flash";
+-        ".zip" ->
+-            "application/zip";
+-        ".bz2" ->
+-            "application/x-bzip2";
+-        ".gz" ->
+-            "application/x-gzip";
+-        ".tar" ->
+-            "application/x-tar";
+-        ".tgz" ->
+-            "application/x-gzip";
+-        ".txt" ->
+-            "text/plain";
+-        ".doc" ->
+-            "application/msword";
+-        ".pdf" ->
+-            "application/pdf";
+-        ".xls" ->
+-            "application/vnd.ms-excel";
+-        ".rtf" ->
+-            "application/rtf";
+-        ".mov" ->
+-            "video/quicktime";
+-        ".mp3" ->
+-            "audio/mpeg";
+-        ".z" ->
+-            "application/x-compress";
+-        ".wav" ->
+-            "audio/x-wav";
+-        ".ico" ->
+-            "image/x-icon";
+-        ".bmp" ->
+-            "image/bmp";
+-        ".m4a" ->
+-            "audio/mpeg";
+-        ".m3u" ->
+-            "audio/x-mpegurl";
+-        ".exe" ->
+-            "application/octet-stream";
+-        ".csv" ->
+-            "text/csv";
+-        _ ->
+-            "text/plain"
+-    end.
+-
+-%% @spec parse_header(string()) -> {Type, [{K, V}]}
+-%% @doc  Parse a Content-Type like header, return the main Content-Type
+-%%       and a property list of options.
+-parse_header(String) ->
+-    %% TODO: This is exactly as broken as Python's cgi module.
+-    %%       Should parse properly like mochiweb_cookies.
+-    [Type | Parts] = [string:strip(S) || S <- string:tokens(String, ";")],
+-    F = fun (S, Acc) ->
+-                case lists:splitwith(fun (C) -> C =/= $= end, S) of
+-                    {"", _} ->
+-                        %% Skip anything with no name
+-                        Acc;
+-                    {_, ""} ->
+-                        %% Skip anything with no value
+-                        Acc;
+-                    {Name, [$\= | Value]} ->
+-                        [{string:to_lower(string:strip(Name)),
+-                          unquote_header(string:strip(Value))} | Acc]
+-                end
+-        end,
+-    {string:to_lower(Type),
+-     lists:foldr(F, [], Parts)}.
+-
+-unquote_header("\"" ++ Rest) ->
+-    unquote_header(Rest, []);
+-unquote_header(S) ->
+-    S.
+-
+-unquote_header("", Acc) ->
+-    lists:reverse(Acc);
+-unquote_header("\"", Acc) ->
+-    lists:reverse(Acc);
+-unquote_header([$\\, C | Rest], Acc) ->
+-    unquote_header(Rest, [C | Acc]);
+-unquote_header([C | Rest], Acc) ->
+-    unquote_header(Rest, [C | Acc]).
+-
+-%% @spec record_to_proplist(Record, Fields) -> proplist()
+-%% @doc calls record_to_proplist/3 with a default TypeKey of '__record'
+-record_to_proplist(Record, Fields) ->
+-    record_to_proplist(Record, Fields, '__record').
+-
+-%% @spec record_to_proplist(Record, Fields, TypeKey) -> proplist()
+-%% @doc Return a proplist of the given Record with each field in the
+-%%      Fields list set as a key with the corresponding value in the Record.
+-%%      TypeKey is the key that is used to store the record type
+-%%      Fields should be obtained by calling record_info(fields, record_type)
+-%%      where record_type is the record type of Record
+-record_to_proplist(Record, Fields, TypeKey)
+-  when is_tuple(Record),
+-       is_list(Fields),
+-       size(Record) - 1 =:= length(Fields) ->
+-    lists:zip([TypeKey | Fields], tuple_to_list(Record)).
+-
+-
+-shell_quote([], Acc) ->
+-    lists:reverse([$\" | Acc]);
+-shell_quote([C | Rest], Acc) when C =:= $\" orelse C =:= $\` orelse
+-                                  C =:= $\\ orelse C =:= $\$ ->
+-    shell_quote(Rest, [C, $\\ | Acc]);
+-shell_quote([C | Rest], Acc) ->
+-    shell_quote(Rest, [C | Acc]).
+-
+-test() ->
+-    test_join(),
+-    test_quote_plus(),
+-    test_unquote(),
+-    test_urlencode(),
+-    test_parse_qs(),
+-    test_urlsplit_path(),
+-    test_urlunsplit_path(),
+-    test_urlsplit(),
+-    test_urlunsplit(),
+-    test_path_split(),
+-    test_guess_mime(),
+-    test_parse_header(),
+-    test_shell_quote(),
+-    test_cmd(),
+-    test_cmd_string(),
+-    test_partition(),
+-    test_safe_relative_path(),
+-    ok.
+-
+-test_shell_quote() ->
+-    "\"foo \\$bar\\\"\\`' baz\"" = shell_quote("foo $bar\"`' baz"),
+-    ok.
+-
+-test_cmd() ->
+-    "$bling$ `word`!\n" = cmd(["echo", "$bling$ `word`!"]),
+-    ok.
+-
+-test_cmd_string() ->
+-    "\"echo\" \"\\$bling\\$ \\`word\\`!\"" = cmd_string(["echo", "$bling$ `word`!"]),
+-    ok.
+-
+-test_parse_header() ->
+-    {"multipart/form-data", [{"boundary", "AaB03x"}]} =
+-        parse_header("multipart/form-data; boundary=AaB03x"),
+-    ok.
+-
+-test_guess_mime() ->
+-    "text/plain" = guess_mime(""),
+-    "text/plain" = guess_mime(".text"),
+-    "application/zip" = guess_mime(".zip"),
+-    "application/zip" = guess_mime("x.zip"),
+-    "text/html" = guess_mime("x.html"),
+-    "application/xhtml+xml" = guess_mime("x.xhtml"),
+-    ok.
+-
+-test_path_split() ->
+-    {"", "foo/bar"} = path_split("/foo/bar"),
+-    {"foo", "bar"} = path_split("foo/bar"),
+-    {"bar", ""} = path_split("bar"),
+-    ok.
+-
+-test_urlsplit() ->
+-    {"", "", "/foo", "", "bar?baz"} = urlsplit("/foo#bar?baz"),
+-    {"http", "host:port", "/foo", "", "bar?baz"} =
+-        urlsplit("http://host:port/foo#bar?baz"),
+-    ok.
+-
+-test_urlsplit_path() ->
+-    {"/foo/bar", "", ""} = urlsplit_path("/foo/bar"),
+-    {"/foo", "baz", ""} = urlsplit_path("/foo?baz"),
+-    {"/foo", "", "bar?baz"} = urlsplit_path("/foo#bar?baz"),
+-    {"/foo", "", "bar?baz#wibble"} = urlsplit_path("/foo#bar?baz#wibble"),
+-    {"/foo", "bar", "baz"} = urlsplit_path("/foo?bar#baz"),
+-    {"/foo", "bar?baz", "baz"} = urlsplit_path("/foo?bar?baz#baz"),
+-    ok.
+-
+-test_urlunsplit() ->
+-    "/foo#bar?baz" = urlunsplit({"", "", "/foo", "", "bar?baz"}),
+-    "http://host:port/foo#bar?baz" =
+-        urlunsplit({"http", "host:port", "/foo", "", "bar?baz"}),
+-    ok.
+-
+-test_urlunsplit_path() ->
+-    "/foo/bar" = urlunsplit_path({"/foo/bar", "", ""}),
+-    "/foo?baz" = urlunsplit_path({"/foo", "baz", ""}),
+-    "/foo#bar?baz" = urlunsplit_path({"/foo", "", "bar?baz"}),
+-    "/foo#bar?baz#wibble" = urlunsplit_path({"/foo", "", "bar?baz#wibble"}),
+-    "/foo?bar#baz" = urlunsplit_path({"/foo", "bar", "baz"}),
+-    "/foo?bar?baz#baz" = urlunsplit_path({"/foo", "bar?baz", "baz"}),
+-    ok.
+-
+-test_join() ->
+-    "foo,bar,baz" = join(["foo", "bar", "baz"], $,),
+-    "foo,bar,baz" = join(["foo", "bar", "baz"], ","),
+-    "foo bar" = join([["foo", " bar"]], ","),
+-    "foo bar,baz" = join([["foo", " bar"], "baz"], ","),
+-    "foo" = join(["foo"], ","),
+-    "foobarbaz" = join(["foo", "bar", "baz"], ""),
+-    ok.
+-
+-test_quote_plus() ->
+-    "foo" = quote_plus(foo),
+-    "1" = quote_plus(1),
+-    "1.1" = quote_plus(1.1),
+-    "foo" = quote_plus("foo"),
+-    "foo+bar" = quote_plus("foo bar"),
+-    "foo%0A" = quote_plus("foo\n"),
+-    "foo%0A" = quote_plus("foo\n"),
+-    "foo%3B%26%3D" = quote_plus("foo;&="),
+-    ok.
+-
+-test_unquote() ->
+-    "foo bar" = unquote("foo+bar"),
+-    "foo bar" = unquote("foo%20bar"),
+-    "foo\r\n" = unquote("foo%0D%0A"),
+-    ok.
+-
+-test_urlencode() ->
+-    "foo=bar&baz=wibble+%0D%0A&z=1" = urlencode([{foo, "bar"},
+-                                                 {"baz", "wibble \r\n"},
+-                                                 {z, 1}]),
+-    ok.
+-
+-test_parse_qs() ->
+-    [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}] =
+-        parse_qs("foo=bar&baz=wibble+%0D%0A&z=1"),
+-    ok.
+-
+-test_partition() ->
+-    {"foo", "", ""} = partition("foo", "/"),
+-    {"foo", "/", "bar"} = partition("foo/bar", "/"),
+-    {"foo", "/", ""} = partition("foo/", "/"),
+-    {"", "/", "bar"} = partition("/bar", "/"),
+-    {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"),
+-    ok.
+-
+-test_safe_relative_path() ->
+-    "foo" = safe_relative_path("foo"),
+-    "foo/" = safe_relative_path("foo/"),
+-    "foo" = safe_relative_path("foo/bar/.."),
+-    "bar" = safe_relative_path("foo/../bar"),
+-    "bar/" = safe_relative_path("foo/../bar/"),
+-    "" = safe_relative_path("foo/.."),
+-    "" = safe_relative_path("foo/../"),
+-    undefined = safe_relative_path("/foo"),
+-    undefined = safe_relative_path("../foo"),
+-    undefined = safe_relative_path("foo/../.."),
+-    undefined = safe_relative_path("foo//"),
+-    ok.
+diff --git a/src/mochiweb/reloader.erl b/src/mochiweb/reloader.erl
+deleted file mode 100644
+index 2ff154b..0000000
+--- a/src/mochiweb/reloader.erl
++++ /dev/null
+@@ -1,124 +0,0 @@
+-%% @copyright 2007 Mochi Media, Inc.
+-%% @author Matthew Dempsky <matthew at mochimedia.com>
+-%%
+-%% @doc Erlang module for automatically reloading modified modules
+-%% during development.
+-
+--module(reloader).
+--author("Matthew Dempsky <matthew at mochimedia.com>").
+-
+--include_lib("kernel/include/file.hrl").
+-
+--behaviour(gen_server).
+--export([start/0, start_link/0]).
+--export([stop/0]).
+--export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
+-
+--record(state, {last, tref}).
+-
+-%% External API
+-
+-%% @spec start() -> ServerRet
+-%% @doc Start the reloader.
+-start() ->
+-    gen_server:start({local, ?MODULE}, ?MODULE, [], []).
+-
+-%% @spec start_link() -> ServerRet
+-%% @doc Start the reloader.
+-start_link() ->
+-    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+-
+-%% @spec stop() -> ok
+-%% @doc Stop the reloader.
+-stop() ->
+-    gen_server:call(?MODULE, stop).
+-
+-%% gen_server callbacks
+-
+-%% @spec init([]) -> {ok, State}
+-%% @doc gen_server init, opens the server in an initial state.
+-init([]) ->
+-    {ok, TRef} = timer:send_interval(timer:seconds(1), doit),
+-    {ok, #state{last = stamp(), tref = TRef}}.
+-
+-%% @spec handle_call(Args, From, State) -> tuple()
+-%% @doc gen_server callback.
+-handle_call(stop, _From, State) ->
+-    {stop, shutdown, stopped, State};
+-handle_call(_Req, _From, State) ->
+-    {reply, {error, badrequest}, State}.
+-
+-%% @spec handle_cast(Cast, State) -> tuple()
+-%% @doc gen_server callback.
+-handle_cast(_Req, State) ->
+-    {noreply, State}.
+-
+-%% @spec handle_info(Info, State) -> tuple()
+-%% @doc gen_server callback.
+-handle_info(doit, State) ->
+-    Now = stamp(),
+-    doit(State#state.last, Now),
+-    {noreply, State#state{last = Now}};
+-handle_info(_Info, State) ->
+-    {noreply, State}.
+-
+-%% @spec terminate(Reason, State) -> ok
+-%% @doc gen_server termination callback.
+-terminate(_Reason, State) ->
+-    {ok, cancel} = timer:cancel(State#state.tref),
+-    ok.
+-
+-
+-%% @spec code_change(_OldVsn, State, _Extra) -> State
+-%% @doc gen_server code_change callback (trivial).
+-code_change(_Vsn, State, _Extra) ->
+-    {ok, State}.
+-
+-%% Internal API
+-
+-doit(From, To) ->
+-    [case file:read_file_info(Filename) of
+-         {ok, FileInfo} when FileInfo#file_info.mtime >= From,
+-                             FileInfo#file_info.mtime < To ->
+-             reload(Module);
+-         {ok, _} ->
+-             unmodified;
+-         {error, enoent} ->
+-             %% The Erlang compiler deletes existing .beam files if
+-             %% recompiling fails.  Maybe it's worth spitting out a
+-             %% warning here, but I'd want to limit it to just once.
+-             gone;
+-         {error, Reason} ->
+-             io:format("Error reading ~s's file info: ~p~n",
+-                       [Filename, Reason]),
+-             error
+-     end || {Module, Filename} <- code:all_loaded(), is_list(Filename)].
+-
+-reload(Module) ->
+-    io:format("Reloading ~p ...", [Module]),
+-    code:purge(Module),
+-    case code:load_file(Module) of
+-        {module, Module} ->
+-            io:format(" ok.~n"),
+-            case erlang:function_exported(Module, test, 0) of
+-                true ->
+-                    io:format(" - Calling ~p:test() ...", [Module]),
+-                    case catch Module:test() of
+-                        ok ->
+-                            io:format(" ok.~n"),
+-                            reload;
+-                        Reason ->
+-                            io:format(" fail: ~p.~n", [Reason]),
+-                            reload_but_test_failed
+-                    end;
+-                false ->
+-                    reload
+-            end;
+-        {error, Reason} ->
+-            io:format(" fail: ~p.~n", [Reason]),
+-            error
+-    end.
+-
+-
+-stamp() ->
+-    erlang:localtime().
+diff --git a/test/etap/030-doc-from-json.t b/test/etap/030-doc-from-json.t
+index dc3327a..c984c66 100755
+--- a/test/etap/030-doc-from-json.t
++++ b/test/etap/030-doc-from-json.t
+@@ -1,6 +1,6 @@
+ #!/usr/bin/env escript
+ %% -*- erlang -*-
+-%%! -pa ./src/couchdb -pa ./src/mochiweb -sasl errlog_type false -noshell
++%%! -pa ./src/couchdb -sasl errlog_type false -noshell
+ 
+ % Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ % use this file except in compliance with the License. You may obtain a copy of
+diff --git a/test/etap/031-doc-to-json.t b/test/etap/031-doc-to-json.t
+index 4e0c3f7..819c1fe 100755
+--- a/test/etap/031-doc-to-json.t
++++ b/test/etap/031-doc-to-json.t
+@@ -1,6 +1,6 @@
+ #!/usr/bin/env escript
+ %% -*- erlang -*-
+-%%! -pa ./src/couchdb -pa ./src/mochiweb -sasl errlog_type false -noshell
++%%! -pa ./src/couchdb -sasl errlog_type false -noshell
+ 
+ % Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ % use this file except in compliance with the License. You may obtain a copy of
+diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in
+index c57d7a8..948958c 100644
+--- a/test/etap/test_util.erl.in
++++ b/test/etap/test_util.erl.in
+@@ -22,7 +22,7 @@ builddir() ->
+     "@abs_top_builddir@".
+ 
+ init_code_path() ->
+-    Paths = ["couchdb", "ibrowse", "mochiweb"],
++    Paths = ["couchdb", "ibrowse"],
+     lists:foreach(fun(Name) ->
+         code:add_pathz(filename:join([builddir(), "src", Name]))
+     end, Paths).
+diff --git a/test/run_native_process.es b/test/run_native_process.es
+index 48092e4..4e15d2c 100755
+--- a/test/run_native_process.es
++++ b/test/run_native_process.es
+@@ -49,7 +49,6 @@ loop(Pid) ->
+ 
+ main([]) ->
+     code:add_pathz("../src/couchdb"),
+-    code:add_pathz("../src/mochiweb"),
+     {ok, Pid} = couch_native_process:start_link(),
+     loop(Pid).
+ 
+diff --git a/test/runner.sh b/test/runner.sh
+index e1936b7..a7d518b 100755
+--- a/test/runner.sh
++++ b/test/runner.sh
+@@ -14,7 +14,7 @@
+ 
+ erlc runner.erl
+ 
+-erl -noshell -pa ../src/couchdb -pa ../src/mochiweb -eval "runner:run()"
++erl -noshell -pa ../src/couchdb -eval "runner:run()"
+ 
+ cat ../share/www/script/couch.js \
+     ../share/www/script/couch_test_runner.js \
+-- 
+1.6.6.1
+
diff --git a/couchdb-0006-Remove-pid-file-after-stop.patch b/couchdb-0006-Remove-pid-file-after-stop.patch
new file mode 100644
index 0000000..9ad7cdd
--- /dev/null
+++ b/couchdb-0006-Remove-pid-file-after-stop.patch
@@ -0,0 +1,28 @@
+From 24308e72761870d7608ebead2a8ebd32106c4d83 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov at gmail.com>
+Date: Mon, 7 Jun 2010 15:08:42 +0400
+Subject: [PATCH 6/7] Remove pid-file after stop
+
+---
+ bin/couchdb.tpl.in |    4 +---
+ 1 files changed, 1 insertions(+), 3 deletions(-)
+
+diff --git a/bin/couchdb.tpl.in b/bin/couchdb.tpl.in
+index 78cf755..e74f708 100644
+--- a/bin/couchdb.tpl.in
++++ b/bin/couchdb.tpl.in
+@@ -266,10 +266,8 @@ EOF
+ 
+ stop_couchdb () {
+     PID=`_get_pid`
++    rm -f $PID_FILE
+     if test -n "$PID"; then
+-        if test "$1" = "false"; then
+-            echo > $PID_FILE
+-        fi
+         if kill -0 $PID 2> /dev/null; then
+             if kill -1 $PID 2> /dev/null; then
+                 if test "$1" = "false"; then
+-- 
+1.6.6.1
+
diff --git a/couchdb-0007-Fix-for-system-wide-mochiweb.patch b/couchdb-0007-Fix-for-system-wide-mochiweb.patch
new file mode 100644
index 0000000..30b83a5
--- /dev/null
+++ b/couchdb-0007-Fix-for-system-wide-mochiweb.patch
@@ -0,0 +1,60 @@
+From 4cd1b65aa50d551664bec8d8ac8241d223c9448a Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov at gmail.com>
+Date: Mon, 7 Jun 2010 23:01:25 +0400
+Subject: [PATCH 7/7] Fix for system-wide mochiweb
+
+---
+ src/couchdb/couch_db.hrl   |    4 ++--
+ src/couchdb/couch_util.erl |   14 ++++++++++++++
+ 2 files changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl
+index ead7841..a07863f 100644
+--- a/src/couchdb/couch_db.hrl
++++ b/src/couchdb/couch_db.hrl
+@@ -14,8 +14,8 @@
+ -define(DESIGN_DOC_PREFIX0, "_design").
+ -define(DESIGN_DOC_PREFIX, "_design/").
+ 
+--define(JSON_ENCODE(V), mochijson2:encode(V)).
+--define(JSON_DECODE(V), mochijson2:decode(V)).
++-define(JSON_ENCODE(V), couch_util:json_encode(V)).
++-define(JSON_DECODE(V), couch_util:json_decode(V)).
+ 
+ -define(b2l(V), binary_to_list(V)).
+ -define(l2b(V), list_to_binary(V)).
+diff --git a/src/couchdb/couch_util.erl b/src/couchdb/couch_util.erl
+index d655989..67854fb 100644
+--- a/src/couchdb/couch_util.erl
++++ b/src/couchdb/couch_util.erl
+@@ -21,6 +21,7 @@
+ -export([file_read_size/1, get_nested_json_value/2, json_user_ctx/1]).
+ -export([to_binary/1, to_list/1, url_encode/1]).
+ -export([verify/2]).
++-export([json_encode/1, json_decode/1]).
+ 
+ -include("couch_db.hrl").
+ -include_lib("kernel/include/file.hrl").
+@@ -397,6 +398,19 @@ url_encode([H|T]) ->
+ url_encode([]) ->
+     [].
+ 
++json_encode(V) ->
++    Handler =
++        fun({L}) when is_list(L) ->
++            {struct,L};
++        (Bad) ->
++	    exit({json_encode, {bad_term, Bad}})
++    end,
++    (mochijson2:encoder([{handler, Handler}]))(V).
++
++json_decode(V) ->
++    try (mochijson2:decoder([{object_hook, fun({struct,L}) -> {L} end}]))(V)
++    catch _:_ -> throw({invalid_json,V}) end.
++
+ verify([X|RestX], [Y|RestY], Result) ->
+     verify(RestX, RestY, (X bxor Y) bor Result);
+ verify([], [], Result) ->
+-- 
+1.6.6.1
+
diff --git a/couchdb.spec b/couchdb.spec
index 8c7841f..51582c3 100644
--- a/couchdb.spec
+++ b/couchdb.spec
@@ -4,7 +4,7 @@
 
 Name:           couchdb
 Version:        0.10.2
-Release:        8%{?dist}
+Release:        10%{?dist}
 Summary:        A document database server, accessible via a RESTful JSON API
 
 Group:          Applications/Databases
@@ -16,17 +16,36 @@ Patch1:		couchdb-0001-Force-init-script-installation.patch
 Patch2:		couchdb-0002-Install-into-erllibdir-by-default.patch
 Patch3:		couchdb-0003-Remove-bundled-erlang-oauth-library.patch
 Patch4:		couchdb-0004-Remove-bundled-erlang-etap-library.patch
+Patch5:		couchdb-0005-Remove-bundled-mochiweb-library.patch
+Patch6:		couchdb-0006-Remove-pid-file-after-stop.patch
+# Backported from 0.11.0
+Patch7:		couchdb-0007-Fix-for-system-wide-mochiweb.patch
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
+BuildRequires:  curl-devel
 BuildRequires:  erlang
-BuildRequires:  libicu-devel
-BuildRequires:  js-devel
+BuildRequires:	erlang-etap
+BuildRequires:	erlang-mochiweb
+BuildRequires:	erlang-oauth
 BuildRequires:  help2man
-BuildRequires:  curl-devel
-#BuildRequires:	erlang-etap
-
+BuildRequires:  js-devel
+BuildRequires:  libicu-devel
+# /usr/bin/prove
+BuildRequires:	perl(Test::Harness)
+
+#Requires: erlang-crypto
+#Requires: erlang-erts
+#Requires: erlang-ibrowse
+#Requires: erlang-inets
+#Requires: erlang-kernel
+#Requires: erlang-mochiweb
+#Requires: erlang-oauth
+#Requires: erlang-ssl
+#Requires: erlang-stdlib
+#Requires: erlang-tools
 Requires:       erlang
 Requires:	erlang-oauth
+Requires:	erlang-mochiweb
 # For %{_bindir}/icu-config
 Requires:       libicu-devel
 
@@ -46,14 +65,16 @@ with bi-directional conflict detection and resolution, and is
 queryable and indexable using a table-oriented view engine with
 JavaScript acting as the default view definition language.
 
+
 %prep
 %setup -q -n apache-%{name}-%{version}
 %patch1 -p1 -b .initenabled
 %patch2 -p1 -b .fix_lib_path
 %patch3 -p1 -b .remove_bundled_oauth
 %patch4 -p1 -b .remove_bundled_etap
-rm -rf src/erlang-oauth
-rm -rf src/etap
+%patch5 -p1 -b .remove_bundled_mochiweb
+%patch6 -p1 -b .remove_pid_file
+%patch7 -p1 -b .fix_for_mochi
 # Restore original timestamps to avoid reconfiguring
 touch -r configure.ac.initenabled configure.ac
 touch -r configure.fix_lib_path configure
@@ -102,6 +123,11 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
 # fix respawn timeout to match default value
 sed -i s,^COUCHDB_RESPAWN_TIMEOUT=5,COUCHDB_RESPAWN_TIMEOUT=0,g $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/couchdb
 
+
+%check
+make check && cd test && sh runner.sh || exit 1
+
+
 %clean
 rm -rf $RPM_BUILD_ROOT
 
@@ -149,6 +175,13 @@ fi
 %dir %attr(0755, %{couchdb_user}, root) %{_localstatedir}/lib/couchdb
 
 %changelog
+* Mon Jun  7 2010 Peter Lemenkov <lemenkov at gmail.com> 0.10.2-10
+- Use system-wide erlang-mochiweb instead of bundled copy (rhbz #581284)
+- Added %%check target and necessary BuildRequires - etap, oauth, mochiweb
+
+* Wed Jun  2 2010 Peter Lemenkov <lemenkov at gmail.com> 0.10.2-9
+- Remove pid-file after stopping CouchDB
+
 * Tue Jun  1 2010 Peter Lemenkov <lemenkov at gmail.com> 0.10.2-8
 - Suppress unneeded message while stopping CouchDB via init-script
 
@@ -159,10 +192,10 @@ fi
 - Fix 'stop' and 'status' targets in the init-script (see rhbz #591026)
 
 * Thu May 27 2010 Peter Lemenkov <lemenkov at gmail.com> 0.10.2-5
-- Use system-wide erlang-etap instead of bundled copy
+- Use system-wide erlang-etap instead of bundled copy (rhbz #581281)
 
 * Fri May 14 2010 Peter Lemenkov <lemenkov at gmail.com> 0.10.2-4
-- Use system-wide erlang-oauth instead of bundled copy
+- Use system-wide erlang-oauth instead of bundled copy (rhbz #581283)
 
 * Thu May 13 2010 Peter Lemenkov <lemenkov at gmail.com> 0.10.2-3
 - Fixed init-script to use /etc/sysconfig/couchdb values (see rhbz #583004)


More information about the scm-commits mailing list