[osm2pgsql] 01/05: Imported Upstream version 0.87.3

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Thu Apr 30 07:27:53 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository osm2pgsql.

commit 004d913459a3bfc475e1d74519e063b18f8ddab1
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Thu Apr 30 09:01:25 2015 +0200

    Imported Upstream version 0.87.3
---
 .gitignore                                      |   3 +
 Makefile.am                                     |  20 +-
 README.md                                       |   9 +-
 configure.ac                                    |   6 +-
 docs/multi.md                                   |   5 +-
 expire-tiles.cpp                                | 115 +++---
 expire-tiles.hpp                                |   8 +-
 geometry-builder.cpp                            | 141 +++----
 geometry-builder.hpp                            |  15 +-
 geometry-processor.cpp                          |  91 ++---
 geometry-processor.hpp                          |  27 +-
 id-tracker.hpp                                  |   1 -
 keyvals.cpp                                     | 144 --------
 keyvals.hpp                                     |  64 ----
 m4/ax_append_flag.m4                            |  30 +-
 m4/ax_boost_base.m4                             |  19 +-
 m4/ax_compare_version.m4                        |   1 -
 m4/ax_compile_check_sizeof.m4                   |   2 +-
 m4/ax_lib_postgresql.m4                         |  38 +-
 m4/ax_lua.m4                                    |  27 +-
 m4/ax_pthread.m4                                | 156 +++++---
 middle-pgsql.cpp                                | 472 +++++++++++-------------
 middle-pgsql.hpp                                |  24 +-
 middle-ram.cpp                                  | 212 +++--------
 middle-ram.hpp                                  |  31 +-
 middle.cpp                                      |  12 -
 middle.hpp                                      |  31 +-
 multi.lua                                       | 217 +++++++++--
 multi.style.json                                |  30 +-
 node-persistent-cache-reader.cpp                |  36 +-
 node-persistent-cache.cpp                       |  85 ++---
 node-persistent-cache.hpp                       |   3 +-
 node-ram-cache.cpp                              |  68 ++--
 node-ram-cache.hpp                              |  12 +-
 options.hpp                                     |   3 +-
 osm2pgsql.cpp                                   |   2 +-
 osmdata.cpp                                     |  38 +-
 osmdata.hpp                                     |  21 +-
 osmtypes.hpp                                    | 105 +++++-
 output-gazetteer.cpp                            | 204 ++++------
 output-gazetteer.hpp                            |  53 ++-
 output-multi.cpp                                | 116 +++---
 output-multi.hpp                                |  22 +-
 output-null.cpp                                 |  12 +-
 output-null.hpp                                 |  12 +-
 output-pgsql.cpp                                | 229 ++++++------
 output-pgsql.hpp                                |  23 +-
 output.hpp                                      |  23 +-
 parse-o5m.cpp                                   |  84 ++---
 parse-pbf.cpp                                   | 364 ++++++++----------
 parse-pbf.hpp                                   |  15 +-
 parse-xml2.cpp                                  | 105 +++---
 parse-xml2.hpp                                  |   6 +-
 parse.cpp                                       |  69 +---
 parse.hpp                                       |  56 +--
 processor-line.cpp                              |   4 +-
 processor-line.hpp                              |   2 +-
 processor-polygon.cpp                           |   8 +-
 processor-polygon.hpp                           |   4 +-
 table.cpp                                       |  50 +--
 table.hpp                                       |  14 +-
 tagtransform.cpp                                | 441 ++++++++++------------
 tagtransform.hpp                                |  31 +-
 tests/middle-tests.cpp                          |  63 ++--
 tests/regression-test.py                        |  26 +-
 tests/test-expire-tiles.cpp                     |   2 +-
 tests/test-output-multi-line-storage.cpp        | 112 ++++++
 tests/test-output-multi-poly-trivial.cpp        | 137 +++++++
 tests/test-output-multi-tags.cpp                | 130 +++++++
 tests/test-parse-options.cpp                    |   6 +-
 tests/test-parse-xml2.cpp                       |  37 +-
 tests/test-pgsql-escape.cpp                     |   1 +
 tests/test_output_multi_line_storage.osm        |  35 ++
 tests/test_output_multi_line_trivial.lua        |  10 +
 tests/test_output_multi_line_trivial.style.json |  14 +
 tests/test_output_multi_poly_trivial.lua        |  24 ++
 tests/test_output_multi_poly_trivial.osm        |  39 ++
 tests/test_output_multi_poly_trivial.style.json |  15 +
 tests/test_output_multi_tags.json               |  99 +++++
 tests/test_output_multi_tags.lua                |  63 ++++
 tests/test_output_multi_tags.osm                |  28 ++
 81 files changed, 2759 insertions(+), 2353 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3f51b71..6ca8a4d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,10 +47,13 @@ tests/test-middle-ram
 tests/test-middle-pgsql
 tests/test-pgsql-escape
 tests/test-parse-options
+tests/test-output-multi-tags
 tests/test-output-multi-line
+tests/test-output-multi-line-storage
 tests/test-output-multi-point
 tests/test-output-multi-point-multi-table
 tests/test-output-multi-polygon
+tests/test-output-multi-poly-trivial
 tests/test-output-pgsql
 tests/test-expire-tiles
 tests/*.log
diff --git a/Makefile.am b/Makefile.am
index 2203d04..b4c0ef9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,6 @@ osm2pgsql_SOURCES = osm2pgsql.cpp \
 	geometry-builder.hpp \
 	expire-tiles.hpp \
 	input.hpp \
-	keyvals.hpp \
 	middle-pgsql.hpp \
 	middle-ram.hpp \
 	middle.hpp \
@@ -42,7 +41,6 @@ libosm2pgsql_la_SOURCES = \
 	geometry-processor.cpp \
 	id-tracker.cpp \
 	input.cpp \
-	keyvals.cpp \
 	middle.cpp \
 	middle-pgsql.cpp \
 	middle-ram.cpp \
@@ -80,10 +78,13 @@ check_PROGRAMS = \
 	tests/test-parse-xml2 \
 	tests/test-middle-ram \
 	tests/test-middle-pgsql \
+	tests/test-output-multi-tags \
 	tests/test-output-multi-line \
+	tests/test-output-multi-line-storage \
 	tests/test-output-multi-point \
 	tests/test-output-multi-point-multi-table \
 	tests/test-output-multi-polygon \
+	tests/test-output-multi-poly-trivial \
 	tests/test-output-pgsql \
 	tests/test-pgsql-escape \
 	tests/test-parse-options \
@@ -95,14 +96,20 @@ tests_test_middle_ram_SOURCES = tests/test-middle-ram.cpp tests/middle-tests.cpp
 tests_test_middle_ram_LDADD = libosm2pgsql.la
 tests_test_middle_pgsql_SOURCES = tests/test-middle-pgsql.cpp tests/middle-tests.cpp tests/common-pg.cpp
 tests_test_middle_pgsql_LDADD = libosm2pgsql.la
+tests_test_output_multi_tags_SOURCES = tests/test-output-multi-tags.cpp tests/common-pg.cpp
+tests_test_output_multi_tags_LDADD = libosm2pgsql.la
 tests_test_output_multi_line_SOURCES = tests/test-output-multi-line.cpp tests/common-pg.cpp
 tests_test_output_multi_line_LDADD = libosm2pgsql.la
+tests_test_output_multi_line_storage_SOURCES = tests/test-output-multi-line-storage.cpp tests/common-pg.cpp
+tests_test_output_multi_line_storage_LDADD = libosm2pgsql.la
 tests_test_output_multi_point_SOURCES = tests/test-output-multi-point.cpp tests/common-pg.cpp
 tests_test_output_multi_point_LDADD = libosm2pgsql.la
 tests_test_output_multi_point_multi_table_SOURCES = tests/test-output-multi-point-multi-table.cpp tests/common-pg.cpp
 tests_test_output_multi_point_multi_table_LDADD = libosm2pgsql.la
 tests_test_output_multi_polygon_SOURCES = tests/test-output-multi-polygon.cpp tests/common-pg.cpp
 tests_test_output_multi_polygon_LDADD = libosm2pgsql.la
+tests_test_output_multi_poly_trivial_SOURCES = tests/test-output-multi-poly-trivial.cpp tests/common-pg.cpp
+tests_test_output_multi_poly_trivial_LDADD = libosm2pgsql.la
 tests_test_output_pgsql_SOURCES = tests/test-output-pgsql.cpp tests/common-pg.cpp
 tests_test_output_pgsql_LDADD = libosm2pgsql.la
 tests_test_pgsql_escape_SOURCES = tests/test-pgsql-escape.cpp
@@ -150,18 +157,21 @@ endif
 
 osm2pgsqldir = $(datadir)/osm2pgsql
 
-AM_CFLAGS = @PTHREAD_CFLAGS@ @LFS_CFLAGS@ @POSTGRESQL_CFLAGS@ @XML2_CFLAGS@ @BZIP2_CFLAGS@ @GEOS_CFLAGS@ @PROJ_CFLAGS@ @PROTOBUF_C_CFLAGS@ @ZLIB_CFLAGS@ -DOSM2PGSQL_DATADIR='"$(osm2pgsqldir)"' -DVERSION='"@PACKAGE_VERSION@"' @LUA_INCLUDE@
-AM_CPPFLAGS = @PTHREAD_CFLAGS@ @POSTGRESQL_CFLAGS@ @XML2_CFLAGS@ @BZIP2_CFLAGS@ @GEOS_CFLAGS@ @PROJ_CFLAGS@ -DOSM2PGSQL_DATADIR='"$(osm2pgsqldir)"' -Igeos-fallback @LUA_INCLUDE@ @BOOST_CPPFLAGS@
+AM_CFLAGS = @PTHREAD_CFLAGS@ @LFS_CFLAGS@ @POSTGRESQL_CPPFLAGS@ @XML2_CFLAGS@ @BZIP2_CFLAGS@ @GEOS_CFLAGS@ @PROJ_CFLAGS@ @PROTOBUF_C_CFLAGS@ @ZLIB_CFLAGS@ -DOSM2PGSQL_DATADIR='"$(osm2pgsqldir)"' -DVERSION='"@PACKAGE_VERSION@"' @LUA_INCLUDE@
+AM_CPPFLAGS = @PTHREAD_CFLAGS@ @POSTGRESQL_CPPFLAGS@ @XML2_CFLAGS@ @BZIP2_CFLAGS@ @GEOS_CFLAGS@ @PROJ_CFLAGS@ -DOSM2PGSQL_DATADIR='"$(osm2pgsqldir)"' -Igeos-fallback @LUA_INCLUDE@ @BOOST_CPPFLAGS@
 
-GLOBAL_LDFLAGS = @PTHREAD_CFLAGS@ @ZLIB_LDFLAGS@ @ZLIB_LIBS@ @POSTGRESQL_LDFLAGS@ @POSTGRESQL_LIBS@ @XML2_LDFLAGS@ @BZIP2_LDFLAGS@ @BZIP2_LIBS@ @GEOS_LDFLAGS@ @GEOS_LIBS@ @PROJ_LDFLAGS@ @PROJ_LIBS@ @PROTOBUF_C_LDFLAGS@ @PROTOBUF_C_LIBS@ -L/usr/lib/x86_64-linux-gnu @LUA_LIB@ @BOOST_LDFLAGS@ @BOOST_FILESYSTEM_LIB@ @BOOST_SYSTEM_LIB@ @BOOST_THREAD_LIB@ -lstdc++
+GLOBAL_LDFLAGS = @PTHREAD_CFLAGS@ @ZLIB_LDFLAGS@ @ZLIB_LIBS@ @POSTGRESQL_LDFLAGS@ @POSTGRESQL_LIBS@ @XML2_LDFLAGS@ @BZIP2_LDFLAGS@ @BZIP2_LIBS@ @GEOS_LDFLAGS@ @GEOS_LIBS@ @PROJ_LDFLAGS@ @PROJ_LIBS@ @PROTOBUF_C_LDFLAGS@ @PROTOBUF_C_LIBS@ -L/usr/lib/x86_64-linux-gnu @LUA_LIB@ @BOOST_LDFLAGS@ @BOOST_FILESYSTEM_LIB@ @BOOST_SYSTEM_LIB@ @BOOST_THREAD_LIB@
 osm2pgsql_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_parse_xml2_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_middle_ram_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_middle_pgsql_LDADD += $(GLOBAL_LDFLAGS)
+tests_test_output_multi_tags_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_output_multi_line_LDADD += $(GLOBAL_LDFLAGS)
+tests_test_output_multi_line_storage_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_output_multi_point_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_output_multi_point_multi_table_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_output_multi_polygon_LDADD += $(GLOBAL_LDFLAGS)
+tests_test_output_multi_poly_trivial_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_output_pgsql_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_pgsql_escape_LDADD += $(GLOBAL_LDFLAGS)
 tests_test_parse_options_LDADD += $(GLOBAL_LDFLAGS)
diff --git a/README.md b/README.md
index a37391e..f6bdd68 100644
--- a/README.md
+++ b/README.md
@@ -51,15 +51,16 @@ To install on a Debian or Ubuntu system, first install the prerequisites:
 ```sh
 sudo apt-get install autoconf automake libtool make g++ libboost-dev \
   libboost-system-dev libboost-filesystem-dev libboost-thread-dev libxml2-dev \
-  libgeos-dev libgeos++-dev libpq-dev libbz2-dev libproj-dev
+  libgeos-dev libgeos++-dev libpq-dev libbz2-dev libproj-dev \
   protobuf-c-compiler libprotobuf-c0-dev lua5.2 liblua5.2-dev
 ```
 
 To install on a Fedora system, use
 
 ```sh
-sudo yum install gcc-c++ boost-devel libxml2-devel geos-devel \
-  postgresql-devel bzip2-devel proj-devel protobuf-compiler
+sudo yum install gcc-c++ automake libtool boost-devel libxml2-devel \
+  bzip2-devel postgresql-devel geos-devel proj-devel lua-devel \
+  protobuf-c-devel
 ```
 
 To install on a FreeBSD system, use
@@ -83,7 +84,7 @@ process.
 
 On FreeBSD instead bootstrap and then run
 
-    LUA_LIB=`pkg-config --libs lua-5.2` ./configure --without-lockfree && gmake && gmake install
+    LUA_LIB=`pkg-config --libs lua-5.2` ./configure && gmake && gmake install
 
 ## Usage ##
 
diff --git a/configure.ac b/configure.ac
index 3b08a07..91a3d1d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT(osm2pgsql, 0.87.2)
+AC_INIT(osm2pgsql, 0.87.3)
 
 dnl Required autoconf version
 AC_PREREQ(2.61)
@@ -37,8 +37,8 @@ then
   AC_MSG_ERROR([Could not find a c++ compiler]);
 fi
 
-dnl AX_CFLAGS_WARN_ALL
-dnl AX_CXXFLAGS_WARN_ALL
+AX_CFLAGS_WARN_ALL
+AX_CXXFLAGS_WARN_ALL
 
 dnl Make sure we have libtool installed
 AM_PROG_LIBTOOL
diff --git a/docs/multi.md b/docs/multi.md
index 67ebc63..887319f 100644
--- a/docs/multi.md
+++ b/docs/multi.md
@@ -52,8 +52,9 @@ as a string of flag names seprated by commas.
 
 ## Example ##
 An example based on the above is in [multi.lua](../multi.lua) and
-[multi.style.json](../multi.style.json). It creates two tables, one for bus stops
-and one for buildings. Some Lua processing is done to unify tagging values.
+[multi.style.json](../multi.style.json). It creates three tables, one for bus
+stops, one for buildings, and one for highways. Some Lua processing is done to
+unify tagging values.
 
 ## Importing ##
 
diff --git a/expire-tiles.cpp b/expire-tiles.cpp
index 5c088f7..9e14fd8 100644
--- a/expire-tiles.cpp
+++ b/expire-tiles.cpp
@@ -259,7 +259,7 @@ expire_tiles::~expire_tiles() {
 }
 
 expire_tiles::expire_tiles(const struct options_t *options)
-    : Options(options), map_width(0), tile_width(0),
+    : map_width(0), tile_width(0), Options(options),
       dirty(NULL)
 {
 	if (Options->expire_tiles_zoom < 0) return;
@@ -413,80 +413,71 @@ int expire_tiles::from_bbox(double min_lon, double min_lat, double max_lon, doub
 	return 0;
 }
 
-void expire_tiles::from_nodes_line(const struct osmNode * nodes, int count) {
-	int	i;
-	double	last_lat;
-	double	last_lon;
-
-	if (Options->expire_tiles_zoom < 0) return;
-	if (count < 1) return;
-	last_lat = nodes[0].lat;
-	last_lon = nodes[0].lon;
-	if (count < 2) {
-		from_bbox(last_lon, last_lat, last_lon, last_lat);
-		return;
-	}
-	for (i = 1; i < count; i ++) {
-		from_line(last_lon, last_lat, nodes[i].lon, nodes[i].lat);
-		last_lat = nodes[i].lat;
-		last_lon = nodes[i].lon;
-	}
+void expire_tiles::from_nodes_line(const nodelist_t &nodes)
+{
+    if (Options->expire_tiles_zoom < 0 || nodes.empty())
+        return;
+
+    if (nodes.size() == 1) {
+        from_bbox(nodes[0].lon, nodes[0].lat, nodes[0].lon, nodes[0].lat);
+    } else {
+        for (size_t i = 1; i < nodes.size(); ++i)
+            from_line(nodes[i-1].lon, nodes[i-1].lat, nodes[i].lon, nodes[i].lat);
+    }
 }
 
 /*
  * Calculate a bounding box from a list of nodes and expire all tiles within it
  */
-void expire_tiles::from_nodes_poly(const struct osmNode * nodes, int count, osmid_t osm_id) {
-	int	i;
-	int	got_coords = 0;
-	double	min_lon = 0.0;
-	double	min_lat = 0.0;
-	double	max_lon = 0.0;
-	double	max_lat = 0.0;
+void expire_tiles::from_nodes_poly(const nodelist_t &nodes, osmid_t osm_id)
+{
+    if (Options->expire_tiles_zoom < 0 || nodes.empty())
+        return;
+
+    double min_lon = nodes[0].lon;
+    double min_lat = nodes[0].lat;
+    double max_lon = nodes[0].lon;
+    double max_lat = nodes[0].lat;
+
+    for (size_t i = 1; i < nodes.size(); ++i) {
+        if (nodes[i].lon < min_lon) min_lon = nodes[i].lon;
+        if (nodes[i].lat < min_lat) min_lat = nodes[i].lat;
+        if (nodes[i].lon > max_lon) max_lon = nodes[i].lon;
+        if (nodes[i].lat > max_lat) max_lat = nodes[i].lat;
+    }
 
-	if (Options->expire_tiles_zoom < 0) return;
-	for (i = 0; i < count; i++) {
-		if ((! got_coords) || (nodes[i].lon < min_lon)) min_lon = nodes[i].lon;
-		if ((! got_coords) || (nodes[i].lat < min_lat)) min_lat = nodes[i].lat;
-		if ((! got_coords) || (nodes[i].lon > max_lon)) max_lon = nodes[i].lon;
-		if ((! got_coords) || (nodes[i].lat > max_lat)) max_lat = nodes[i].lat;
-		got_coords = 1;
-	}
-	if (got_coords) {
-		if (from_bbox(min_lon, min_lat, max_lon, max_lat)) {
-			/* Bounding box too big - just expire tiles on the line */
-			fprintf(stderr, "\rLarge polygon (%.0f x %.0f metres, OSM ID %" PRIdOSMID ") - only expiring perimeter\n", max_lon - min_lon, max_lat - min_lat, osm_id);
-			from_nodes_line(nodes, count);
-		}
-	}
+    if (from_bbox(min_lon, min_lat, max_lon, max_lat)) {
+        /* Bounding box too big - just expire tiles on the line */
+        fprintf(stderr, "\rLarge polygon (%.0f x %.0f metres, OSM ID %" PRIdOSMID ") - only expiring perimeter\n", max_lon - min_lon, max_lat - min_lat, osm_id);
+        from_nodes_line(nodes);
+    }
 }
 
-void expire_tiles::from_xnodes_poly(const struct osmNode * const * xnodes, int * xcount, osmid_t osm_id) {
-	int	i;
-
-        for (i = 0; xnodes[i]; i++) from_nodes_poly(xnodes[i], xcount[i], osm_id);
+void expire_tiles::from_xnodes_poly(const multinodelist_t &xnodes, osmid_t osm_id)
+{
+    for (multinodelist_t::const_iterator it = xnodes.begin(); it != xnodes.end(); ++it)
+        from_nodes_poly(*it, osm_id);
 }
 
-void expire_tiles::from_xnodes_line(const struct osmNode * const * xnodes, int * xcount) {
-	int	i;
-
-        for (i = 0; xnodes[i]; i++) from_nodes_line(xnodes[i], xcount[i]);
+void expire_tiles::from_xnodes_line(const multinodelist_t &xnodes)
+{
+    for (multinodelist_t::const_iterator it = xnodes.begin(); it != xnodes.end(); ++it)
+        from_nodes_line(*it);
 }
 
-void expire_tiles::from_wkt(const char * wkt, osmid_t osm_id) {
-	struct osmNode **	xnodes;
-	int *			xcount;
-	int			polygon;
-	int			i;
+void expire_tiles::from_wkt(const char * wkt, osmid_t osm_id)
+{
+    if (Options->expire_tiles_zoom < 0) return;
 
-	if (Options->expire_tiles_zoom < 0) return;
-	if (! geometry_builder::parse_wkt(wkt, &xnodes, &xcount, &polygon)) {
-		if (polygon) from_xnodes_poly(xnodes, xcount, osm_id);
-		else from_xnodes_line(xnodes, xcount);
-		for (i = 0; xnodes[i]; i++) free(xnodes[i]);
-		free(xnodes);
-		free(xcount);
-	}
+    multinodelist_t xnodes;
+    int polygon;
+
+    if (!geometry_builder::parse_wkt(wkt, xnodes, &polygon)) {
+        if (polygon)
+            from_xnodes_poly(xnodes, osm_id);
+        else
+            from_xnodes_line(xnodes);
+    }
 }
 
 /*
diff --git a/expire-tiles.hpp b/expire-tiles.hpp
index 7dfca61..08d6839 100644
--- a/expire-tiles.hpp
+++ b/expire-tiles.hpp
@@ -15,8 +15,8 @@ struct expire_tiles : public boost::noncopyable {
     //TODO: copy constructor
 
     int from_bbox(double min_lon, double min_lat, double max_lon, double max_lat);
-    void from_nodes_line(const struct osmNode * nodes, int count);
-    void from_nodes_poly(const struct osmNode * nodes, int count, osmid_t osm_id);
+    void from_nodes_line(const nodelist_t &nodes);
+    void from_nodes_poly(const nodelist_t &nodes, osmid_t osm_id);
     void from_wkt(const char * wkt, osmid_t osm_id);
     int from_db(table_t* table, osmid_t osm_id);
 
@@ -52,8 +52,8 @@ private:
     void expire_tile(int x, int y);
     int normalise_tile_x_coord(int x);
     void from_line(double lon_a, double lat_a, double lon_b, double lat_b);
-    void from_xnodes_poly(const struct osmNode * const * xnodes, int * xcount, osmid_t osm_id);
-    void from_xnodes_line(const struct osmNode * const * xnodes, int * xcount);
+    void from_xnodes_poly(const multinodelist_t &xnodes, osmid_t osm_id);
+    void from_xnodes_line(const multinodelist_t &xnodes);
 
     int map_width;
     double tile_width;
diff --git a/geometry-builder.cpp b/geometry-builder.cpp
index 5603665..0aaf578 100644
--- a/geometry-builder.cpp
+++ b/geometry-builder.cpp
@@ -21,6 +21,7 @@
 */
 
 #include <iostream>
+#include <algorithm>
 #include <cstring>
 #include <cstdlib>
 #include <cmath>
@@ -56,20 +57,15 @@ typedef std::auto_ptr<Geometry> geom_ptr;
 
 namespace {
 
-int coords2nodes(CoordinateSequence * coords, struct osmNode ** nodes) {
-    size_t			num_coords;
-    size_t			i;
-    Coordinate		coord;
-
-    num_coords = coords->getSize();
-    *nodes = (struct osmNode *) malloc(num_coords * sizeof(struct osmNode));
+void coords2nodes(CoordinateSequence * coords, nodelist_t &nodes)
+{
+    size_t num_coords = coords->getSize();
+    nodes.reserve(num_coords);
 
-    for (i = 0; i < num_coords; i++) {
-        coord = coords->getAt(i);
-        (*nodes)[i].lon = coord.x;
-        (*nodes)[i].lat = coord.y;
+    for (size_t i = 0; i < num_coords; i++) {
+        Coordinate coord = coords->getAt(i);
+        nodes.push_back(osmNode(coord.x, coord.y));
     }
-    return num_coords;
 }
 
 struct polygondata
@@ -81,29 +77,23 @@ struct polygondata
     unsigned        containedbyid;
 };
 
-int polygondata_comparearea(const void* vp1, const void* vp2)
-{
-    const polygondata* p1 = (const polygondata*)vp1;
-    const polygondata* p2 = (const polygondata*)vp2;
+struct polygondata_comparearea {
+  bool operator()(const polygondata& lhs, const polygondata& rhs) {
+    return lhs.area > rhs.area;
+  }
+};
 
-    if (p1->area == p2->area) return 0;
-    if (p1->area > p2->area) return -1;
-    return 1;
-}
 } // anonymous namespace
 
-geometry_builder::maybe_wkt_t geometry_builder::get_wkt_simple(const osmNode *nodes, int count, int polygon) const
+geometry_builder::maybe_wkt_t geometry_builder::get_wkt_simple(const nodelist_t &nodes, int polygon) const
 {
     GeometryFactory gf;
     std::auto_ptr<CoordinateSequence> coords(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2));
 
     try
     {
-        for (int i = 0; i < count ; i++) {
-            Coordinate c;
-            c.x = nodes[i].lon;
-            c.y = nodes[i].lat;
-            coords->add(c, 0);
+        for (nodelist_t::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
+            coords->add(Coordinate(it->lon, it->lat), 0);
         }
 
         maybe_wkt_t wkt(new geometry_builder::wkt_t());
@@ -130,12 +120,12 @@ geometry_builder::maybe_wkt_t geometry_builder::get_wkt_simple(const osmNode *no
         wkt->geom = WKTWriter().write(geom.get());
         return wkt;
     }
-    catch (std::bad_alloc&)
+    catch (const std::bad_alloc&)
     {
         std::cerr << std::endl << "Exception caught processing way. You are likelly running out of memory." << std::endl;
         std::cerr << "Try in slim mode, using -s parameter." << std::endl;
     }
-    catch (std::runtime_error& e)
+    catch (const std::runtime_error& e)
     {
         //std::cerr << std::endl << "Exception caught processing way: " << e.what() << std::endl;
     }
@@ -147,7 +137,7 @@ geometry_builder::maybe_wkt_t geometry_builder::get_wkt_simple(const osmNode *no
     return maybe_wkt_t();
 }
 
-geometry_builder::maybe_wkts_t geometry_builder::get_wkt_split(const osmNode *nodes, int count, int polygon, double split_at) const
+geometry_builder::maybe_wkts_t geometry_builder::get_wkt_split(const nodelist_t &nodes, int polygon, double split_at) const
 {
     GeometryFactory gf;
     std::auto_ptr<CoordinateSequence> coords(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2));
@@ -157,11 +147,8 @@ geometry_builder::maybe_wkts_t geometry_builder::get_wkt_split(const osmNode *no
 
     try
     {
-        for (int i = 0; i < count ; i++) {
-            Coordinate c;
-            c.x = nodes[i].lon;
-            c.y = nodes[i].lat;
-            coords->add(c, 0);
+        for (nodelist_t::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
+            coords->add(Coordinate(it->lon, it->lat), 0);
         }
 
         geom_ptr geom;
@@ -247,12 +234,12 @@ geometry_builder::maybe_wkts_t geometry_builder::get_wkt_split(const osmNode *no
             }
         }
     }
-    catch (std::bad_alloc&)
+    catch (const std::bad_alloc&)
     {
         std::cerr << std::endl << "Exception caught processing way. You are likely running out of memory." << std::endl;
         std::cerr << "Try in slim mode, using -s parameter." << std::endl;
     }
-    catch (std::runtime_error& e)
+    catch (const std::runtime_error& e)
     {
         //std::cerr << std::endl << "Exception caught processing way: " << e.what() << std::endl;
     }
@@ -263,20 +250,17 @@ geometry_builder::maybe_wkts_t geometry_builder::get_wkt_split(const osmNode *no
     return wkts;
 }
 
-int geometry_builder::parse_wkt(const char * wkt, struct osmNode *** xnodes, int ** xcount, int * polygon) {
+int geometry_builder::parse_wkt(const char * wkt, multinodelist_t &nodes, int *polygon) {
     GeometryFactory		gf;
     WKTReader		reader(&gf);
     std::string		wkt_string(wkt);
-    Geometry *		geometry;
-    const Geometry *	subgeometry;
     GeometryCollection *	gc;
     CoordinateSequence *	coords;
     size_t			num_geometries;
-    size_t			i;
 
     *polygon = 0;
     try {
-        geometry = reader.read(wkt_string);
+        Geometry * geometry = reader.read(wkt_string);
         switch (geometry->getGeometryTypeId()) {
             // Single geometries
             case GEOS_POLYGON:
@@ -287,11 +271,9 @@ int geometry_builder::parse_wkt(const char * wkt, struct osmNode *** xnodes, int
             case GEOS_POINT:
                 // Drop through
             case GEOS_LINESTRING:
-                *xnodes = (struct osmNode **) malloc(2 * sizeof(struct osmNode *));
-                *xcount = (int *) malloc(sizeof(int));
+                nodes.push_back(nodelist_t());
                 coords = geometry->getCoordinates();
-                (*xcount)[0] = coords2nodes(coords, &((*xnodes)[0]));
-                (*xnodes)[1] = NULL;
+                coords2nodes(coords, nodes.back());
                 delete coords;
                 break;
             // Geometry collections
@@ -301,17 +283,15 @@ int geometry_builder::parse_wkt(const char * wkt, struct osmNode *** xnodes, int
             case GEOS_MULTIPOINT:
                 // Drop through
             case GEOS_MULTILINESTRING:
-                gc = dynamic_cast<GeometryCollection *>(geometry);;
+                gc = dynamic_cast<GeometryCollection *>(geometry);
                 num_geometries = gc->getNumGeometries();
-                *xnodes = (struct osmNode **) malloc((num_geometries + 1) * sizeof(struct osmNode *));
-                *xcount = (int *) malloc(num_geometries * sizeof(int));
-                for (i = 0; i < num_geometries; i++) {
-                    subgeometry = gc->getGeometryN(i);
+                nodes.assign(num_geometries, nodelist_t());
+                for (size_t i = 0; i < num_geometries; i++) {
+                    const Geometry *subgeometry = gc->getGeometryN(i);
                     coords = subgeometry->getCoordinates();
-                    (*xcount)[i] = coords2nodes(coords, &((*xnodes)[i]));
+                    coords2nodes(coords, nodes[i]);
                     delete coords;
                 }
-                (*xnodes)[i] = NULL;
                 break;
             default:
                 std::cerr << std::endl << "unexpected object type while processing PostGIS data" << std::endl;
@@ -326,7 +306,8 @@ int geometry_builder::parse_wkt(const char * wkt, struct osmNode *** xnodes, int
     return 0;
 }
 
-geometry_builder::maybe_wkts_t geometry_builder::build_polygons(const osmNode * const * xnodes, const int *xcount, bool enable_multi, osmid_t osm_id) const
+geometry_builder::maybe_wkts_t geometry_builder::build_polygons(const multinodelist_t &xnodes,
+                                                                bool enable_multi, osmid_t osm_id) const
 {
     std::auto_ptr<std::vector<Geometry*> > lines(new std::vector<Geometry*>);
     GeometryFactory gf;
@@ -338,13 +319,12 @@ geometry_builder::maybe_wkts_t geometry_builder::build_polygons(const osmNode *
 
     try
     {
-        for (int c=0; xnodes[c]; c++) {
+        for (multinodelist_t::const_iterator it = xnodes.begin(); it != xnodes.end(); ++it) {
             std::auto_ptr<CoordinateSequence> coords(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2));
-            for (int i = 0; i < xcount[c]; i++) {
-                const osmNode *nodes = xnodes[c];
+            for (nodelist_t::const_iterator node = it->begin(); node != it->end(); ++node) {
                 Coordinate c;
-                c.x = nodes[i].lon;
-                c.y = nodes[i].lat;
+                c.x = node->lon;
+                c.y = node->lat;
                 coords->add(c, 0);
             }
             if (coords->getSize() > 1) {
@@ -363,7 +343,7 @@ geometry_builder::maybe_wkts_t geometry_builder::build_polygons(const osmNode *
         WKTWriter writer;
 
         // Procces ways into lines or simple polygon list
-        polygondata* polys = new polygondata[merged->size()];
+        std::vector<polygondata> polys(merged->size());
 
         unsigned totalpolys = 0;
         for (unsigned i=0 ;i < merged->size(); ++i)
@@ -387,7 +367,7 @@ geometry_builder::maybe_wkts_t geometry_builder::build_polygons(const osmNode *
 
         if (totalpolys)
         {
-            qsort(polys, totalpolys, sizeof(polygondata), polygondata_comparearea);
+            std::sort(polys.begin(), polys.begin() + totalpolys, polygondata_comparearea());
 
             unsigned toplevelpolygons = 0;
             int istoplevelafterall;
@@ -509,9 +489,8 @@ geometry_builder::maybe_wkts_t geometry_builder::build_polygons(const osmNode *
         {
             delete(polys[i].polygon);
         }
-        delete[](polys);
     }//TODO: don't show in message id when osm_id == -1
-    catch (std::exception& e)
+    catch (const std::exception& e)
     {
         std::cerr << std::endl << "Standard exception processing way_id="<< osm_id << ": " << e.what()  << std::endl;
     }
@@ -523,7 +502,7 @@ geometry_builder::maybe_wkts_t geometry_builder::build_polygons(const osmNode *
     return wkts;
 }
 
-geometry_builder::maybe_wkt_t geometry_builder::build_multilines(const osmNode * const * xnodes, const int *xcount, osmid_t osm_id) const
+geometry_builder::maybe_wkt_t geometry_builder::build_multilines(const multinodelist_t &xnodes, osmid_t osm_id) const
 {
     std::auto_ptr<std::vector<Geometry*> > lines(new std::vector<Geometry*>);
     GeometryFactory gf;
@@ -533,13 +512,12 @@ geometry_builder::maybe_wkt_t geometry_builder::build_multilines(const osmNode *
 
     try
     {
-        for (int c=0; xnodes[c]; c++) {
+        for (multinodelist_t::const_iterator it = xnodes.begin(); it != xnodes.end(); ++it) {
             std::auto_ptr<CoordinateSequence> coords(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2));
-            for (int i = 0; i < xcount[c]; i++) {
-                const osmNode *nodes = xnodes[c];
+            for (nodelist_t::const_iterator node = it->begin(); node != it->end(); ++node) {
                 Coordinate c;
-                c.x = nodes[i].lon;
-                c.y = nodes[i].lat;
+                c.x = node->lon;
+                c.y = node->lat;
                 coords->add(c, 0);
             }
             if (coords->getSize() > 1) {
@@ -553,10 +531,10 @@ geometry_builder::maybe_wkt_t geometry_builder::build_multilines(const osmNode *
         //geom_ptr noded (segment->Union(mline.get()));
 
         WKTWriter writer;
-	wkt->geom = writer.write(mline.get());
-	wkt->area = 0;
+        wkt->geom = writer.write(mline.get());
+        wkt->area = 0;
     }//TODO: don't show in message id when osm_id == -1
-    catch (std::exception& e)
+    catch (const std::exception& e)
     {
         std::cerr << std::endl << "Standard exception processing way_id="<< osm_id << ": " << e.what()  << std::endl;
     }
@@ -567,8 +545,9 @@ geometry_builder::maybe_wkt_t geometry_builder::build_multilines(const osmNode *
     return wkt;
 }
 
-geometry_builder::maybe_wkts_t geometry_builder::build_both(const osmNode * const * xnodes, const int *xcount, int make_polygon,
-                                                                             int enable_multi, double split_at, osmid_t osm_id) const
+geometry_builder::maybe_wkts_t geometry_builder::build_both(const multinodelist_t &xnodes,
+                                                            int make_polygon, int enable_multi,
+                                                            double split_at, osmid_t osm_id) const
 {
     std::auto_ptr<std::vector<Geometry*> > lines(new std::vector<Geometry*>);
     GeometryFactory gf;
@@ -579,13 +558,12 @@ geometry_builder::maybe_wkts_t geometry_builder::build_both(const osmNode * cons
 
     try
     {
-        for (int c=0; xnodes[c]; c++) {
+        for (multinodelist_t::const_iterator it = xnodes.begin(); it != xnodes.end(); ++it) {
             std::auto_ptr<CoordinateSequence> coords(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2));
-            for (int i = 0; i < xcount[c]; i++) {
-                const osmNode *nodes = xnodes[c];
+            for (nodelist_t::const_iterator node = it->begin(); node != it->end(); ++node) {
                 Coordinate c;
-                c.x = nodes[i].lon;
-                c.y = nodes[i].lat;
+                c.x = node->lon;
+                c.y = node->lat;
                 coords->add(c, 0);
             }
             if (coords->getSize() > 1) {
@@ -604,7 +582,7 @@ geometry_builder::maybe_wkts_t geometry_builder::build_both(const osmNode * cons
         WKTWriter writer;
 
         // Procces ways into lines or simple polygon list
-        polygondata* polys = new polygondata[merged->size()];
+        std::vector<polygondata> polys(merged->size());
 
         unsigned totalpolys = 0;
         for (unsigned i=0 ;i < merged->size(); ++i)
@@ -657,7 +635,7 @@ geometry_builder::maybe_wkts_t geometry_builder::build_both(const osmNode * cons
 
         if (totalpolys)
         {
-            qsort(polys, totalpolys, sizeof(polygondata), polygondata_comparearea);
+            std::sort(polys.begin(), polys.begin() + totalpolys, polygondata_comparearea());
 
             unsigned toplevelpolygons = 0;
             int istoplevelafterall;
@@ -779,9 +757,8 @@ geometry_builder::maybe_wkts_t geometry_builder::build_both(const osmNode * cons
         {
             delete(polys[i].polygon);
         }
-        delete[](polys);
     }//TODO: don't show in message id when osm_id == -1
-    catch (std::exception& e)
+    catch (const std::exception& e)
     {
         std::cerr << std::endl << "Standard exception processing relation id="<< osm_id << ": " << e.what()  << std::endl;
     }
diff --git a/geometry-builder.hpp b/geometry-builder.hpp
index e206497..4f8cac7 100644
--- a/geometry-builder.hpp
+++ b/geometry-builder.hpp
@@ -48,15 +48,14 @@ struct geometry_builder : public boost::noncopyable
     geometry_builder();
     ~geometry_builder();
 
-    static int parse_wkt(const char * wkt, struct osmNode *** xnodes, int ** xcount, int * polygon);
-    maybe_wkt_t get_wkt_simple(const osmNode *, int count, int polygon) const;
-    maybe_wkts_t get_wkt_split(const osmNode *, int count, int polygon, double split_at) const;
-    maybe_wkts_t build_both(const osmNode * const * xnodes, const int *xcount, int make_polygon, int enable_multi,
-                                                     double split_at, osmid_t osm_id = -1) const;
-    maybe_wkts_t build_lines(const osmNode * const * xnodes, const int *xcount, double split_at, osmid_t osm_id = -1) const;
-    maybe_wkts_t build_polygons(const osmNode * const * xnodes, const int *xcount, bool enable_multi, osmid_t osm_id = -1) const;
+    static int parse_wkt(const char *wkt, multinodelist_t &nodes, int *polygon);
+    maybe_wkt_t get_wkt_simple(const nodelist_t &nodes, int polygon) const;
+    maybe_wkts_t get_wkt_split(const nodelist_t &nodes, int polygon, double split_at) const;
+    maybe_wkts_t build_both(const multinodelist_t &xnodes, int make_polygon,
+                            int enable_multi, double split_at, osmid_t osm_id = -1) const;
+    maybe_wkts_t build_polygons(const multinodelist_t &xnodes, bool enable_multi, osmid_t osm_id = -1) const;
     // Used by gazetteer. Outputting a multiline, it only ever returns one WKT
-    maybe_wkt_t build_multilines(const osmNode * const * xnodes, const int *xcount, osmid_t osm_id) const;
+    maybe_wkt_t build_multilines(const multinodelist_t &xnodes, osmid_t osm_id) const;
 
     void set_exclude_broken_polygon(int exclude);
 
diff --git a/geometry-processor.cpp b/geometry-processor.cpp
index 16e302b..4261693 100644
--- a/geometry-processor.cpp
+++ b/geometry-processor.cpp
@@ -59,11 +59,11 @@ geometry_builder::maybe_wkt_t geometry_processor::process_node(double lat, doubl
     return geometry_builder::maybe_wkt_t();
 }
 
-geometry_builder::maybe_wkt_t geometry_processor::process_way(const osmNode *nodes, const size_t node_count) {
+geometry_builder::maybe_wkt_t geometry_processor::process_way(const nodelist_t &nodes) {
     return geometry_builder::maybe_wkt_t();
 }
 
-geometry_builder::maybe_wkts_t geometry_processor::process_relation(const osmNode * const * nodes, const int* node_counts) {
+geometry_builder::maybe_wkts_t geometry_processor::process_relation(const multinodelist_t &nodes) {
     return geometry_builder::maybe_wkts_t();
 }
 
@@ -73,84 +73,65 @@ way_helper::way_helper()
 way_helper::~way_helper()
 {
 }
-size_t way_helper::set(const osmid_t *node_ids, size_t node_count, const middle_query_t *mid)
+size_t way_helper::set(const idlist_t &node_ids, const middle_query_t *mid)
 {
-    //if we don't have enough space already get more
-    if(node_cache.size() < node_count)
-        node_cache.resize(node_count);
-    //get the node data
-    mid->nodes_get_list(&node_cache.front(), node_ids, node_count);
+    node_cache.clear();
+    mid->nodes_get_list(node_cache, node_ids);
+
+    // equivalent to returning node_count for complete ways, different for partial extracts
     return node_cache.size();
 }
 
-relation_helper::relation_helper():members(NULL), member_count(0), way_count(0)
+relation_helper::relation_helper()
 {
 }
+
 relation_helper::~relation_helper()
 {
-    //clean up
-    for(size_t i = 0; i < way_count; ++i)
-    {
-        tags[i].resetList();
-        free(nodes[i]);
-    }
 }
 
-size_t& relation_helper::set(const member* member_list, const int member_list_length, const middle_t* mid)
+size_t relation_helper::set(const memberlist_t *member_list, const middle_t* mid)
 {
-    //clean up
-    for(size_t i = 0; i < way_count; ++i)
-    {
-        tags[i].resetList();
-        free(nodes[i]);
-    }
+    // cleanup
+    input_way_ids.clear();
+    ways.clear();
+    tags.clear();
+    nodes.clear();
+    roles.clear();
 
     //keep a few things
     members = member_list;
-    member_count = member_list_length;
 
     //grab the way members' ids
-    input_way_ids.resize(member_count);
-    size_t used = 0;
-    for(size_t i = 0; i < member_count; ++i)
-        if(members[i].type == OSMTYPE_WAY)
-            input_way_ids[used++] = members[i].id;
-
-    //if we didnt end up using any well bail
-    if(used == 0)
-    {
-        way_count = 0;
-        return way_count;
+    input_way_ids.reserve(member_list->size());
+    for (memberlist_t::const_iterator it = members->begin(); it != members->end(); ++it) {
+        if(it->type == OSMTYPE_WAY)
+            input_way_ids.push_back(it->id);
     }
 
+    //if we didn't end up using any we'll bail
+    if (input_way_ids.empty())
+        return 0;
+
     //get the nodes of the ways
-    tags.resize(used + 1);
-    node_counts.resize(used + 1);
-    nodes.resize(used + 1);
-    ways.resize(used + 1);
-    //this is mildly abusive treating vectors like arrays but the memory is contiguous so...
-    way_count = mid->ways_get_list(&input_way_ids.front(), used, &ways.front(), &tags.front(), &nodes.front(), &node_counts.front());
+    mid->ways_get_list(input_way_ids, ways, tags, nodes);
 
     //grab the roles of each way
-    roles.resize(way_count + 1);
-    roles[way_count] = NULL;
-    for (size_t i = 0; i < way_count; ++i)
-    {
-        size_t j = i;
-        for (; j < member_count; ++j)
-        {
-            if (members[j].id == ways[i])
-            {
+    roles.reserve(ways.size());
+    size_t memberpos = 0;
+    for (idlist_t::const_iterator it = ways.begin(); it != ways.end(); ++it) {
+        while (memberpos < members->size()) {
+            if (members->at(memberpos).id == *it) {
+                roles.push_back(&(members->at(memberpos).role));
+                memberpos++;
                 break;
             }
+            memberpos++;
         }
-        roles[i] = members[j].role;
     }
 
     //mark the ends of each so whoever uses them will know where they end..
-    nodes[way_count] = NULL;
-    node_counts[way_count] = 0;
-    ways[way_count] = 0;
-    superseeded.resize(way_count);
-    return way_count;
+    superseeded.resize(ways.size());
+
+    return ways.size();
 }
diff --git a/geometry-processor.hpp b/geometry-processor.hpp
index 6b57740..98e9077 100644
--- a/geometry-processor.hpp
+++ b/geometry-processor.hpp
@@ -39,12 +39,12 @@ struct geometry_processor {
     // process a way
     // position data and optionally returning WKT-encoded geometry
     // for insertion into the table.
-    virtual geometry_builder::maybe_wkt_t process_way(const osmNode *nodes, const size_t node_count);
+    virtual geometry_builder::maybe_wkt_t process_way(const nodelist_t &nodes);
 
     // process a way, taking a middle query object to get way and
     // node position data. optionally returns a WKT-encoded geometry
     // for insertion into the table.
-    virtual geometry_builder::maybe_wkts_t process_relation(const osmNode * const * nodes, const int* node_counts);
+    virtual geometry_builder::maybe_wkts_t process_relation(const multinodelist_t &nodes);
 
     // returns the SRID of the output geometry.
     int srid() const;
@@ -69,9 +69,9 @@ struct way_helper
 {
     way_helper();
     ~way_helper();
-    size_t set(const osmid_t *node_ids, size_t node_count, const middle_query_t *mid);
+    size_t set(const idlist_t &node_ids, const middle_query_t *mid);
 
-    std::vector<osmNode> node_cache;
+    nodelist_t node_cache;
 };
 
 //various bits for continuous processing of members of relations
@@ -79,20 +79,17 @@ struct relation_helper
 {
     relation_helper();
     ~relation_helper();
-    size_t& set(const member* member_list, const int member_list_length, const middle_t* mid);
-
-    const member* members;
-    size_t member_count;
-    std::vector<keyval> tags;
-    std::vector<int> node_counts;
-    std::vector<osmNode*> nodes;
-    std::vector<osmid_t> ways;
-    size_t way_count;
-    std::vector<const char*> roles;
+    size_t set(const memberlist_t *member_list, const middle_t *mid);
+
+    const memberlist_t *members;
+    multitaglist_t tags;
+    multinodelist_t nodes;
+    idlist_t ways;
+    rolelist_t roles;
     std::vector<int> superseeded;
 
 private:
-    std::vector<osmid_t> input_way_ids;
+    idlist_t input_way_ids;
 };
 
 #endif /* GEOMETRY_PROCESSOR_HPP */
diff --git a/id-tracker.hpp b/id-tracker.hpp
index db11053..023bbb7 100644
--- a/id-tracker.hpp
+++ b/id-tracker.hpp
@@ -2,7 +2,6 @@
 #define ID_TRACKER_HPP
 
 #include "osmtypes.hpp"
-#include <string>
 #include <boost/noncopyable.hpp>
 #include <boost/scoped_ptr.hpp>
 
diff --git a/keyvals.cpp b/keyvals.cpp
deleted file mode 100644
index 8c5b9cd..0000000
--- a/keyvals.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/* Common key-value list processing
- *
- * Used as a small general purpose store for
- * tags, segment lists etc
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <string.h>
-#include "keyvals.hpp"
-
-#include <algorithm>
-
-
-keyval::keyval(const keyval &other)
-{
-    key = other.key;
-    value = other.value;
-    // the list cannot be copied, so initialise as empty
-    next = this;
-    prev = this;
-    has_column = other.has_column;
-}
-
-
-unsigned int keyval::countList() const
-{
-    unsigned int count = 0;
-
-    keyval *p = next;
-    while(p != this) {
-        count++;
-        p = p->next;
-    }
-    return count;
-}
-
-
-const std::string *keyval::getItem(const std::string &name) const
-{
-    keyval *p = next;
-    while(p != this) {
-        if (!p->key.compare(name))
-            return &(p->value);
-        p = p->next;
-    }
-    return NULL;
-}
-
-/* unlike getItem this function gives a pointer to the whole
-   list item which can be used to remove the tag from the linked list
-   with the removeTag function
-*/
-struct keyval *keyval::getTag(const std::string &name)
-{
-    keyval *p = next;
-    while(p != this) {
-        if (!p->key.compare(name))
-            return p;
-        p = p->next;
-    }
-    return NULL;
-}
-
-void keyval::removeTag()
-{
-  prev->next = next;
-  next->prev = prev;
-  delete(this);
-}
-
-
-struct keyval *keyval::popItem()
-{
-    keyval *p = next;
-    if (p == this)
-        return NULL;
-
-    next = p->next;
-    p->next->prev = this;
-
-    p->next = NULL;
-    p->prev = NULL;
-
-    return p;
-}
-
-
-void keyval::pushItem(struct keyval *item)
-{
-    assert(item);
-
-    item->next = this;
-    item->prev = prev;
-    prev->next = item;
-    prev = item;
-}
-
-int keyval::addItem(const std::string &name, const std::string &value, bool noDupe)
-{
-    if (noDupe) {
-        keyval *item = next;
-        while (item != this) {
-            if (!value.compare(item->value) && !name.compare(item->key))
-                return 1;
-            item = item->next;
-        }
-    }
-
-    new keyval(name, value, this);
-
-    return 0;
-}
-
-void keyval::resetList()
-{
-    struct keyval *item;
-
-    while((item = popItem()))
-        delete(item);
-}
-
-void keyval::cloneList(struct keyval *target)
-{
-  for(keyval *ptr = firstItem(); ptr; ptr = nextItem(ptr))
-    target->addItem(ptr->key, ptr->value, false);
-}
-
-void keyval::moveList(keyval *target)
-{
-    target->resetList();
-
-    if (listHasData()) {
-        target->next = next;
-        target->prev = prev;
-        next->prev = target;
-        prev->next = target;
-
-        next = this;
-        prev = this;
-    }
-}
diff --git a/keyvals.hpp b/keyvals.hpp
deleted file mode 100644
index 2e63115..0000000
--- a/keyvals.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Common key-value list processing
- *
- * Used as a small general purpose store for
- * tags, segment lists etc
- *
- */
-
-#ifndef KEYVAL_H
-#define KEYVAL_H
-
-#include <boost/shared_ptr.hpp>
-
-struct keyval {
-    std::string key;
-    std::string value;
-    /* if a hstore column is requested we need a flag to store if a key
-       has its own column because it should not be added to the hstore
-       in this case
-    */
-    int has_column;
-private:
-    keyval *next;
-    keyval *prev;
-
-public:
-    keyval() : has_column(0), next(this), prev(this) {}
-
-    keyval(const std::string &name_, const std::string &value_)
-    : key(name_), value(value_), has_column(0), next(NULL), prev(NULL)
-    {}
-
-    keyval(const keyval &other);
-    ~keyval() {}
-
-    unsigned int countList() const;
-    bool listHasData() const { return next != this; }
-    const std::string *getItem(const std::string &name) const;
-    keyval *getTag(const std::string &name);
-    void removeTag();
-    keyval *firstItem() const { return listHasData() ? next : NULL; }
-    keyval *nextItem(const keyval *item) const {
-        return item->next == this ? NULL : item->next;
-    }
-    keyval *popItem();
-    void pushItem(keyval *item);
-    int addItem(const std::string &name, const std::string &value, bool noDupe);
-    void resetList();
-    void cloneList(keyval *target);
-    void moveList(keyval *target);
-
-private:
-    keyval(const std::string &key_, const std::string &value_, keyval *after)
-    : key(key_), value(value_), has_column(0)
-    {
-        next = after->next;
-        prev = after;
-        after->next->prev = this;
-        after->next = this;
-    }
-
-};
-
-
-#endif
diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4
index 1d38b76..aeab899 100644
--- a/m4/ax_append_flag.m4
+++ b/m4/ax_append_flag.m4
@@ -49,21 +49,23 @@
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 2
+#serial 5
 
 AC_DEFUN([AX_APPEND_FLAG],
-[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
-AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])dnl
-AS_VAR_SET_IF(FLAGS,
-  [case " AS_VAR_GET(FLAGS) " in
-    *" $1 "*)
-      AC_RUN_LOG([: FLAGS already contains $1])
-      ;;
-    *)
-      AC_RUN_LOG([: FLAGS="$FLAGS $1"])
-      AS_VAR_SET(FLAGS, ["AS_VAR_GET(FLAGS) $1"])
-      ;;
-   esac],
-  [AS_VAR_SET(FLAGS,["$1"])])
+[dnl
+AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
+AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
+AS_VAR_SET_IF(FLAGS,[
+  AS_CASE([" AS_VAR_GET(FLAGS) "],
+    [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
+    [
+     AS_VAR_APPEND(FLAGS," $1")
+     AC_RUN_LOG([: FLAGS="$FLAGS"])
+    ])
+  ],
+  [
+  AS_VAR_SET(FLAGS,[$1])
+  AC_RUN_LOG([: FLAGS="$FLAGS"])
+  ])
 AS_VAR_POPDEF([FLAGS])dnl
 ])dnl AX_APPEND_FLAG
diff --git a/m4/ax_boost_base.m4 b/m4/ax_boost_base.m4
index 8e6ee9a..f3279f2 100644
--- a/m4/ax_boost_base.m4
+++ b/m4/ax_boost_base.m4
@@ -33,7 +33,7 @@
 #   and this notice are preserved. This file is offered as-is, without any
 #   warranty.
 
-#serial 23
+#serial 26
 
 AC_DEFUN([AX_BOOST_BASE],
 [
@@ -92,8 +92,11 @@ if test "x$want_boost" = "xyes"; then
     libsubdirs="lib"
     ax_arch=`uname -m`
     case $ax_arch in
-      x86_64|ppc64|s390x|sparc64|aarch64)
-        libsubdirs="lib64 lib lib64"
+      x86_64)
+        libsubdirs="lib64 libx32 lib lib64"
+        ;;
+      ppc64|s390x|sparc64|aarch64|ppc64le)
+        libsubdirs="lib64 lib lib64 ppc64le"
         ;;
     esac
 
@@ -170,6 +173,10 @@ if test "x$want_boost" = "xyes"; then
     dnl if we found no boost with system layout we search for boost libraries
     dnl built and installed without the --layout=system option or for a staged(not installed) version
     if test "x$succeeded" != "xyes"; then
+        CPPFLAGS="$CPPFLAGS_SAVED"
+        LDFLAGS="$LDFLAGS_SAVED"
+        BOOST_CPPFLAGS=
+        BOOST_LDFLAGS=
         _version=0
         if test "$ac_boost_path" != ""; then
             if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
@@ -182,6 +189,12 @@ if test "x$want_boost" = "xyes"; then
                     VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
                     BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
                 done
+                dnl if nothing found search for layout used in Windows distributions
+                if test -z "$BOOST_CPPFLAGS"; then
+                    if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then
+                        BOOST_CPPFLAGS="-I$ac_boost_path"
+                    fi
+                fi
             fi
         else
             if test "$cross_compiling" != yes; then
diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4
index 21524dc..74dc0fd 100644
--- a/m4/ax_compare_version.m4
+++ b/m4/ax_compare_version.m4
@@ -175,4 +175,3 @@ x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
     m4_ifvaln([$5],[else $5])dnl
   fi
 ]) dnl AX_COMPARE_VERSION
-
diff --git a/m4/ax_compile_check_sizeof.m4 b/m4/ax_compile_check_sizeof.m4
index 06e5d1c..00d0bcc 100644
--- a/m4/ax_compile_check_sizeof.m4
+++ b/m4/ax_compile_check_sizeof.m4
@@ -111,4 +111,4 @@ AC_MSG_RESULT($AC_CV_NAME)
 AC_DEFINE_UNQUOTED(AC_TYPE_NAME, $AC_CV_NAME, [The number of bytes in type $1])
 undefine([AC_TYPE_NAME])dnl
 undefine([AC_CV_NAME])dnl
-])
\ No newline at end of file
+])
diff --git a/m4/ax_lib_postgresql.m4 b/m4/ax_lib_postgresql.m4
index aaca176..51ae4d8 100644
--- a/m4/ax_lib_postgresql.m4
+++ b/m4/ax_lib_postgresql.m4
@@ -1,5 +1,5 @@
 # ===========================================================================
-#        http://www.nongnu.org/autoconf-archive/ax_lib_postgresql.html
+#     http://www.gnu.org/software/autoconf-archive/ax_lib_postgresql.html
 # ===========================================================================
 #
 # SYNOPSIS
@@ -27,7 +27,7 @@
 #
 #   This macro calls:
 #
-#     AC_SUBST(POSTGRESQL_CFLAGS)
+#     AC_SUBST(POSTGRESQL_CPPFLAGS)
 #     AC_SUBST(POSTGRESQL_LDFLAGS)
 #     AC_SUBST(POSTGRESQL_LIBS)
 #     AC_SUBST(POSTGRESQL_VERSION)
@@ -39,15 +39,19 @@
 # LICENSE
 #
 #   Copyright (c) 2008 Mateusz Loskot <mateusz at loskot.net>
+#   Copyright (c) 2014 Sree Harsha Totakura <sreeharsha at totakura.in>
 #
 #   Copying and distribution of this file, with or without modification, are
 #   permitted in any medium without royalty provided the copyright notice
-#   and this notice are preserved.
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 12
 
 AC_DEFUN([AX_LIB_POSTGRESQL],
 [
     AC_ARG_WITH([postgresql],
-        AC_HELP_STRING([--with-postgresql=@<:@ARG@:>@],
+        AS_HELP_STRING([--with-postgresql=@<:@ARG@:>@],
             [use PostgreSQL library @<:@default=yes@:>@, optionally specify path to pg_config]
         ),
         [
@@ -63,7 +67,7 @@ AC_DEFUN([AX_LIB_POSTGRESQL],
         [want_postgresql="yes"]
     )
 
-    POSTGRESQL_CFLAGS=""
+    POSTGRESQL_CPPFLAGS=""
     POSTGRESQL_LDFLAGS=""
     POSTGRESQL_LIBS=""
     POSTGRESQL_VERSION=""
@@ -87,22 +91,16 @@ AC_DEFUN([AX_LIB_POSTGRESQL],
         if test "$PG_CONFIG" != "no"; then
             AC_MSG_CHECKING([for PostgreSQL libraries])
 
-            POSTGRESQL_CFLAGS="-I`$PG_CONFIG --includedir`"
-            POSTGRESQL_SERVER_CFLAGS="-I`$PG_CONFIG --includedir-server`"
+            POSTGRESQL_CPPFLAGS="-I`$PG_CONFIG --includedir`"
             POSTGRESQL_LDFLAGS="-L`$PG_CONFIG --libdir`"
-	    POSTGRESQL_LIBS="-lpq"
-
+            POSTGRESQL_LIBS="-lpq"
             POSTGRESQL_VERSION=`$PG_CONFIG --version | sed -e 's#PostgreSQL ##'`
 
             AC_DEFINE([HAVE_POSTGRESQL], [1],
                 [Define to 1 if PostgreSQL libraries are available])
 
-            POSTGRESQL_PGXS=`$PG_CONFIG --pgxs`
-	    if test -f "$POSTGRESQL_PGXS"
-	    then
-	      found_postgresql="yes"
-              AC_MSG_RESULT([yes])
-	    fi
+            found_postgresql="yes"
+            AC_MSG_RESULT([yes])
         else
             found_postgresql="no"
             AC_MSG_RESULT([no])
@@ -151,14 +149,16 @@ AC_DEFUN([AX_LIB_POSTGRESQL],
             AC_MSG_RESULT([yes])
         else
             AC_MSG_RESULT([no])
+            AC_DEFINE([HAVE_POSTGRESQL], [0],
+                [A required version of PostgreSQL is not found])
+            POSTGRESQL_CPPFLAGS=""
+            POSTGRESQL_LDFLAGS=""
+            POSTGRESQL_LIBS=""
         fi
     fi
 
-    AC_SUBST([POSTGRESQL_PGXS])
     AC_SUBST([POSTGRESQL_VERSION])
-    AC_SUBST([POSTGRESQL_CFLAGS])
-    AC_SUBST([POSTGRESQL_SERVER_CFLAGS])
+    AC_SUBST([POSTGRESQL_CPPFLAGS])
     AC_SUBST([POSTGRESQL_LDFLAGS])
     AC_SUBST([POSTGRESQL_LIBS])
 ])
-
diff --git a/m4/ax_lua.m4 b/m4/ax_lua.m4
index 1553491..8da0a07 100644
--- a/m4/ax_lua.m4
+++ b/m4/ax_lua.m4
@@ -51,7 +51,7 @@
 #
 #   If MINIMUM-VERSION is supplied, then only Lua interpreters with a
 #   version number greater or equal to MINIMUM-VERSION will be accepted. If
-#   TOO-BIG- VERSION is also supplied, then only Lua interpreters with a
+#   TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
 #   version number greater or equal to MINIMUM-VERSION and less than
 #   TOO-BIG-VERSION will be accepted.
 #
@@ -66,12 +66,13 @@
 #     luaexecdir         Directory to install Lua modules.
 #     pkgluaexecdir      $luaexecdir/$PACKAGE
 #
-#   These paths a found based on $prefix, $exec_prefix, Lua's package.path,
-#   and package.cpath. The first path of package.path beginning with $prefix
-#   is selected as luadir. The first path of package.cpath beginning with
-#   $exec_prefix is used as luaexecdir. This should work on all reasonable
-#   Lua installations. If a path cannot be determined, a default path is
-#   used. Of course, the user can override these later when invoking make.
+#   These paths are found based on $prefix, $exec_prefix, Lua's
+#   package.path, and package.cpath. The first path of package.path
+#   beginning with $prefix is selected as luadir. The first path of
+#   package.cpath beginning with $exec_prefix is used as luaexecdir. This
+#   should work on all reasonable Lua installations. If a path cannot be
+#   determined, a default path is used. Of course, the user can override
+#   these later when invoking make.
 #
 #     luadir             Default: $prefix/share/lua/$LUA_VERSION
 #     luaexecdir         Default: $exec_prefix/lib/lua/$LUA_VERSION
@@ -151,7 +152,7 @@
 #
 # LICENSE
 #
-#   Copyright (c) 2014 Reuben Thomas <rrt at sc3d.org>
+#   Copyright (c) 2015 Reuben Thomas <rrt at sc3d.org>
 #   Copyright (c) 2014 Tim Perkins <tprk77 at gmail.com>
 #
 #   This program is free software: you can redistribute it and/or modify it
@@ -180,7 +181,7 @@
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 36
+#serial 38
 
 dnl =========================================================================
 dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
@@ -304,7 +305,7 @@ AC_DEFUN([AX_PROG_LUA],
         ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
 
         dnl Try to find a path with the prefix.
-        _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [package.path])
+        _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script])
         AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
         [ dnl Fix the prefix.
           _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'`
@@ -331,7 +332,7 @@ AC_DEFUN([AX_PROG_LUA],
 
         dnl Try to find a path with the prefix.
         _AX_LUA_FND_PRFX_PTH([$LUA],
-          [$ax_lua_exec_prefix], [package.cpath])
+          [$ax_lua_exec_prefix], [module])
         AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
         [ dnl Fix the prefix.
           _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'`
@@ -415,7 +416,7 @@ AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
 [
   dnl Get the script or module directory by querying the Lua interpreter,
   dnl filtering on the given prefix, and selecting the shallowest path. If no
-  dnl path  is found matching the prefix, the result will be an empty string.
+  dnl path is found matching the prefix, the result will be an empty string.
   dnl The third argument determines the type of search, it can be 'script' or
   dnl 'module'. Supplying 'script' will perform the search with package.path
   dnl and LUA_PATH, and supplying 'module' will search with package.cpath and
@@ -431,7 +432,7 @@ AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
       paths = (package and package.cpath) or LUA_CPATH
     end
     -- search for the prefix
-    local prefix = "$2"
+    local prefix = "'$2'"
     local minpath = ""
     local mindepth = 1e9
     string.gsub(paths, "(@<:@^;@:>@+)",
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
index d374436..d383ad5 100644
--- a/m4/ax_pthread.m4
+++ b/m4/ax_pthread.m4
@@ -33,6 +33,10 @@
 #   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
 #   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
 #
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
 #   ACTION-IF-FOUND is a list of shell commands to run if a threads library
 #   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
 #   is not found. If ACTION-IF-FOUND is not specified, the default action
@@ -45,9 +49,12 @@
 #   Alejandro Forero Cuervo to the autoconf macro repository. We are also
 #   grateful for the helpful feedback of numerous users.
 #
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
 # LICENSE
 #
 #   Copyright (c) 2008 Steven G. Johnson <stevenj at alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk at iSKUNK.ORG>
 #
 #   This program is free software: you can redistribute it and/or modify it
 #   under the terms of the GNU General Public License as published by the
@@ -75,13 +82,12 @@
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 11
+#serial 21
 
 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
 AC_DEFUN([AX_PTHREAD], [
 AC_REQUIRE([AC_CANONICAL_HOST])
-AC_LANG_SAVE
-AC_LANG_C
+AC_LANG_PUSH([C])
 ax_pthread_ok=no
 
 # We used to check for pthread.h first, but this fails if pthread.h
@@ -97,8 +103,8 @@ if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
         save_LIBS="$LIBS"
         LIBS="$PTHREAD_LIBS $LIBS"
         AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
-        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
-        AC_MSG_RESULT($ax_pthread_ok)
+        AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes])
+        AC_MSG_RESULT([$ax_pthread_ok])
         if test x"$ax_pthread_ok" = xno; then
                 PTHREAD_LIBS=""
                 PTHREAD_CFLAGS=""
@@ -139,8 +145,8 @@ ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mt
 # --thread-safe: KAI C++
 # pthread-config: use pthread-config program (for GNU Pth library)
 
-case "${host_cpu}-${host_os}" in
-        *solaris*)
+case ${host_os} in
+        solaris*)
 
         # On Solaris (at least, for some versions), libc contains stubbed
         # (non-functional) versions of the pthreads routines, so link-based
@@ -153,11 +159,25 @@ case "${host_cpu}-${host_os}" in
         ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
         ;;
 
-	*-darwin*)
-	ax_pthread_flags="-pthread $ax_pthread_flags"
-	;;
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
 esac
 
+# Clang doesn't consider unrecognized options an error unless we specify
+# -Werror. We throw in some extra Clang-specific options to ensure that
+# this doesn't happen for GCC, which also accepts -Werror.
+
+AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
+save_CFLAGS="$CFLAGS"
+ax_pthread_extra_flags="-Werror"
+CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
+                  [AC_MSG_RESULT([yes])],
+                  [ax_pthread_extra_flags=
+                   AC_MSG_RESULT([no])])
+CFLAGS="$save_CFLAGS"
+
 if test x"$ax_pthread_ok" = xno; then
 for flag in $ax_pthread_flags; do
 
@@ -171,12 +191,12 @@ for flag in $ax_pthread_flags; do
                 PTHREAD_CFLAGS="$flag"
                 ;;
 
-		pthread-config)
-		AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
-		if test x"$ax_pthread_config" = xno; then continue; fi
-		PTHREAD_CFLAGS="`pthread-config --cflags`"
-		PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
-		;;
+                pthread-config)
+                AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
 
                 *)
                 AC_MSG_CHECKING([for the pthreads library -l$flag])
@@ -187,7 +207,7 @@ for flag in $ax_pthread_flags; do
         save_LIBS="$LIBS"
         save_CFLAGS="$CFLAGS"
         LIBS="$PTHREAD_LIBS $LIBS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
 
         # Check for various functions.  We must include pthread.h,
         # since some functions may be macros.  (On the Sequent, we
@@ -198,21 +218,22 @@ for flag in $ax_pthread_flags; do
         # pthread_cleanup_push because it is one of the few pthread
         # functions on Solaris that doesn't have a non-functional libc stub.
         # We try pthread_create on general principles.
-        AC_TRY_LINK([#include <pthread.h>
-		     static void routine(void* a) {a=0;}
-		     static void* start_routine(void* a) {return a;}],
-                    [pthread_t th; pthread_attr_t attr;
-                     pthread_create(&th,0,start_routine,0);
-                     pthread_join(th, 0);
-                     pthread_attr_init(&attr);
-                     pthread_cleanup_push(routine, 0);
-                     pthread_cleanup_pop(0); ],
-                    [ax_pthread_ok=yes])
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
 
         LIBS="$save_LIBS"
         CFLAGS="$save_CFLAGS"
 
-        AC_MSG_RESULT($ax_pthread_ok)
+        AC_MSG_RESULT([$ax_pthread_ok])
         if test "x$ax_pthread_ok" = xyes; then
                 break;
         fi
@@ -230,55 +251,82 @@ if test "x$ax_pthread_ok" = xyes; then
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
 
         # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
-	AC_MSG_CHECKING([for joinable pthread attribute])
-	attr_name=unknown
-	for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
-	    AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
-                        [attr_name=$attr; break])
-	done
-        AC_MSG_RESULT($attr_name)
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT([$attr_name])
         if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
-            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+            AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name],
                                [Define to necessary symbol if this constant
                                 uses a non-standard name on your system.])
         fi
 
         AC_MSG_CHECKING([if more special flags are required for pthreads])
         flag=no
-        case "${host_cpu}-${host_os}" in
-            *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
-            *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                # TODO: What about Clang on Solaris?
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
         esac
-        AC_MSG_RESULT(${flag})
+        AC_MSG_RESULT([$flag])
         if test "x$flag" != xno; then
             PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
         fi
 
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            [ax_cv_PTHREAD_PRIO_INHERIT], [
+                AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+                                                [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
+
         LIBS="$save_LIBS"
         CFLAGS="$save_CFLAGS"
 
-        # More AIX lossage: must compile with xlc_r or cc_r
-	if test x"$GCC" != xyes; then
-          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
-        else
-          PTHREAD_CC=$CC
-	fi
-else
-        PTHREAD_CC="$CC"
+        # More AIX lossage: compile with *_r variant
+        if test "x$GCC" != xyes; then
+            case $host_os in
+                aix*)
+                AS_CASE(["x/$CC"],
+                  [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+                  [#handle absolute path differently from PATH based program lookup
+                   AS_CASE(["x$CC"],
+                     [x/*],
+                     [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+                     [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+                ;;
+            esac
+        fi
 fi
 
-AC_SUBST(PTHREAD_LIBS)
-AC_SUBST(PTHREAD_CFLAGS)
-AC_SUBST(PTHREAD_CC)
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST([PTHREAD_LIBS])
+AC_SUBST([PTHREAD_CFLAGS])
+AC_SUBST([PTHREAD_CC])
 
 # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
 if test x"$ax_pthread_ok" = xyes; then
-        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
         :
 else
         ax_pthread_ok=no
         $2
 fi
-AC_LANG_RESTORE
+AC_LANG_POP
 ])dnl AX_PTHREAD
-
diff --git a/middle-pgsql.cpp b/middle-pgsql.cpp
index 62eeda7..03b17c5 100644
--- a/middle-pgsql.cpp
+++ b/middle-pgsql.cpp
@@ -8,15 +8,6 @@
 
 #include "config.h"
 
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <math.h>
-#include <time.h>
-#include <errno.h>
-
 #ifdef HAVE_PTHREAD
 #include <pthread.h>
 #endif
@@ -54,16 +45,16 @@ using namespace std;
 #include "util.hpp"
 
 #include <stdexcept>
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
 #include <boost/format.hpp>
+#include <boost/foreach.hpp>
 #include <boost/unordered_map.hpp>
 
-struct progress_info {
-  time_t start;
-  time_t end;
-  int count;
-  int finished;
-};
-
 enum table_id {
     t_node, t_way, t_rel
 } ;
@@ -101,37 +92,34 @@ middle_pgsql_t::table_desc::table_desc(const char *name_,
 #define HELPER_STATE_FAILED 3
 
 namespace {
-char *pgsql_store_nodes(const osmid_t *nds, const int& nd_count)
-{
+char *pgsql_store_nodes(const idlist_t &nds) {
   static char *buffer;
-  static int buflen;
-
-  char *ptr;
-  int i, first;
+  static size_t buflen;
 
-  if( buflen <= nd_count * 10 )
+  if( buflen <= nds.size() * 10 )
   {
-    buflen = ((nd_count * 10) | 4095) + 1;  // Round up to next page */
+    buflen = ((nds.size() * 10) | 4095) + 1;  // Round up to next page */
     buffer = (char *)realloc( buffer, buflen );
   }
 _restart:
 
-  ptr = buffer;
-  first = 1;
+  char *ptr = buffer;
+  bool first = true;
   *ptr++ = '{';
-  for( i=0; i<nd_count; i++ )
+  for (idlist_t::const_iterator it = nds.begin(); it != nds.end(); ++it)
   {
-    if( !first ) *ptr++ = ',';
-    ptr += sprintf(ptr, "%" PRIdOSMID, nds[i] );
+    if (!first)
+      *ptr++ = ',';
+    ptr += sprintf(ptr, "%" PRIdOSMID, *it);
 
-    if( (ptr-buffer) > (buflen-20) ) // Almost overflowed? */
+    if( (size_t) (ptr-buffer) > (buflen-20) ) // Almost overflowed? */
     {
       buflen <<= 1;
       buffer = (char *)realloc( buffer, buflen );
 
       goto _restart;
     }
-    first = 0;
+    first = false;
   }
 
   *ptr++ = '}';
@@ -141,11 +129,11 @@ _restart:
 }
 
 // Special escape routine for escaping strings in array constants: double quote, backslash,newline, tab*/
-inline char *escape_tag( char *ptr, const char *in, const int& escape )
+inline char *escape_tag( char *ptr, const std::string &in, bool escape )
 {
-  while( *in )
+  BOOST_FOREACH(const char c, in)
   {
-    switch(*in)
+    switch(c)
     {
       case '"':
         if( escape ) *ptr++ = '\\';
@@ -174,24 +162,20 @@ inline char *escape_tag( char *ptr, const char *in, const int& escape )
         *ptr++ = 't';
         break;
       default:
-        *ptr++ = *in;
+        *ptr++ = c;
         break;
     }
-    in++;
   }
   return ptr;
 }
 
 // escape means we return '\N' for copy mode, otherwise we return just NULL */
-const char *pgsql_store_tags(const struct keyval *tags, const int& escape)
+const char *pgsql_store_tags(const taglist_t &tags, bool escape)
 {
   static char *buffer;
   static int buflen;
 
-  char *ptr;
-  int first;
-
-  int countlist = tags->countList();
+  int countlist = tags.size();
   if( countlist == 0 )
   {
     if( escape )
@@ -207,13 +191,13 @@ const char *pgsql_store_tags(const struct keyval *tags, const int& escape)
   }
 _restart:
 
-  ptr = buffer;
-  first = 1;
+  char *ptr = buffer;
+  bool first = true;
   *ptr++ = '{';
 
-  for(keyval* i = tags->firstItem(); i; i = tags->nextItem(i))
+  for (taglist_t::const_iterator it = tags.begin(); it != tags.end(); ++it)
   {
-    int maxlen = (i->key.length() + i->value.length()) * 4;
+    int maxlen = (it->key.length() + it->value.length()) * 4;
     if( (ptr+maxlen-buffer) > (buflen-20) ) // Almost overflowed? */
     {
       buflen <<= 1;
@@ -223,14 +207,14 @@ _restart:
     }
     if( !first ) *ptr++ = ',';
     *ptr++ = '"';
-    ptr = escape_tag( ptr, i->key.c_str(), escape );
+    ptr = escape_tag(ptr, it->key, escape);
     *ptr++ = '"';
     *ptr++ = ',';
     *ptr++ = '"';
-    ptr = escape_tag( ptr, i->value.c_str(), escape );
+    ptr = escape_tag(ptr, it->value, escape);
     *ptr++ = '"';
 
-    first=0;
+    first = false;
   }
 
   *ptr++ = '}';
@@ -266,7 +250,7 @@ inline const char *decode_upto( const char *src, char *dst )
   return src;
 }
 
-void pgsql_parse_tags( const char *string, struct keyval *tags )
+void pgsql_parse_tags(const char *string, taglist_t &tags)
 {
   char key[1024];
   char val[1024];
@@ -283,51 +267,40 @@ void pgsql_parse_tags( const char *string, struct keyval *tags )
     string++;
     string = decode_upto( string, val );
     // String points to the comma or closing '}' */
-    tags->addItem( key, val, false );
+    tags.push_back(tag(key, val));
     if( *string == ',' )
       string++;
   }
 }
 
 // Parses an array of integers */
-void pgsql_parse_nodes(const char *src, osmid_t *nds, const int& nd_count )
+void pgsql_parse_nodes(const char *string, idlist_t &nds)
 {
-  int count = 0;
-  const char *string = src;
-
   if( *string++ != '{' )
     return;
+
   while( *string != '}' )
   {
     char *ptr;
-    nds[count] = strtoosmid( string, &ptr, 10 );
+    nds.push_back(strtoosmid( string, &ptr, 10 ));
     string = ptr;
     if( *string == ',' )
       string++;
-    count++;
-  }
-  if( count != nd_count )
-  {
-    fprintf( stderr, "parse_nodes problem: '%s' expected %d got %d\n", src, nd_count, count );
-    util::exit_nicely();
   }
 }
 
-int pgsql_endCopy( struct middle_pgsql_t::table_desc *table)
+int pgsql_endCopy(middle_pgsql_t::table_desc *table)
 {
-    PGresult *res;
-    PGconn *sql_conn;
-    int stop;
     // Terminate any pending COPY */
     if (table->copyMode) {
-        sql_conn = table->sql_conn;
-        stop = PQputCopyEnd(sql_conn, NULL);
+        PGconn *sql_conn = table->sql_conn;
+        int stop = PQputCopyEnd(sql_conn, NULL);
         if (stop != 1) {
             fprintf(stderr, "COPY_END for %s failed: %s\n", table->copy, PQerrorMessage(sql_conn));
             util::exit_nicely();
         }
 
-        res = PQgetResult(sql_conn);
+        PGresult *res = PQgetResult(sql_conn);
         if (PQresultStatus(res) != PGRES_COMMAND_OK) {
             fprintf(stderr, "COPY_END for %s failed: %s\n", table->copy, PQerrorMessage(sql_conn));
             PQclear(res);
@@ -340,17 +313,14 @@ int pgsql_endCopy( struct middle_pgsql_t::table_desc *table)
 }
 } // anonymous namespace
 
-int middle_pgsql_t::local_nodes_set(const osmid_t& id, const double& lat, const double& lon, const struct keyval *tags)
+int middle_pgsql_t::local_nodes_set(const osmid_t& id, const double& lat,
+                                    const double& lon, const taglist_t &tags)
 {
-    // Four params: id, lat, lon, tags */
-    const char *paramValues[4];
-    char *buffer;
-
     if( node_table->copyMode )
     {
       const char *tag_buf = pgsql_store_tags(tags,1);
       int length = strlen(tag_buf) + 64;
-      buffer = (char *)alloca( length );
+      char *buffer = (char *)alloca( length );
 #ifdef FIXED_POINT
       if( snprintf( buffer, length, "%" PRIdOSMID "\t%d\t%d\t%s\n", id, util::double_to_fix(lat, out_options->scale), util::double_to_fix(lon, out_options->scale), tag_buf ) > (length-10) )
       { fprintf( stderr, "buffer overflow node id %" PRIdOSMID "\n", id); return 1; }
@@ -361,7 +331,10 @@ int middle_pgsql_t::local_nodes_set(const osmid_t& id, const double& lat, const
       pgsql_CopyData(__FUNCTION__, node_table->sql_conn, buffer);
       return 0;
     }
-    buffer = (char *)alloca(64);
+
+    // Four params: id, lat, lon, tags */
+    const char *paramValues[4];
+    char *buffer = (char *)alloca(64);
     char *ptr = buffer;
     paramValues[0] = ptr;
     ptr += sprintf( ptr, "%" PRIdOSMID, id ) + 1;
@@ -381,53 +354,54 @@ int middle_pgsql_t::local_nodes_set(const osmid_t& id, const double& lat, const
 }
 
 // This should be made more efficient by using an IN(ARRAY[]) construct */
-int middle_pgsql_t::local_nodes_get_list(struct osmNode *nodes, const osmid_t *ndids, const int& nd_count) const
+int middle_pgsql_t::local_nodes_get_list(nodelist_t &out, const idlist_t nds) const
 {
-    char tmp[16];
-    char *tmp2;
-    int count,  countDB, countPG, i,j;
-    char const *paramValues[1];
+    assert(out.empty());
 
-    PGresult *res;
-    PGconn *sql_conn = node_table->sql_conn;
-
-    count = 0; countDB = 0;
+    char tmp[16];
 
-    tmp2 = (char *)malloc(sizeof(char)*nd_count*16);
+    char *tmp2 = (char *)malloc(sizeof(char) * nds.size() * 16);
     if (tmp2 == NULL) return 0; //failed to allocate memory, return */
 
+
     // create a list of ids in tmp2 to query the database  */
     sprintf(tmp2, "{");
-    for( i=0; i<nd_count; i++ ) {
+    int countDB = 0;
+    for(idlist_t::const_iterator it = nds.begin(); it != nds.end(); ++it) {
         // Check cache first */
-        if( cache->get( &nodes[i], ndids[i]) == 0 ) {
-            count++;
+        osmNode loc;
+        if (cache->get(&loc, *it) == 0) {
+            out.push_back(loc);
             continue;
         }
+
         countDB++;
         // Mark nodes as needing to be fetched from the DB */
-        nodes[i].lat = NAN;
-        nodes[i].lon = NAN;
-        snprintf(tmp, sizeof(tmp), "%" PRIdOSMID ",", ndids[i]);
-        strncat(tmp2, tmp, sizeof(char)*(nd_count*16 - 2));
+        out.push_back(osmNode());
+
+        snprintf(tmp, sizeof(tmp), "%" PRIdOSMID ",", *it);
+        strncat(tmp2, tmp, sizeof(char)*(nds.size()*16 - 2));
     }
     tmp2[strlen(tmp2) - 1] = '}'; // replace last , with } to complete list of ids*/
 
     if (countDB == 0) {
         free(tmp2);
-        return count; // All ids where in cache, so nothing more to do */
+        return nds.size(); // All ids where in cache, so nothing more to do */
     }
 
     pgsql_endCopy(node_table);
 
+    PGconn *sql_conn = node_table->sql_conn;
+
+    char const *paramValues[1];
     paramValues[0] = tmp2;
-    res = pgsql_execPrepared(sql_conn, "get_node_list", 1, paramValues, PGRES_TUPLES_OK);
-    countPG = PQntuples(res);
+    PGresult *res = pgsql_execPrepared(sql_conn, "get_node_list", 1, paramValues, PGRES_TUPLES_OK);
+    int countPG = PQntuples(res);
 
     //store the pg results in a hashmap and telling it how many we expect
     boost::unordered_map<osmid_t, osmNode> pg_nodes(countPG);
 
-    for (i = 0; i < countPG; i++) {
+    for (int i = 0; i < countPG; i++) {
         osmid_t id = strtoosmid(PQgetvalue(res, i, 0), NULL, 10);
         osmNode node;
 #ifdef FIXED_POINT
@@ -440,43 +414,44 @@ int middle_pgsql_t::local_nodes_get_list(struct osmNode *nodes, const osmid_t *n
         pg_nodes.emplace(id, node);
     }
 
-    //copy the nodes back out of the hashmap to the output
-    for(i = 0; i < nd_count; ++i){
-        //if we can find a matching id
-        boost::unordered_map<osmid_t, osmNode>::iterator found = pg_nodes.find(ndids[i]);
-        if(found != pg_nodes.end()) {
-            nodes[i] = boost::move(found->second); //this trashes whats in the hashmap but who cares
-            count++;
-        }
-    }
+    PQclear(res);
+    free(tmp2);
 
     // If some of the nodes in the way don't exist, the returning list has holes.
-    //   As the rest of the code expects a continuous list, it needs to be re-compacted */
-    if (count != nd_count) {
-        j = 0;
-        for (i = 0; i < nd_count; i++) {
-            if ( !std::isnan(nodes[i].lat)) {
-                nodes[j] = nodes[i];
-                j++;
+    // Merge the two lists removing any holes.
+    unsigned wrtidx = 0;
+    for (unsigned i = 0; i < nds.size(); ++i) {
+        if (std::isnan(out[i].lat)) {
+            boost::unordered_map<osmid_t, osmNode>::iterator found = pg_nodes.find(nds[i]);
+            if(found != pg_nodes.end()) {
+                out[wrtidx] = found->second;
+                ++wrtidx;
             }
-         }
+        } else {
+            if (wrtidx < i)
+                out[wrtidx] = out[i];
+            ++wrtidx;
+        }
     }
+    out.resize(wrtidx);
 
-    PQclear(res);
-    free(tmp2);
-    return count;
+    return wrtidx;
 }
 
 
-int middle_pgsql_t::nodes_set(osmid_t id, double lat, double lon, struct keyval *tags) {
+int middle_pgsql_t::nodes_set(osmid_t id, double lat, double lon, const taglist_t &tags) {
     cache->set( id, lat, lon, tags );
 
-    return (out_options->flat_node_cache_enabled) ? persistent_cache->set(id, lat, lon) : local_nodes_set(id, lat, lon, tags);
+    return (out_options->flat_node_cache_enabled)
+             ? persistent_cache->set(id, lat, lon)
+             : local_nodes_set(id, lat, lon, tags);
 }
 
-int middle_pgsql_t::nodes_get_list(struct osmNode *nodes, const osmid_t *ndids, int nd_count) const
+int middle_pgsql_t::nodes_get_list(nodelist_t &out, const idlist_t nds) const
 {
-    return (out_options->flat_node_cache_enabled) ? persistent_cache->get_list(nodes, ndids, nd_count) : local_nodes_get_list(nodes, ndids, nd_count);
+    return (out_options->flat_node_cache_enabled)
+             ? persistent_cache->get_list(out, nds)
+             : local_nodes_get_list(out, nds);
 }
 
 int middle_pgsql_t::local_nodes_delete(osmid_t osm_id)
@@ -499,6 +474,9 @@ int middle_pgsql_t::nodes_delete(osmid_t osm_id)
 
 int middle_pgsql_t::node_changed(osmid_t osm_id)
 {
+    if (!mark_pending)
+        return 0;
+
     char const *paramValues[1];
     char buffer[64];
     // Make sure we're out of copy mode */
@@ -532,7 +510,7 @@ int middle_pgsql_t::node_changed(osmid_t osm_id)
     return 0;
 }
 
-int middle_pgsql_t::ways_set(osmid_t way_id, osmid_t *nds, int nd_count, struct keyval *tags)
+int middle_pgsql_t::ways_set(osmid_t way_id, const idlist_t &nds, const taglist_t &tags)
 {
     // Three params: id, nodes, tags */
     const char *paramValues[4];
@@ -541,7 +519,7 @@ int middle_pgsql_t::ways_set(osmid_t way_id, osmid_t *nds, int nd_count, struct
     if( way_table->copyMode )
     {
       const char *tag_buf = pgsql_store_tags(tags,1);
-      char *node_buf = pgsql_store_nodes(nds, nd_count);
+      char *node_buf = pgsql_store_nodes(nds);
       int length = strlen(tag_buf) + strlen(node_buf) + 64;
       buffer = (char *)alloca(length);
       if( snprintf( buffer, length, "%" PRIdOSMID "\t%s\t%s\n",
@@ -554,29 +532,25 @@ int middle_pgsql_t::ways_set(osmid_t way_id, osmid_t *nds, int nd_count, struct
     char *ptr = buffer;
     paramValues[0] = ptr;
     sprintf(ptr, "%" PRIdOSMID, way_id);
-    paramValues[1] = pgsql_store_nodes(nds, nd_count);
+    paramValues[1] = pgsql_store_nodes(nds);
     paramValues[2] = pgsql_store_tags(tags,0);
     pgsql_execPrepared(way_table->sql_conn, "insert_way", 3, (const char * const *)paramValues, PGRES_COMMAND_OK);
     return 0;
 }
 
-// Caller is responsible for freeing nodesptr & keyval::resetList(tags) */
-int middle_pgsql_t::ways_get(osmid_t id, struct keyval *tags, struct osmNode **nodes_ptr, int *count_ptr) const
+int middle_pgsql_t::ways_get(osmid_t id, taglist_t &tags, nodelist_t &nodes) const
 {
-    PGresult   *res;
-    char tmp[16];
     char const *paramValues[1];
     PGconn *sql_conn = way_table->sql_conn;
-    int num_nodes;
-    osmid_t *list;
 
     // Make sure we're out of copy mode */
     pgsql_endCopy( way_table );
 
+    char tmp[16];
     snprintf(tmp, sizeof(tmp), "%" PRIdOSMID, id);
     paramValues[0] = tmp;
 
-    res = pgsql_execPrepared(sql_conn, "get_way", 1, paramValues, PGRES_TUPLES_OK);
+    PGresult *res = pgsql_execPrepared(sql_conn, "get_way", 1, paramValues, PGRES_TUPLES_OK);
 
     if (PQntuples(res) != 1) {
         PQclear(res);
@@ -585,86 +559,87 @@ int middle_pgsql_t::ways_get(osmid_t id, struct keyval *tags, struct osmNode **n
 
     pgsql_parse_tags( PQgetvalue(res, 0, 1), tags );
 
-    num_nodes = strtol(PQgetvalue(res, 0, 2), NULL, 10);
-    list = (osmid_t *)alloca(sizeof(osmid_t)*num_nodes );
-    *nodes_ptr = (struct osmNode *)malloc(sizeof(struct osmNode) * num_nodes);
-    pgsql_parse_nodes( PQgetvalue(res, 0, 0), list, num_nodes);
-
-    *count_ptr = nodes_get_list(*nodes_ptr, list, num_nodes);
+    size_t num_nodes = strtoul(PQgetvalue(res, 0, 2), NULL, 10);
+    idlist_t list;
+    pgsql_parse_nodes( PQgetvalue(res, 0, 0), list);
+    if (num_nodes != list.size()) {
+        fprintf(stderr, "parse_nodes problem for way %s: expected nodes %zu got %zu\n",
+                tmp, num_nodes, list.size());
+        util::exit_nicely();
+    }
     PQclear(res);
+
+    nodes_get_list(nodes, list);
     return 0;
 }
 
-int middle_pgsql_t::ways_get_list(const osmid_t *ids, int way_count, osmid_t *way_ids, struct keyval *tags, struct osmNode **nodes_ptr, int *count_ptr) const {
+int middle_pgsql_t::ways_get_list(const idlist_t &ids, idlist_t &way_ids,
+                                  multitaglist_t &tags, multinodelist_t &nodes) const {
+    if (ids.empty())
+        return 0;
 
     char tmp[16];
     char *tmp2;
-    int count, countPG, i, j;
-    osmid_t *wayidspg;
     char const *paramValues[1];
-    int num_nodes;
-    osmid_t *list;
 
-    PGresult *res;
-    PGconn *sql_conn = way_table->sql_conn;
-
-    if (way_count == 0) return 0;
-
-    tmp2 = (char *)malloc(sizeof(char)*way_count*16);
+    tmp2 = (char *)malloc(sizeof(char)*ids.size()*16);
     if (tmp2 == NULL) return 0; //failed to allocate memory, return */
 
     // create a list of ids in tmp2 to query the database  */
     sprintf(tmp2, "{");
-    for( i=0; i<way_count; i++ ) {
-        snprintf(tmp, sizeof(tmp), "%" PRIdOSMID ",", ids[i]);
-        strncat(tmp2,tmp, sizeof(char)*(way_count*16 - 2));
+    for(idlist_t::const_iterator it = ids.begin(); it != ids.end(); ++it) {
+        snprintf(tmp, sizeof(tmp), "%" PRIdOSMID ",", *it);
+        strncat(tmp2,tmp, sizeof(char)*(ids.size()*16 - 2));
     }
     tmp2[strlen(tmp2) - 1] = '}'; // replace last , with } to complete list of ids*/
 
     pgsql_endCopy(way_table);
 
-    paramValues[0] = tmp2;
-    res = pgsql_execPrepared(sql_conn, "get_way_list", 1, paramValues, PGRES_TUPLES_OK);
-    countPG = PQntuples(res);
+    PGconn *sql_conn = way_table->sql_conn;
 
-    wayidspg = (osmid_t *)malloc(sizeof(osmid_t)*countPG);
+    paramValues[0] = tmp2;
+    PGresult *res = pgsql_execPrepared(sql_conn, "get_way_list", 1, paramValues, PGRES_TUPLES_OK);
+    int countPG = PQntuples(res);
 
-    if (wayidspg == NULL) return 0; //failed to allocate memory, return */
+    idlist_t wayidspg;
 
-    for (i = 0; i < countPG; i++) {
-        wayidspg[i] = strtoosmid(PQgetvalue(res, i, 0), NULL, 10);
+    for (int i = 0; i < countPG; i++) {
+        wayidspg.push_back(strtoosmid(PQgetvalue(res, i, 0), NULL, 10));
     }
 
 
     // Match the list of ways coming from postgres in a different order
     //   back to the list of ways given by the caller */
-    count = 0;
-    for (i = 0; i < way_count; i++) {
-        for (j = 0; j < countPG; j++) {
-            if (ids[i] == wayidspg[j]) {
-                way_ids[count] = ids[i];
-                pgsql_parse_tags( PQgetvalue(res, j, 2), &(tags[count]) );
-
-                num_nodes = strtol(PQgetvalue(res, j, 3), NULL, 10);
-                list = (osmid_t *)alloca(sizeof(osmid_t)*num_nodes );
-                nodes_ptr[count] = (struct osmNode *)malloc(sizeof(struct osmNode) * num_nodes);
-                pgsql_parse_nodes( PQgetvalue(res, j, 1), list, num_nodes);
+    for(idlist_t::const_iterator it = ids.begin(); it != ids.end(); ++it) {
+        for (int j = 0; j < countPG; j++) {
+            if (*it == wayidspg[j]) {
+                way_ids.push_back(*it);
+                tags.push_back(taglist_t());
+                pgsql_parse_tags(PQgetvalue(res, j, 2), tags.back());
+
+                size_t num_nodes = strtoul(PQgetvalue(res, j, 3), NULL, 10);
+                idlist_t list;
+                pgsql_parse_nodes( PQgetvalue(res, j, 1), list);
+                if (num_nodes != list.size()) {
+                    fprintf(stderr, "parse_nodes problem for way %s: expected nodes %zu got %zu\n",
+                            tmp, num_nodes, list.size());
+                    util::exit_nicely();
+                }
 
-                count_ptr[count] = nodes_get_list(nodes_ptr[count], list, num_nodes);
+                nodes.push_back(nodelist_t());
+                nodes_get_list(nodes.back(), list);
 
-                count++;
                 break;
             }
         }
     }
 
-    assert(count<=way_count);
+    assert(way_ids.size() <= ids.size());
 
     PQclear(res);
     free(tmp2);
-    free(wayidspg);
 
-    return count;
+    return way_ids.size();
 }
 
 
@@ -724,54 +699,51 @@ int middle_pgsql_t::way_changed(osmid_t osm_id)
     return 0;
 }
 
-int middle_pgsql_t::relations_set(osmid_t id, struct member *members, int member_count, struct keyval *tags)
+int middle_pgsql_t::relations_set(osmid_t id, const memberlist_t &members, const taglist_t &tags)
 {
     // Params: id, way_off, rel_off, parts, members, tags */
     const char *paramValues[6];
     char *buffer;
-    int i;
-    struct keyval member_list;
+    taglist_t member_list;
     char buf[64];
 
-    int node_count = 0, way_count = 0, rel_count = 0;
+    idlist_t all_parts, node_parts, way_parts, rel_parts;
+    all_parts.reserve(members.size());
+    node_parts.reserve(members.size());
+    way_parts.reserve(members.size());
+    rel_parts.reserve(members.size());
 
-    std::vector<osmid_t> all_parts(member_count), node_parts, way_parts, rel_parts;
-    node_parts.reserve(member_count);
-    way_parts.reserve(member_count);
-    rel_parts.reserve(member_count);
-
-    for( i=0; i<member_count; i++ )
-    {
-      char tag = 0;
-      switch( members[i].type )
-      {
-        case OSMTYPE_NODE:     node_count++; node_parts.push_back(members[i].id); tag = 'n'; break;
-        case OSMTYPE_WAY:      way_count++; way_parts.push_back(members[i].id); tag = 'w'; break;
-        case OSMTYPE_RELATION: rel_count++; rel_parts.push_back(members[i].id); tag = 'r'; break;
-        default: fprintf( stderr, "Internal error: Unknown member type %d\n", members[i].type ); util::exit_nicely();
-      }
-      sprintf( buf, "%c%" PRIdOSMID, tag, members[i].id );
-      member_list.addItem( buf, members[i].role, false );
+    for (memberlist_t::const_iterator it = members.begin(); it != members.end(); ++it) {
+        char type = 0;
+        switch (it->type)
+        {
+            case OSMTYPE_NODE:     node_parts.push_back(it->id); type = 'n'; break;
+            case OSMTYPE_WAY:      way_parts.push_back(it->id); type = 'w'; break;
+            case OSMTYPE_RELATION: rel_parts.push_back(it->id); type = 'r'; break;
+            default:
+                fprintf(stderr, "Internal error: Unknown member type %d\n", it->type);
+                util::exit_nicely();
+        }
+        sprintf( buf, "%c%" PRIdOSMID, type, it->id );
+        member_list.push_back(tag(buf, it->role));
     }
 
-    int all_count = 0;
-    std::copy( node_parts.begin(), node_parts.end(), all_parts.begin() );
-    std::copy( way_parts.begin(), way_parts.end(), all_parts.begin() + node_count );
-    std::copy( rel_parts.begin(), rel_parts.end(), all_parts.begin() + node_count + way_count);
-    all_count = node_count + way_count + rel_count;
+    all_parts.insert(all_parts.end(), node_parts.begin(), node_parts.end());
+    all_parts.insert(all_parts.end(), way_parts.begin(), way_parts.end());
+    all_parts.insert(all_parts.end(), rel_parts.begin(), rel_parts.end());
 
     if( rel_table->copyMode )
     {
       char *tag_buf = strdup(pgsql_store_tags(tags,1));
-      const char *member_buf = pgsql_store_tags(&member_list,1);
-      char *parts_buf = pgsql_store_nodes(&all_parts[0], all_count);
+      const char *member_buf = pgsql_store_tags(member_list,1);
+      char *parts_buf = pgsql_store_nodes(all_parts);
       int length = strlen(member_buf) + strlen(tag_buf) + strlen(parts_buf) + 64;
       buffer = (char *)alloca(length);
-      if( snprintf( buffer, length, "%" PRIdOSMID "\t%d\t%d\t%s\t%s\t%s\n",
-              id, node_count, node_count+way_count, parts_buf, member_buf, tag_buf ) > (length-10) )
+      if( snprintf( buffer, length, "%" PRIdOSMID "\t%zu\t%zu\t%s\t%s\t%s\n",
+                    id, node_parts.size(), node_parts.size() + way_parts.size(),
+                    parts_buf, member_buf, tag_buf ) > (length-10) )
       { fprintf( stderr, "buffer overflow relation id %" PRIdOSMID "\n", id); return 1; }
       free(tag_buf);
-      member_list.resetList();
       pgsql_CopyData(__FUNCTION__, rel_table->sql_conn, buffer);
       return 0;
     }
@@ -780,33 +752,26 @@ int middle_pgsql_t::relations_set(osmid_t id, struct member *members, int member
     paramValues[0] = ptr;
     ptr += sprintf(ptr, "%" PRIdOSMID, id ) + 1;
     paramValues[1] = ptr;
-    ptr += sprintf(ptr, "%d", node_count ) + 1;
+    ptr += sprintf(ptr, "%zu", node_parts.size() ) + 1;
     paramValues[2] = ptr;
-    sprintf( ptr, "%d", node_count+way_count );
-    paramValues[3] = pgsql_store_nodes(&all_parts[0], all_count);
-    paramValues[4] = pgsql_store_tags(&member_list,0);
+    sprintf( ptr, "%zu", node_parts.size() + way_parts.size() );
+    paramValues[3] = pgsql_store_nodes(all_parts);
+    paramValues[4] = pgsql_store_tags(member_list,0);
     if( paramValues[4] )
         paramValues[4] = strdup(paramValues[4]);
     paramValues[5] = pgsql_store_tags(tags,0);
     pgsql_execPrepared(rel_table->sql_conn, "insert_rel", 6, (const char * const *)paramValues, PGRES_COMMAND_OK);
     if( paramValues[4] )
         free((void *)paramValues[4]);
-    member_list.resetList();
     return 0;
 }
 
-// Caller is responsible for freeing members & keyval::resetList(tags) */
-int middle_pgsql_t::relations_get(osmid_t id, struct member **members, int *member_count, struct keyval *tags) const
+int middle_pgsql_t::relations_get(osmid_t id, memberlist_t &members, taglist_t &tags) const
 {
-    PGresult   *res;
     char tmp[16];
     char const *paramValues[1];
     PGconn *sql_conn = rel_table->sql_conn;
-    struct keyval member_temp;
-    char tag;
-    int num_members;
-    struct member *list;
-    int i=0;
+    taglist_t member_temp;
 
     // Make sure we're out of copy mode */
     pgsql_endCopy( rel_table );
@@ -814,7 +779,7 @@ int middle_pgsql_t::relations_get(osmid_t id, struct member **members, int *memb
     snprintf(tmp, sizeof(tmp), "%" PRIdOSMID, id);
     paramValues[0] = tmp;
 
-    res = pgsql_execPrepared(sql_conn, "get_rel", 1, paramValues, PGRES_TUPLES_OK);
+    PGresult *res = pgsql_execPrepared(sql_conn, "get_rel", 1, paramValues, PGRES_TUPLES_OK);
     // Fields are: members, tags, member_count */
 
     if (PQntuples(res) != 1) {
@@ -822,30 +787,23 @@ int middle_pgsql_t::relations_get(osmid_t id, struct member **members, int *memb
         return 1;
     }
 
-    pgsql_parse_tags( PQgetvalue(res, 0, 1), tags );
-    pgsql_parse_tags( PQgetvalue(res, 0, 0), &member_temp );
-
-    num_members = strtol(PQgetvalue(res, 0, 2), NULL, 10);
-    list = (struct member *)malloc( sizeof(struct member)*num_members );
+    pgsql_parse_tags(PQgetvalue(res, 0, 1), tags);
+    pgsql_parse_tags(PQgetvalue(res, 0, 0), member_temp);
 
-    keyval *item;
-    while((item = member_temp.popItem()))
-    {
-        if( i >= num_members )
-        {
-            fprintf(stderr, "Unexpected member_count reading relation %" PRIdOSMID "\n", id);
-            util::exit_nicely();
-        }
-        tag = item->key[0];
-        list[i].type = (tag == 'n')?OSMTYPE_NODE:(tag == 'w')?OSMTYPE_WAY:(tag == 'r')?OSMTYPE_RELATION:((OsmType)-1);
-        list[i].id = strtoosmid(item->key.c_str()+1, NULL, 10 );
-        list[i].role = strdup( item->value.c_str() );
-        delete(item);
-        i++;
+    if (member_temp.size() != strtoul(PQgetvalue(res, 0, 2), NULL, 10)) {
+        fprintf(stderr, "Unexpected member_count reading relation %" PRIdOSMID "\n", id);
+        util::exit_nicely();
     }
-    *members = list;
-    *member_count = num_members;
+
     PQclear(res);
+
+    for (taglist_t::const_iterator it = member_temp.begin(); it != member_temp.end(); ++it) {
+        char tag = it->key[0];
+        OsmType type = (tag == 'n')?OSMTYPE_NODE:(tag == 'w')?OSMTYPE_WAY:(tag == 'r')?OSMTYPE_RELATION:((OsmType)-1);
+        members.push_back(member(type,
+                                 strtoosmid(it->key.c_str()+1, NULL, 10 ),
+                                 it->value));
+    }
     return 0;
 }
 
@@ -916,7 +874,7 @@ int middle_pgsql_t::relation_changed(osmid_t osm_id)
     return 0;
 }
 
-std::vector<osmid_t> middle_pgsql_t::relations_using_way(osmid_t way_id) const
+idlist_t middle_pgsql_t::relations_using_way(osmid_t way_id) const
 {
     char const *paramValues[1];
     char buffer[64];
@@ -929,7 +887,7 @@ std::vector<osmid_t> middle_pgsql_t::relations_using_way(osmid_t way_id) const
     PGresult *result = pgsql_execPrepared(rel_table->sql_conn, "rels_using_way",
                                           1, paramValues, PGRES_TUPLES_OK );
     const int ntuples = PQntuples(result);
-    std::vector<osmid_t> rel_ids(ntuples);
+    idlist_t rel_ids(ntuples);
     for (int i = 0; i < ntuples; ++i) {
         rel_ids[i] = strtoosmid(PQgetvalue(result, i, 0), NULL, 10);
     }
@@ -940,9 +898,7 @@ std::vector<osmid_t> middle_pgsql_t::relations_using_way(osmid_t way_id) const
 
 void middle_pgsql_t::analyze(void)
 {
-    int i;
-
-    for (i=0; i<num_tables; i++) {
+    for (int i=0; i<num_tables; i++) {
         PGconn *sql_conn = tables[i].sql_conn;
 
         if (tables[i].analyze) {
@@ -953,9 +909,7 @@ void middle_pgsql_t::analyze(void)
 
 void middle_pgsql_t::end(void)
 {
-    int i;
-
-    for (i=0; i<num_tables; i++) {
+    for (int i=0; i<num_tables; i++) {
         PGconn *sql_conn = tables[i].sql_conn;
 
         // Commit transaction */
@@ -1089,7 +1043,18 @@ int middle_pgsql_t::start(const options_t *out_options_)
     ways_pending_tracker.reset(new id_tracker());
     rels_pending_tracker.reset(new id_tracker());
 
-    Append = out_options->append;
+    // Gazetter doesn't use mark-pending processing and consequently
+    // needs no way-node index.
+    // TODO Currently, set here to keep the impact on the code small.
+    // We actually should have the output plugins report their needs
+    // and pass that via the constructor to middle_t, so that middle_t
+    // itself doesn't need to know about details of the output.
+    if (out_options->output_backend == "gazetteer") {
+        way_table->array_indexes = NULL;
+        mark_pending = false;
+    }
+
+    append = out_options->append;
     // reset this on every start to avoid options from last run
     // staying set for the second.
     build_indexes = 0;
@@ -1197,7 +1162,7 @@ int middle_pgsql_t::start(const options_t *out_options_)
             pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", tables[i].prepare);
         }
 
-        if (Append && tables[i].prepare_intarray) {
+        if (append && tables[i].prepare_intarray) {
             pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", tables[i].prepare_intarray);
         }
 
@@ -1263,7 +1228,7 @@ struct pthread_thunk {
 extern "C" void *pthread_middle_pgsql_stop_one(void *arg) {
     pthread_thunk *thunk = static_cast<pthread_thunk *>(arg);
     return thunk->obj->pgsql_stop_one(thunk->ptr);
-};
+}
 } // anonymous namespace
 
 void middle_pgsql_t::stop(void)
@@ -1306,7 +1271,7 @@ void middle_pgsql_t::stop(void)
 
 middle_pgsql_t::middle_pgsql_t()
     : tables(), num_tables(0), node_table(NULL), way_table(NULL), rel_table(NULL),
-      Append(0), cache(), persistent_cache(), build_indexes(0)
+      append(0), mark_pending(true), cache(), persistent_cache(), build_indexes(0)
 {
     /*table = t_node,*/
     tables.push_back(table_desc(
@@ -1390,7 +1355,8 @@ middle_pgsql_t::~middle_pgsql_t() {
 boost::shared_ptr<const middle_query_t> middle_pgsql_t::get_instance() const {
     middle_pgsql_t* mid = new middle_pgsql_t();
     mid->out_options = out_options;
-    mid->Append = out_options->append;
+    mid->append = out_options->append;
+    mid->mark_pending = mark_pending;
 
     //NOTE: this is thread safe for use in pending async processing only because
     //during that process they are only read from
@@ -1411,7 +1377,7 @@ boost::shared_ptr<const middle_query_t> middle_pgsql_t::get_instance() const {
             pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", tables[i].prepare);
         }
 
-        if (Append && tables[i].prepare_intarray) {
+        if (append && tables[i].prepare_intarray) {
             pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", tables[i].prepare_intarray);
         }
     }
diff --git a/middle-pgsql.hpp b/middle-pgsql.hpp
index 3f7ef03..f418832 100644
--- a/middle-pgsql.hpp
+++ b/middle-pgsql.hpp
@@ -27,20 +27,21 @@ struct middle_pgsql_t : public slim_middle_t {
     void end(void);
     void commit(void);
 
-    int nodes_set(osmid_t id, double lat, double lon, struct keyval *tags);
-    int nodes_get_list(struct osmNode *out, const osmid_t *nds, int nd_count) const;
+    int nodes_set(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int nodes_get_list(nodelist_t &out, const idlist_t nds) const;
     int nodes_delete(osmid_t id);
     int node_changed(osmid_t id);
 
-    int ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags);
-    int ways_get(osmid_t id, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const;
-    int ways_get_list(const osmid_t *ids, int way_count, osmid_t *way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const;
+    int ways_set(osmid_t id, const idlist_t &nds, const taglist_t &tags);
+    int ways_get(osmid_t id, taglist_t &tags, nodelist_t &nodes) const;
+    int ways_get_list(const idlist_t &ids, idlist_t &way_ids,
+                      multitaglist_t &tags, multinodelist_t &nodes) const;
 
     int ways_delete(osmid_t id);
     int way_changed(osmid_t id);
 
-    int relations_get(osmid_t id, struct member **members, int *member_count, struct keyval *tags) const;
-    int relations_set(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int relations_get(osmid_t id, memberlist_t &members, taglist_t &tags) const;
+    int relations_set(osmid_t id, const memberlist_t &members, const taglist_t &tags);
     int relations_delete(osmid_t id);
     int relation_changed(osmid_t id);
 
@@ -85,15 +86,16 @@ struct middle_pgsql_t : public slim_middle_t {
 private:
 
     int connect(table_desc& table);
-    int local_nodes_set(const osmid_t& id, const double& lat, const double& lon, const struct keyval *tags);
-    int local_nodes_get_list(struct osmNode *nodes, const osmid_t *ndids, const int& nd_count) const;
+    int local_nodes_set(const osmid_t& id, const double& lat, const double& lon, const taglist_t &tags);
+    int local_nodes_get_list(nodelist_t &out, const idlist_t nds) const;
     int local_nodes_delete(osmid_t osm_id);
 
     std::vector<table_desc> tables;
     int num_tables;
-    struct table_desc *node_table, *way_table, *rel_table;
+    table_desc *node_table, *way_table, *rel_table;
 
-    int Append;
+    int append;
+    bool mark_pending;
 
     boost::shared_ptr<node_ram_cache> cache;
     boost::shared_ptr<node_persistent_cache> persistent_cache;
diff --git a/middle-ram.cpp b/middle-ram.cpp
index 01deb43..fcaf290 100644
--- a/middle-ram.cpp
+++ b/middle-ram.cpp
@@ -7,14 +7,11 @@
  * emit the final geometry-enabled output formats
 */
 
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <libpq-fe.h>
-
 #include <stdexcept>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
 
 #include "osmtypes.hpp"
 #include "middle-ram.hpp"
@@ -56,96 +53,48 @@ static  osmid_t id2offset(osmid_t id)
     return id & (PER_BLOCK-1);
 }
 
-static int block2id(int block, int offset)
-{
-    return ((block - NUM_BLOCKS/2) << BLOCK_SHIFT) + offset;
-}
-
-int middle_ram_t::nodes_set(osmid_t id, double lat, double lon, struct keyval *tags) {
+int middle_ram_t::nodes_set(osmid_t id, double lat, double lon, const taglist_t &tags) {
     return cache->set(id, lat, lon, tags);
 }
 
-int middle_ram_t::ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags)
+int middle_ram_t::ways_set(osmid_t id, const idlist_t &nds, const taglist_t &tags)
 {
     int block  = id2block(id);
     int offset = id2offset(id);
 
-    if (!ways[block]) {
-        ways[block] = (struct ramWay *)calloc(PER_BLOCK, sizeof(struct ramWay));
-        if (!ways[block]) {
-            fprintf(stderr, "Error allocating ways\n");
-            util::exit_nicely();
-        }
-        way_blocks++;
+    if (ways[block].empty()) {
+        ways[block].assign(PER_BLOCK, ramWay());
     }
 
-    free(ways[block][offset].ndids);
-    ways[block][offset].ndids = NULL;
-
-    /* Copy into length prefixed array */
-    ways[block][offset].ndids = (osmid_t *)malloc( (nd_count+1)*sizeof(osmid_t) );
-    memcpy( ways[block][offset].ndids+1, nds, nd_count*sizeof(osmid_t) );
-    ways[block][offset].ndids[0] = nd_count;
-
-    if (!ways[block][offset].tags) {
-        ways[block][offset].tags = new keyval();
-    } else
-        ways[block][offset].tags->resetList();
-
-    tags->cloneList(ways[block][offset].tags);
+    ways[block][offset].ndids = nds;
+    ways[block][offset].tags = tags;
 
     return 0;
 }
 
-int middle_ram_t::relations_set(osmid_t id, struct member *members, int member_count, struct keyval *tags)
+int middle_ram_t::relations_set(osmid_t id, const memberlist_t &members, const taglist_t &tags)
 {
-    struct member *ptr;
     int block  = id2block(id);
     int offset = id2offset(id);
-    if (!rels[block]) {
-        rels[block] = (struct ramRel *)calloc(PER_BLOCK, sizeof(struct ramRel));
-        if (!rels[block]) {
-            fprintf(stderr, "Error allocating rels\n");
-            util::exit_nicely();
-        }
+    if (rels[block].empty()) {
+        rels[block].assign(PER_BLOCK, ramRel());
     }
 
-    if (!rels[block][offset].tags) {
-        rels[block][offset].tags = new keyval();
-    } else
-        rels[block][offset].tags->resetList();
-
-    tags->cloneList(rels[block][offset].tags);
-
-    free( rels[block][offset].members );
-    rels[block][offset].members = NULL;
-
-    ptr = (struct member *)malloc(sizeof(struct member) * member_count);
-    if (ptr) {
-        memcpy( ptr, members, sizeof(struct member) * member_count );
-        rels[block][offset].member_count = member_count;
-        rels[block][offset].members = ptr;
-    } else {
-        fprintf(stderr, "%s malloc failed\n", __FUNCTION__);
-        util::exit_nicely();
-    }
+    rels[block][offset].tags = tags;
+    rels[block][offset].members = members;
 
     return 0;
 }
 
-int middle_ram_t::nodes_get_list(struct osmNode *nodes, const osmid_t *ndids, int nd_count) const
+int middle_ram_t::nodes_get_list(nodelist_t &out, const idlist_t nds) const
 {
-    int i, count;
-
-    count = 0;
-    for( i=0; i<nd_count; i++ )
-    {
-        if (cache->get(&nodes[count], ndids[i]))
-            continue;
-
-        count++;
+    for (idlist_t::const_iterator it = nds.begin(); it != nds.end(); ++it) {
+        osmNode n;
+        if (!cache->get(&n, *it))
+            out.push_back(n);
     }
-    return count;
+
+    return out.size();
 }
 
 void middle_ram_t::iterate_relations(pending_processor& pf)
@@ -160,7 +109,8 @@ void middle_ram_t::iterate_relations(pending_processor& pf)
     pf.process_relations();
 }
 
-size_t middle_ram_t::pending_count() const {
+size_t middle_ram_t::pending_count() const
+{
     return 0;
 }
 
@@ -176,111 +126,71 @@ void middle_ram_t::iterate_ways(middle_t::pending_processor& pf)
 
 void middle_ram_t::release_relations()
 {
-    int block, offset;
-
-    for(block=NUM_BLOCKS-1; block>=0; block--) {
-        if (!rels[block])
-            continue;
-
-        for (offset=0; offset < PER_BLOCK; offset++) {
-            if (rels[block][offset].members) {
-                free(rels[block][offset].members);
-                rels[block][offset].members = NULL;
-                rels[block][offset].tags->resetList();
-                delete rels[block][offset].tags;
-                rels[block][offset].tags=NULL;
-            }
-        }
-        free(rels[block]);
-        rels[block] = NULL;
-    }
+    rels.clear();
 }
 
 void middle_ram_t::release_ways()
 {
-    int i, j = 0;
-
-    for (i=0; i<NUM_BLOCKS; i++) {
-        if (ways[i]) {
-            for (j=0; j<PER_BLOCK; j++) {
-                if (ways[i][j].tags) {
-                    ways[i][j].tags->resetList();
-                    delete ways[i][j].tags;
-                }
-                if (ways[i][j].ndids)
-                    free(ways[i][j].ndids);
-            }
-            free(ways[i]);
-            ways[i] = NULL;
-        }
-    }
+    ways.clear();
 }
 
-/* Caller must free nodes_ptr and keyval::resetList(tags_ptr) */
-int middle_ram_t::ways_get(osmid_t id, struct keyval *tags_ptr, struct osmNode **nodes_ptr, int *count_ptr) const
+int middle_ram_t::ways_get(osmid_t id, taglist_t &tags, nodelist_t &nodes) const
 {
-    int block = id2block(id), offset = id2offset(id);
-    struct osmNode *nodes;
-
     if (simulate_ways_deleted)
         return 1;
 
-    if (!ways[block])
+    int block = id2block(id), offset = id2offset(id);
+
+    if (ways[block].empty() || ways[block][offset].ndids.empty())
         return 1;
 
-    if (ways[block][offset].ndids) {
-        /* First element contains number of nodes */
-        nodes = (struct osmNode *)malloc( sizeof(struct osmNode) * ways[block][offset].ndids[0]);
-        int ndCount = nodes_get_list(nodes, ways[block][offset].ndids+1, ways[block][offset].ndids[0]);
+    tags = ways[block][offset].tags;
 
-        if (ndCount) {
-            ways[block][offset].tags->cloneList(tags_ptr);
-            *nodes_ptr = nodes;
-            *count_ptr = ndCount;
-            return 0;
-        }
-        free(nodes);
-    }
-    return 1;
+    nodes_get_list(nodes, ways[block][offset].ndids);
+
+    return 0;
 }
 
-int middle_ram_t::ways_get_list(const osmid_t *ids, int way_count, osmid_t *way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const {
-    int count = 0;
-    int i;
+int middle_ram_t::ways_get_list(const idlist_t &ids, idlist_t &way_ids,
+                                multitaglist_t &tags, multinodelist_t &nodes) const
+{
+    if (ids.empty())
+        return 0;
 
-    for (i = 0; i < way_count; i++) {
+    assert(way_ids.empty());
+    tags.assign(ids.size(), taglist_t());
+    nodes.assign(ids.size(), nodelist_t());
 
-        if (ways_get(ids[i], &(tag_ptr[count]), &(node_ptr[count]), &(count_ptr[count])) == 0) {
-            way_ids[count] = ids[i];
+    size_t count = 0;
+    for (idlist_t::const_iterator it = ids.begin(); it != ids.end(); ++it) {
+        if (ways_get(*it, tags[count], nodes[count]) == 0) {
+            way_ids.push_back(*it);
             count++;
+        } else {
+            tags[count].clear();
+            nodes[count].clear();
         }
     }
+
+    if (count < ids.size()) {
+        tags.resize(count);
+        nodes.resize(count);
+    }
+
     return count;
 }
 
-/* Caller must free members_ptr and keyval::resetList(tags_ptr).
- * Note that the members in members_ptr are copied, but the roles
- * within the members are not, and should not be freed.
- */
-int middle_ram_t::relations_get(osmid_t id, struct member **members_ptr, int *member_count, struct keyval *tags_ptr) const
+int middle_ram_t::relations_get(osmid_t id, memberlist_t &members, taglist_t &tags) const
 {
     int block = id2block(id), offset = id2offset(id);
-    struct member *members;
 
-    if (!rels[block])
+    if (rels[block].empty() || rels[block][offset].members.empty())
         return 1;
 
-    if (rels[block][offset].members) {
-        const size_t member_bytes = sizeof(struct member) * rels[block][offset].member_count;
-        members = (struct member *)malloc(member_bytes);
-        memcpy(members, rels[block][offset].members, member_bytes);
-        rels[block][offset].tags->cloneList(tags_ptr);
+    members = rels[block][offset].members;
+    tags = rels[block][offset].tags;
 
-        *members_ptr = members;
-        *member_count = rels[block][offset].member_count;
-        return 0;
-    }
-    return 1;
+    return 0;
 }
 
 void middle_ram_t::analyze(void)
@@ -318,7 +228,7 @@ void middle_ram_t::commit(void) {
 }
 
 middle_ram_t::middle_ram_t():
-    ways(), rels(), way_blocks(0), cache(),
+    ways(), rels(), cache(),
     simulate_ways_deleted(false)
 {
     ways.resize(NUM_BLOCKS); memset(&ways[0], 0, NUM_BLOCKS * sizeof ways[0]);
diff --git a/middle-ram.hpp b/middle-ram.hpp
index d9c7fc3..2c68ea4 100644
--- a/middle-ram.hpp
+++ b/middle-ram.hpp
@@ -11,7 +11,6 @@
 
 #include "middle.hpp"
 #include "node-ram-cache.hpp"
-#include <memory>
 #include <vector>
 
 struct middle_ram_t : public middle_t {
@@ -24,20 +23,21 @@ struct middle_ram_t : public middle_t {
     void end(void);
     void commit(void);
 
-    int nodes_set(osmid_t id, double lat, double lon, struct keyval *tags);
-    int nodes_get_list(struct osmNode *out, const osmid_t *nds, int nd_count) const;
+    int nodes_set(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int nodes_get_list(nodelist_t &out, const idlist_t nds) const;
     int nodes_delete(osmid_t id);
     int node_changed(osmid_t id);
 
-    int ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags);
-    int ways_get(osmid_t id, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const;
-    int ways_get_list(const osmid_t *ids, int way_count, osmid_t *way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const;
+    int ways_set(osmid_t id, const idlist_t &nds, const taglist_t &tags);
+    int ways_get(osmid_t id, taglist_t &tags, nodelist_t &nodes) const;
+    int ways_get_list(const idlist_t &ids, idlist_t &way_ids,
+                      multitaglist_t &tags, multinodelist_t &nodes) const;
 
     int ways_delete(osmid_t id);
     int way_changed(osmid_t id);
 
-    int relations_get(osmid_t id, struct member **members, int *member_count, struct keyval *tags) const;
-    int relations_set(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int relations_get(osmid_t id, memberlist_t &members, taglist_t &tags) const;
+    int relations_set(osmid_t id, const memberlist_t &members, const taglist_t &tags);
     int relations_delete(osmid_t id);
     int relation_changed(osmid_t id);
 
@@ -55,20 +55,17 @@ private:
     void release_relations();
 
     struct ramWay {
-        struct keyval *tags;
-        osmid_t *ndids;
+        taglist_t tags;
+        idlist_t ndids;
     };
 
     struct ramRel {
-        struct keyval *tags;
-        struct member *members;
-        int member_count;
+        taglist_t tags;
+        memberlist_t members;
     };
 
-    std::vector<ramWay *> ways;
-    std::vector<ramRel *> rels;
-
-    int way_blocks;
+    std::vector<std::vector<ramWay> > ways;
+    std::vector<std::vector<ramRel> > rels;
 
     std::auto_ptr<node_ram_cache> cache;
 
diff --git a/middle.cpp b/middle.cpp
index 63cc0a3..dcaae5b 100644
--- a/middle.cpp
+++ b/middle.cpp
@@ -12,15 +12,3 @@ boost::shared_ptr<middle_t> middle_t::create_middle(const bool slim)
          return boost::make_shared<middle_ram_t>();
 }
 
-
-middle_query_t::~middle_query_t() {
-}
-
-middle_t::~middle_t() {
-}
-
-slim_middle_t::~slim_middle_t() {
-}
-
-middle_t::pending_processor::~pending_processor() {
-}
diff --git a/middle.hpp b/middle.hpp
index e880413..ae685a8 100644
--- a/middle.hpp
+++ b/middle.hpp
@@ -11,29 +11,28 @@
 #include "options.hpp"
 #include <vector>
 
-struct keyval;
-struct member;
-
 struct middle_query_t {
-    virtual ~middle_query_t();
+    virtual ~middle_query_t() {}
 
-    virtual int nodes_get_list(struct osmNode *out, const osmid_t *nds, int nd_count) const = 0;
+    virtual int nodes_get_list(nodelist_t &out, const idlist_t nds) const = 0;
 
-    virtual int ways_get(osmid_t id, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const = 0;
+    virtual int ways_get(osmid_t id, taglist_t &tags, nodelist_t &nodes) const = 0;
 
-    virtual int ways_get_list(const osmid_t *ids, int way_count, osmid_t *way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const = 0;
+    virtual int ways_get_list(const idlist_t &ids, idlist_t &way_ids,
+                              multitaglist_t &tags,
+                              multinodelist_t &nodes) const = 0;
 
-    virtual int relations_get(osmid_t id, struct member **members, int *member_count, struct keyval *tags) const = 0;
+    virtual int relations_get(osmid_t id, memberlist_t &members, taglist_t &tags) const = 0;
 
-    virtual std::vector<osmid_t> relations_using_way(osmid_t way_id) const = 0;
+    virtual idlist_t relations_using_way(osmid_t way_id) const = 0;
 
     virtual boost::shared_ptr<const middle_query_t> get_instance() const = 0;
 };
 
 struct middle_t : public middle_query_t {
-    static boost::shared_ptr<middle_t> create_middle(const bool slim);
+    static boost::shared_ptr<middle_t> create_middle(bool slim);
 
-    virtual ~middle_t();
+    virtual ~middle_t() {}
 
     virtual int start(const options_t *out_options_) = 0;
     virtual void stop(void) = 0;
@@ -41,12 +40,12 @@ struct middle_t : public middle_query_t {
     virtual void end(void) = 0;
     virtual void commit(void) = 0;
 
-    virtual int nodes_set(osmid_t id, double lat, double lon, struct keyval *tags) = 0;
-    virtual int ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags) = 0;
-    virtual int relations_set(osmid_t id, struct member *members, int member_count, struct keyval *tags) = 0;
+    virtual int nodes_set(osmid_t id, double lat, double lon, const taglist_t &tags) = 0;
+    virtual int ways_set(osmid_t id, const idlist_t &nds, const taglist_t &tags) = 0;
+    virtual int relations_set(osmid_t id, const memberlist_t &members, const taglist_t &tags) = 0;
 
     struct pending_processor {
-        virtual ~pending_processor();
+        virtual ~pending_processor() {}
         virtual void enqueue_ways(osmid_t id) = 0;
         virtual void process_ways() = 0;
         virtual void enqueue_relations(osmid_t id) = 0;
@@ -62,7 +61,7 @@ struct middle_t : public middle_query_t {
 };
 
 struct slim_middle_t : public middle_t {
-    virtual ~slim_middle_t();
+    virtual ~slim_middle_t() {}
 
     virtual int nodes_delete(osmid_t id) = 0;
     virtual int node_changed(osmid_t id) = 0;
diff --git a/multi.lua b/multi.lua
index f1969c0..283b33b 100644
--- a/multi.lua
+++ b/multi.lua
@@ -4,61 +4,200 @@
 --
 -- See docs/lua.md and docs/multi.md
 
--- A function to determine if the tags make the object "interesting" to the buildings table
-function building_interesting (keyvals)
-  return keyvals["building"] and keyvals["building"] ~= "no"
+-- These are copied from default.style
+-- If new "tags" are being generated in the Lua code they should normally be
+-- added here. This is why name_.* is dropped. In the raw OSM data
+-- multi-lingual names are stored in name:*.
+
+delete_tags = {'name_.*', 'note', 'note:.*', 'source', 'source_ref', 'source:.*',
+               'attribution', 'comment', 'fixme', 'created_by', 'odbl',
+               'odbl:note', 'SK53_bulk:load', 'tiger:.*', 'NHD:.*', 'nhd:.*',
+               'gnis:.*', 'geobase:.*', 'accuracy:meters', 'sub_sea:type',
+               'waterway:type', 'KSJ2:.*', 'yh:.*', 'osak:.*', 'kms:.*', 'ngbe:.*',
+               'naptan:.*', 'CLC:.*', '3dshapes:ggmodelk', 'AND_nosr_r',
+               'import', 'it:fvg:.*'}
+
+-- In a real transform the Lua code might be split into multiple files with
+-- common code included with "dofile" but a single file is easier for an example
+
+-- A function to determine if the tags make the object "interesting" to the
+-- buildings table
+function building_interesting (kv)
+  return kv["building"] and kv["building"] ~= "no"
 end
 
-function building_ways (keyvals, num_keys)
-  return generic_ways(building_interesting, keyvals)
+function building_transform (kv)
+  kv["name_en"] = name_lang(kv, "en")
+  kv["name_de"] = name_lang(kv, "de")
+  kv["name_fr"] = name_lang(kv, "fr")
+  return kv
 end
 
-function building_rels (keyvals, num_keys)
-  return generic_rels(building_interesting, keyvals)
+-- If we weren't generating multilingual names we could omit building_transform
+function building_ways (kv, num_keys)
+  return generic_ways(building_interesting, kv, true, building_transform)
 end
 
-function builing_rel_members (keyvals, keyvaluemembers, roles, membercount)
-  return generic_rel_members(building_interesting, keyvals, keyvaluemembers, roles, membercount)
+function building_rels (kv, num_keys)
+  return generic_rels(building_interesting, kv)
 end
 
-function bus_nodes_proc (keyvals, num_tags)
-  if keyvals["highway"] == "bus_stop" then
-    tags = keyvals
-    -- Turns values into true unless they're no, leaving empty tags as null.
-    -- This lets these columns be boolean, vastly simplifying stylesheet
-    -- logic
-    if tags["shelter"] then
-      -- Checks if the value is no or false, then returns a string that can be turned into a boolean
-      tags["shelter"] = ((tags["shelter"] ~= "no" and tags["shelter"] ~= "false") and "true" or "false")
-    end
-    if tags["bench"] then
-      tags["bench"] = ((tags["bench"] ~= "no" and tags["bench"] ~= "false") and "true" or "false")
+function builing_rel_members (kv, keyvaluemembers, roles, membercount)
+  return generic_rel_members(building_interesting, kv, keyvaluemembers, roles, membercount, building_transform)
+end
+
+-- A function to determine if the tags make the object "interesting" to the
+-- bus stop table
+function bus_interesting (kv)
+  return kv["highway"] == "bus_stop"
+end
+
+function bus_transform (kv)
+  kv["shelter"] = yesno(kv["shelter"])
+  kv["bench"] = yesno(kv["bench"])
+  kv["wheelchair"] = yesno(kv["wheelchair"])
+  kv["name_en"] = name_lang(kv, "en")
+  kv["name_de"] = name_lang(kv, "de")
+  kv["name_fr"] = name_lang(kv, "fr")
+  return kv
+end
+function bus_nodes (kv, num_keys)
+  return generic_nodes(bus_interesting, kv, bus_transform)
+end
+
+-- lookup tables for highways. Using an enum would be better in some ways, but
+-- would require creating the type before importing with osm2pgsql, which is
+-- not well suited to an example.
+
+highway_lookup = {motorway          = 0,
+                  trunk             = 1,
+                  primary           = 2,
+                  secondary         = 3,
+                  tertiary          = 4,
+                  unclassified      = 5,
+                  residential       = 5}
+
+link_lookup    = {motorway_link     = 0,
+                  trunk_link        = 1,
+                  primary_link      = 2,
+                  secondary_link    = 3,
+                  tertiary_link     = 4}
+
+function highway_interesting (kv)
+  -- The kv["highway"] check is not necessary but helps performance
+  return kv["highway"] and (highway_lookup[kv["highway"]] or link_lookup[kv["highway"]])
+end
+
+function highway_transform (kv)
+  -- Thanks to highway_interesting we know that kv["highway"] is in one of
+  -- highway_lookup or link_lookup
+  kv["road_class"] = highway_lookup[kv["highway"]] or link_lookup[kv["highway"]]
+  -- This is a lua way of doing an inline conditional
+  kv["road_type"] = highway_lookup[kv["highway"]] and "road" or "link"
+  kv["name_en"] = name_lang(kv, "en")
+  kv["name_de"] = name_lang(kv, "de")
+  kv["name_fr"] = name_lang(kv, "fr")
+  return kv
+end
+
+function highway_ways (kv, num_keys)
+  return generic_ways(highway_interesting, kv, false, highway_transform)
+end
+
+-- Some generic and utility helper functions
+
+-- This function normalizes a tag to true/false. It turns no or false into
+-- false and anything else to true. The result can then be used with a
+-- boolean column.
+-- > = yesno(nil)
+-- nil
+-- > = yesno("no")
+-- false
+-- > = yesno("false")
+-- false
+-- > = yesno("yes")
+-- true
+-- > = yesno("foo")
+-- true
+-- 
+-- A typical usage would be on a tag like bridge, tunnel, or shelter, but not
+-- a tag like oneway which could be yes, no, reverse, or unset
+function yesno (v)
+  -- This is a way of doing an inline condition in Lua
+  return v ~= nil and ((v == "no" or v == "false") and "false" or "true") or nil
+end
+
+-- Converts a name and name:lang tag into one combined name
+-- By passing an optional name_tag parameter it can also work with other
+-- multi-lingual tags
+function name_lang(kv, lang, name_tag)
+  if kv then
+    -- Default to the name tag, which is what this will generally be used on.
+    name_tag = name_tag or "name"
+    -- Defaulting to en is a bit of complete Anglo-centrism
+    lang = lang or "en"
+    name = kv[name_tag]
+    name_trans = kv[name_tag .. ":" .. lang]
+    -- If we don't have a translated name, just use the name (which may be blank)
+    if not name_trans then return name end
+    -- If we do have a translated name and not a local language name, use the translated
+    if not name then return name_trans end
+    -- if they're the same, return one of them
+    if name == name_trans then return name end
+    -- This method presents some problems when multiple names get put in the
+    -- name tag.
+    return name_trans .. "(" .. name .. ")"
+  end
+end
+
+-- This function gets rid of an object we don't care about
+function drop_all (...)
+  return 1, {}
+end
+
+-- This eliminates tags to be deleted
+function preprocess_tags (kv)
+  tags = {}
+  for k, v in pairs (kv) do
+    match = false
+    for _, d in ipairs(delete_tags) do
+      match = match or string.find(k, d)
     end
-    if tags["wheelchair"] then
-      tags["wheelchair"] = ((tags["wheelchair"] ~= "no" and tags["wheelchair"] ~= "false") and "true" or "false")
+    if not match then
+      tags[k] = v
     end
+  end
+  return tags
+end
+
+-- A generic way to process nodes, given a function which determines if tags are interesting
+-- Takes an optional function to process tags
+function generic_nodes (f, kv, t)
+  if f(kv) then
+    t = t or function (kv) return kv end
+    tags = t(kv)
     return 0, tags
   else
     return 1, {}
   end
 end
 
--- This function gets rid of something we don't care about
-function drop_all (...)
-  return 1, {}
-end
 
 -- A generic way to process ways, given a function which determines if tags are interesting
-function generic_ways (f, kv)
-  if f(kv) then
-    tags = kv
-    return 0, tags, 1, 0
+-- Takes an optional function to process tags.
+function generic_ways (interesting, kv, area, transform)
+  if interesting(kv) then
+    t = transform or function (kv) return kv end
+    tags = t(preprocess_tags(kv))
+    return 0, tags, area and 1 or 0, 0
   else
     return 1, {}, 0, 0
   end
 end
 
--- A generic way to process relations, given a function which determines if tags are interesting
+-- A generic way to process relations, given a function which determines if
+-- tags are interesting. The tag transformation work is done in
+-- generic_rel_members so we don't need to pass in a transformation function.
 function generic_rels (f, kv)
   if kv["type"] == "multipolygon" and f(kv) then
     tags = kv
@@ -68,12 +207,13 @@ function generic_rels (f, kv)
   end
 end
 
--- Basically taken from style.lua
-function generic_rel_members (f, keyvals, keyvaluemembers, roles, membercount)
+-- Basically taken from style.lua, with the potential for a transform added
+function generic_rel_members (f, keyvals, keyvaluemembers, roles, membercount, transform)
   filter = 0
   boundary = 0
   polygon = 0
   roads = 0
+  t = transform or function (kv) return kv end
 
   --mark each way of the relation to tell the caller if its going
   --to be used in the relation or by itself as its own standalone way
@@ -108,7 +248,8 @@ function generic_rel_members (f, keyvals, keyvaluemembers, roles, membercount)
       end
     end
     if filter == 1 then
-      return filter, keyvals, membersuperseeded, boundary, polygon, roads
+      tags =  t(keyvals)
+      return filter, tags, membersuperseeded, boundary, polygon, roads
     end
 
     --for each tag of each member if the relation have the tag or has a non matching value for it
@@ -125,6 +266,6 @@ function generic_rel_members (f, keyvals, keyvaluemembers, roles, membercount)
       membersuperseeded[i] = superseeded
     end
   end
-
-  return filter, keyvals, membersuperseeded, boundary, polygon, roads
-end
+  tags =  t(keyvals)
+  return filter, tags, membersuperseeded, boundary, polygon, roads
+end
\ No newline at end of file
diff --git a/multi.style.json b/multi.style.json
index e063812..400146c 100644
--- a/multi.style.json
+++ b/multi.style.json
@@ -2,20 +2,25 @@
  *
  * It contains comments so is invalid JSON but the parser doesn't complain.
  *
- * To use this file, pass it in as a style file with the --style switch
+ * To use this file, pass it in as a style file with the --style switch, e.g.
+ * with osm2pgsql --output multi --style multi.style.json
  */
 [
   {
     "name": "stops",
     "type": "point",
     "tagtransform": "multi.lua",
-    "tagtransform-node-function": "bus_nodes_proc",
+    "tagtransform-node-function": "bus_nodes",
     "tagtransform-way-function": "drop_all",
     "tagtransform-relation-function": "drop_all",
     "tagtransform-relation-member-function": "drop_all",
     "tags": [
       /* We don't need a column for highway, since it's always highway=bus_stop */
       {"name": "name", "type": "text"},
+      /* multi-lingual names can be generated at import time */
+      {"name": "name_en", "type": "text"},
+      {"name": "name_fr", "type": "text"},
+      {"name": "name_de", "type": "text"},
       {"name": "ref", "type": "text"},
       {"name": "operator", "type": "text"},
       /* By using boolean columns stylesheet processing can be simplified */
@@ -25,6 +30,23 @@
     ]
   },
   {
+    "name": "highways",
+    "type": "line",
+    "tagtransform": "multi.lua",
+    "tagtransform-node-function": "drop_all",
+    "tagtransform-way-function": "highway_ways",
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "name", "type": "text"},
+      {"name": "name_en", "type": "text"},
+      {"name": "name_fr", "type": "text"},
+      {"name": "name_de", "type": "text"},
+      {"name": "road_class", "type": "integer"},
+      {"name": "road_type", "type": "text"}
+    ]
+  },
+  {
     "name": "buildings",
     "type": "polygon",
     "tagtransform": "multi.lua",
@@ -33,6 +55,10 @@
     "tagtransform-relation-function": "building_rels",
     "tagtransform-relation-member-function": "builing_rel_members",
     "tags": [
+      {"name": "name", "type": "text"},
+      {"name": "name_en", "type": "text"},
+      {"name": "name_fr", "type": "text"},
+      {"name": "name_de", "type": "text"},
       {"name": "building", "type": "text"},
       {"name": "shop", "type": "text"},
       {"name": "amenity", "type": "text"}
diff --git a/node-persistent-cache-reader.cpp b/node-persistent-cache-reader.cpp
index 9fb8ec0..4ca6572 100644
--- a/node-persistent-cache-reader.cpp
+++ b/node-persistent-cache-reader.cpp
@@ -20,10 +20,10 @@
 void test_get_node_list(boost::shared_ptr<node_persistent_cache> cache,
                         int itterations, int max_size, int process_number) {
     int i, j, node_cnt, node_cnt_total;
-    struct osmNode *nodes;
+    nodelist_t nodes;
     struct timeval start, stop;
     struct timeval start_overall, stop_overall;
-    osmid_t *osmids;
+    idlist_t osmids;
 
     node_cnt_total = 0;
     gettimeofday(&start_overall, NULL);
@@ -32,18 +32,16 @@ void test_get_node_list(boost::shared_ptr<node_persistent_cache> cache,
         node_cnt_total += node_cnt;
 
         printf("Process %i: Getting %i nodes....\n", process_number, node_cnt);
-        nodes = (struct osmNode *)malloc(sizeof(struct osmNode) * node_cnt);
-        osmids = (osmid_t *)malloc(sizeof(osmid_t) * node_cnt);
         for (j = 0; j < node_cnt; j++) {
-            osmids[j] = random() % (1 << 31);
+            osmids.push_back(random() % (1 << 31));
         }
         gettimeofday(&start, NULL);
-        cache->get_list(nodes,osmids,node_cnt);
+        cache->get_list(nodes,osmids);
         gettimeofday(&stop, NULL);
         double duration = ((stop.tv_sec - start.tv_sec)*1000000.0 + (stop.tv_usec - start.tv_usec))/1000000.0;
         printf("Process %i: Got nodes in %f at a rate of %f/s\n", process_number, duration, node_cnt / duration);
-        free(nodes);
-        free(osmids);
+        nodes.clear();
+        osmids.clear();
     }
     gettimeofday(&stop_overall, NULL);
     double duration = ((stop_overall.tv_sec - start_overall.tv_sec)*1000000.0 + (stop_overall.tv_usec - start_overall.tv_usec))/1000000.0;
@@ -53,10 +51,10 @@ void test_get_node_list(boost::shared_ptr<node_persistent_cache> cache,
 int main(int argc, char *argv[]) {
 	int i,p;
 	options_t options;
-	struct osmNode node;
-	struct osmNode *nodes;
+	osmNode node;
+	nodelist_t nodes;
 	struct timeval start;
-	osmid_t *osmids;
+	idlist_t osmids;
 	int node_cnt;
 	options.append = 1;
 	options.flat_node_cache_enabled = 1;
@@ -68,12 +66,10 @@ int main(int argc, char *argv[]) {
 	if (argc > 3) {
                 cache.reset(new node_persistent_cache(&options, 1, ram_cache));
 		node_cnt = argc - 2;
-		nodes = (struct osmNode *)malloc(sizeof(struct osmNode) * node_cnt);
-		osmids = (osmid_t *)malloc(sizeof(osmid_t) * node_cnt);
 		for (i = 0; i < node_cnt; i++) {
-			osmids[i] = atoi(argv[2 + i]);
+			osmids.push_back(strtoosmid(argv[2 + i], NULL, 10));
 		}
-		cache->get_list(nodes,osmids,node_cnt);
+		cache->get_list(nodes, osmids);
 		for (i = 0; i < node_cnt; i++) {
 			printf("lat: %f / lon: %f\n", nodes[i].lat, nodes[i].lon);
 		}
@@ -120,7 +116,7 @@ int main(int argc, char *argv[]) {
 	} else {
                 cache.reset(new node_persistent_cache(&options, 1, ram_cache));
 		if (strstr(argv[2],",") == NULL) {
-			cache->get(&node, atoi(argv[2]));
+			cache->get(&node, strtoosmid(argv[2], NULL, 10));
 			printf("lat: %f / lon: %f\n", node.lat, node.lon);
 		} else {
                     char * node_list = (char *)malloc(sizeof(char) * (strlen(argv[2]) + 1));
@@ -129,15 +125,13 @@ int main(int argc, char *argv[]) {
 			strtok(node_list,",");
 			while (strtok(NULL,",") != NULL) node_cnt++;
 			printf("Processing %i nodes\n", node_cnt);
-			nodes = (struct osmNode *)malloc(sizeof(struct osmNode) * node_cnt);
-			osmids = (osmid_t *)malloc(sizeof(osmid_t) * node_cnt);
 			strcpy(node_list,argv[2]);
-			osmids[0] = atoi(strtok(node_list,","));
+			osmids.push_back(strtoosmid(strtok(node_list,","), NULL, 10));
 			for (i = 1; i < node_cnt; i++) {
 				char * tmp = strtok(NULL,",");
-				osmids[i] = atoi(tmp);
+				osmids.push_back(strtoosmid(tmp, NULL, 10));
 			}
-			cache->get_list(nodes,osmids,node_cnt);
+			cache->get_list(nodes,osmids);
 			for (i = 0; i < node_cnt; i++) {
 				printf("lat: %f / lon: %f\n", nodes[i].lat, nodes[i].lon);
 			}
diff --git a/node-persistent-cache.cpp b/node-persistent-cache.cpp
index 0b37718..17eca7d 100644
--- a/node-persistent-cache.cpp
+++ b/node-persistent-cache.cpp
@@ -62,7 +62,7 @@ void node_persistent_cache::writeout_dirty_nodes(osmid_t id)
         };
         if (write(node_cache_fd, writeNodeBlock.nodes,
                 WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode))
-                < WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode))
+                < ssize_t(WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode)))
         {
             fprintf(stderr, "Failed to write out node cache: %s\n",
                     strerror(errno));
@@ -108,7 +108,7 @@ void node_persistent_cache::writeout_dirty_nodes(osmid_t id)
                 };
                 if (write(node_cache_fd, readNodeBlockCache[i].nodes,
                         READ_NODE_BLOCK_SIZE * sizeof(struct ramNode))
-                        < READ_NODE_BLOCK_SIZE * sizeof(struct ramNode))
+                        < ssize_t(READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)))
                 {
                     fprintf(stderr, "Failed to write out node cache: %s\n",
                             strerror(errno));
@@ -226,7 +226,7 @@ void node_persistent_cache::expand_cache(osmid_t block_offset)
     {
         if (write(node_cache_fd, dummyNodes,
                 READ_NODE_BLOCK_SIZE * sizeof(struct ramNode))
-                < READ_NODE_BLOCK_SIZE * sizeof(struct ramNode))
+                < ssize_t(READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)))
         {
             fprintf(stderr, "Failed to expand persistent node cache: %s\n",
                     strerror(errno));
@@ -241,7 +241,7 @@ void node_persistent_cache::expand_cache(osmid_t block_offset)
         util::exit_nicely();
     };
     if (write(node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader))
-            != sizeof(struct persistentCacheHeader))
+            != ssize_t(sizeof(struct persistentCacheHeader)))
     {
         fprintf(stderr, "Failed to update persistent cache header: %s\n",
                 strerror(errno));
@@ -298,7 +298,7 @@ int node_persistent_cache::load_block(osmid_t block_offset)
         };
         if (write(node_cache_fd, readNodeBlockCache[block_id].nodes,
                 READ_NODE_BLOCK_SIZE * sizeof(struct ramNode))
-                < READ_NODE_BLOCK_SIZE * sizeof(struct ramNode))
+                < ssize_t(READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)))
         {
             fprintf(stderr, "Failed to write out node cache: %s\n",
                     strerror(errno));
@@ -345,7 +345,7 @@ void node_persistent_cache::nodes_set_create_writeout_block()
 {
     if (write(node_cache_fd, writeNodeBlock.nodes,
               WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode))
-        < WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode))
+        < ssize_t(WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode)))
     {
         fprintf(stderr, "Failed to write out node cache: %s\n",
                 strerror(errno));
@@ -528,61 +528,38 @@ int node_persistent_cache::get(struct osmNode *out, osmid_t id)
     return 0;
 }
 
-int node_persistent_cache::get_list(struct osmNode *nodes, const osmid_t *ndids,
-        int nd_count)
+int node_persistent_cache::get_list(nodelist_t &out, const idlist_t nds)
 {
-    int count = 0;
-    int i;
-    for (i = 0; i < nd_count; i++)
-    {
+    out.assign(nds.size(), osmNode());
+
+    bool need_fetch = false;;
+    for (size_t i = 0; i < nds.size(); ++i) {
         /* Check cache first */
-        if (ram_cache && (ram_cache->get(&nodes[i], ndids[i]) == 0))
-        {
-            count++;
+        if (ram_cache && (ram_cache->get(&out[i], nds[i]) != 0)) {
+            /* In order to have a higher OS level I/O queue depth
+               issue posix_fadvise(WILLNEED) requests for all I/O */
+            nodes_prefetch_async(nds[i]);
+            need_fetch = true;
         }
-        else
-        {
-            nodes[i].lat = NAN;
-            nodes[i].lon = NAN;
-        }
-    }
-    if (count == nd_count)
-        return count;
-
-    for (i = 0; i < nd_count; i++)
-    {
-        /* In order to have a higher OS level I/O queue depth
-           issue posix_fadvise(WILLNEED) requests for all I/O */
-        if (isnan(nodes[i].lat) && isnan(nodes[i].lon))
-            nodes_prefetch_async(ndids[i]);
     }
-    for (i = 0; i < nd_count; i++)
-    {
-        if ((isnan(nodes[i].lat) && isnan(nodes[i].lon))
-                && (get(&(nodes[i]), ndids[i]) == 0))
-            count++;
-    }
-
-    if (count < nd_count)
-    {
-        int j = 0;
-        for (i = 0; i < nd_count; i++)
-        {
-            if (!isnan(nodes[i].lat))
-            {
-                nodes[j].lat = nodes[i].lat;
-                nodes[j].lon = nodes[i].lon;
-                j++;
-            }
-        }
-        for (i = count; i < nd_count; i++)
-        {
-            nodes[i].lat = NAN;
-            nodes[i].lon = NAN;
+    if (!need_fetch)
+        return out.size();
+
+    size_t wrtidx = 0;
+    for (size_t i = 0; i < nds.size(); i++) {
+        if (isnan(out[i].lat) && isnan(out[i].lon)) {
+            if (get(&(out[wrtidx]), nds[i]) == 0)
+                wrtidx++;
+        } else {
+            if (wrtidx < i)
+                out[wrtidx] = out[i];
+            wrtidx++;
         }
     }
 
-    return count;
+    out.resize(wrtidx);
+
+    return wrtidx;
 }
 
 node_persistent_cache::node_persistent_cache(const options_t *options, int append,
diff --git a/node-persistent-cache.hpp b/node-persistent-cache.hpp
index 11722e8..a0c2a48 100644
--- a/node-persistent-cache.hpp
+++ b/node-persistent-cache.hpp
@@ -1,6 +1,7 @@
 #ifndef NODE_PERSISTENT_CACHE_H
 #define NODE_PERSISTENT_CACHE_H
 
+#include "osmtypes.hpp"
 #include "node-ram-cache.hpp"
 #include <boost/shared_ptr.hpp>
 
@@ -56,7 +57,7 @@ struct node_persistent_cache : public boost::noncopyable
 
     int set(osmid_t id, double lat, double lon);
     int get(struct osmNode *out, osmid_t id);
-    int get_list(struct osmNode *nodes, const osmid_t *ndids, int nd_count);
+    int get_list(nodelist_t &out, const idlist_t nds);
 
 private:
 
diff --git a/node-ram-cache.cpp b/node-ram-cache.cpp
index 09d33fd..7a467ba 100644
--- a/node-ram-cache.cpp
+++ b/node-ram-cache.cpp
@@ -63,7 +63,7 @@
 #define PER_BLOCK  (((osmid_t)1) << BLOCK_SHIFT)
 #define NUM_BLOCKS (((osmid_t)1) << (36 - BLOCK_SHIFT))
 
-#define SAFETY_MARGIN 1024*PER_BLOCK*sizeof(struct ramNode)
+#define SAFETY_MARGIN 1024*PER_BLOCK*sizeof(ramNode)
 
 static int id2block(osmid_t id)
 {
@@ -81,7 +81,7 @@ static osmid_t block2id(int block, int offset)
     return (((osmid_t) block - NUM_BLOCKS/2) << BLOCK_SHIFT) + (osmid_t) offset;
 }
 
-#define Swap(a,b) { struct ramNodeBlock * __tmp = a; a = b; b = __tmp; }
+#define Swap(a,b) { ramNodeBlock * __tmp = a; a = b; b = __tmp; }
 
 void node_ram_cache::percolate_up( int pos )
 {
@@ -99,21 +99,21 @@ void node_ram_cache::percolate_up( int pos )
     }
 }
 
-struct ramNode *node_ram_cache::next_chunk(size_t count, size_t size) {
+ramNode *node_ram_cache::next_chunk(size_t count, size_t size) {
     if ( (allocStrategy & ALLOC_DENSE_CHUNK) == 0 ) {
         static size_t pos = 0;
         char *result;
         pos += count * size;
         result = blockCache + cacheSize - pos + SAFETY_MARGIN;
 
-        return (struct ramNode *)result;
+        return (ramNode *)result;
     } else {
-        return (struct ramNode *)calloc(PER_BLOCK, sizeof(struct ramNode));
+        return (ramNode *)calloc(PER_BLOCK, sizeof(ramNode));
     }
 }
 
 
-int node_ram_cache::set_sparse(osmid_t id, double lat, double lon, struct keyval *tags) {
+int node_ram_cache::set_sparse(osmid_t id, double lat, double lon) {
     if ((sizeSparseTuples > maxSparseTuples) || ( cacheUsed > cacheSize)) {
         if ((allocStrategy & ALLOC_LOSSY) > 0)
             return 1;
@@ -131,12 +131,12 @@ int node_ram_cache::set_sparse(osmid_t id, double lat, double lon, struct keyval
     sparseBlock[sizeSparseTuples].coord.lon = lon;
 #endif
     sizeSparseTuples++;
-    cacheUsed += sizeof(struct ramNodeID);
+    cacheUsed += sizeof(ramNodeID);
     storedNodes++;
     return 0;
 }
 
-int node_ram_cache::set_dense(osmid_t id, double lat, double lon, struct keyval *tags) {
+int node_ram_cache::set_dense(osmid_t id, double lat, double lon) {
     int block  = id2block(id);
     int offset = id2offset(id);
     int i = 0;
@@ -155,12 +155,12 @@ int node_ram_cache::set_dense(osmid_t id, double lat, double lon, struct keyval
                  * to the sparse node cache and reuse memory of the previous block for the current block */
                 if ( ((allocStrategy & ALLOC_SPARSE) == 0) ||
                      ((queue[usedBlocks - 1]->used / (double)(1<< BLOCK_SHIFT)) >
-                      (sizeof(struct ramNode) / (double)sizeof(struct ramNodeID)))) {
+                      (sizeof(ramNode) / (double)sizeof(ramNodeID)))) {
                     /* Block has reached the level to keep it in dense representation */
                     /* We've just finished with the previous block, so we need to percolate it up the queue to its correct position */
                     /* Upto log(usedBlocks) iterations */
                     percolate_up( usedBlocks-1 );
-                    blocks[block].nodes = next_chunk(PER_BLOCK, sizeof(struct ramNode));
+                    blocks[block].nodes = next_chunk(PER_BLOCK, sizeof(ramNode));
                 } else {
                     /* previous block was not dense enough, so push it into the sparse node cache instead */
                     for (i = 0; i < (1 << BLOCK_SHIFT); i++) {
@@ -168,24 +168,24 @@ int node_ram_cache::set_dense(osmid_t id, double lat, double lon, struct keyval
                             set_sparse(block2id(queue[usedBlocks - 1]->block_offset,i),
 #ifdef FIXED_POINT
                                                        util::fix_to_double(queue[usedBlocks -1]->nodes[i].lat, scale_),
-                                                       util::fix_to_double(queue[usedBlocks -1]->nodes[i].lon, scale_),
+                                                       util::fix_to_double(queue[usedBlocks -1]->nodes[i].lon, scale_)
 #else
                                                        queue[usedBlocks -1]->nodes[i].lat,
-                                                       queue[usedBlocks -1]->nodes[i].lon,
+                                                       queue[usedBlocks -1]->nodes[i].lon
 #endif
-                                                       NULL);
+                                                       );
                         }
                     }
                     /* reuse previous block, as it's content is now in the dense representation */
                     storedNodes -= queue[usedBlocks - 1]->used;
                     blocks[block].nodes = queue[usedBlocks - 1]->nodes;
                     blocks[queue[usedBlocks - 1]->block_offset].nodes = NULL;
-                    memset( blocks[block].nodes, 0, PER_BLOCK * sizeof(struct ramNode) );
+                    memset( blocks[block].nodes, 0, PER_BLOCK * sizeof(ramNode) );
                     usedBlocks--;
-                    cacheUsed -= PER_BLOCK * sizeof(struct ramNode);
+                    cacheUsed -= PER_BLOCK * sizeof(ramNode);
                 }
             } else {
-                blocks[block].nodes = next_chunk(PER_BLOCK, sizeof(struct ramNode));
+                blocks[block].nodes = next_chunk(PER_BLOCK, sizeof(ramNode));
             }
 
             blocks[block].used = 0;
@@ -196,7 +196,7 @@ int node_ram_cache::set_dense(osmid_t id, double lat, double lon, struct keyval
             }
             queue[usedBlocks] = &blocks[block];
             usedBlocks++;
-            cacheUsed += PER_BLOCK * sizeof(struct ramNode);
+            cacheUsed += PER_BLOCK * sizeof(ramNode);
 
             /* If we've just used up the last possible block we enter the
              * transition and we change the invariant. To do this we percolate
@@ -232,7 +232,7 @@ int node_ram_cache::set_dense(osmid_t id, double lat, double lon, struct keyval
             /* Now the head of the queue is the smallest, so it becomes our replacement candidate */
             blocks[block].nodes = queue[0]->nodes;
             blocks[block].used = 0;
-            memset( blocks[block].nodes, 0, PER_BLOCK * sizeof(struct ramNode) );
+            memset( blocks[block].nodes, 0, PER_BLOCK * sizeof(ramNode) );
 
             /* Clear old head block and point to new block */
             storedNodes -= queue[0]->used;
@@ -273,7 +273,7 @@ int node_ram_cache::set_dense(osmid_t id, double lat, double lon, struct keyval
 }
 
 
-int node_ram_cache::get_sparse(struct osmNode *out, osmid_t id) {
+int node_ram_cache::get_sparse(osmNode *out, osmid_t id) {
     int64_t pivotPos = sizeSparseTuples >> 1;
     int64_t minPos = 0;
     int64_t maxPos = sizeSparseTuples;
@@ -303,7 +303,7 @@ int node_ram_cache::get_sparse(struct osmNode *out, osmid_t id) {
     return 1;
 }
 
-int node_ram_cache::get_dense(struct osmNode *out, osmid_t id) {
+int node_ram_cache::get_dense(osmNode *out, osmid_t id) {
     int block  = id2block(id);
     int offset = id2offset(id);
 
@@ -336,19 +336,19 @@ node_ram_cache::node_ram_cache( int strategy, int cacheSizeMB, int fixpointscale
     cacheUsed = 0;
     cacheSize = (int64_t)cacheSizeMB*(1024*1024);
     /* How much we can fit, and make sure it's odd */
-    maxBlocks = (cacheSize/(PER_BLOCK*sizeof(struct ramNode)));
-    maxSparseTuples = (cacheSize/sizeof(struct ramNodeID))+1;
+    maxBlocks = (cacheSize/(PER_BLOCK*sizeof(ramNode)));
+    maxSparseTuples = (cacheSize/sizeof(ramNodeID))+1;
 
     allocStrategy = strategy;
 
     if ((allocStrategy & ALLOC_DENSE) > 0 ) {
         fprintf(stderr, "Allocating memory for dense node cache\n");
-        blocks = (struct ramNodeBlock *)calloc(NUM_BLOCKS,sizeof(struct ramNodeBlock));
+        blocks = (ramNodeBlock *)calloc(NUM_BLOCKS,sizeof(ramNodeBlock));
         if (!blocks) {
             fprintf(stderr, "Out of memory for node cache dense index, try using \"--cache-strategy sparse\" instead \n");
             util::exit_nicely();
         }
-        queue = (struct ramNodeBlock **)calloc( maxBlocks,sizeof(struct ramNodeBlock *) );
+        queue = (ramNodeBlock **)calloc( maxBlocks,sizeof(ramNodeBlock *) );
         /* Use this method of allocation if virtual memory is limited,
          * or if OS allocs physical memory right away, rather than page by page
          * once it is needed.
@@ -361,7 +361,7 @@ node_ram_cache::node_ram_cache( int strategy, int cacheSizeMB, int fixpointscale
             }
         } else {
             fprintf(stderr, "Allocating dense node cache in one big chunk\n");
-            blockCache = (char *)calloc(maxBlocks + 1024,PER_BLOCK * sizeof(struct ramNode));
+            blockCache = (char *)calloc(maxBlocks + 1024,PER_BLOCK * sizeof(ramNode));
             if (!queue || !blockCache) {
                 fprintf(stderr, "Out of memory for dense node cache, reduce --cache size\n");
                 util::exit_nicely();
@@ -379,10 +379,10 @@ node_ram_cache::node_ram_cache( int strategy, int cacheSizeMB, int fixpointscale
     if ((allocStrategy & ALLOC_SPARSE) > 0 ) {
         fprintf(stderr, "Allocating memory for sparse node cache\n");
         if (!blockCache) {
-            sparseBlock = (struct ramNodeID *)calloc(maxSparseTuples,sizeof(struct ramNodeID));
+            sparseBlock = (ramNodeID *)calloc(maxSparseTuples,sizeof(ramNodeID));
         } else {
             fprintf(stderr, "Sharing dense sparse\n");
-            sparseBlock = (struct ramNodeID *)blockCache;
+            sparseBlock = (ramNodeID *)blockCache;
         }
         if (!sparseBlock) {
             fprintf(stderr, "Out of memory for sparse node cache, reduce --cache size\n");
@@ -391,16 +391,16 @@ node_ram_cache::node_ram_cache( int strategy, int cacheSizeMB, int fixpointscale
     }
 
 #ifdef __MINGW_H
-    fprintf( stderr, "Node-cache: cache=%ldMB, maxblocks=%d*%d, allocation method=%i\n", (cacheSize >> 20), maxBlocks, PER_BLOCK*sizeof(struct ramNode), allocStrategy );
+    fprintf( stderr, "Node-cache: cache=%ldMB, maxblocks=%d*%d, allocation method=%i\n", (cacheSize >> 20), maxBlocks, PER_BLOCK*sizeof(ramNode), allocStrategy );
 #else
-    fprintf( stderr, "Node-cache: cache=%ldMB, maxblocks=%d*%zd, allocation method=%i\n", (cacheSize >> 20), maxBlocks, PER_BLOCK*sizeof(struct ramNode), allocStrategy );
+    fprintf( stderr, "Node-cache: cache=%ldMB, maxblocks=%d*%zd, allocation method=%i\n", (cacheSize >> 20), maxBlocks, PER_BLOCK*sizeof(ramNode), allocStrategy );
 #endif
 }
 
 node_ram_cache::~node_ram_cache() {
   int i;
   fprintf( stderr, "node cache: stored: %" PRIdOSMID "(%.2f%%), storage efficiency: %.2f%% (dense blocks: %i, sparse nodes: %li), hit rate: %.2f%%\n",
-           storedNodes, 100.0f*storedNodes/totalNodes, 100.0f*storedNodes*sizeof(struct ramNode)/cacheUsed,
+           storedNodes, 100.0f*storedNodes/totalNodes, 100.0f*storedNodes*sizeof(ramNode)/cacheUsed,
            usedBlocks, sizeSparseTuples,
            100.0f*nodesCacheHits/nodesCacheLookups );
 
@@ -422,21 +422,21 @@ node_ram_cache::~node_ram_cache() {
   }
 }
 
-int node_ram_cache::set(osmid_t id, double lat, double lon, struct keyval *tags) {
+int node_ram_cache::set(osmid_t id, double lat, double lon, const taglist_t &) {
     totalNodes++;
     /* if ALLOC_DENSE and ALLOC_SPARSE are set, send it through
      * ram_nodes_set_dense. If a block is non dense, it will automatically
      * get pushed to the sparse cache if a block is sparse and ALLOC_SPARSE is set
      */
     if ( (allocStrategy & ALLOC_DENSE) > 0 ) {
-        return set_dense(id, lat, lon, tags);
+        return set_dense(id, lat, lon);
     }
     if ( (allocStrategy & ALLOC_SPARSE) > 0 )
-        return set_sparse(id, lat, lon, tags);
+        return set_sparse(id, lat, lon);
     return 1;
 }
 
-int node_ram_cache::get(struct osmNode *out, osmid_t id) {
+int node_ram_cache::get(osmNode *out, osmid_t id) {
     nodesCacheLookups++;
 
     if ((allocStrategy & ALLOC_DENSE) > 0) {
diff --git a/node-ram-cache.hpp b/node-ram-cache.hpp
index d54d504..8c64eb3 100644
--- a/node-ram-cache.hpp
+++ b/node-ram-cache.hpp
@@ -47,16 +47,16 @@ struct node_ram_cache : public boost::noncopyable
     node_ram_cache(int strategy, int cacheSizeMB, int fixpointscale);
     ~node_ram_cache();
 
-    int set(osmid_t id, double lat, double lon, struct keyval *tags);
-    int get(struct osmNode *out, osmid_t id);
+    int set(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int get(osmNode *out, osmid_t id);
 
 private:
     void percolate_up( int pos );
     struct ramNode *next_chunk(size_t count, size_t size);
-    int set_sparse(osmid_t id, double lat, double lon, struct keyval *tags);
-    int set_dense(osmid_t id, double lat, double lon, struct keyval *tags);
-    int get_sparse(struct osmNode *out, osmid_t id);
-    int get_dense(struct osmNode *out, osmid_t id);
+    int set_sparse(osmid_t id, double lat, double lon);
+    int set_dense(osmid_t id, double lat, double lon);
+    int get_sparse(osmNode *out, osmid_t id);
+    int get_dense(osmNode *out, osmid_t id);
 
     int allocStrategy;
 
diff --git a/options.hpp b/options.hpp
index 84cb9cf..f0abf97 100644
--- a/options.hpp
+++ b/options.hpp
@@ -1,7 +1,6 @@
 #ifndef OPTION_H
 #define OPTION_H
 
-#include "keyvals.hpp"
 #include "node-ram-cache.hpp"
 #include "reprojection.hpp"
 
@@ -19,7 +18,7 @@
 #define HSTORE_ALL 2
 
 /* Scale is chosen such that 40,000 * SCALE < 2^32          */
-static const int DEFAULT_SCALE = 100;
+enum { DEFAULT_SCALE = 100 };
 
 //TODO: GO THROUGH AND UPDATE TO BOOL WHERE MEMBER DENOTES ONLY ON OR OFF OPTION
 struct options_t {
diff --git a/osm2pgsql.cpp b/osm2pgsql.cpp
index 59a1915..fadb41c 100644
--- a/osm2pgsql.cpp
+++ b/osm2pgsql.cpp
@@ -119,7 +119,7 @@ int main(int argc, char *argv[])
 
         return 0;
     }//something went wrong along the way
-    catch(std::runtime_error& e)
+    catch(const std::runtime_error& e)
     {
         fprintf(stderr, "Osm2pgsql failed due to ERROR: %s\n", e.what());
         exit(EXIT_FAILURE);
diff --git a/osmdata.cpp b/osmdata.cpp
index eedf942..aa03556 100644
--- a/osmdata.cpp
+++ b/osmdata.cpp
@@ -1,4 +1,6 @@
 #include "osmdata.hpp"
+#include "output.hpp"
+#include "middle.hpp"
 
 #include <boost/foreach.hpp>
 #include <boost/make_shared.hpp>
@@ -33,7 +35,7 @@ osmdata_t::~osmdata_t()
 {
 }
 
-int osmdata_t::node_add(osmid_t id, double lat, double lon, struct keyval *tags) {
+int osmdata_t::node_add(osmid_t id, double lat, double lon, const taglist_t &tags) {
     mid->nodes_set(id, lat, lon, tags);
 
     int status = 0;
@@ -43,27 +45,27 @@ int osmdata_t::node_add(osmid_t id, double lat, double lon, struct keyval *tags)
     return status;
 }
 
-int osmdata_t::way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) {
-    mid->ways_set(id, nodes, node_count, tags);
+int osmdata_t::way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags) {
+    mid->ways_set(id, nodes, tags);
 
     int status = 0;
     BOOST_FOREACH(boost::shared_ptr<output_t>& out, outs) {
-        status |= out->way_add(id, nodes, node_count, tags);
+        status |= out->way_add(id, nodes, tags);
     }
     return status;
 }
 
-int osmdata_t::relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags) {
-    mid->relations_set(id, members, member_count, tags);
+int osmdata_t::relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags) {
+    mid->relations_set(id, members, tags);
 
     int status = 0;
     BOOST_FOREACH(boost::shared_ptr<output_t>& out, outs) {
-        status |= out->relation_add(id, members, member_count, tags);
+        status |= out->relation_add(id, members, tags);
     }
     return status;
 }
 
-int osmdata_t::node_modify(osmid_t id, double lat, double lon, struct keyval *tags) {
+int osmdata_t::node_modify(osmid_t id, double lat, double lon, const taglist_t &tags) {
     slim_middle_t *slim = dynamic_cast<slim_middle_t *>(mid.get());
 
     slim->nodes_delete(id);
@@ -79,15 +81,15 @@ int osmdata_t::node_modify(osmid_t id, double lat, double lon, struct keyval *ta
     return status;
 }
 
-int osmdata_t::way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) {
+int osmdata_t::way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags) {
     slim_middle_t *slim = dynamic_cast<slim_middle_t *>(mid.get());
 
     slim->ways_delete(id);
-    slim->ways_set(id, nodes, node_count, tags);
+    slim->ways_set(id, nodes, tags);
 
     int status = 0;
     BOOST_FOREACH(boost::shared_ptr<output_t>& out, outs) {
-        status |= out->way_modify(id, nodes, node_count, tags);
+        status |= out->way_modify(id, nodes, tags);
     }
 
     slim->way_changed(id);
@@ -95,15 +97,15 @@ int osmdata_t::way_modify(osmid_t id, osmid_t *nodes, int node_count, struct key
     return status;
 }
 
-int osmdata_t::relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags) {
+int osmdata_t::relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags) {
     slim_middle_t *slim = dynamic_cast<slim_middle_t *>(mid.get());
 
     slim->relations_delete(id);
-    slim->relations_set(id, members, member_count, tags);
+    slim->relations_set(id, members, tags);
 
     int status = 0;
     BOOST_FOREACH(boost::shared_ptr<output_t>& out, outs) {
-        status |= out->relation_modify(id, members, member_count, tags);
+        status |= out->relation_modify(id, members, tags);
     }
 
     slim->relation_changed(id);
@@ -185,9 +187,9 @@ struct pending_threaded_processor : public middle_t::pending_processor {
 
             //process it
             if(ways)
-                outputs.at(job.second)->pending_way(job.first, append);
+                outputs.at(job.output_id)->pending_way(job.osm_id, append);
             else
-                outputs.at(job.second)->pending_relation(job.first, append);
+                outputs.at(job.output_id)->pending_relation(job.osm_id, append);
 
             mutex.lock();
             ++ids_done;
@@ -199,9 +201,9 @@ struct pending_threaded_processor : public middle_t::pending_processor {
         pending_job_t job;
         while (queue.pop(job)) {
             if(ways)
-                outputs.at(job.second)->pending_way(job.first, append);
+                outputs.at(job.output_id)->pending_way(job.osm_id, append);
             else
-                outputs.at(job.second)->pending_relation(job.first, append);
+                outputs.at(job.output_id)->pending_relation(job.osm_id, append);
             ++ids_done;
         }
     }
diff --git a/osmdata.hpp b/osmdata.hpp
index 0dd04bf..460a5f5 100644
--- a/osmdata.hpp
+++ b/osmdata.hpp
@@ -3,10 +3,15 @@
 
 // when __cplusplus is defined, we need to define this macro as well
 // to get the print format specifiers in the inttypes.h header.
-#include <config.h>
+#include "config.h"
+
+#include <boost/shared_ptr.hpp>
 #include <vector>
 
-#include "output.hpp"
+#include "osmtypes.hpp"
+
+class output_t;
+struct middle_t;
 
 class osmdata_t {
 public:
@@ -17,13 +22,13 @@ public:
     void start();
     void stop();
 
-    int node_add(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_add(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
-    int node_modify(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_modify(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
     int node_delete(osmid_t id);
     int way_delete(osmid_t id);
diff --git a/osmtypes.hpp b/osmtypes.hpp
index 1684458..0c69670 100644
--- a/osmtypes.hpp
+++ b/osmtypes.hpp
@@ -7,7 +7,11 @@
 // to get the print format specifiers in the inttypes.h header.
 #define __STDC_FORMAT_MACROS
 #include <inttypes.h>
-#include <config.h>
+#include "config.h"
+
+#include <string>
+#include <vector>
+#include <cmath>
 
 typedef int64_t osmid_t;
 #define strtoosmid strtoll
@@ -17,14 +21,103 @@ typedef int64_t osmid_t;
 enum OsmType { OSMTYPE_WAY, OSMTYPE_NODE, OSMTYPE_RELATION };
 
 struct osmNode {
-    double lon;
-    double lat;
+  double lon;
+  double lat;
+
+  osmNode() : lon(NAN), lat(NAN) {}
+
+  osmNode(double x, double y) : lon(x), lat(y) {}
 };
 
+typedef std::vector<osmNode> nodelist_t;
+typedef std::vector<nodelist_t> multinodelist_t;
+
 struct member {
-    enum OsmType type;
-    osmid_t id;
-    char *role;
+  OsmType type;
+  osmid_t id;
+  std::string role;
+
+  member(OsmType t, osmid_t i, const std::string &r) : type(t), id(i), role(r) {}
+};
+
+typedef std::vector<member> memberlist_t;
+
+struct tag {
+  std::string key;
+  std::string value;
+
+  tag(const std::string &k, const std::string &v) : key(k), value(v) {}
+};
+
+
+class taglist_t : public std::vector<tag> {
+
+  typedef std::vector<tag> base_t;
+
+public:
+  const tag *find(const std::string &key) const { return _find(key); }
+
+  tag *find(const std::string &key) {  return const_cast<tag *>(_find(key)); }
+
+  size_t indexof(const std::string &key) const
+  {
+      for (size_t i = 0; i < size(); ++i)
+          if (at(i).key == key)
+              return i;
+
+      return -1;
+  }
+
+  const std::string *get(const std::string &key) const
+  {
+    for (base_t::const_iterator it = begin() ; it != end(); ++it)
+      if (it->key == key)
+        return &(it->value);
+
+    return 0;
+  }
+
+  bool get_bool(const std::string &key, bool defval) const
+  {
+    for (base_t::const_iterator it = begin() ; it != end(); ++it)
+      if (it->key == key) {
+          if (!defval &&
+              (it->value == "yes" || it->value == "true" || it->value == "1"))
+              return true;
+          if (defval &&
+              (it->value == "no" || it->value == "false" || it->value == "0"))
+              return false;
+          return defval;
+      }
+
+    return defval;
+  }
+
+  void push_dedupe(const tag& t)
+  {
+      if (find(t.key) == 0)
+          push_back(t);
+  }
+
+  bool contains(const std::string &key) const { return _find(key) != 0; }
+
+private:
+  const tag *_find(const std::string &key) const
+  {
+    for (base_t::const_iterator it = begin() ; it != end(); ++it)
+      if (it->key == key)
+        return &(*it);
+
+    return 0;
+  }
+
+
 };
 
+typedef std::vector<taglist_t> multitaglist_t;
+
+typedef std::vector<osmid_t> idlist_t;
+
+typedef std::vector<const std::string *> rolelist_t;
+
 #endif
diff --git a/output-gazetteer.cpp b/output-gazetteer.cpp
index 689e0fb..f556fa4 100644
--- a/output-gazetteer.cpp
+++ b/output-gazetteer.cpp
@@ -59,33 +59,33 @@ void place_tag_processor::clear()
 
 struct UnnamedPredicate
 {
-    bool operator()(const keyval *val) const {
-        return val->key == "natural" ||
-               val->key == "railway" ||
-               val->key == "waterway" ||
-               val->key == "boundary" ||
-               (val->key == "highway" &&
-                (val->value == "traffic_signals" ||
-                 val->value == "service" ||
-                 val->value == "cycleway" ||
-                 val->value == "path" ||
-                 val->value == "footway" ||
-                 val->value == "steps" ||
-                 val->value == "bridleway" ||
-                 val->value == "track" ||
-                 val->value == "byway" ||
-                 boost::ends_with(val->value, "_link")));
+    bool operator()(const tag &val) const {
+        return val.key == "natural" ||
+               val.key == "railway" ||
+               val.key == "waterway" ||
+               val.key == "boundary" ||
+               (val.key == "highway" &&
+                (val.value == "traffic_signals" ||
+                 val.value == "service" ||
+                 val.value == "cycleway" ||
+                 val.value == "path" ||
+                 val.value == "footway" ||
+                 val.value == "steps" ||
+                 val.value == "bridleway" ||
+                 val.value == "track" ||
+                 val.value == "byway" ||
+                 boost::ends_with(val.value, "_link")));
     }
 };
 
-void place_tag_processor::process_tags(keyval *tags)
+void place_tag_processor::process_tags(const taglist_t &tags)
 {
     bool placeadmin = false;
     bool placehouse = false;
     bool placebuilding = false;
-    const keyval *place = 0;
-    const keyval *junction = 0;
-    const keyval *landuse = 0;
+    const tag *place = 0;
+    const tag *junction = 0;
+    const tag *landuse = 0;
     bool isnamed = false;
     bool isinterpolation = false;
     const std::string *house_nr = 0;
@@ -93,11 +93,11 @@ void place_tag_processor::process_tags(keyval *tags)
     const std::string *street_nr = 0;
 
     clear();
-    src = tags;
+    src = &tags;
 
-    for (keyval *item = tags->firstItem(); item; item = tags->nextItem(item)) {
+    for (taglist_t::const_iterator item = tags.begin(); item != tags.end(); ++item) {
         if (item->key == "name:prefix") {
-            extratags.push_back(item);
+            extratags.push_back(&*item);
         } else if (item->key == "ref" ||
                    item->key == "int_ref" ||
                    item->key == "nat_ref" ||
@@ -109,7 +109,7 @@ void place_tag_processor::process_tags(keyval *tags)
                    item->key == "operator" ||
                    item->key == "pcode" ||
                    boost::starts_with(item->key, "pcode:")) {
-            names.push_back(item);
+            names.push_back(&*item);
         } else if (item->key == "name" ||
                    boost::starts_with(item->key, "name:") ||
                    item->key == "int_name" ||
@@ -132,54 +132,54 @@ void place_tag_processor::process_tags(keyval *tags)
                    item->key == "short_name" ||
                    boost::starts_with(item->key, "short_name:") ||
                    item->key == "brand") {
-            names.push_back(item);
+            names.push_back(&*item);
             isnamed = true;
         } else if (item->key == "addr:housename") {
-            names.push_back(item);
+            names.push_back(&*item);
             placehouse = true;
         } else if (item->key == "emergency") {
             if (item->value != "fire_hydrant" &&
                 item->value != "yes" &&
                 item->value != "no")
-                places.push_back(item);
+                places.push_back(*item);
         } else if (item->key == "tourism" ||
                    item->key == "historic" ||
                    item->key == "military") {
             if (item->value != "no" && item->value != "yes")
-                places.push_back(item);
+                places.push_back(*item);
         } else if (item->key == "natural") {
             if (item->value != "no" &&
                 item->value != "yes" &&
-                item->value != "costaline")
-                places.push_back(item);
+                item->value != "coastline")
+                places.push_back(*item);
         } else if (item->key == "landuse") {
             if (item->value == "cemetry")
-                places.push_back(item);
+                places.push_back(*item);
             else
-                landuse = item;
+                landuse = &*item;
         } else if (item->key == "highway") {
             if (item->value != "no" &&
                 item->value != "turning_circle" &&
                 item->value != "mini_roundabout" &&
                 item->value != "noexit" &&
                 item->value != "crossing")
-                places.push_back(item);
+                places.push_back(*item);
         } else if (item->key == "railway") {
             if (item->value != "level_crossing" &&
                 item->value != "no")
-                places.push_back(item);
+                places.push_back(*item);
         } else if (item->key == "man_made") {
             if (item->value != "survey_point" &&
                 item->value != "cutline")
-                places.push_back(item);
+                places.push_back(*item);
         } else if (item->key == "aerialway") {
             if (item->value != "pylon" &&
                 item->value != "no")
-                places.push_back(item);
+                places.push_back(*item);
         } else if (item->key == "boundary") {
             if (item->value == "administrative")
                 placeadmin = true;
-            places.push_back(item);
+            places.push_back(*item);
         } else if (item->key == "aeroway" ||
                    item->key == "amenity" ||
                    item->key == "boundary" ||
@@ -192,15 +192,15 @@ void place_tag_processor::process_tags(keyval *tags)
                    item->key == "mountain_pass") {
             if (item->value != "no")
             {
-                places.push_back(item);
+                places.push_back(*item);
             }
         } else if (item->key == "waterway") {
             if (item->value != "riverbank")
-                places.push_back(item);
+                places.push_back(*item);
         } else if (item->key == "place") {
-            place = item;
+            place = &*item;
         } else if (item->key == "junction") {
-            junction = item;
+            junction = &*item;
         } else if (item->key == "addr:interpolation") {
             housenumber.clear();
             escape(item->value, housenumber);
@@ -236,7 +236,7 @@ void place_tag_processor::process_tags(keyval *tags)
                    item->key == "is_in" ||
                    boost::starts_with(item->key, "is_in:") ||
                    item->key == "tiger:county") {
-            address.push_back(item);
+            address.push_back(&*item);
         } else if (item->key == "admin_level") {
             admin_level = atoi(item->value.c_str());
             if (admin_level <= 0 || admin_level > 100)
@@ -311,7 +311,7 @@ void place_tag_processor::process_tags(keyval *tags)
                    item->key == "locality" ||
                    item->key == "wikipedia" ||
                    boost::starts_with(item->key, "wikipedia:")) {
-            extratags.push_back(item);
+            extratags.push_back(&*item);
         } else if (item->key == "building") {
             placebuilding = true;
         }
@@ -325,11 +325,8 @@ void place_tag_processor::process_tags(keyval *tags)
                          places.end());
     }
 
-    if (isinterpolation) {
-        keyval *b = new keyval("place", "houses");
-        src->pushItem(b); // to make sure it gets deleted
-        places.push_back(b);
-    }
+    if (isinterpolation)
+        places.push_back(tag("place", "houses"));
 
     if (place) {
         if (isinterpolation ||
@@ -338,29 +335,23 @@ void place_tag_processor::process_tags(keyval *tags)
               place ->value != "islet"))
             extratags.push_back(place);
         else
-            places.push_back(place);
+            places.push_back(*place);
     }
 
     if (isnamed && places.empty()) {
         if (junction)
-            places.push_back(junction);
+            places.push_back(*junction);
         else if (landuse)
-            places.push_back(landuse);
+            places.push_back(*landuse);
     }
 
     if (places.empty()) {
         if (placebuilding && (!names.empty() || placehouse || postcode)) {
-            keyval *b = new keyval("building", "yes");
-            src->pushItem(b); // to make sure it gets deleted
-            places.push_back(b);
+            places.push_back(tag("building", "yes"));
         } else if (placehouse) {
-            keyval *b = new keyval("place", "house");
-            src->pushItem(b); // to make sure it gets deleted
-            places.push_back(b);
+            places.push_back(tag("place", "house"));
         } else if (postcode) {
-            keyval *b = new keyval("place", "postcode");
-            src->pushItem(b); // to make sure it gets deleted
-            places.push_back(b);
+            places.push_back(tag("place", "postcode"));
         }
     }
 
@@ -389,10 +380,10 @@ void place_tag_processor::copy_out(char osm_type, osmid_t osm_id,
                                    const std::string &wkt,
                                    std::string &buffer)
 {
-    BOOST_FOREACH(const keyval* place, places) {
+    BOOST_FOREACH(const tag &place, places) {
         std::string name;
-        if (place->key == "bridge" || place->key == "tunnel") {
-            name = domain_name(place->key);
+        if (place.key == "bridge" || place.key == "tunnel") {
+            name = domain_name(place.key);
             if (name.empty())
                 continue; // don't include unnamed bridges and tunnels
         }
@@ -403,10 +394,10 @@ void place_tag_processor::copy_out(char osm_type, osmid_t osm_id,
         // osm_id
         buffer += (single_fmt % osm_id).str();
         // class
-        escape(place->key, buffer);
+        escape(place.key, buffer);
         buffer += '\t';
         // type
-        escape(place->value, buffer);
+        escape(place.value, buffer);
         buffer += '\t';
         // names
         if (!name.empty()) {
@@ -416,10 +407,10 @@ void place_tag_processor::copy_out(char osm_type, osmid_t osm_id,
             bool first = true;
             // operator will be ignored on anything but these classes
             // (amenity for restaurant and fuel)
-            bool shop = (place->key == "shop") ||
-                        (place->key == "amenity") ||
-                        (place->key == "tourism");
-            BOOST_FOREACH(const keyval *entry, names) {
+            bool shop = (place.key == "shop") ||
+                        (place.key == "amenity") ||
+                        (place.key == "tourism");
+            BOOST_FOREACH(const tag *entry, names) {
                 if (!shop && (entry->key == "operator"))
                     continue;
 
@@ -448,7 +439,7 @@ void place_tag_processor::copy_out(char osm_type, osmid_t osm_id,
         copy_opt_string(addr_place, buffer);
         // isin
         if (!address.empty()) {
-            BOOST_FOREACH(const keyval *entry, address) {
+            BOOST_FOREACH(const tag *entry, address) {
                 if (entry->key == "tiger:county") {
                     escape(std::string(entry->value, 0, entry->value.find(",")),
                            buffer);
@@ -470,7 +461,7 @@ void place_tag_processor::copy_out(char osm_type, osmid_t osm_id,
             buffer += "\\N\t";
         } else {
             bool first = true;
-            BOOST_FOREACH(const keyval *entry, extratags) {
+            BOOST_FOREACH(const tag *entry, extratags) {
                 if (first)
                     first = false;
                 else
@@ -665,7 +656,7 @@ void output_gazetteer_t::stop()
 }
 
 int output_gazetteer_t::process_node(osmid_t id, double lat, double lon,
-                                     struct keyval *tags)
+                                     const taglist_t &tags)
 {
     places.process_tags(tags);
 
@@ -682,8 +673,7 @@ int output_gazetteer_t::process_node(osmid_t id, double lat, double lon,
     return 0;
 }
 
-int output_gazetteer_t::process_way(osmid_t id, osmid_t *ndv, int ndc,
-                                    struct keyval *tags)
+int output_gazetteer_t::process_way(osmid_t id, const idlist_t &nds, const taglist_t &tags)
 {
     places.process_tags(tags);
 
@@ -692,39 +682,31 @@ int output_gazetteer_t::process_way(osmid_t id, osmid_t *ndv, int ndc,
 
     /* Are we interested in this item? */
     if (places.has_data()) {
-        struct osmNode *nodev;
-        int nodec;
-
         /* Fetch the node details */
-        nodev = (struct osmNode *)malloc(ndc * sizeof(struct osmNode));
-        nodec = m_mid->nodes_get_list(nodev, ndv, ndc);
+        nodelist_t nodes;
+        m_mid->nodes_get_list(nodes, nds);
 
         /* Get the geometry of the object */
-        geometry_builder::maybe_wkt_t wkt = builder.get_wkt_simple(nodev, nodec, 1);
+        geometry_builder::maybe_wkt_t wkt = builder.get_wkt_simple(nodes, 1);
         if (wkt) {
             places.copy_out('W', id, wkt->geom, buffer);
             flush_place_buffer();
         }
-
-        /* Free the nodes */
-        free(nodev);
     }
 
     return 0;
 }
 
-int output_gazetteer_t::process_relation(osmid_t id, struct member *members,
-        int member_count, struct keyval *tags)
+int output_gazetteer_t::process_relation(osmid_t id, const memberlist_t &members,
+                                         const taglist_t &tags)
 {
-    int cmp_waterway;
-
-    const std::string *type = tags->getItem("type");
+    const std::string *type = tags.get("type");
     if (!type) {
         delete_unused_full('R', id);
         return 0;
     }
 
-    cmp_waterway = type->compare("waterway");
+    int cmp_waterway = type->compare("waterway");
 
     if (*type == "associatedStreet"
             || !(*type == "boundary" || *type == "multipolygon" || !cmp_waterway)) {
@@ -742,37 +724,27 @@ int output_gazetteer_t::process_relation(osmid_t id, struct member *members,
         return 0;
 
     /* get the boundary path (ways) */
-    osmid_t *xid2 = new osmid_t[member_count+1];
-
-    int count = 0;
-    for (int i=0; i<member_count; ++i) {
+    idlist_t xid2;
+    for (memberlist_t::const_iterator it = members.begin(); it != members.end(); ++it) {
         /* only interested in ways */
-        if (members[i].type != OSMTYPE_WAY)
-            continue;
-        xid2[count] = members[i].id;
-        count++;
+        if (it->type == OSMTYPE_WAY)
+            xid2.push_back(it->id);
     }
 
-    if (count == 0) {
+    if (xid2.empty()) {
         if (m_options.append)
             delete_unused_full('R', id);
 
-        delete [] xid2;
-
         return 0;
     }
 
-    int *xcount = new int[count + 1];
-    keyval *xtags  = new keyval[count+1];
-    struct osmNode **xnodes = new osmNode*[count + 1];
-    osmid_t *xid = new osmid_t[count + 1];
-    count = m_mid->ways_get_list(xid2, count, xid, xtags, xnodes, xcount);
-
-    xnodes[count] = NULL;
-    xcount[count] = 0;
+    multitaglist_t xtags;
+    multinodelist_t xnodes;
+    idlist_t xid;
+    m_mid->ways_get_list(xid2, xid, xtags, xnodes);
 
     if (cmp_waterway) {
-        geometry_builder::maybe_wkts_t wkts = builder.build_both(xnodes, xcount, 1, 1, 1000000, id);
+        geometry_builder::maybe_wkts_t wkts = builder.build_both(xnodes, 1, 1, 1000000, id);
         for (geometry_builder::wkt_itr wkt = wkts->begin(); wkt != wkts->end(); ++wkt) {
             if (boost::starts_with(wkt->geom,  "POLYGON")
                     || boost::starts_with(wkt->geom,  "MULTIPOLYGON")) {
@@ -784,25 +756,13 @@ int output_gazetteer_t::process_relation(osmid_t id, struct member *members,
         }
     } else {
         /* waterways result in multilinestrings */
-        geometry_builder::maybe_wkt_t wkt = builder.build_multilines(xnodes, xcount, id);
+        geometry_builder::maybe_wkt_t wkt = builder.build_multilines(xnodes, id);
         if ((wkt->geom).length() > 0) {
             places.copy_out('R', id, wkt->geom, buffer);
             flush_place_buffer();
         }
     }
 
-    for (int i=0; i<count; ++i)
-    {
-        xtags[i].resetList();
-        free(xnodes[i]);
-    }
-
-    free(xid);
-    delete [] xid2;
-    delete [] xcount;
-    delete [] xtags;
-    delete [] xnodes;
-
     return 0;
 }
 
diff --git a/output-gazetteer.hpp b/output-gazetteer.hpp
index 4788848..95cd318 100644
--- a/output-gazetteer.hpp
+++ b/output-gazetteer.hpp
@@ -1,9 +1,10 @@
 #ifndef OUTPUT_GAZETTEER_H
 #define OUTPUT_GAZETTEER_H
 
+#include "osmtypes.hpp"
 #include "output.hpp"
 #include "geometry-builder.hpp"
-#include "reprojection.hpp"
+#include "pgsql.hpp"
 #include "util.hpp"
 
 #include <boost/shared_ptr.hpp>
@@ -30,14 +31,14 @@ public:
 
     ~place_tag_processor() {}
 
-    void process_tags(keyval *tags);
+    void process_tags(const taglist_t &tags);
 
     bool has_data() const { return !places.empty(); }
 
     bool has_place(const std::string &cls)
     {
-        BOOST_FOREACH(const keyval *item, places) {
-            if (cls == item->key)
+        BOOST_FOREACH(const tag &item, places) {
+            if (cls == item.key)
                 return true;
         }
 
@@ -67,7 +68,7 @@ private:
 
         std::string prefix(cls + ":name");
 
-        for (keyval *item = src->firstItem(); item; item = src->nextItem(item)) {
+        for (taglist_t::const_iterator item = src->begin(); item != src->end(); ++item) {
             if (boost::starts_with(item->key, prefix) &&
                 (item->key.length() == prefix.length()
                  || item->key[prefix.length()] == ':')) {
@@ -105,11 +106,11 @@ private:
     }
 
 
-    std::vector<const keyval *> places;
-    std::vector<const keyval *> names;
-    std::vector<const keyval *> extratags;
-    std::vector<const keyval *> address;
-    keyval *src;
+    std::vector<tag> places;
+    std::vector<const tag *> names;
+    std::vector<const tag *> extratags;
+    std::vector<const tag *> address;
+    const taglist_t *src;
     int admin_level;
     const std::string *countrycode;
     std::string housenumber;
@@ -171,34 +172,34 @@ public:
     void enqueue_relations(pending_queue_t &job_queue, osmid_t id, size_t output_id, size_t& added) {}
     int pending_relation(osmid_t id, int exists) { return 0; }
 
-    int node_add(osmid_t id, double lat, double lon, struct keyval *tags)
+    int node_add(osmid_t id, double lat, double lon, const taglist_t &tags)
     {
         return process_node(id, lat, lon, tags);
     }
 
-    int way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags)
+    int way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags)
     {
-        return process_way(id, nodes, node_count, tags);
+        return process_way(id, nodes, tags);
     }
 
-    int relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags)
+    int relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags)
     {
-        return process_relation(id, members, member_count, tags);
+        return process_relation(id, members, tags);
     }
 
-    int node_modify(osmid_t id, double lat, double lon, struct keyval *tags)
+    int node_modify(osmid_t id, double lat, double lon, const taglist_t &tags)
     {
         return process_node(id, lat, lon, tags);
     }
 
-    int way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags)
+    int way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags)
     {
-        return process_way(id, nodes, node_count, tags);
+        return process_way(id, nodes, tags);
     }
 
-    int relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags)
+    int relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags)
     {
-        return process_relation(id, members, member_count, tags);
+        return process_relation(id, members, tags);
     }
 
     int node_delete(osmid_t id)
@@ -224,15 +225,10 @@ private:
 
     void stop_copy(void);
     void delete_unused_classes(char osm_type, osmid_t osm_id);
-    void add_place(char osm_type, osmid_t osm_id, const std::string &key_class, const std::string &type,
-                   struct keyval *names, struct keyval *extratags, int adminlevel,
-                   struct keyval *housenumber, struct keyval *street, struct keyval *addr_place,
-                   const char *isin, struct keyval *postcode, struct keyval *countrycode,
-                   const std::string &wkt);
     void delete_place(char osm_type, osmid_t osm_id);
-    int process_node(osmid_t id, double lat, double lon, keyval *tags);
-    int process_way(osmid_t id, osmid_t *ndv, int ndc, keyval *tags);
-    int process_relation(osmid_t id, member *members, int member_count, keyval *tags);
+    int process_node(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int process_way(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int process_relation(osmid_t id, const memberlist_t &members, const taglist_t &tags);
     int connect();
 
     void flush_place_buffer()
@@ -260,7 +256,6 @@ private:
     struct pg_conn *ConnectionError;
 
     bool copy_active;
-    bool append_mode;
 
     std::string buffer;
     place_tag_processor places;
diff --git a/output-multi.cpp b/output-multi.cpp
index 9a0fbd9..eeb9edb 100644
--- a/output-multi.cpp
+++ b/output-multi.cpp
@@ -88,18 +88,15 @@ void output_multi_t::enqueue_ways(pending_queue_t &job_queue, osmid_t id, size_t
 }
 
 int output_multi_t::pending_way(osmid_t id, int exists) {
-    keyval tags_int;
-    osmNode *nodes_int;
-    int count_int;
+    taglist_t tags_int;
+    nodelist_t nodes_int;
     int ret = 0;
 
     // Try to fetch the way from the DB
-    if (!m_mid->ways_get(id, &tags_int, &nodes_int, &count_int)) {
+    if (!m_mid->ways_get(id, tags_int, nodes_int)) {
         // Output the way
-        ret = reprocess_way(id, nodes_int, count_int, &tags_int, exists);
-        free(nodes_int);
+        ret = reprocess_way(id, nodes_int, tags_int, exists);
     }
-    tags_int.resetList();
 
     return ret;
 }
@@ -134,17 +131,14 @@ void output_multi_t::enqueue_relations(pending_queue_t &job_queue, osmid_t id, s
 }
 
 int output_multi_t::pending_relation(osmid_t id, int exists) {
-    keyval tags_int;
-    member *members_int;
-    int count_int;
+    taglist_t tags_int;
+    memberlist_t members_int;
     int ret = 0;
 
     // Try to fetch the relation from the DB
-    if (!m_mid->relations_get(id, &members_int, &count_int, &tags_int)) {
-        ret = process_relation(id, members_int, count_int, &tags_int, exists);
-        free(members_int);
+    if (!m_mid->relations_get(id, members_int, tags_int)) {
+        ret = process_relation(id, members_int, tags_int, exists);
     }
-    tags_int.resetList();
 
     return ret;
 }
@@ -159,29 +153,29 @@ void output_multi_t::commit() {
     m_table->commit();
 }
 
-int output_multi_t::node_add(osmid_t id, double lat, double lon, struct keyval *tags) {
+int output_multi_t::node_add(osmid_t id, double lat, double lon, const taglist_t &tags) {
     if (m_processor->interests(geometry_processor::interest_node)) {
         return process_node(id, lat, lon, tags);
     }
     return 0;
 }
 
-int output_multi_t::way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) {
-    if (m_processor->interests(geometry_processor::interest_way) && node_count > 1) {
-        return process_way(id, nodes, node_count, tags);
+int output_multi_t::way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags) {
+    if (m_processor->interests(geometry_processor::interest_way) && nodes.size() > 1) {
+        return process_way(id, nodes, tags);
     }
     return 0;
 }
 
 
-int output_multi_t::relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags) {
-    if (m_processor->interests(geometry_processor::interest_relation) && member_count > 0) {
-        return process_relation(id, members, member_count, tags, 0);
+int output_multi_t::relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags) {
+    if (m_processor->interests(geometry_processor::interest_relation) && !members.empty()) {
+        return process_relation(id, members, tags, 0);
     }
     return 0;
 }
 
-int output_multi_t::node_modify(osmid_t id, double lat, double lon, struct keyval *tags) {
+int output_multi_t::node_modify(osmid_t id, double lat, double lon, const taglist_t &tags) {
     if (m_processor->interests(geometry_processor::interest_node)) {
         // TODO - need to know it's a node?
         delete_from_output(id);
@@ -195,28 +189,28 @@ int output_multi_t::node_modify(osmid_t id, double lat, double lon, struct keyva
     }
 }
 
-int output_multi_t::way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) {
+int output_multi_t::way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags) {
     if (m_processor->interests(geometry_processor::interest_way)) {
         // TODO - need to know it's a way?
         delete_from_output(id);
 
         // TODO: need to mark any relations using it - depends on what
         // type of output this is... delegate to the geometry processor??
-        return process_way(id, nodes, node_count, tags);
+        return process_way(id, nodes, tags);
 
     } else {
         return 0;
     }
 }
 
-int output_multi_t::relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags) {
+int output_multi_t::relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags) {
     if (m_processor->interests(geometry_processor::interest_relation)) {
         // TODO - need to know it's a relation?
         delete_from_output(-id);
 
         // TODO: need to mark any other relations using it - depends on what
         // type of output this is... delegate to the geometry processor??
-        return process_relation(id, members, member_count, tags, false);
+        return process_relation(id, members, tags, false);
 
     } else {
         return 0;
@@ -247,21 +241,22 @@ int output_multi_t::relation_delete(osmid_t id) {
     return 0;
 }
 
-int output_multi_t::process_node(osmid_t id, double lat, double lon, struct keyval *tags) {
+int output_multi_t::process_node(osmid_t id, double lat, double lon, const taglist_t &tags) {
     //check if we are keeping this node
-    unsigned int filter = m_tagtransform->filter_node_tags(tags, m_export_list.get(), true);
+    taglist_t outtags;
+    unsigned int filter = m_tagtransform->filter_node_tags(tags, *m_export_list.get(), outtags, true);
     if (!filter) {
         //grab its geom
         geometry_builder::maybe_wkt_t wkt = m_processor->process_node(lat, lon);
         if (wkt) {
             m_expire->from_bbox(lon, lat, lon, lat);
-            copy_to_table(id, wkt->geom.c_str(), tags);
+            copy_to_table(id, wkt->geom.c_str(), outtags);
         }
     }
     return 0;
 }
 
-int output_multi_t::reprocess_way(osmid_t id, const osmNode* nodes, int node_count, struct keyval *tags, bool exists)
+int output_multi_t::reprocess_way(osmid_t id, const nodelist_t &nodes, const taglist_t &tags, bool exists)
 {
     //if the way could exist already we have to make the relation pending and reprocess it later
     //but only if we actually care about relations
@@ -275,34 +270,38 @@ int output_multi_t::reprocess_way(osmid_t id, const osmNode* nodes, int node_cou
 
     //check if we are keeping this way
     int polygon = 0, roads = 0;
-    unsigned int filter = m_tagtransform->filter_way_tags(tags, &polygon, &roads, m_export_list.get(), true);
+    taglist_t outtags;
+    unsigned int filter = m_tagtransform->filter_way_tags(tags, &polygon, &roads,
+                                                          *m_export_list.get(), outtags, true);
     if (!filter) {
         //grab its geom
-        geometry_builder::maybe_wkt_t wkt = m_processor->process_way(nodes, node_count);
+        geometry_builder::maybe_wkt_t wkt = m_processor->process_way(nodes);
         if (wkt) {
             //TODO: need to know if we care about polygons or lines for this output
             //the difference only being that if its a really large bbox for the poly
             //it downgrades to just invalidating the line/perimeter anyway
             if(boost::starts_with(wkt->geom, "POLYGON") || boost::starts_with(wkt->geom, "MULTIPOLYGON"))
-                m_expire->from_nodes_poly(nodes, node_count, id);
+                m_expire->from_nodes_poly(nodes, id);
             else
-                m_expire->from_nodes_line(nodes, node_count);
-            copy_to_table(id, wkt->geom.c_str(), tags);
+                m_expire->from_nodes_line(nodes);
+            copy_to_table(id, wkt->geom.c_str(), outtags);
         }
     }
     return 0;
 }
 
-int output_multi_t::process_way(osmid_t id, const osmid_t* node_ids, int node_count, struct keyval *tags) {
+int output_multi_t::process_way(osmid_t id, const idlist_t &nodes, const taglist_t &tags) {
     //check if we are keeping this way
     int polygon = 0, roads = 0;
-    unsigned int filter = m_tagtransform->filter_way_tags(tags, &polygon, &roads, m_export_list.get(), true);
+    taglist_t outtags;
+    unsigned filter = m_tagtransform->filter_way_tags(tags, &polygon, &roads,
+                                                      *m_export_list.get(), outtags, true);
     if (!filter) {
         //get the geom from the middle
-        if(m_way_helper.set(node_ids, node_count, m_mid) < 1)
+        if(m_way_helper.set(nodes, m_mid) < 1)
             return 0;
         //grab its geom
-        geometry_builder::maybe_wkt_t wkt = m_processor->process_way(&m_way_helper.node_cache.front(), m_way_helper.node_cache.size());
+        geometry_builder::maybe_wkt_t wkt = m_processor->process_way(m_way_helper.node_cache);
 
         if (wkt) {
             //if we are also interested in relations we need to mark
@@ -315,27 +314,30 @@ int output_multi_t::process_way(osmid_t id, const osmid_t* node_ids, int node_co
                 //the difference only being that if its a really large bbox for the poly
                 //it downgrades to just invalidating the line/perimeter anyway
                 if(boost::starts_with(wkt->geom, "POLYGON") || boost::starts_with(wkt->geom, "MULTIPOLYGON"))
-                    m_expire->from_nodes_poly(&m_way_helper.node_cache.front(), m_way_helper.node_cache.size(), id);
+                    m_expire->from_nodes_poly(m_way_helper.node_cache, id);
                 else
-                    m_expire->from_nodes_line(&m_way_helper.node_cache.front(), m_way_helper.node_cache.size());
-                copy_to_table(id, wkt->geom.c_str(), tags);
+                    m_expire->from_nodes_line(m_way_helper.node_cache);
+                copy_to_table(id, wkt->geom.c_str(), outtags);
             }
         }
     }
     return 0;
 }
 
-int output_multi_t::process_relation(osmid_t id, const member *members, int member_count, keyval *tags, bool exists, bool pending) {
+int output_multi_t::process_relation(osmid_t id, const memberlist_t &members,
+                                     const taglist_t &tags, bool exists, bool pending) {
     //if it may exist already, delete it first
     if(exists)
         relation_delete(id);
 
     //does this relation have anything interesting to us
-    unsigned int filter = m_tagtransform->filter_rel_tags(tags, m_export_list.get(), true);
+    taglist_t rel_outtags;
+    unsigned filter = m_tagtransform->filter_rel_tags(tags, *m_export_list.get(),
+                                                      rel_outtags, true);
     if (!filter) {
         //TODO: move this into geometry processor, figure a way to come back for tag transform
         //grab ways/nodes of the members in the relation, bail if none were used
-        if(m_relation_helper.set(members, member_count, (middle_t*)m_mid) < 1)
+        if(m_relation_helper.set(&members, (middle_t*)m_mid) < 1)
             return 0;
 
         //filter the tags on each member because we got them from the middle
@@ -343,9 +345,11 @@ int output_multi_t::process_relation(osmid_t id, const member *members, int memb
         //shares any kind of tag transform and therefore has all original tags
         //so we filter here because each individual outputs cares about different tags
         int polygon, roads;
-        for(size_t i = 0; i < m_relation_helper.way_count; ++i)
+        multitaglist_t filtered(m_relation_helper.tags.size(), taglist_t());
+        for(size_t i = 0; i < m_relation_helper.tags.size(); ++i)
         {
-            m_tagtransform->filter_way_tags(&m_relation_helper.tags[i], &polygon, &roads, m_export_list.get());
+            m_tagtransform->filter_way_tags(m_relation_helper.tags[i], &polygon,
+                                            &roads, *m_export_list.get(), filtered[i]);
             //TODO: if the filter says that this member is now not interesting we
             //should decrement the count and remove his nodes and tags etc. for
             //now we'll just keep him with no tags so he will get filtered later
@@ -359,12 +363,14 @@ int output_multi_t::process_relation(osmid_t id, const member *members, int memb
         //TODO: find a less hacky way to do the matching/superseeded and tag copying stuff without
         //all this trickery
         int make_boundary, make_polygon = 1;
-        filter = m_tagtransform->filter_rel_member_tags(tags, m_relation_helper.way_count, &m_relation_helper.tags.front(),
-                                                   &m_relation_helper.roles.front(), &m_relation_helper.superseeded.front(),
-                                                   &make_boundary, &make_polygon, &roads, m_export_list.get(), true);
-        if(!filter)
+        taglist_t outtags;
+        filter = m_tagtransform->filter_rel_member_tags(rel_outtags, filtered, m_relation_helper.roles,
+                                                        &m_relation_helper.superseeded.front(),
+                                                        &make_boundary, &make_polygon, &roads,
+                                                        *m_export_list.get(), outtags, true);
+        if (!filter)
         {
-            geometry_builder::maybe_wkts_t wkts = m_processor->process_relation(&m_relation_helper.nodes.front(), &m_relation_helper.node_counts.front());
+            geometry_builder::maybe_wkts_t wkts = m_processor->process_relation(m_relation_helper.nodes);
             if (wkts) {
                 for(geometry_builder::wkt_itr wkt = wkts->begin(); wkt != wkts->end(); ++wkt)
                 {
@@ -372,13 +378,13 @@ int output_multi_t::process_relation(osmid_t id, const member *members, int memb
                     //instead of having to reparse the wkt in the expiry code
                     m_expire->from_wkt(wkt->geom.c_str(), -id);
                     //what part of the code relies on relation members getting negative ids?
-                    copy_to_table(-id, wkt->geom.c_str(), tags);
+                    copy_to_table(-id, wkt->geom.c_str(), outtags);
                 }
             }
 
             //TODO: should this loop be inside the if above just in case?
             //take a look at each member to see if its superseeded (tags on it matched the tags on the relation)
-            for(size_t i = 0; i < m_relation_helper.way_count; ++i) {
+            for(size_t i = 0; i < m_relation_helper.ways.size(); ++i) {
                 //tags matched so we are keeping this one with this relation
                 if (m_relation_helper.superseeded[i]) {
                     //just in case it wasnt previously with this relation we get rid of them
@@ -395,7 +401,7 @@ int output_multi_t::process_relation(osmid_t id, const member *members, int memb
     return 0;
 }
 
-void output_multi_t::copy_to_table(osmid_t id, const char *wkt, struct keyval *tags) {
+void output_multi_t::copy_to_table(osmid_t id, const char *wkt, const taglist_t &tags) {
     m_table->write_wkt(id, tags, wkt);
 }
 
diff --git a/output-multi.hpp b/output-multi.hpp
index 021f97a..fd4f4d9 100644
--- a/output-multi.hpp
+++ b/output-multi.hpp
@@ -39,13 +39,13 @@ public:
     void enqueue_relations(pending_queue_t &job_queue, osmid_t id, size_t output_id, size_t& added);
     int pending_relation(osmid_t id, int exists);
 
-    int node_add(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_add(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
-    int node_modify(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_modify(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
     int node_delete(osmid_t id);
     int way_delete(osmid_t id);
@@ -61,11 +61,11 @@ public:
 protected:
 
     void delete_from_output(osmid_t id);
-    int process_node(osmid_t id, double lat, double lon, struct keyval *tags);
-    int process_way(osmid_t id, const osmid_t* node_ids, int node_count, struct keyval *tags);
-    int reprocess_way(osmid_t id, const osmNode* nodes, int node_count, struct keyval *tags, bool exists);
-    int process_relation(osmid_t id, const member *members, int member_count, struct keyval *tags, bool exists, bool pending=false);
-    void copy_to_table(osmid_t id, const char *wkt, struct keyval *tags);
+    int process_node(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int process_way(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int reprocess_way(osmid_t id, const nodelist_t &nodes, const taglist_t &tags, bool exists);
+    int process_relation(osmid_t id, const memberlist_t &members, const taglist_t &tags, bool exists, bool pending=false);
+    void copy_to_table(osmid_t id, const char *wkt, const taglist_t &tags);
 
     boost::scoped_ptr<tagtransform> m_tagtransform;
     boost::scoped_ptr<export_list> m_export_list;
diff --git a/output-null.cpp b/output-null.cpp
index 5d73859..5cd18e2 100644
--- a/output-null.cpp
+++ b/output-null.cpp
@@ -38,15 +38,15 @@ int output_null_t::pending_relation(osmid_t id, int exists) {
     return 0;
 }
 
-int output_null_t::node_add(osmid_t a, double b, double c, struct keyval *k) {
+int output_null_t::node_add(osmid_t, double, double, const taglist_t &) {
   return 0;
 }
 
-int output_null_t::way_add(osmid_t a, osmid_t *b, int c, struct keyval *k) {
+int output_null_t::way_add(osmid_t a, const idlist_t &, const taglist_t &) {
   return 0;
 }
 
-int output_null_t::relation_add(osmid_t a, struct member *b, int c, struct keyval *k) {
+int output_null_t::relation_add(osmid_t a, const memberlist_t &, const taglist_t &) {
   return 0;
 }
 
@@ -62,15 +62,15 @@ int output_null_t::relation_delete(osmid_t i) {
   return 0;
 }
 
-int output_null_t::node_modify(osmid_t a, double b, double c, struct keyval * k) {
+int output_null_t::node_modify(osmid_t, double, double, const taglist_t &) {
   return 0;
 }
 
-int output_null_t::way_modify(osmid_t a, osmid_t *b, int c, struct keyval *k) {
+int output_null_t::way_modify(osmid_t, const idlist_t &, const taglist_t &) {
   return 0;
 }
 
-int output_null_t::relation_modify(osmid_t a, struct member *b, int c, struct keyval *k) {
+int output_null_t::relation_modify(osmid_t, const memberlist_t &, const taglist_t &) {
   return 0;
 }
 
diff --git a/output-null.hpp b/output-null.hpp
index e735aed..7b0c8f9 100644
--- a/output-null.hpp
+++ b/output-null.hpp
@@ -25,13 +25,13 @@ public:
     void enqueue_relations(pending_queue_t &job_queue, osmid_t id, size_t output_id, size_t& added);
     int pending_relation(osmid_t id, int exists);
 
-    int node_add(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_add(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
-    int node_modify(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_modify(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
     int node_delete(osmid_t id);
     int way_delete(osmid_t id);
diff --git a/output-pgsql.cpp b/output-pgsql.cpp
index 0a5939d..b75ab82 100644
--- a/output-pgsql.cpp
+++ b/output-pgsql.cpp
@@ -75,33 +75,18 @@ psql - 01 01000020 E6100000 30CCA462B6C3D4BF92998C9B38E04940
 Workaround - output SRID=4326;<WKB>
 */
 
-int output_pgsql_t::pgsql_out_node(osmid_t id, struct keyval *tags, double node_lat, double node_lon)
+int output_pgsql_t::pgsql_out_node(osmid_t id, const taglist_t &tags, double node_lat, double node_lon)
 {
-
-    int filter = m_tagtransform->filter_node_tags(tags, m_export_list.get());
-
-    if (filter) return 1;
+    taglist_t outtags;
+    if (m_tagtransform->filter_node_tags(tags, *m_export_list.get(), outtags))
+        return 1;
 
     expire->from_bbox(node_lon, node_lat, node_lon, node_lat);
-    m_tables[t_point]->write_node(id, tags, node_lat, node_lon);
+    m_tables[t_point]->write_node(id, outtags, node_lat, node_lon);
 
     return 0;
 }
 
-/*static int tag_indicates_polygon(enum OsmType type, const char *key)
-{
-    int i;
-
-    if (!strcmp(key, "area"))
-        return 1;
-
-    for (i=0; i < exportListCount[type]; i++) {
-        if( strcmp( exportList[type][i].name, key ) == 0 )
-            return exportList[type][i].flags & FLAG_POLYGON;
-    }
-
-    return 0;
-}*/
 
 /*
 COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
@@ -111,25 +96,27 @@ E4C1421D5BF24D06053E7DF4940
 212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
 5BF5BB39597FCDF4940
 */
-int output_pgsql_t::pgsql_out_way(osmid_t id, struct keyval *tags, const struct osmNode *nodes, int count, int exists)
+int output_pgsql_t::pgsql_out_way(osmid_t id, const taglist_t &tags, const nodelist_t &nodes, int exists)
 {
     int polygon = 0, roads = 0;
     double split_at;
 
     /* If the flag says this object may exist already, delete it first */
-    if(exists) {
+    if (exists) {
         pgsql_delete_way_from_output(id);
         // TODO: this now only has an effect when called from the iterate_ways
         // call-back, so we need some alternative way to trigger this within
         // osmdata_t.
-        const std::vector<osmid_t> rel_ids = m_mid->relations_using_way(id);
-        for (std::vector<osmid_t>::const_iterator itr = rel_ids.begin();
+        const idlist_t rel_ids = m_mid->relations_using_way(id);
+        for (idlist_t::const_iterator itr = rel_ids.begin();
              itr != rel_ids.end(); ++itr) {
             rels_pending_tracker->mark(*itr);
         }
     }
 
-    if (m_tagtransform->filter_way_tags(tags, &polygon, &roads, m_export_list.get()))
+    taglist_t outtags;
+    if (m_tagtransform->filter_way_tags(tags, &polygon, &roads, *m_export_list.get(),
+                                        outtags))
         return 0;
     /* Split long ways after around 1 degree or 100km */
     if (m_options.projection->get_proj_id() == PROJ_LATLONG)
@@ -137,45 +124,53 @@ int output_pgsql_t::pgsql_out_way(osmid_t id, struct keyval *tags, const struct
     else
         split_at = 100 * 1000;
 
-    geometry_builder::maybe_wkts_t wkts = builder.get_wkt_split(nodes, count, polygon, split_at);
-    for(geometry_builder::wkt_itr wkt = wkts->begin(); wkt != wkts->end(); ++wkt)
-    {
+    tag *areatag = 0;
+    geometry_builder::maybe_wkts_t wkts = builder.get_wkt_split(nodes, polygon, split_at);
+    for(geometry_builder::wkt_itr wkt = wkts->begin(); wkt != wkts->end(); ++wkt) {
         /* FIXME: there should be a better way to detect polygons */
         if (boost::starts_with(wkt->geom, "POLYGON") || boost::starts_with(wkt->geom, "MULTIPOLYGON")) {
-            expire->from_nodes_poly(nodes, count, id);
+            expire->from_nodes_poly(nodes, id);
             if ((wkt->area > 0.0) && m_enable_way_area) {
                 char tmp[32];
                 snprintf(tmp, sizeof(tmp), "%g", wkt->area);
-                tags->addItem("way_area", tmp, false);
+                if (!areatag) {
+                    outtags.push_dedupe(tag("way_area", tmp));
+                    areatag = outtags.find("way_area");
+                } else
+                    areatag->value = tmp;
             }
-            m_tables[t_poly]->write_wkt(id, tags, wkt->geom.c_str());
+            m_tables[t_poly]->write_wkt(id, outtags, wkt->geom.c_str());
         } else {
-            expire->from_nodes_line(nodes, count);
-            m_tables[t_line]->write_wkt(id, tags, wkt->geom.c_str());
+            expire->from_nodes_line(nodes);
+            m_tables[t_line]->write_wkt(id, outtags, wkt->geom.c_str());
             if (roads)
-                m_tables[t_roads]->write_wkt(id, tags, wkt->geom.c_str());
+                m_tables[t_roads]->write_wkt(id, outtags, wkt->geom.c_str());
         }
     }
 
     return 0;
 }
 
-int output_pgsql_t::pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int member_count, const struct osmNode * const *xnodes, struct keyval *xtags, const int *xcount, const osmid_t *xid, const char * const *xrole, bool pending)
+int output_pgsql_t::pgsql_out_relation(osmid_t id, const taglist_t &rel_tags,
+                           const multinodelist_t &xnodes, const multitaglist_t & xtags,
+                           const idlist_t &xid, const rolelist_t &xrole,
+                           bool pending)
 {
-    if (member_count == 0)
+    if (xnodes.empty())
         return 0;
 
     int roads = 0;
     int make_polygon = 0;
     int make_boundary = 0;
-    int * members_superseeded;
     double split_at;
 
-    members_superseeded = (int *)calloc(sizeof(int), member_count);
+    std::vector<int> members_superseeded(xnodes.size(), 0);
+    taglist_t outtags;
 
     //if its a route relation make_boundary and make_polygon will be false otherwise one or the other will be true
-    if (m_tagtransform->filter_rel_member_tags(rel_tags, member_count, xtags, xrole, members_superseeded, &make_boundary, &make_polygon, &roads, m_export_list.get())) {
-        free(members_superseeded);
+    if (m_tagtransform->filter_rel_member_tags(rel_tags, xtags, xrole, 
+              &(members_superseeded[0]), &make_boundary, &make_polygon, &roads,
+              *m_export_list.get(), outtags)) {
         return 0;
     }
 
@@ -187,13 +182,13 @@ int output_pgsql_t::pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int
 
     //this will either make lines or polygons (unless the lines arent a ring or are less than 3 pts) depending on the tag transform above
     //TODO: pick one or the other based on which we expect to care about
-    geometry_builder::maybe_wkts_t wkts  = builder.build_both(xnodes, xcount, make_polygon, m_options.enable_multi, split_at, id);
+    geometry_builder::maybe_wkts_t wkts  = builder.build_both(xnodes, make_polygon, m_options.enable_multi, split_at, id);
 
     if (!wkts->size()) {
-        free(members_superseeded);
         return 0;
     }
 
+    tag *areatag = 0;
     for(geometry_builder::wkt_itr wkt = wkts->begin(); wkt != wkts->end(); ++wkt)
     {
         expire->from_wkt(wkt->geom.c_str(), -id);
@@ -202,13 +197,17 @@ int output_pgsql_t::pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int
             if ((wkt->area > 0.0) && m_enable_way_area) {
                 char tmp[32];
                 snprintf(tmp, sizeof(tmp), "%g", wkt->area);
-                rel_tags->addItem("way_area", tmp, false);
+                if (!areatag) {
+                    outtags.push_dedupe(tag("way_area", tmp));
+                    areatag = outtags.find("way_area");
+                } else
+                    areatag->value = tmp;
             }
-            m_tables[t_poly]->write_wkt(-id, rel_tags, wkt->geom.c_str());
+            m_tables[t_poly]->write_wkt(-id, outtags, wkt->geom.c_str());
         } else {
-            m_tables[t_line]->write_wkt(-id, rel_tags, wkt->geom.c_str());
+            m_tables[t_line]->write_wkt(-id, outtags, wkt->geom.c_str());
             if (roads)
-                m_tables[t_roads]->write_wkt(-id, rel_tags, wkt->geom.c_str());
+                m_tables[t_roads]->write_wkt(-id, outtags, wkt->geom.c_str());
         }
     }
 
@@ -218,7 +217,7 @@ int output_pgsql_t::pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int
      * have duplicates */
     //dont do this when working with pending relations as its not needed
     if (make_polygon) {
-        for (int i=0; xcount[i]; i++) {
+        for (size_t i=0; i < xid.size(); i++) {
             if (members_superseeded[i]) {
                 pgsql_delete_way_from_output(xid[i]);
                 if(!pending)
@@ -227,22 +226,25 @@ int output_pgsql_t::pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int
         }
     }
 
-    free(members_superseeded);
-
     // If the tag transform said the polygon looked like a boundary we want to make that as well
     // If we are making a boundary then also try adding any relations which form complete rings
     // The linear variants will have already been processed above
     if (make_boundary) {
-        wkts = builder.build_polygons(xnodes, xcount, m_options.enable_multi, id);
+        tag *areatag = 0;
+        wkts = builder.build_polygons(xnodes, m_options.enable_multi, id);
         for(geometry_builder::wkt_itr wkt = wkts->begin(); wkt != wkts->end(); ++wkt)
         {
             expire->from_wkt(wkt->geom.c_str(), -id);
             if ((wkt->area > 0.0) && m_enable_way_area) {
                 char tmp[32];
                 snprintf(tmp, sizeof(tmp), "%g", wkt->area);
-                rel_tags->addItem("way_area", tmp, false);
+                if (!areatag) {
+                    outtags.push_dedupe(tag("way_area", tmp));
+                    areatag = outtags.find("way_area");
+                } else
+                    areatag->value = tmp;
             }
-            m_tables[t_poly]->write_wkt(-id, rel_tags, wkt->geom.c_str());
+            m_tables[t_poly]->write_wkt(-id, outtags, wkt->geom.c_str());
         }
     }
 
@@ -270,7 +272,7 @@ extern "C" void *pthread_output_pgsql_stop_one(void *arg) {
   }
 
   return NULL;
-};
+}
 } // anonymous namespace
 
 void output_pgsql_t::enqueue_ways(pending_queue_t &job_queue, osmid_t id, size_t output_id, size_t& added) {
@@ -305,19 +307,16 @@ void output_pgsql_t::enqueue_ways(pending_queue_t &job_queue, osmid_t id, size_t
 }
 
 int output_pgsql_t::pending_way(osmid_t id, int exists) {
-    keyval tags_int;
-    osmNode *nodes_int;
-    int count_int;
+    taglist_t tags_int;
+    nodelist_t nodes_int;
     int ret = 0;
 
     // Try to fetch the way from the DB
-    if (!m_mid->ways_get(id, &tags_int, &nodes_int, &count_int)) {
+    if (!m_mid->ways_get(id, tags_int, nodes_int)) {
         // Output the way
         //ret = reprocess_way(id, nodes_int, count_int, &tags_int, exists);
-        ret = pgsql_out_way(id, &tags_int, nodes_int, count_int, exists);
-        free(nodes_int);
+        ret = pgsql_out_way(id, tags_int, nodes_int, exists);
     }
-    tags_int.resetList();
 
     return ret;
 }
@@ -352,17 +351,14 @@ void output_pgsql_t::enqueue_relations(pending_queue_t &job_queue, osmid_t id, s
 }
 
 int output_pgsql_t::pending_relation(osmid_t id, int exists) {
-    keyval tags_int;
-    member *members_int;
-    int count_int;
+    taglist_t tags_int;
+    memberlist_t members_int;
     int ret = 0;
 
     // Try to fetch the relation from the DB
-    if (!m_mid->relations_get(id, &members_int, &count_int, &tags_int)) {
-        ret = pgsql_process_relation(id, members_int, count_int, &tags_int, exists, true);
-        free(members_int);
+    if (!m_mid->relations_get(id, members_int, tags_int)) {
+        ret = pgsql_process_relation(id, members_int, tags_int, exists, true);
     }
-    tags_int.resetList();
 
     return ret;
 }
@@ -431,21 +427,21 @@ void output_pgsql_t::stop()
     expire.reset();
 }
 
-int output_pgsql_t::node_add(osmid_t id, double lat, double lon, struct keyval *tags)
+int output_pgsql_t::node_add(osmid_t id, double lat, double lon, const taglist_t &tags)
 {
   pgsql_out_node(id, tags, lat, lon);
 
   return 0;
 }
 
-int output_pgsql_t::way_add(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags)
+int output_pgsql_t::way_add(osmid_t id, const idlist_t &nds, const taglist_t &tags)
 {
   int polygon = 0;
   int roads = 0;
-
+  taglist_t outtags;
 
   /* Check whether the way is: (1) Exportable, (2) Maybe a polygon */
-  int filter = m_tagtransform->filter_way_tags(tags, &polygon, &roads, m_export_list.get());
+  int filter = m_tagtransform->filter_way_tags(tags, &polygon, &roads, *m_export_list.get(), outtags);
 
   /* If this isn't a polygon then it can not be part of a multipolygon
      Hence only polygons are "pending" */
@@ -454,91 +450,72 @@ int output_pgsql_t::way_add(osmid_t id, osmid_t *nds, int nd_count, struct keyva
   if( !polygon && !filter )
   {
     /* Get actual node data and generate output */
-    struct osmNode *nodes = (struct osmNode *)malloc( sizeof(struct osmNode) * nd_count );
-    int count = m_mid->nodes_get_list( nodes, nds, nd_count );
-    pgsql_out_way(id, tags, nodes, count, 0);
-    free(nodes);
+    nodelist_t nodes;
+    m_mid->nodes_get_list(nodes, nds);
+    pgsql_out_way(id, outtags, nodes, 0);
   }
   return 0;
 }
 
 
 /* This is the workhorse of pgsql_add_relation, split out because it is used as the callback for iterate relations */
-int output_pgsql_t::pgsql_process_relation(osmid_t id, const struct member *members, int member_count, struct keyval *tags, int exists, bool pending)
+int output_pgsql_t::pgsql_process_relation(osmid_t id, const memberlist_t &members,
+                                           const taglist_t &tags, int exists, bool pending)
 {
-    int i, j, count, count2;
-
   /* If the flag says this object may exist already, delete it first */
   if(exists)
       pgsql_delete_relation_from_output(id);
 
-  if (m_tagtransform->filter_rel_tags(tags, m_export_list.get())) {
+  taglist_t outtags;
+
+  if (m_tagtransform->filter_rel_tags(tags, *m_export_list.get(), outtags))
       return 1;
-  }
 
-  osmid_t *xid2 = (osmid_t *)malloc( (member_count+1) * sizeof(osmid_t) );
-  const char **xrole = (const char **)malloc( (member_count+1) * sizeof(const char *) );
-  int *xcount = (int *)malloc( (member_count+1) * sizeof(int) );
-  keyval *xtags  = new keyval[member_count+1];
-  struct osmNode **xnodes = (struct osmNode **)malloc( (member_count+1) * sizeof(struct osmNode*) );
+  idlist_t xid2;
+  multitaglist_t xtags2;
+  multinodelist_t xnodes;
 
-  count = 0;
-  for( i=0; i<member_count; i++ )
+  for (memberlist_t::const_iterator it = members.begin(); it != members.end(); ++it)
   {
     /* Need to handle more than just ways... */
-    if( members[i].type != OSMTYPE_WAY )
-        continue;
-    xid2[count] = members[i].id;
-    count++;
+    if (it->type == OSMTYPE_WAY)
+        xid2.push_back(it->id);
   }
 
-  osmid_t *xid = (osmid_t *)malloc( sizeof(osmid_t) * (count + 1));
-  count2 = m_mid->ways_get_list(xid2, count, xid, xtags, xnodes, xcount);
-  int polygon = 0, roads = 0;;
+  idlist_t xid;
+  m_mid->ways_get_list(xid2, xid, xtags2, xnodes);
+  int polygon = 0, roads = 0;
+  multitaglist_t xtags(xid.size(), taglist_t());
+  rolelist_t xrole(xid.size(), 0);
 
-  for (i = 0; i < count2; i++) {
-      for (j = i; j < member_count; j++) {
+  for (size_t i = 0; i < xid.size(); i++) {
+      for (size_t j = i; j < members.size(); j++) {
           if (members[j].id == xid[i]) {
               //filter the tags on this member because we got it from the middle
               //and since the middle is no longer tied to the output it no longer
               //shares any kind of tag transform and therefore all original tags
               //will come back and need to be filtered by individual outputs before
               //using these ways
-              m_tagtransform->filter_way_tags(&xtags[i], &polygon, &roads, m_export_list.get());
+              m_tagtransform->filter_way_tags(xtags2[i], &polygon, &roads,
+                                              *m_export_list.get(), xtags[i]);
               //TODO: if the filter says that this member is now not interesting we
               //should decrement the count and remove his nodes and tags etc. for
               //now we'll just keep him with no tags so he will get filtered later
+              xrole[i] = &members[j].role;
               break;
           }
       }
-      xrole[i] = members[j].role;
   }
-  xnodes[count2] = NULL;
-  xcount[count2] = 0;
-  xid[count2] = 0;
-  xrole[count2] = NULL;
 
   /* At some point we might want to consider storing the retrieved data in the members, rather than as separate arrays */
-  pgsql_out_relation(id, tags, count2, xnodes, xtags, xcount, xid, xrole, pending);
-
-  for( i=0; i<count2; i++ )
-  {
-    xtags[i].resetList();
-    free( xnodes[i] );
-  }
+  pgsql_out_relation(id, outtags, xnodes, xtags, xid, xrole, pending);
 
-  free(xid2);
-  free(xid);
-  free(xrole);
-  free(xcount);
-  delete [] xtags;
-  free(xnodes);
   return 0;
 }
 
-int output_pgsql_t::relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags)
+int output_pgsql_t::relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags)
 {
-  const std::string *type = tags->getItem("type");
+  const std::string *type = tags.get("type");
 
   /* Must have a type field or we ignore it */
   if (!type)
@@ -549,7 +526,7 @@ int output_pgsql_t::relation_add(osmid_t id, struct member *members, int member_
     return 0;
 
 
-  return pgsql_process_relation(id, members, member_count, tags, 0);
+  return pgsql_process_relation(id, members, tags, 0);
 }
 
 /* Delete is easy, just remove all traces of this object. We don't need to
@@ -623,7 +600,7 @@ int output_pgsql_t::relation_delete(osmid_t osm_id)
 /* Modify is slightly trickier. The basic idea is we simply delete the
  * object and create it with the new parameters. Then we need to mark the
  * objects that depend on this one */
-int output_pgsql_t::node_modify(osmid_t osm_id, double lat, double lon, struct keyval *tags)
+int output_pgsql_t::node_modify(osmid_t osm_id, double lat, double lon, const taglist_t &tags)
 {
     if( !m_options.slim )
     {
@@ -635,7 +612,7 @@ int output_pgsql_t::node_modify(osmid_t osm_id, double lat, double lon, struct k
     return 0;
 }
 
-int output_pgsql_t::way_modify(osmid_t osm_id, osmid_t *nodes, int node_count, struct keyval *tags)
+int output_pgsql_t::way_modify(osmid_t osm_id, const idlist_t &nodes, const taglist_t &tags)
 {
     if( !m_options.slim )
     {
@@ -643,12 +620,12 @@ int output_pgsql_t::way_modify(osmid_t osm_id, osmid_t *nodes, int node_count, s
         util::exit_nicely();
     }
     way_delete(osm_id);
-    way_add(osm_id, nodes, node_count, tags);
+    way_add(osm_id, nodes, tags);
 
     return 0;
 }
 
-int output_pgsql_t::relation_modify(osmid_t osm_id, struct member *members, int member_count, struct keyval *tags)
+int output_pgsql_t::relation_modify(osmid_t osm_id, const memberlist_t &members, const taglist_t &tags)
 {
     if( !m_options.slim )
     {
@@ -656,7 +633,7 @@ int output_pgsql_t::relation_modify(osmid_t osm_id, struct member *members, int
         util::exit_nicely();
     }
     relation_delete(osm_id);
-    relation_add(osm_id, members, member_count, tags);
+    relation_add(osm_id, members, tags);
     return 0;
 }
 
@@ -696,7 +673,7 @@ output_pgsql_t::output_pgsql_t(const middle_query_t* mid_, const options_t &opti
     try {
         m_tagtransform.reset(new tagtransform(&m_options));
     }
-    catch(std::runtime_error& e) {
+    catch(const std::runtime_error& e) {
         fprintf(stderr, "%s\n", e.what());
         fprintf(stderr, "Error: Failed to initialise tag processing.\n");
         util::exit_nicely();
@@ -753,8 +730,8 @@ output_pgsql_t::output_pgsql_t(const middle_query_t* mid_, const options_t &opti
 output_pgsql_t::output_pgsql_t(const output_pgsql_t& other):
     output_t(other.m_mid, other.m_options), m_tagtransform(new tagtransform(&m_options)), m_enable_way_area(other.m_enable_way_area),
     m_export_list(new export_list(*other.m_export_list)), reproj(other.reproj),
-    ways_pending_tracker(new id_tracker()), ways_done_tracker(new id_tracker()), rels_pending_tracker(new id_tracker()),
-    expire(new expire_tiles(&m_options))
+    expire(new expire_tiles(&m_options)),
+    ways_pending_tracker(new id_tracker()), ways_done_tracker(new id_tracker()), rels_pending_tracker(new id_tracker())
 {
     builder.set_exclude_broken_polygon(m_options.excludepoly);
     for(std::vector<boost::shared_ptr<table_t> >::const_iterator t = other.m_tables.begin(); t != other.m_tables.end(); ++t) {
diff --git a/output-pgsql.hpp b/output-pgsql.hpp
index fb438f8..59a3567 100644
--- a/output-pgsql.hpp
+++ b/output-pgsql.hpp
@@ -39,13 +39,13 @@ public:
     void enqueue_relations(pending_queue_t &job_queue, osmid_t id, size_t output_id, size_t& added);
     int pending_relation(osmid_t id, int exists);
 
-    int node_add(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_add(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
-    int node_modify(osmid_t id, double lat, double lon, struct keyval *tags);
-    int way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags);
-    int relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags);
+    int node_modify(osmid_t id, double lat, double lon, const taglist_t &tags);
+    int way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags);
+    int relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags);
 
     int node_delete(osmid_t id);
     int way_delete(osmid_t id);
@@ -60,10 +60,13 @@ public:
 
 protected:
 
-    int pgsql_out_node(osmid_t id, struct keyval *tags, double node_lat, double node_lon);
-    int pgsql_out_way(osmid_t id, struct keyval *tags, const struct osmNode *nodes, int count, int exists);
-    int pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int member_count, const struct osmNode * const * xnodes, struct keyval *xtags, const int *xcount, const osmid_t *xid, const char * const *xrole, bool pending);
-    int pgsql_process_relation(osmid_t id, const struct member *members, int member_count, struct keyval *tags, int exists, bool pending=false);
+    int pgsql_out_node(osmid_t id, const taglist_t &tags, double node_lat, double node_lon);
+    int pgsql_out_way(osmid_t id, const taglist_t &tags, const nodelist_t &nodes, int exists);
+    int pgsql_out_relation(osmid_t id, const taglist_t &rel_tags,
+                           const multinodelist_t &xnodes, const multitaglist_t & xtags,
+                           const idlist_t &xid, const rolelist_t &xrole,
+                           bool pending);
+    int pgsql_process_relation(osmid_t id, const memberlist_t &members, const taglist_t &tags, int exists, bool pending=false);
     int pgsql_delete_way_from_output(osmid_t osm_id);
     int pgsql_delete_relation_from_output(osmid_t osm_id);
 
diff --git a/output.hpp b/output.hpp
index 851cef9..2e5c7cb 100644
--- a/output.hpp
+++ b/output.hpp
@@ -19,7 +19,14 @@
 
 #include <utility>
 
-typedef std::pair<osmid_t, size_t> pending_job_t;
+struct pending_job_t {
+    osmid_t osm_id;
+    size_t  output_id;
+
+    pending_job_t() : osm_id(0), output_id(0) {}
+    pending_job_t(osmid_t id, size_t oid) : osm_id(id), output_id(oid) {}
+};
+
 #ifndef HAVE_LOCKFREE
 #include <stack>
 typedef std::stack<pending_job_t> pending_queue_t;
@@ -47,13 +54,13 @@ public:
     virtual void enqueue_relations(pending_queue_t &job_queue, osmid_t id, size_t output_id, size_t& added) = 0;
     virtual int pending_relation(osmid_t id, int exists) = 0;
 
-    virtual int node_add(osmid_t id, double lat, double lon, struct keyval *tags) = 0;
-    virtual int way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) = 0;
-    virtual int relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags) = 0;
+    virtual int node_add(osmid_t id, double lat, double lon, const taglist_t &tags) = 0;
+    virtual int way_add(osmid_t id, const idlist_t &nodes, const taglist_t &tags) = 0;
+    virtual int relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags) = 0;
 
-    virtual int node_modify(osmid_t id, double lat, double lon, struct keyval *tags) = 0;
-    virtual int way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) = 0;
-    virtual int relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags) = 0;
+    virtual int node_modify(osmid_t id, double lat, double lon, const taglist_t &tags) = 0;
+    virtual int way_modify(osmid_t id, const idlist_t &nodes, const taglist_t &tags) = 0;
+    virtual int relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags) = 0;
 
     virtual int node_delete(osmid_t id) = 0;
     virtual int way_delete(osmid_t id) = 0;
@@ -74,6 +81,4 @@ protected:
     const options_t m_options;
 };
 
-unsigned int pgsql_filter_tags(enum OsmType type, struct keyval *tags, int *polygon);
-
 #endif
diff --git a/parse-o5m.cpp b/parse-o5m.cpp
index b6fc9de..1e3ca36 100644
--- a/parse-o5m.cpp
+++ b/parse-o5m.cpp
@@ -402,7 +402,7 @@ static void read_close() {
 return;
   fd= read_infop->fd;
   if(loglevel>=1) {  /* verbose */
-      fprintf(stderr,"osm2pgsql: Number of bytes read: %"PRIu64"\n",
+      fprintf(stderr,"osm2pgsql: Number of bytes read: %" PRIu64 "\n",
       read_infop->read__counter);
     }
   if(loglevel>=2) {
@@ -647,10 +647,8 @@ int parse_o5m_t::streamFile(const char *filename, const int sanitize, osmdata_t
   /* 0: node; 1: way; 2: relation; */
   uint32_t hisver;
   int64_t histime;
-  int64_t hiscset;
   uint32_t hisuid;
   char* hisuser;
-  str_info_t* str;  /* string unit handle (if o5m format) */
   bool endoffile;
   int64_t o5id;  /* for o5m delta coding */
   int32_t o5lon,o5lat;  /* for o5m delta coding */
@@ -660,13 +658,12 @@ int parse_o5m_t::streamFile(const char *filename, const int sanitize, osmdata_t
   byte* bufp;  /* pointer in read buffer */
 #define bufsp ((char*)bufp)  /* for signed char */
   byte* bufe;  /* pointer in read buffer, end of object */
-  char c;  /* latest character which has been read */
   byte b;  /* latest byte which has been read */
   int l;
   byte* bp;
 
   /* procedure initializations */
-  str= str_open();
+  str_open();
   /* call some initialization of string read module */
   str_reset();
   o5id= 0;
@@ -715,7 +712,7 @@ return 1;
       /* get next object */
     read_input();
     bufp= read_bufp;
-    b= *bufp; c= (char)b;
+    b= *bufp;
 
     /* care about file end */
     if(read_bufp>=read_bufe)  /* at end of input file; */
@@ -759,11 +756,11 @@ return 1;
     /* object initialization */
     hisver= 0;
     histime= 0;
-    hiscset= 0;
     hisuid= 0;
     hisuser= NULL;
-    nd_count= 0;
-    member_count= 0;
+
+    nds.clear();
+    members.clear();
 
     /* read object id */
     bufp++;
@@ -780,7 +777,7 @@ return 1;
         time(&start_node);
       }
       count_node++;
-      if(count_node%10000==0) printStatus();
+      if(count_node%10000==0) print_status();
       break;
     case 1:  /* way */
       if(osm_id>max_way)
@@ -789,7 +786,7 @@ return 1;
         time(&start_way);
       }
       count_way++;
-      if(count_way%1000==0) printStatus();
+      if(count_way%1000==0) print_status();
       break;
     case 2:  /* relation */
       if(osm_id>max_rel)
@@ -798,7 +795,7 @@ return 1;
         time(&start_rel);
       }
       count_rel++;
-      if(count_rel%10==0) printStatus();
+      if(count_rel%10==0) print_status();
       break;
     default: ;
       }
@@ -809,18 +806,18 @@ return 1;
 
       hisver= pbf_uint32(&bufp);
       uint32toa(hisver,tmpstr);
-      tags.addItem("osm_version",tmpstr,false);
+      tags.push_back(tag("osm_version",tmpstr));
       if(hisver!=0) {  /* history information available */
         histime= o5histime+= pbf_sint64(&bufp);
         createtimestamp(histime,tmpstr);
-        tags.addItem("osm_timestamp",tmpstr, false);
+        tags.push_back(tag("osm_timestamp",tmpstr));
         if(histime!=0) {
-            hiscset= o5hiscset+= pbf_sint32(&bufp);  /* (not used) */
+          o5hiscset+= pbf_sint32(&bufp);  /* (not used) */
           str_read(&bufp,&sp,&hisuser);
           hisuid= pbf_uint64((byte**)&sp);
           uint32toa(hisuid,tmpstr);
-          tags.addItem("osm_uid",tmpstr,false);
-          tags.addItem("osm_user",hisuser,false);
+          tags.push_back(tag("osm_uid",tmpstr));
+          tags.push_back(tag("osm_user",hisuser));
           }
       }  /* end   history information available */
     }  /* end   read history */
@@ -841,7 +838,7 @@ return 1;
         break;
       default: ;
         }
-      tags.resetList();
+      tags.clear();
       continue;  /* end processing for this object */
     }  /* end   delete request */
     else {  /* not a delete request */
@@ -858,7 +855,7 @@ return 1;
         node_lon= (double)(o5lon+= pbf_sint32(&bufp))/10000000;
         node_lat= (double)(o5lat+= pbf_sint32(&bufp))/10000000;
         if(!node_wanted(node_lat,node_lon)) {
-          tags.resetList();
+          tags.clear();
   continue;
           }
         proj->reproject(&(node_lat),&(node_lon));
@@ -870,9 +867,8 @@ return 1;
         bp= bufp+l;
         if(bp>bufe) bp= bufe;  /* (format error) */
         while(bufp<bp) {  /* for all noderefs of this way */
-          nds[nd_count++]= o5rid[0]+= pbf_sint64(&bufp);
-          if(nd_count>=nd_max)
-            realloc_nodes();
+          o5rid[0]+= pbf_sint64(&bufp);
+          nds.push_back(o5rid[0]);
         }  /* end   for all noderefs of this way */
       }  /* end   way */
 
@@ -889,22 +885,20 @@ return 1;
           ri= pbf_sint64(&bufp);
           str_read(&bufp,&rr,NULL);
           rt= (*rr++ -'0')%3;
+          OsmType type = OSMTYPE_NODE;
           switch(rt) {
           case 0:  /* node */
-            members[member_count].type= OSMTYPE_NODE;
+            type= OSMTYPE_NODE;
             break;
           case 1:  /* way */
-            members[member_count].type= OSMTYPE_WAY;
+            type= OSMTYPE_WAY;
             break;
           case 2:  /* relation */
-            members[member_count].type= OSMTYPE_RELATION;
+            type= OSMTYPE_RELATION;
             break;
             }
-          members[member_count].id= o5rid[rt]+= ri;
-          members[member_count].role= rr;
-          member_count++;
-          if(member_count>=member_max)
-            realloc_members();
+          o5rid[rt]+= ri;
+          members.push_back(member(type, o5rid[rt], rr));
         }  /* end   for all references of this relation */
       }  /* end   relation */
 
@@ -913,15 +907,13 @@ return 1;
         char* k,*v,*p;
 
         str_read(&bufp,&k,&v);
-        if(strcmp(k,"created_by") && strcmp(k,"source")) {
-          p= k;
-          while(*p!=0) {
-            if(*p==' ') *p= '_';
-            /* replace all blanks in key by underlines */
-            p++;
-            }
-          tags.addItem(k,v,false);
+        p= k;
+        while(*p!=0) {
+          if(*p==' ') *p= '_';
+          /* replace all blanks in key by underlines */
+          p++;
           }
+        tags.push_back(tag(k,v));
       }  /* end   for all tags of this object */
 
       /* write object into database */
@@ -929,39 +921,39 @@ return 1;
       case 0:  /* node */
         if(action==ACTION_CREATE)
           osmdata->node_add(osm_id,
-            node_lat,node_lon,&(tags));
+            node_lat,node_lon,tags);
         else /* ACTION_MODIFY */
           osmdata->node_modify(osm_id,
-            node_lat,node_lon,&(tags));
+            node_lat,node_lon,tags);
         break;
       case 1:  /* way */
         if(action==ACTION_CREATE)
           osmdata->way_add(osm_id,
-            nds,nd_count,&(tags));
+            nds,tags);
         else /* ACTION_MODIFY */
           osmdata->way_modify(osm_id,
-            nds,nd_count,&(tags));
+            nds,tags);
         break;
       case 2:  /* relation */
         if(action==ACTION_CREATE)
           osmdata->relation_add(osm_id,
-            members,member_count,&(tags));
+            members,tags);
         else /* ACTION_MODIFY */
           osmdata->relation_modify(osm_id,
-            members,member_count,&(tags));
+            members,tags);
         break;
       default: ;
         }
 
       /* reset temporary storage lists */
-      tags.resetList();
+      tags.clear();
 
     }  /* end   not a delete request */
 
   }  /* end   read input file */
 
   /* close the input file */
-  printStatus();
+  print_status();
   read_close();
   return 0;
 }  /* streamFileO5m() */
diff --git a/parse-pbf.cpp b/parse-pbf.cpp
index 051894d..e64f603 100644
--- a/parse-pbf.cpp
+++ b/parse-pbf.cpp
@@ -48,27 +48,14 @@
 
 static uint32_t get_length(FILE *input)
 {
-  char buf[4];
+  uint32_t buf;
 
-  if (1 != fread(buf, sizeof(buf), 1, input))
+  if (1 != fread(reinterpret_cast<char *>(&buf), sizeof(buf), 1, input))
     return 0;
 
-  return ntohl(*((size_t *)buf));
+  return ntohl(buf);
 }
 
-#if 0
-static void *realloc_or_free(void *p, size_t len)
-{
-  void *new = realloc(p, len);
-
-  if (new == NULL) {
-    free(p);
-  }
-
-  return new;
-}
-#endif
-
 static BlockHeader *read_header(FILE *input, void *buf)
 {
   BlockHeader *header_msg;
@@ -170,59 +157,47 @@ static size_t uncompress_blob(Blob *bmsg, void *buf, int32_t max_size)
   return 0;
 }
 
-int addProtobufItem(struct keyval *head, ProtobufCBinaryData key, ProtobufCBinaryData val, int noDupe)
+void parse_pbf_t::addProtobufItem(ProtobufCBinaryData &key,
+                                  ProtobufCBinaryData &val)
 {
   std::string keystr((const char *) key.data, key.len);
-
   assert(keystr.find('\0') == std::string::npos);
 
-  /* drop certain keys (matching parse-xml2) */
-  if ((keystr == "created_by") || (keystr == "source"))
-    return 0;
-
   std::string valstr((const char *) val.data, val.len);
-
   assert(valstr.find('\0') == std::string::npos);
 
-  return head->addItem(keystr, valstr, noDupe);
+  tags.push_back(tag(keystr, valstr));
 }
 
-int addIntItem(struct keyval *head, const char *key, int val, int noDupe)
+void parse_pbf_t::addIntItem(const char *key, int val)
 {
   char buf[100];
-
   sprintf(buf, "%d", val);
-  return head->addItem(key, buf, noDupe);
+
+  tags.push_back(tag(key, buf));
 }
 
-int addInfoItems(struct keyval *head, Info *info, StringTable *string_table)
+void parse_pbf_t::addInfoItems(Info *info, StringTable *string_table)
 {
-      if (info->has_version) {
-	addIntItem(head, "osm_version", info->version, 0);
-      }
-      if (info->has_changeset) {
-	addIntItem(head, "osm_changeset", info->changeset, 0);
-      }
-      if (info->has_uid) {
-	addIntItem(head, "osm_uid", info->uid, 0);
-      }
-      if (info->has_user_sid) {
-	ProtobufCBinaryData user = string_table->s[info->user_sid];
-	char *username;
+  if (info->has_version)
+    addIntItem("osm_version", info->version);
 
-        username = (char *)calloc(user.len + 1, 1);
-	memcpy(username, user.data, user.len);
+  if (info->has_changeset)
+    addIntItem("osm_changeset", info->changeset);
 
-	head->addItem("osm_user", username, false);
-    free(username);
-      }
+  if (info->has_uid)
+    addIntItem("osm_uid", info->uid);
 
-      /* TODO timestamp */
+  if (info->has_user_sid) {
+    ProtobufCBinaryData user = string_table->s[info->user_sid];
 
-      return 0;
+    tags.push_back(tag("osm_user", std::string((const char *) user.data, user.len)));
+  }
+
+  /* TODO timestamp */
 }
 
-int processOsmHeader(void *data, size_t length)
+static int processOsmHeader(void *data, size_t length)
 {
   HeaderBlock *hmsg = header_block__unpack (NULL, length, (const uint8_t *)data);
   if (hmsg == NULL) {
@@ -235,32 +210,30 @@ int processOsmHeader(void *data, size_t length)
   return 1;
 }
 
-int parse_pbf_t::processOsmDataNodes(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table, double lat_offset, double lon_offset, double granularity)
+int parse_pbf_t::processOsmDataNodes(osmdata_t *osmdata, PrimitiveGroup *group,
+                                     StringTable *string_table, double lat_offset,
+                                     double lon_offset, double granularity)
 {
-    unsigned node_id, key_id;
-  for (node_id = 0; node_id < group->n_nodes; node_id++) {
+  for (unsigned node_id = 0; node_id < group->n_nodes; node_id++) {
     Node *node = group->nodes[node_id];
-    double lat, lon;
 
-    tags.resetList();
+    tags.clear();
 
     if (node->info && extra_attributes) {
-      addInfoItems(&(tags), node->info, string_table);
+      addInfoItems(node->info, string_table);
     }
 
-    for (key_id = 0; key_id < node->n_keys; key_id++) {
-      addProtobufItem(&(tags),
-		      string_table->s[node->keys[key_id]],
-		      string_table->s[node->vals[key_id]],
-		      0);
+    for (unsigned key_id = 0; key_id < node->n_keys; key_id++) {
+      addProtobufItem(string_table->s[node->keys[key_id]],
+                      string_table->s[node->vals[key_id]]);
     }
 
-    lat = lat_offset + (node->lat * granularity);
-    lon = lon_offset + (node->lon * granularity);
+    double lat = lat_offset + (node->lat * granularity);
+    double lon = lon_offset + (node->lon * granularity);
     if (node_wanted(lat, lon)) {
         proj->reproject(&lat, &lon);
 
-        osmdata->node_add(node->id, lat, lon, &(tags));
+        osmdata->node_add(node->id, lat, lon, tags);
 
         if (node->id > max_node) {
             max_node = node->id;
@@ -271,226 +244,191 @@ int parse_pbf_t::processOsmDataNodes(struct osmdata_t *osmdata, PrimitiveGroup *
         }
         count_node++;
         if (count_node%10000 == 0)
-            printStatus();
+            print_status();
     }
   }
 
   return 1;
 }
 
-int parse_pbf_t::processOsmDataDenseNodes(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table, double lat_offset, double lon_offset, double granularity)
+int parse_pbf_t::processOsmDataDenseNodes(osmdata_t *osmdata, PrimitiveGroup *group,
+                                          StringTable *string_table,
+                                          double lat_offset, double lon_offset,
+                                          double granularity)
 {
-    unsigned node_id;
-    if (group->dense) {
-        unsigned l = 0;
-        osmid_t deltaid = 0;
-        long int deltalat = 0;
-        long int deltalon = 0;
-        unsigned long int deltatimestamp = 0;
-        unsigned long int deltachangeset = 0;
-        long int deltauid = 0;
-        unsigned long int deltauser_sid = 0;
-        double lat, lon;
-
-        DenseNodes *dense = group->dense;
-
-        for (node_id = 0; node_id < dense->n_id; node_id++) {
-            tags.resetList();
-
-            deltaid += dense->id[node_id];
-            deltalat += dense->lat[node_id];
-            deltalon += dense->lon[node_id];
-
-            if (dense->denseinfo && extra_attributes) {
-                DenseInfo *denseinfo = dense->denseinfo;
-
-                deltatimestamp += denseinfo->timestamp[node_id];
-                deltachangeset += denseinfo->changeset[node_id];
-                deltauid += denseinfo->uid[node_id];
-                deltauser_sid += denseinfo->user_sid[node_id];
-
-                addIntItem(&(tags), "osm_version", denseinfo->version[node_id], 0);
-                addIntItem(&(tags), "osm_changeset", deltachangeset, 0);
-
-                if (deltauid != -1) { /* osmosis devs failed to read the specs */
-                    char * valstr;
-                    addIntItem(&(tags), "osm_uid", deltauid, 0);
-                    valstr = (char *)calloc(string_table->s[deltauser_sid].len + 1, 1);
-                    memcpy(valstr, string_table->s[deltauser_sid].data, string_table->s[deltauser_sid].len);
-                    tags.addItem("osm_user", valstr, false);
-                    free(valstr);
-                }
-            }
-
-            if (l < dense->n_keys_vals) {
-                while (dense->keys_vals[l] != 0 && l < dense->n_keys_vals) {
-                    addProtobufItem(&(tags),
-                                    string_table->s[dense->keys_vals[l]],
-                                    string_table->s[dense->keys_vals[l+1]],
-                                    0);
-
-                    l += 2;
-                }
-                l += 1;
-            }
-
-            lat = lat_offset + (deltalat * granularity);
-            lon = lon_offset + (deltalon * granularity);
-            if (node_wanted(lat, lon)) {
-                proj->reproject(&lat, &lon);
-
-                osmdata->node_add(deltaid, lat, lon, &(tags));
-
-                if (deltaid > max_node) {
-                    max_node = deltaid;
-                }
-
-                if (count_node == 0) {
-                    time(&start_node);
-                }
-                count_node++;
-                if (count_node%10000 == 0)
-                    printStatus();
-            }
-        }
-    }
-
+  if (!group->dense)
     return 1;
+
+  unsigned l = 0;
+  osmid_t deltaid = 0;
+  long int deltalat = 0;
+  long int deltalon = 0;
+  unsigned long int deltatimestamp = 0;
+  unsigned long int deltachangeset = 0;
+  long int deltauid = 0;
+  unsigned long int deltauser_sid = 0;
+
+  DenseNodes *dense = group->dense;
+
+  for (unsigned node_id = 0; node_id < dense->n_id; node_id++) {
+      tags.clear();
+
+      deltaid += dense->id[node_id];
+      deltalat += dense->lat[node_id];
+      deltalon += dense->lon[node_id];
+
+      if (dense->denseinfo && extra_attributes) {
+          DenseInfo *denseinfo = dense->denseinfo;
+
+          deltatimestamp += denseinfo->timestamp[node_id];
+          deltachangeset += denseinfo->changeset[node_id];
+          deltauid += denseinfo->uid[node_id];
+          deltauser_sid += denseinfo->user_sid[node_id];
+
+          addIntItem("osm_version", denseinfo->version[node_id]);
+          addIntItem("osm_changeset", deltachangeset);
+
+          if (deltauid != -1) { /* osmosis devs failed to read the specs */
+              addIntItem("osm_uid", deltauid);
+              tags.push_back(tag("osm_user",
+                                 std::string((const char *) string_table->s[deltauser_sid].data,
+                                             string_table->s[deltauser_sid].len)));
+          }
+      }
+
+      if (l < dense->n_keys_vals) {
+          while (dense->keys_vals[l] != 0 && l < dense->n_keys_vals) {
+              addProtobufItem(string_table->s[dense->keys_vals[l]],
+                              string_table->s[dense->keys_vals[l+1]]);
+
+              l += 2;
+          }
+          l += 1;
+      }
+
+      double lat = lat_offset + (deltalat * granularity);
+      double lon = lon_offset + (deltalon * granularity);
+      if (node_wanted(lat, lon)) {
+          proj->reproject(&lat, &lon);
+
+          osmdata->node_add(deltaid, lat, lon, tags);
+
+          if (deltaid > max_node) {
+              max_node = deltaid;
+          }
+
+          if (count_node == 0) {
+              time(&start_node);
+          }
+          count_node++;
+          if (count_node%10000 == 0)
+              print_status();
+      }
+  }
+
+  return 1;
 }
 
-int parse_pbf_t::processOsmDataWays(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table)
+int parse_pbf_t::processOsmDataWays(osmdata_t *osmdata, PrimitiveGroup *group,
+                                    StringTable *string_table)
 {
-    unsigned way_id, key_id, ref_id;
-  for (way_id = 0; way_id < group->n_ways; way_id++) {
+  for (unsigned way_id = 0; way_id < group->n_ways; way_id++) {
     Way *way = group->ways[way_id];
     osmid_t deltaref = 0;
 
-    tags.resetList();
+    tags.clear();
 
     if (way->info && extra_attributes) {
-      addInfoItems(&(tags), way->info, string_table);
+      addInfoItems(way->info, string_table);
     }
 
-    nd_count = 0;
+    nds.clear();
 
-    for (ref_id = 0; ref_id < way->n_refs; ref_id++) {
+    for (unsigned ref_id = 0; ref_id < way->n_refs; ref_id++) {
       deltaref += way->refs[ref_id];
 
-      nds[nd_count++] = deltaref;
-
-      if( nd_count >= nd_max )
-	realloc_nodes();
+      nds.push_back(deltaref);
     }
 
-    for (key_id = 0; key_id < way->n_keys; key_id++) {
-      addProtobufItem(&(tags),
-		      string_table->s[way->keys[key_id]],
-		      string_table->s[way->vals[key_id]],
-		      0);
+    for (unsigned key_id = 0; key_id < way->n_keys; key_id++) {
+      addProtobufItem(string_table->s[way->keys[key_id]],
+                      string_table->s[way->vals[key_id]]);
     }
 
-    osmdata->way_add(way->id,
-                     nds,
-                     nd_count,
-                     &(tags) );
+    osmdata->way_add(way->id, nds, tags);
 
     if (way->id > max_way) {
       max_way = way->id;
     }
 
-	if (count_way == 0) {
-		time(&start_way);
-	}
+    if (count_way == 0) {
+        time(&start_way);
+    }
     count_way++;
     if (count_way%1000 == 0)
-      printStatus();
+      print_status();
   }
 
   return 1;
 }
 
-int parse_pbf_t::processOsmDataRelations(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table)
+int parse_pbf_t::processOsmDataRelations(osmdata_t *osmdata, PrimitiveGroup *group,
+                                         StringTable *string_table)
 {
-    unsigned rel_id, member_id, key_id;
-  for (rel_id = 0; rel_id < group->n_relations; rel_id++) {
+  for (unsigned rel_id = 0; rel_id < group->n_relations; rel_id++) {
     Relation *relation = group->relations[rel_id];
     osmid_t deltamemids = 0;
 
-    tags.resetList();
-
-    member_count = 0;
+    tags.clear();
+    members.clear();
 
     if (relation->info && extra_attributes) {
-      addInfoItems(&(tags), relation->info, string_table);
+      addInfoItems(relation->info, string_table);
     }
 
-    for (member_id = 0; member_id < relation->n_memids; member_id++) {
+    for (unsigned member_id = 0; member_id < relation->n_memids; member_id++) {
       ProtobufCBinaryData role =  string_table->s[relation->roles_sid[member_id]];
-      char *rolestr;
 
       deltamemids += relation->memids[member_id];
 
-      members[member_count].id = deltamemids;
-
-      rolestr = (char *)calloc(role.len + 1, 1);
-      memcpy(rolestr, role.data, role.len);
-      members[member_count].role = rolestr;
-
+      OsmType type;
       switch (relation->types[member_id]) {
-      case RELATION__MEMBER_TYPE__NODE:
-	members[member_count].type = OSMTYPE_NODE;
-	break;
-      case RELATION__MEMBER_TYPE__WAY:
-	members[member_count].type = OSMTYPE_WAY;
-	break;
-      case RELATION__MEMBER_TYPE__RELATION:
-	members[member_count].type = OSMTYPE_RELATION;
-	break;
-      default:
-	fprintf(stderr, "Unsupported type: %u""\n", relation->types[member_id]);
-	return 0;
+        case RELATION__MEMBER_TYPE__NODE: type = OSMTYPE_NODE; break;
+        case RELATION__MEMBER_TYPE__WAY: type = OSMTYPE_WAY; break;
+        case RELATION__MEMBER_TYPE__RELATION: type = OSMTYPE_RELATION; break;
+        default:
+          fprintf(stderr, "Unsupported type: %u""\n", relation->types[member_id]);
+          return 0;
       }
 
-      member_count++;
-
-      if( member_count >= member_max ) {
-	realloc_members();
-      }
+      members.push_back(member(type, deltamemids,
+                               std::string((const char *)role.data, role.len)));
     }
 
-    for (key_id = 0; key_id < relation->n_keys; key_id++) {
-      addProtobufItem(&(tags),
-		      string_table->s[relation->keys[key_id]],
-		      string_table->s[relation->vals[key_id]],
-		      0);
+    for (unsigned key_id = 0; key_id < relation->n_keys; key_id++) {
+      addProtobufItem(string_table->s[relation->keys[key_id]],
+                      string_table->s[relation->vals[key_id]]);
     }
 
-    osmdata->relation_add(relation->id,
-                          members,
-                          member_count,
-                          &(tags));
-
-    for (member_id = 0; member_id < member_count; member_id++) {
-      free(members[member_id].role);
-    }
+    osmdata->relation_add(relation->id, members, tags);
 
     if (relation->id > max_rel) {
       max_rel = relation->id;
     }
 
-	if (count_rel == 0) {
-		time(&start_rel);
-	}
+    if (count_rel == 0) {
+        time(&start_rel);
+    }
     count_rel++;
     if (count_rel%10 == 0)
-      printStatus();
+      print_status();
   }
 
   return 1;
 }
 
 
-int parse_pbf_t::processOsmData(struct osmdata_t *osmdata, void *data, size_t length)
+int parse_pbf_t::processOsmData(osmdata_t *osmdata, void *data, size_t length)
 {
   unsigned int j;
   double lat_offset, lon_offset, granularity;
diff --git a/parse-pbf.hpp b/parse-pbf.hpp
index 6382e74..024fecc 100644
--- a/parse-pbf.hpp
+++ b/parse-pbf.hpp
@@ -44,11 +44,16 @@ public:
 	virtual int streamFile(const char *filename, const int sanitize, osmdata_t *osmdata);
 protected:
 	parse_pbf_t();
-	int processOsmDataNodes(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table, double lat_offset, double lon_offset, double granularity);
-	int processOsmDataDenseNodes(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table, double lat_offset, double lon_offset, double granularity);
-	int processOsmDataWays(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table);
-	int processOsmDataRelations(struct osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table);
-	int processOsmData(struct osmdata_t *osmdata, void *data, size_t length);
+	int processOsmDataNodes(osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table, double lat_offset, double lon_offset, double granularity);
+	int processOsmDataDenseNodes(osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table, double lat_offset, double lon_offset, double granularity);
+	int processOsmDataWays(osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table);
+	int processOsmDataRelations(osmdata_t *osmdata, PrimitiveGroup *group, StringTable *string_table);
+	int processOsmData(osmdata_t *osmdata, void *data, size_t length);
+
+private:
+    void addProtobufItem(ProtobufCBinaryData &key, ProtobufCBinaryData &val);
+    void addIntItem(const char *key, int val);
+    void addInfoItems(Info *info, StringTable *string_table);
 };
 
 #endif //BUILD_READER_PBF
diff --git a/parse-xml2.cpp b/parse-xml2.cpp
index 2c4f58d..8718f76 100644
--- a/parse-xml2.cpp
+++ b/parse-xml2.cpp
@@ -22,7 +22,6 @@
 #-----------------------------------------------------------------------------
 */
 
-#include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
@@ -82,10 +81,9 @@ void parse_xml2_t::SetFiletype(const xmlChar* name, osmdata_t* osmdata)
 	}
 }
 
-void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, struct osmdata_t *osmdata)
+void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, osmdata_t *osmdata)
 {
   xmlChar *xid, *xlat, *xlon, *xk, *xv, *xrole, *xtype;
-  char *k;
 
   //first time in we figure out what kind of data this is
   if (filetype == FILETYPE_NONE)
@@ -122,7 +120,7 @@ void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, st
     }
     count_node++;
     if (count_node%10000 == 0)
-      printStatus();
+      print_status();
 
     xmlFree(xid);
     xmlFree(xlon);
@@ -143,9 +141,9 @@ void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, st
     }
     count_way++;
     if (count_way%1000 == 0)
-      printStatus();
+      print_status();
 
-    nd_count = 0;
+    nds.clear();
     xmlFree(xid);
 
   } else if (xmlStrEqual(name, BAD_CAST "relation")) {
@@ -164,36 +162,27 @@ void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, st
     }
     count_rel++;
     if (count_rel%10 == 0)
-      printStatus();
+      print_status();
 
-    member_count = 0;
+    members.clear();
     xmlFree(xid);
   } else if (xmlStrEqual(name, BAD_CAST "tag")) {
     xk = xmlTextReaderGetAttribute(reader, BAD_CAST "k");
     assert(xk);
 
-    /* 'created_by' and 'source' are common and not interesting to mapnik renderer */
-    if (strcmp((char *)xk, "created_by") && strcmp((char *)xk, "source")) {
-      char *p;
-      xv = xmlTextReaderGetAttribute(reader, BAD_CAST "v");
-      assert(xv);
-      k  = (char *)xmlStrdup(xk);
-      while ((p = strchr(k, ' ')))
-        *p = '_';
-
-      tags.addItem(k, (char *)xv, 0);
-      xmlFree(k);
-      xmlFree(xv);
-    }
+    xv = xmlTextReaderGetAttribute(reader, BAD_CAST "v");
+    assert(xv);
+
+    tags.push_back(tag((const char *)xk, (const char *)xv));
+
+    xmlFree(xv);
     xmlFree(xk);
   } else if (xmlStrEqual(name, BAD_CAST "nd")) {
       xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
       assert(xid);
 
-      nds[nd_count++] = strtoosmid( (char *)xid, NULL, 10 );
+      nds.push_back(strtoosmid( (char *)xid, NULL, 10 ));
 
-      if( nd_count >= nd_max )
-        realloc_nodes();
       xmlFree(xid);
   } else if (xmlStrEqual(name, BAD_CAST "member")) {
     xrole = xmlTextReaderGetAttribute(reader, BAD_CAST "role");
@@ -205,20 +194,16 @@ void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, st
     xid = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
     assert(xid);
 
-    members[member_count].id = strtoosmid((char *) xid, NULL, 0);
-    members[member_count].role = strdup((char *) xrole);
-
-    /* Currently we are only interested in 'way' members since these form polygons with holes */
+    OsmType type = OSMTYPE_NODE;
     if (xmlStrEqual(xtype, BAD_CAST "way"))
-      members[member_count].type = OSMTYPE_WAY;
-    if (xmlStrEqual(xtype, BAD_CAST "node"))
-      members[member_count].type = OSMTYPE_NODE;
-    if (xmlStrEqual(xtype, BAD_CAST "relation"))
-      members[member_count].type = OSMTYPE_RELATION;
-    member_count++;
-
-    if (member_count >= member_max)
-      realloc_members();
+      type = OSMTYPE_WAY;
+    else if (xmlStrEqual(xtype, BAD_CAST "node"))
+      type = OSMTYPE_NODE;
+    else if (xmlStrEqual(xtype, BAD_CAST "relation"))
+      type = OSMTYPE_RELATION;
+
+    members.push_back(member(type, strtoosmid((char *) xid, NULL, 0),
+                             (const char *) xrole));
     xmlFree(xid);
     xmlFree(xrole);
     xmlFree(xtype);
@@ -246,46 +231,46 @@ void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, st
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "user");
       if (xtmp) {
-          tags.addItem("osm_user", (char *)xtmp, false);
+          tags.push_back(tag("osm_user", (const char *)xtmp));
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "uid");
       if (xtmp) {
-          tags.addItem("osm_uid", (char *)xtmp, false);
+          tags.push_back(tag("osm_uid", (const char *)xtmp));
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "version");
       if (xtmp) {
-          tags.addItem("osm_version", (char *)xtmp, false);
+          tags.push_back(tag("osm_version", (const char *)xtmp));
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp");
       if (xtmp) {
-          tags.addItem("osm_timestamp", (char *)xtmp, false);
+          tags.push_back(tag("osm_timestamp", (const char *)xtmp));
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "changeset");
       if (xtmp) {
-          tags.addItem("osm_changeset", (char *)xtmp, false);
+          tags.push_back(tag("osm_changeset", (const char *)xtmp));
           xmlFree(xtmp);
       }
   }
 }
 
 
-void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
+void parse_xml2_t::EndElement(const xmlChar *name, osmdata_t *osmdata)
 {
     if (xmlStrEqual(name, BAD_CAST "node")) {
-      if (node_wanted(node_lat, node_lon)) {
-	  proj->reproject(&(node_lat), &(node_lon));
+        if (node_wanted(node_lat, node_lon)) {
+            proj->reproject(&(node_lat), &(node_lon));
             if( action == ACTION_CREATE )
-	        osmdata->node_add(osm_id, node_lat, node_lon, &(tags));
+                osmdata->node_add(osm_id, node_lat, node_lon, tags);
             else if( action == ACTION_MODIFY )
-	        osmdata->node_modify(osm_id, node_lat, node_lon, &(tags));
+                osmdata->node_modify(osm_id, node_lat, node_lon, tags);
             else if( action == ACTION_DELETE )
                 osmdata->node_delete(osm_id);
             else
@@ -294,12 +279,12 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
                 util::exit_nicely();
             }
         }
-        tags.resetList();
+        tags.clear();
     } else if (xmlStrEqual(name, BAD_CAST "way")) {
         if( action == ACTION_CREATE )
-	    osmdata->way_add(osm_id, nds, nd_count, &(tags) );
+            osmdata->way_add(osm_id, nds, tags);
         else if( action == ACTION_MODIFY )
-	    osmdata->way_modify(osm_id, nds, nd_count, &(tags) );
+            osmdata->way_modify(osm_id, nds, tags);
         else if( action == ACTION_DELETE )
             osmdata->way_delete(osm_id);
         else
@@ -307,12 +292,12 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
             fprintf( stderr, "Don't know action for way %" PRIdOSMID "\n", osm_id );
             util::exit_nicely();
         }
-        tags.resetList();
+        tags.clear();
     } else if (xmlStrEqual(name, BAD_CAST "relation")) {
         if( action == ACTION_CREATE )
-	    osmdata->relation_add(osm_id, members, member_count, &(tags));
+            osmdata->relation_add(osm_id, members, tags);
         else if( action == ACTION_MODIFY )
-	    osmdata->relation_modify(osm_id, members, member_count, &(tags));
+            osmdata->relation_modify(osm_id, members, tags);
         else if( action == ACTION_DELETE )
             osmdata->relation_delete(osm_id);
         else
@@ -320,8 +305,8 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
             fprintf( stderr, "Don't know action for relation %" PRIdOSMID "\n", osm_id );
             util::exit_nicely();
         }
-        tags.resetList();
-        resetMembers();
+        tags.clear();
+        members.clear();
     } else if (xmlStrEqual(name, BAD_CAST "tag")) {
         /* ignore */
     } else if (xmlStrEqual(name, BAD_CAST "nd")) {
@@ -329,13 +314,13 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
     } else if (xmlStrEqual(name, BAD_CAST "member")) {
 	/* ignore */
     } else if (xmlStrEqual(name, BAD_CAST "osm")) {
-        printStatus();
+        print_status();
         filetype = FILETYPE_NONE;
     } else if (xmlStrEqual(name, BAD_CAST "osmChange")) {
-        printStatus();
+        print_status();
         filetype = FILETYPE_NONE;
     } else if (xmlStrEqual(name, BAD_CAST "planetdiff")) {
-        printStatus();
+        print_status();
         filetype = FILETYPE_NONE;
     } else if (xmlStrEqual(name, BAD_CAST "bound")) {
         /* ignore */
@@ -343,7 +328,7 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
         /* ignore */
     } else if (xmlStrEqual(name, BAD_CAST "changeset")) {
         /* ignore */
-    tags.resetList(); /* We may have accumulated some tags even if we ignored the changeset */
+        tags.clear(); /* We may have accumulated some tags even if we ignored the changeset */
     } else if (xmlStrEqual(name, BAD_CAST "add")) {
         action = ACTION_NONE;
     } else if (xmlStrEqual(name, BAD_CAST "create")) {
@@ -357,7 +342,7 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
     }
 }
 
-void parse_xml2_t::processNode(xmlTextReaderPtr reader, struct osmdata_t *osmdata) {
+void parse_xml2_t::processNode(xmlTextReaderPtr reader, osmdata_t *osmdata) {
   xmlChar *name;
   name = xmlTextReaderName(reader);
   if (name == NULL)
diff --git a/parse-xml2.hpp b/parse-xml2.hpp
index 10ae1b5..55d6112 100644
--- a/parse-xml2.hpp
+++ b/parse-xml2.hpp
@@ -54,9 +54,9 @@ protected:
 	parse_xml2_t();
 	actions_t ParseAction( xmlTextReaderPtr reader);
 	void SetFiletype(const xmlChar* name, osmdata_t* osmdata);
-	void StartElement(xmlTextReaderPtr reader, const xmlChar *name, struct osmdata_t *osmdata);
-	void EndElement(const xmlChar *name, struct osmdata_t *osmdata);
-	void processNode(xmlTextReaderPtr reader, struct osmdata_t *osmdata);
+	void StartElement(xmlTextReaderPtr reader, const xmlChar *name, osmdata_t *osmdata);
+	void EndElement(const xmlChar *name, osmdata_t *osmdata);
+	void processNode(xmlTextReaderPtr reader, osmdata_t *osmdata);
 };
 
 
diff --git a/parse.cpp b/parse.cpp
index 8ff775f..cf760ca 100644
--- a/parse.cpp
+++ b/parse.cpp
@@ -24,14 +24,14 @@ m_extra_attributes(extra_attributes), m_proj(projection), m_count_node(0), m_max
 m_count_way(0), m_max_way(0), m_count_rel(0), m_max_rel(0), m_start_node(0), m_start_way(0), m_start_rel(0)
 {
     m_bbox = bool(bbox);
-    if (m_bbox) {
-	parse_bbox(*bbox);
-    }
+    if (m_bbox)
+        parse_bbox(*bbox);
 }
 
 parse_delegate_t::~parse_delegate_t()
 {
 }
+
 int parse_delegate_t::streamFile(const char* input_reader, const char* filename,const int sanitize, osmdata_t *osmdata)
 {
 	//process the input file with the right parser
@@ -143,65 +143,20 @@ parse_t::parse_t(const int extra_attributes_, const bool bbox_, const boost::sha
 		extra_attributes(extra_attributes_), bbox(bbox_), minlon(minlon_), minlat(minlat_),
 		maxlon(maxlon_), maxlat(maxlat_), proj(projection_)
 {
-	osm_id = nd_max = count_node = max_node = count_way = max_way = count_rel = 0;
+	osm_id = count_node = max_node = count_way = max_way = count_rel = 0;
 	max_rel = parallel_indexing = start_node = start_way = start_rel = 0;
-	nd_count = nd_max = node_lon = node_lat = member_count = member_max = 0;
-	members = NULL;
-	nds = NULL;
+	node_lon = node_lat = 0;
 
 	filetype = FILETYPE_NONE;
 	action   = ACTION_NONE;
-
-	realloc_nodes();
-	realloc_members();
 }
 
 parse_t::~parse_t()
 {
-	if(nds != NULL)
-		free(nds);
-	if(members != NULL)
-		free(members);
 }
 
-void parse_t::realloc_nodes()
-{
-  if( nd_max == 0 )
-    nd_max = INIT_MAX_NODES;
-  else
-    nd_max <<= 1;
-
-  nds = (osmid_t *)realloc( nds, nd_max * sizeof( nds[0] ) );
-  if( !nds )
-  {
-    fprintf( stderr, "Failed to expand node list to %d\n", nd_max );
-    util::exit_nicely();
-  }
-}
-
-void parse_t::realloc_members()
-{
-  if( member_max == 0 )
-    member_max = INIT_MAX_NODES;
-  else
-    member_max <<= 1;
-
-  members = (struct member *)realloc( members, member_max * sizeof( members[0] ) );
-  if( !members )
-  {
-    fprintf( stderr, "Failed to expand member list to %d\n", member_max );
-    util::exit_nicely();
-  }
-}
 
-void parse_t::resetMembers()
-{
-  unsigned i;
-  for(i = 0; i < member_count; i++ )
-    free( members[i].role );
-}
-
-void parse_t::printStatus()
+void parse_t::print_status()
 {
 	time_t now = time(NULL);
 	time_t end_nodes = start_way > 0 ? start_way : now;
@@ -216,14 +171,4 @@ void parse_t::printStatus()
 			count_rel > 0 ?	(double) count_rel / ((double) (end_rel - start_rel) > 0.0 ? (double) (end_rel - start_rel) : 1.0) : 0.0);
 }
 
-int parse_t::node_wanted(double lat, double lon)
-{
-    if (!bbox)
-        return 1;
-
-    if (lat < minlat || lat > maxlat)
-        return 0;
-    if (lon < minlon || lon > maxlon)
-        return 0;
-    return 1;
-}
+
diff --git a/parse.hpp b/parse.hpp
index 35c754b..718cb30 100644
--- a/parse.hpp
+++ b/parse.hpp
@@ -2,9 +2,9 @@
 #define PARSE_H
 
 #include <time.h>
-#include <config.h>
 
-#include "keyvals.hpp"
+#include "config.h"
+#include "osmtypes.hpp"
 #include "reprojection.hpp"
 #include "osmdata.hpp"
 
@@ -31,13 +31,13 @@ private:
         void parse_bbox(const std::string &bbox);
 	parse_t* get_input_reader(const char* input_reader, const char* filename);
 
+	const int m_extra_attributes;
+	boost::shared_ptr<reprojection> m_proj;
 	osmid_t m_count_node, m_max_node;
 	osmid_t m_count_way,  m_max_way;
 	osmid_t m_count_rel,  m_max_rel;
 	time_t  m_start_node, m_start_way, m_start_rel;
 
-	const int m_extra_attributes;
-	boost::shared_ptr<reprojection> m_proj;
 	bool m_bbox;
 	double m_minlon, m_minlat, m_maxlon, m_maxlat;
 };
@@ -54,38 +54,44 @@ public:
 	virtual int streamFile(const char *filename, const int sanitize, osmdata_t *osmdata) = 0;
 
 protected:
-	parse_t();
+    parse_t();
 
-	virtual void realloc_nodes();
-	virtual void realloc_members();
-	virtual void resetMembers();
-	virtual void printStatus();
-	virtual int node_wanted(double lat, double lon);
+    void print_status();
+
+    int node_wanted(double lat, double lon)
+    {
+        if (!bbox)
+            return 1;
+
+        if (lat < minlat || lat > maxlat)
+            return 0;
+        if (lon < minlon || lon > maxlon)
+            return 0;
+        return 1;
+    }
 
 	osmid_t count_node,    max_node;
 	osmid_t count_way,     max_way;
 	osmid_t count_rel,     max_rel;
 	time_t  start_node, start_way, start_rel;
 
-	/* Since {node,way} elements are not nested we can guarantee the
-	values in an end tag must match those of the corresponding
-	start tag and can therefore be cached.
-	*/
-	double node_lon, node_lat;
-	keyval tags;
-	osmid_t *nds;
-	int nd_count, nd_max;
-	member *members;
-	int member_count, member_max;
-	osmid_t osm_id;
-	filetypes_t filetype;
-	actions_t action;
-	int parallel_indexing;
+    /* Since {node,way} elements are not nested we can guarantee the
+    values in an end tag must match those of the corresponding
+    start tag and can therefore be cached.
+    */
+    double node_lon, node_lat;
+    taglist_t tags;
+    idlist_t nds;
+    memberlist_t members;
+    osmid_t osm_id;
+    filetypes_t filetype;
+    actions_t action;
+    int parallel_indexing;
 
 	const int extra_attributes;
 	mutable bool bbox;
-	const boost::shared_ptr<reprojection> proj;
 	mutable double minlon, minlat, maxlon, maxlat;
+	const boost::shared_ptr<reprojection> proj;
 };
 
 #endif
diff --git a/processor-line.cpp b/processor-line.cpp
index c6f16ae..cc540a8 100644
--- a/processor-line.cpp
+++ b/processor-line.cpp
@@ -10,10 +10,10 @@ processor_line::~processor_line()
 {
 }
 
-geometry_builder::maybe_wkt_t processor_line::process_way(const osmNode *nodes, size_t node_count)
+geometry_builder::maybe_wkt_t processor_line::process_way(const nodelist_t &nodes)
 {
     //have the builder make the wkt
-    geometry_builder::maybe_wkt_t wkt = builder.get_wkt_simple(nodes, node_count, false);
+    geometry_builder::maybe_wkt_t wkt = builder.get_wkt_simple(nodes, false);
     //hand back the wkt
     return wkt;
 }
diff --git a/processor-line.hpp b/processor-line.hpp
index 05f7017..f385fcd 100644
--- a/processor-line.hpp
+++ b/processor-line.hpp
@@ -7,7 +7,7 @@ struct processor_line : public geometry_processor {
     processor_line(int srid);
     virtual ~processor_line();
 
-    geometry_builder::maybe_wkt_t process_way(const osmNode *nodes, size_t node_count);
+    geometry_builder::maybe_wkt_t process_way(const nodelist_t &nodes);
 
 private:
     geometry_builder builder;
diff --git a/processor-polygon.cpp b/processor-polygon.cpp
index 7e705d1..e91d55f 100644
--- a/processor-polygon.cpp
+++ b/processor-polygon.cpp
@@ -10,17 +10,17 @@ processor_polygon::~processor_polygon()
 {
 }
 
-geometry_builder::maybe_wkt_t processor_polygon::process_way(const osmNode *nodes, const size_t node_count)
+geometry_builder::maybe_wkt_t processor_polygon::process_way(const nodelist_t &nodes)
 {
     //have the builder make the wkt
-    geometry_builder::maybe_wkt_t wkt = builder.get_wkt_simple(nodes, node_count, true);
+    geometry_builder::maybe_wkt_t wkt = builder.get_wkt_simple(nodes, true);
     //hand back the wkt
     return wkt;
 }
 
-geometry_builder::maybe_wkts_t processor_polygon::process_relation(const osmNode * const * nodes, const int* node_counts)
+geometry_builder::maybe_wkts_t processor_polygon::process_relation(const multinodelist_t &nodes)
 {
     //the hard word was already done for us in getting at the node data for each way. at this point just make the geom
-    geometry_builder::maybe_wkts_t wkts  = builder.build_polygons(nodes, node_counts, enable_multi, -1);
+    geometry_builder::maybe_wkts_t wkts  = builder.build_polygons(nodes, enable_multi, -1);
     return wkts;
 }
diff --git a/processor-polygon.hpp b/processor-polygon.hpp
index 96bdf42..e19181d 100644
--- a/processor-polygon.hpp
+++ b/processor-polygon.hpp
@@ -7,8 +7,8 @@ struct processor_polygon : public geometry_processor {
     processor_polygon(int srid, bool enable_multi);
     virtual ~processor_polygon();
 
-    geometry_builder::maybe_wkt_t process_way(const osmNode *nodes, const size_t node_count);
-    geometry_builder::maybe_wkts_t process_relation(const osmNode * const * nodes, const int* node_counts);
+    geometry_builder::maybe_wkt_t process_way(const nodelist_t &nodes);
+    geometry_builder::maybe_wkts_t process_relation(const multinodelist_t &nodes);
 
 private:
     bool enable_multi;
diff --git a/table.cpp b/table.cpp
index e22109a..84cd7e9 100644
--- a/table.cpp
+++ b/table.cpp
@@ -293,7 +293,7 @@ void table_t::stop_copy()
     copyMode = false;
 }
 
-void table_t::write_node(const osmid_t id, struct keyval *tags, double lat, double lon)
+void table_t::write_node(const osmid_t id, const taglist_t &tags, double lat, double lon)
 {
 #ifdef FIXED_POINT
     // guarantee that we use the same values as in the node cache
@@ -310,21 +310,27 @@ void table_t::delete_row(const osmid_t id)
     pgsql_exec_simple(sql_conn, PGRES_COMMAND_OK, (del_fmt % name % id).str());
 }
 
-void table_t::write_wkt(const osmid_t id, struct keyval *tags, const char *wkt)
+void table_t::write_wkt(const osmid_t id, const taglist_t &tags, const char *wkt)
 {
     //add the osm id
     buffer.append((single_fmt % id).str());
     buffer.push_back('\t');
 
+    // used to remember which columns have been written out already.
+    std::vector<bool> used;
+
+    if (hstore_mode != HSTORE_NONE)
+        used.assign(tags.size(), false);
+
     //get the regular columns' values
-    write_columns(tags, buffer);
+    write_columns(tags, buffer, hstore_mode == HSTORE_NORM?&used:NULL);
 
     //get the hstore columns' values
     write_hstore_columns(tags, buffer);
 
     //get the key value pairs for the tags column
     if (hstore_mode != HSTORE_NONE)
-        write_tags_column(tags, buffer);
+        write_tags_column(tags, buffer, used);
 
     //give the wkt an srid
     buffer.append("SRID=");
@@ -350,18 +356,18 @@ void table_t::write_wkt(const osmid_t id, struct keyval *tags, const char *wkt)
     }
 }
 
-void table_t::write_columns(keyval *tags, string& values)
+void table_t::write_columns(const taglist_t &tags, string& values, std::vector<bool> *used)
 {
     //for each column
     for(columns_t::const_iterator column = columns.begin(); column != columns.end(); ++column)
     {
-        keyval *tag = NULL;
-        if ((tag = tags->getTag(column->first)))
+        int idx;
+        if ((idx = tags.indexof(column->first)) >= 0)
         {
-            escape_type(tag->value, column->second.c_str(), values);
+            escape_type(tags[idx].value, column->second, values);
             //remember we already used this one so we cant use again later in the hstore column
-            if (hstore_mode == HSTORE_NORM)
-                tag->has_column = 1;
+            if (used)
+                (*used)[idx] = true;
         }
         else
             values.append("\\N");
@@ -369,22 +375,24 @@ void table_t::write_columns(keyval *tags, string& values)
     }
 }
 
-void table_t::write_tags_column(keyval *tags, std::string& values)
+void table_t::write_tags_column(const taglist_t &tags, std::string& values,
+                                const std::vector<bool> &used)
 {
     //iterate through the list of tags, first one is always null
     bool added = false;
-    for (keyval* xtags = tags->firstItem(); xtags; xtags = tags->nextItem(xtags))
+    for (size_t i = 0; i < tags.size(); ++i)
     {
+        const tag& xtag = tags[i];
         //skip z_order tag and keys which have their own column
-        if (xtags->has_column || ("z_order" == xtags->key))
+        if (used[i] || ("z_order" == xtag.key))
             continue;
 
         //hstore ASCII representation looks like "key"=>"value"
         if(added)
             values.push_back(',');
-        escape4hstore(xtags->key.c_str(), values);
+        escape4hstore(xtag.key.c_str(), values);
         values.append("=>");
-        escape4hstore(xtags->value.c_str(), values);
+        escape4hstore(xtag.value.c_str(), values);
 
         //we did at least one so we need commas from here on out
         added = true;
@@ -395,7 +403,7 @@ void table_t::write_tags_column(keyval *tags, std::string& values)
 }
 
 /* write an hstore column to the database */
-void table_t::write_hstore_columns(keyval *tags, std::string& values)
+void table_t::write_hstore_columns(const taglist_t &tags, std::string& values)
 {
     //iterate over all configured hstore columns in the options
     for(hstores_t::const_iterator hstore_column = hstore_columns.begin(); hstore_column != hstore_columns.end(); ++hstore_column)
@@ -403,12 +411,12 @@ void table_t::write_hstore_columns(keyval *tags, std::string& values)
         bool added = false;
 
         //iterate through the list of tags, first one is always null
-        for (keyval* xtags = tags->firstItem(); xtags; xtags = tags->nextItem(xtags))
+        for (taglist_t::const_iterator xtags = tags.begin(); xtags != tags.end(); ++xtags)
         {
             //check if the tag's key starts with the name of the hstore column
             if(xtags->key.compare(0, hstore_column->size(), *hstore_column) == 0)
             {
-                //generate the short key name, somehow pointer arithmetic works against this member of the keyval data structure...
+                //generate the short key name, somehow pointer arithmetic works against the key string...
                 const char* shortkey = xtags->key.c_str() + hstore_column->size();
 
                 //and pack the shortkey with its value into the hstore
@@ -463,10 +471,10 @@ void table_t::escape4hstore(const char *src, string& dst)
 }
 
 /* Escape data appropriate to the type */
-void table_t::escape_type(const string &value, const char *type, string& dst) {
+void table_t::escape_type(const string &value, const string &type, string& dst) {
 
     // For integers we take the first number, or the average if it's a-b
-    if (!strcmp(type, "int4")) {
+    if (type == "int4") {
         int from, to;
         int items = sscanf(value.c_str(), "%d-%d", &from, &to);
         if (items == 1)
@@ -483,7 +491,7 @@ void table_t::escape_type(const string &value, const char *type, string& dst) {
          * convert feet to meters (1 foot = 0.3048 meters)
          * reject anything else
          */
-    else if (!strcmp(type, "real"))
+    else if (type == "real")
     {
         string escaped(value);
         std::replace(escaped.begin(), escaped.end(), ',', '.');
diff --git a/table.hpp b/table.hpp
index 0e38def..c51970c 100644
--- a/table.hpp
+++ b/table.hpp
@@ -1,7 +1,6 @@
 #ifndef TABLE_H
 #define TABLE_H
 
-#include "keyvals.hpp"
 #include "pgsql.hpp"
 #include "osmtypes.hpp"
 
@@ -30,8 +29,8 @@ class table_t
         void begin();
         void commit();
 
-        void write_wkt(const osmid_t id, struct keyval *tags, const char *wkt);
-        void write_node(const osmid_t id, struct keyval *tags, double lat, double lon);
+        void write_wkt(const osmid_t id, const taglist_t &tags, const char *wkt);
+        void write_node(const osmid_t id, const taglist_t &tags, double lat, double lon);
         void delete_row(const osmid_t id);
 
         std::string const& get_name();
@@ -58,12 +57,13 @@ class table_t
         void stop_copy();
         void teardown();
 
-        void write_columns(struct keyval *tags, std::string& values);
-        void write_tags_column(keyval *tags, std::string& values);
-        void write_hstore_columns(keyval *tags, std::string& values);
+        void write_columns(const taglist_t &tags, std::string& values, std::vector<bool> *used);
+        void write_tags_column(const taglist_t &tags, std::string& values,
+                               const std::vector<bool> &used);
+        void write_hstore_columns(const taglist_t &tags, std::string& values);
 
         void escape4hstore(const char *src, std::string& dst);
-        void escape_type(const std::string &value, const char *type, std::string& dst);
+        void escape_type(const std::string &value, const std::string &type, std::string& dst);
 
         std::string conninfo;
         std::string name;
diff --git a/tagtransform.cpp b/tagtransform.cpp
index 581f604..45bee84 100644
--- a/tagtransform.cpp
+++ b/tagtransform.cpp
@@ -4,7 +4,6 @@
 #include <string.h>
 #include <stdexcept>
 #include "osmtypes.hpp"
-#include "keyvals.hpp"
 #include "tagtransform.hpp"
 #include "output-pgsql.hpp"
 #include "options.hpp"
@@ -37,16 +36,16 @@ static const struct {
 static const unsigned int nLayers = (sizeof(layers)/sizeof(*layers));
 
 namespace {
-int add_z_order(keyval *tags, int *roads) {
-    const std::string *layer = tags->getItem("layer");
-    const std::string *highway = tags->getItem("highway");
-    const std::string *bridge = tags->getItem("bridge");
-    const std::string *tunnel = tags->getItem("tunnel");
-    const std::string *railway = tags->getItem("railway");
-    const std::string *boundary = tags->getItem("boundary");
+void add_z_order(taglist_t &tags, int *roads)
+{
+    const std::string *layer = tags.get("layer");
+    const std::string *highway = tags.get("highway");
+    bool bridge = tags.get_bool("bridge", false);
+    bool tunnel = tags.get_bool("tunnel", false);
+    const std::string *railway = tags.get("railway");
+    const std::string *boundary = tags.get("boundary");
 
     int z_order = 0;
-    char z[13];
 
     int l = layer ? strtol(layer->c_str(), NULL, 10) : 0;
     z_order = 10 * l;
@@ -54,6 +53,7 @@ int add_z_order(keyval *tags, int *roads) {
 
     if (highway) {
         for (unsigned i = 0; i < nLayers; i++) {
+            //if (layers[i].highway == *highway) {
             if (!strcmp(layers[i].highway, highway->c_str())) {
                 z_order += layers[i].offset;
                 *roads = layers[i].roads;
@@ -70,36 +70,34 @@ int add_z_order(keyval *tags, int *roads) {
     if (boundary && *boundary == "administrative")
         *roads = 1;
 
-    if (bridge  && (*bridge == "true" || *bridge == "yes" || *bridge == "1"))
+    if (bridge)
         z_order += 10;
 
-    if (tunnel && (*tunnel == "true" || *tunnel == "yes" || *tunnel == "1"))
+    if (tunnel)
         z_order -= 10;
 
+    char z[13];
     snprintf(z, sizeof(z), "%d", z_order);
-    tags->addItem("z_order", z, 0);
-
-    return 0;
+    tags.push_back(tag("z_order", z));
 }
 
-unsigned int c_filter_rel_member_tags(
-        keyval *rel_tags, const int member_count,
-        keyval *member_tags, const char * const *member_roles,
-        int * member_superseeded, int * make_boundary, int * make_polygon, int * roads,
-        const export_list *exlist, bool allow_typeless) {
-
-    struct keyval tags, *q, poly_tags;
-    int first_outerway, contains_tag;
-
+unsigned int c_filter_rel_member_tags(const taglist_t &rel_tags,
+        const multitaglist_t &member_tags, const rolelist_t &member_roles,
+        int *member_superseeded, int *make_boundary, int *make_polygon, int *roads,
+        const export_list &exlist, taglist_t &out_tags, bool allow_typeless)
+{
     //if it has a relation figure out what kind it is
-    const std::string *type = rel_tags->getItem("type");
+    const std::string *type = rel_tags.get("type");
     bool is_route = false, is_boundary = false, is_multipolygon = false;
     if (type)
     {
         //what kind of relation is it
-        is_route = *type == "route";
-        is_boundary = *type == "boundary";
-        is_multipolygon = *type == "multipolygon";
+        if (*type == "route")
+            is_route = true;
+        else if (*type == "boundary")
+            is_boundary = true;
+        else if (*type == "multipolygon")
+            is_multipolygon = true;
     }//you didnt have a type and it was required
     else if (!allow_typeless)
     {
@@ -107,21 +105,21 @@ unsigned int c_filter_rel_member_tags(
     }
 
     /* Clone tags from relation */
-    for (keyval *p = rel_tags->firstItem(); p; p = rel_tags->nextItem(p)) {
+    for (taglist_t::const_iterator it = rel_tags.begin(); it != rel_tags.end(); ++it) {
         //copy the name tag as "route_name"
-        if (is_route && (p->key == "name"))
-            tags.addItem("route_name", p->value, true);
+        if (is_route && (it->key == "name"))
+            out_tags.push_dedupe(tag("route_name", it->value));
         //copy all other tags except for "type"
-        else if (p->key != "type")
-            tags.addItem(p->key, p->value, true);
+        else if (it->key != "type")
+            out_tags.push_dedupe(*it);
     }
 
     if (is_route) {
-        const std::string *netw = rel_tags->getItem("network");
+        const std::string *netw = rel_tags.get("network");
         int networknr = -1;
 
         if (netw != NULL) {
-            const std::string *state = rel_tags->getItem("state");
+            const std::string *state = rel_tags.get("state");
             std::string statetype("yes");
             if (state) {
                 if (*state == "alternate")
@@ -131,52 +129,52 @@ unsigned int c_filter_rel_member_tags(
             }
             if (*netw == "lcn") {
                 networknr = 10;
-                tags.addItem("lcn", statetype, true);
+                out_tags.push_dedupe(tag("lcn", statetype));
             } else if (*netw == "rcn") {
                 networknr = 11;
-                tags.addItem("rcn", statetype, true);
+                out_tags.push_dedupe(tag("rcn", statetype));
             } else if (*netw == "ncn") {
                 networknr = 12;
-                tags.addItem("ncn", statetype, true);
+                out_tags.push_dedupe(tag("ncn", statetype));
             } else if (*netw == "lwn") {
                 networknr = 20;
-                tags.addItem("lwn", statetype, true);
+                out_tags.push_dedupe(tag("lwn", statetype));
             } else if (*netw == "rwn") {
                 networknr = 21;
-                tags.addItem("rwn", statetype, true);
+                out_tags.push_dedupe(tag("rwn", statetype));
             } else if (*netw == "nwn") {
                 networknr = 22;
-                tags.addItem("nwn", statetype, true);
+                out_tags.push_dedupe(tag("nwn", statetype));
             }
         }
 
-        const std::string *prefcol = rel_tags->getItem("preferred_color");
+        const std::string *prefcol = rel_tags.get("preferred_color");
         if (prefcol != NULL && prefcol->size() == 1) {
             if ((*prefcol)[0] == '0' || (*prefcol)[0] == '1'
                     || (*prefcol)[0] == '2' || (*prefcol)[0] == '3'
                     || (*prefcol)[0] == '4') {
-                tags.addItem("route_pref_color", *prefcol, true);
+                out_tags.push_dedupe(tag("route_pref_color", *prefcol));
             } else {
-                tags.addItem("route_pref_color", "0", true);
+                out_tags.push_dedupe(tag("route_pref_color", "0"));
             }
         } else {
-            tags.addItem("route_pref_color", "0", true);
+            out_tags.push_dedupe(tag("route_pref_color", "0"));
         }
 
-        const std::string *relref = rel_tags->getItem("ref");
+        const std::string *relref = rel_tags.get("ref");
         if (relref != NULL ) {
             if (networknr == 10) {
-                tags.addItem("lcn_ref", *relref, true);
+                out_tags.push_dedupe(tag("lcn_ref", *relref));
             } else if (networknr == 11) {
-                tags.addItem("rcn_ref", *relref, true);
+                out_tags.push_dedupe(tag("rcn_ref", *relref));
             } else if (networknr == 12) {
-                tags.addItem("ncn_ref", *relref, true);
+                out_tags.push_dedupe(tag("ncn_ref", *relref));
             } else if (networknr == 20) {
-                tags.addItem("lwn_ref", *relref, true);
+                out_tags.push_dedupe(tag("lwn_ref", *relref));
             } else if (networknr == 21) {
-                tags.addItem("rwn_ref", *relref, true);
+                out_tags.push_dedupe(tag("rwn_ref", *relref));
             } else if (networknr == 22) {
-                tags.addItem("nwn_ref", *relref, true);
+                out_tags.push_dedupe(tag("nwn_ref", *relref));
             }
         }
     } else if (is_boundary) {
@@ -185,7 +183,7 @@ unsigned int c_filter_rel_member_tags(
          - Polygon features also go into the polygon table (useful for national_forests)
          The edges of the polygon also get treated as linear fetaures allowing these to be rendered seperately. */
         *make_boundary = 1;
-    } else if (is_multipolygon && tags.getItem("boundary")) {
+    } else if (is_multipolygon && out_tags.contains("boundary")) {
         /* Treat type=multipolygon exactly like type=boundary if it has a boundary tag. */
         *make_boundary = 1;
     } else if (is_multipolygon) {
@@ -193,16 +191,17 @@ unsigned int c_filter_rel_member_tags(
 
         /* Collect a list of polygon-like tags, these are used later to
          identify if an inner rings looks like it should be rendered separately */
-        for (keyval *p = tags.firstItem(); p; p = tags.nextItem(p)) {
-            if (p->key == "area") {
-                poly_tags.addItem(p->key, p->value, true);
+        taglist_t poly_tags;
+        for (taglist_t::const_iterator it = out_tags.begin(); it != out_tags.end(); ++it) {
+            if (it->key == "area") {
+                poly_tags.push_back(*it);
             } else {
-                const std::vector<taginfo> &infos = exlist->get(OSMTYPE_WAY);
-                for (unsigned i = 0; i < infos.size(); i++) {
-                    const taginfo &info = infos[i];
-                    if (info.name == p->key) {
-                        if (info.flags & FLAG_POLYGON) {
-                            poly_tags.addItem(p->key, p->value, true);
+                const std::vector<taginfo> &infos = exlist.get(OSMTYPE_WAY);
+                for (std::vector<taginfo>::const_iterator info = infos.begin();
+                     info != infos.end(); ++info) {
+                    if (info->name == it->key) {
+                        if (info->flags & FLAG_POLYGON) {
+                            poly_tags.push_back(*it);
                         }
                         break;
                     }
@@ -212,73 +211,65 @@ unsigned int c_filter_rel_member_tags(
 
         /* Copy the tags from the outer way(s) if the relation is untagged (with
          * respect to tags that influence its polygon nature. Tags like name or fixme should be fine*/
-        if (!poly_tags.listHasData()) {
-            first_outerway = 1;
-            for (int i = 0; i < member_count; i++) {
-                if (member_roles[i] && !strcmp(member_roles[i], "inner"))
+        if (poly_tags.empty()) {
+            int first_outerway = 1;
+            for (size_t i = 0; i < member_tags.size(); i++) {
+                if (member_roles[i] && *(member_roles[i]) == "inner")
                     continue;
 
                 /* insert all tags of the first outerway to the potential list of copied tags. */
                 if (first_outerway) {
-                    for (keyval *p = member_tags[i].firstItem(); p; p = member_tags[i].nextItem(p))
-                        poly_tags.addItem(p->key, p->value, true);
+                    for (taglist_t::const_iterator it = member_tags[i].begin();
+                         it != member_tags[i].end(); ++it)
+                        poly_tags.push_back(*it);
                 } else {
                     /* Check if all of the tags in the list of potential tags are present on this way,
                        otherwise remove from the list of potential tags. Tags need to be present on
                        all outer ways to be copied over to the relation */
-                    q = poly_tags.firstItem();
-                    while (q) {
-                        const keyval *p = member_tags[i].getTag(q->key);
-                        if ((p != NULL) && (p->value == q->value)) {
-                            q = poly_tags.nextItem(q);
-                        } else {
+                    taglist_t::iterator it = poly_tags.begin();
+                    while (it != poly_tags.end()) {
+                        if (!member_tags[i].contains(it->key))
                             /* This tag is not present on all member outer ways, so don't copy it over to relation */
-                            keyval *qq = poly_tags.nextItem(q);
-                            q->removeTag();
-                            q = qq;
-                        }
+                            it = poly_tags.erase(it);
+                        else
+                            ++it;
                     }
                 }
                 first_outerway = 0;
             }
             /* Copy the list identified outer way tags over to the relation */
-            for (q = poly_tags.firstItem(); q; q = poly_tags.nextItem(q))
-                tags.addItem(q->key, q->value, true);
+            for (taglist_t::const_iterator it = poly_tags.begin(); it != poly_tags.end(); ++it)
+                out_tags.push_dedupe(*it);
 
             /* We need to re-check and only keep polygon tags in the list of polytags */
-            q = poly_tags.firstItem();
-            while (q) {
-                contains_tag = 0;
-                const std::vector<taginfo> &infos = exlist->get(OSMTYPE_WAY);
-                for (unsigned j = 0; j < infos.size(); j++) {
-                    const taginfo &info = infos[j];
-                    if (info.name == q->key) {
-                        if (info.flags & FLAG_POLYGON) {
-                            contains_tag = 1;
-                            break;
+            // TODO what is that for? The list is cleared just below.
+            taglist_t::iterator q = poly_tags.begin();
+            const std::vector<taginfo> &infos = exlist.get(OSMTYPE_WAY);
+            while (q != poly_tags.end()) {
+                bool contains_tag = false;
+                for (std::vector<taginfo>::const_iterator info = infos.begin();
+                     info != infos.end(); ++info) {
+                    if (info->name == q->key) {
+                        if (info->flags & FLAG_POLYGON) {
+                            contains_tag = true;
                         }
+                        break;
                     }
                 }
-                if (contains_tag == 0) {
-                    keyval *qq = poly_tags.nextItem(q);
-                    q->removeTag();
-                    q = qq;
-                } else {
-                    q = poly_tags.nextItem(q);
-                }
+
+                if (contains_tag)
+                    ++q;
+                else
+                    q = poly_tags.erase(q);
             }
         }
-        poly_tags.resetList();
     } else if(!allow_typeless) {
         /* Unknown type, just exit */
-        tags.resetList();
-        poly_tags.resetList();
+        out_tags.clear();
         return 1;
     }
 
-    if (!tags.listHasData()) {
-        tags.resetList();
-        poly_tags.resetList();
+    if (out_tags.empty()) {
         return 1;
     }
 
@@ -286,82 +277,70 @@ unsigned int c_filter_rel_member_tags(
      mark each member so that we can skip them during iterate_ways
      but only if the polygon-tags look the same as the outer ring */
     if (make_polygon) {
-        for (int i = 0; i < member_count; i++) {
-            int match = 1;
-            for (const keyval *p = member_tags[i].firstItem(); p; p = member_tags[i].nextItem(p)) {
-                const std::string *v = tags.getItem(p->key);
+        for (size_t i = 0; i < member_tags.size(); i++) {
+            member_superseeded[i] = 1;
+            for (taglist_t::const_iterator p = member_tags[i].begin();
+                 p != member_tags[i].end(); ++p) {
+                const std::string *v = out_tags.get(p->key);
                 if (!v || *v != p->value) {
                     /* z_order and osm_ are automatically generated tags, so ignore them */
                     if ((p->key != "z_order") && (p->key != "osm_user") &&
                         (p->key != "osm_version") && (p->key != "osm_uid") &&
                         (p->key != "osm_changeset") && (p->key != "osm_timestamp")) {
-                        match = 0;
+                        member_superseeded[i] = 0;
                         break;
                     }
                 }
             }
-            if (match) {
-                member_superseeded[i] = 1;
-            } else {
-                member_superseeded[i] = 0;
-            }
         }
     }
 
-    tags.moveList(rel_tags);
-
-    add_z_order(rel_tags, roads);
+    add_z_order(out_tags, roads);
 
     return 0;
 }
+} // anonymous namespace
 
 #ifdef HAVE_LUA
-unsigned int lua_filter_rel_member_tags(lua_State* L, const char* rel_mem_func, keyval *rel_tags, const int member_count,
-        keyval *member_tags,const char * const * member_roles,
-        int * member_superseeded, int * make_boundary, int * make_polygon, int * roads) {
-
-    int i;
-    int filter;
-    int count = 0;
-    struct keyval *item;
-    const char * key, * value;
-
-    lua_getglobal(L, rel_mem_func);
+unsigned tagtransform::lua_filter_rel_member_tags(const taglist_t &rel_tags,
+        const multitaglist_t &member_tags, const rolelist_t &member_roles,
+        int *member_superseeded, int *make_boundary, int *make_polygon, int *roads,
+        taglist_t &out_tags)
+{
+    lua_getglobal(L, m_rel_mem_func.c_str());
 
     lua_newtable(L);    /* relations key value table */
 
-    while( (item = rel_tags->popItem()) != NULL ) {
-        lua_pushstring(L, item->key.c_str());
-        lua_pushstring(L, item->value.c_str());
+    for (taglist_t::const_iterator it = rel_tags.begin(); it != rel_tags.end(); ++it) {
+        lua_pushstring(L, it->key.c_str());
+        lua_pushstring(L, it->value.c_str());
         lua_rawset(L, -3);
-        delete(item);
-        count++;
     }
 
     lua_newtable(L);    /* member tags table */
 
-    for (i = 1; i <= member_count; i++) {
-        lua_pushnumber(L, i);
+    int idx = 1;
+    for (multitaglist_t::const_iterator list = member_tags.begin();
+         list != member_tags.end(); ++list) {
+        lua_pushnumber(L, idx++);
         lua_newtable(L);    /* member key value table */
-        while( (item = member_tags[i - 1].popItem()) != NULL ) {
-            lua_pushstring(L, item->key.c_str());
-            lua_pushstring(L, item->value.c_str());
+        for (taglist_t::const_iterator it = list->begin(); it != list->end(); ++it) {
+            lua_pushstring(L, it->key.c_str());
+            lua_pushstring(L, it->value.c_str());
             lua_rawset(L, -3);
-            delete(item);
-            count++;
         }
         lua_rawset(L, -3);
     }
 
     lua_newtable(L);    /* member roles table */
 
-    for (i = 0; i < member_count; i++) {
+    for (size_t i = 0; i < member_roles.size(); i++) {
         lua_pushnumber(L, i + 1);
-        lua_pushstring(L, member_roles[i]);
+        lua_pushstring(L, member_roles[i]->c_str());
         lua_rawset(L, -3);
     }
 
-    lua_pushnumber(L, member_count);
+    lua_pushnumber(L, member_roles.size());
 
     if (lua_pcall(L,4,6,0)) {
         fprintf(stderr, "Failed to execute lua function for relation tag processing: %s\n", lua_tostring(L, -1));
@@ -377,7 +356,7 @@ unsigned int lua_filter_rel_member_tags(lua_State* L, const char* rel_mem_func,
     lua_pop(L,1);
 
     lua_pushnil(L);
-    for (i = 0; i < member_count; i++) {
+    for (size_t i = 0; i < member_tags.size(); i++) {
         if (lua_next(L,-2)) {
             member_superseeded[i] = lua_tointeger(L,-1);
             lua_pop(L,1);
@@ -389,21 +368,22 @@ unsigned int lua_filter_rel_member_tags(lua_State* L, const char* rel_mem_func,
 
     lua_pushnil(L);
     while (lua_next(L,-2) != 0) {
-        key = lua_tostring(L,-2);
-        value = lua_tostring(L,-1);
-        rel_tags->addItem(key, value, false);
+        const char *key = lua_tostring(L,-2);
+        const char *value = lua_tostring(L,-1);
+        out_tags.push_back(tag(key, value));
         lua_pop(L,1);
     }
     lua_pop(L,1);
 
-    filter = lua_tointeger(L, -1);
+    int filter = lua_tointeger(L, -1);
 
     lua_pop(L,1);
 
     return filter;
 }
 
-void check_lua_function_exists(lua_State *L, const std::string &func_name) {
+void tagtransform::check_lua_function_exists(const std::string &func_name)
+{
     lua_getglobal(L, func_name.c_str());
     if (!lua_isfunction (L, -1)) {
         throw std::runtime_error((boost::format("Tag transform style does not contain a function %1%")
@@ -412,7 +392,6 @@ void check_lua_function_exists(lua_State *L, const std::string &func_name) {
     lua_pop(L,1);
 }
 #endif
-} // anonymous namespace
 
 tagtransform::tagtransform(const options_t *options_)
     : options(options_), transform_method(options_->tag_transform_script)
@@ -423,7 +402,7 @@ tagtransform::tagtransform(const options_t *options_)
     , m_rel_func(    options->tag_transform_rel_func.    get_value_or("filter_basic_tags_rel"))
     , m_rel_mem_func(options->tag_transform_rel_mem_func.get_value_or("filter_tags_relation_member"))
 #endif /* HAVE_LUA */
- {
+{
 	if (transform_method) {
                 fprintf(stderr, "Using lua based tag processing pipeline with script %s\n", options->tag_transform_script->c_str());
 #ifdef HAVE_LUA
@@ -431,10 +410,10 @@ tagtransform::tagtransform(const options_t *options_)
 		luaL_openlibs(L);
 		luaL_dofile(L, options->tag_transform_script->c_str());
 
-                check_lua_function_exists(L, m_node_func);
-                check_lua_function_exists(L, m_way_func);
-                check_lua_function_exists(L, m_rel_func);
-                check_lua_function_exists(L, m_rel_mem_func);
+        check_lua_function_exists(m_node_func);
+        check_lua_function_exists(m_way_func);
+        check_lua_function_exists(m_rel_func);
+        check_lua_function_exists(m_rel_mem_func);
 #else
 		throw std::runtime_error("Error: Could not init lua tag transform, as lua support was not compiled into this version");
 #endif
@@ -450,61 +429,59 @@ tagtransform::~tagtransform() {
 #endif
 }
 
-unsigned int tagtransform::filter_node_tags(struct keyval *tags, const export_list *exlist, bool strict) {
-    int poly, roads;
+unsigned int tagtransform::filter_node_tags(const taglist_t &tags, const export_list &exlist,
+                                            taglist_t &out_tags, bool strict)
+{
     if (transform_method) {
-        return lua_filter_basic_tags(OSMTYPE_NODE, tags, &poly, &roads);
+        return lua_filter_basic_tags(OSMTYPE_NODE, tags, 0, 0, out_tags);
     } else {
-        return c_filter_basic_tags(OSMTYPE_NODE, tags, &poly, &roads, exlist, strict);
+        return c_filter_basic_tags(OSMTYPE_NODE, tags, 0, 0, exlist, out_tags, strict);
     }
 }
 
 /*
  * This function gets called twice during initial import per way. Once from add_way and once from out_way
  */
-unsigned int tagtransform::filter_way_tags(struct keyval *tags, int * polygon, int * roads,
-                                          const export_list *exlist, bool strict) {
+unsigned tagtransform::filter_way_tags(const taglist_t &tags, int *polygon, int *roads,
+                                       const export_list &exlist, taglist_t &out_tags, bool strict)
+{
     if (transform_method) {
-#ifdef HAVE_LUA
-        return lua_filter_basic_tags(OSMTYPE_WAY, tags, polygon, roads);
-#else
-        return 1;
-#endif
+        return lua_filter_basic_tags(OSMTYPE_WAY, tags, polygon, roads, out_tags);
     } else {
-        return c_filter_basic_tags(OSMTYPE_WAY, tags, polygon, roads, exlist, strict);
+        return c_filter_basic_tags(OSMTYPE_WAY, tags, polygon, roads, exlist, out_tags, strict);
     }
 }
 
-unsigned int tagtransform::filter_rel_tags(struct keyval *tags, const export_list *exlist, bool strict) {
-    int poly, roads;
+unsigned tagtransform::filter_rel_tags(const taglist_t &tags, const export_list &exlist,
+                                       taglist_t &out_tags, bool strict)
+{
     if (transform_method) {
-        return lua_filter_basic_tags(OSMTYPE_RELATION, tags, &poly, &roads);
+        return lua_filter_basic_tags(OSMTYPE_RELATION, tags, 0, 0, out_tags);
     } else {
-        return c_filter_basic_tags(OSMTYPE_RELATION, tags, &poly, &roads, exlist, strict);
+        return c_filter_basic_tags(OSMTYPE_RELATION, tags, 0, 0, exlist, out_tags, strict);
     }
 }
 
-unsigned int tagtransform::filter_rel_member_tags(struct keyval *rel_tags, int member_count, struct keyval *member_tags,const char * const * member_roles, int * member_superseeded, int * make_boundary, int * make_polygon, int * roads, const export_list *exlist, bool allow_typeless) {
+unsigned tagtransform::filter_rel_member_tags(const taglist_t &rel_tags,
+        const multitaglist_t &member_tags, const rolelist_t &member_roles,
+        int *member_superseeded, int *make_boundary, int *make_polygon, int *roads,
+        const export_list &exlist, taglist_t &out_tags, bool allow_typeless)
+{
     if (transform_method) {
 #ifdef HAVE_LUA
-        return lua_filter_rel_member_tags(L, m_rel_mem_func.c_str(), rel_tags, member_count, member_tags, member_roles, member_superseeded, make_boundary, make_polygon, roads);
+        return lua_filter_rel_member_tags(rel_tags, member_tags, member_roles, member_superseeded, make_boundary, make_polygon, roads, out_tags);
 #else
         return 1;
 #endif
     } else {
-        return c_filter_rel_member_tags(rel_tags, member_count, member_tags, member_roles, member_superseeded, make_boundary, make_polygon, roads, exlist, allow_typeless);
+        return c_filter_rel_member_tags(rel_tags, member_tags, member_roles, member_superseeded, make_boundary, make_polygon, roads, exlist, out_tags, allow_typeless);
     }
 }
 
-unsigned int tagtransform::lua_filter_basic_tags(const OsmType type, keyval *tags, int * polygon, int * roads) {
+unsigned tagtransform::lua_filter_basic_tags(OsmType type, const taglist_t &tags,
+                                             int *polygon, int *roads, taglist_t &out_tags)
+{
 #ifdef HAVE_LUA
-    int filter;
-    int count = 0;
-    struct keyval *item;
-    const char * key, * value;
-
-    *polygon = 0; *roads = 0;
-
     switch (type) {
     case OSMTYPE_NODE: {
         lua_getglobal(L, m_node_func.c_str());
@@ -522,16 +499,13 @@ unsigned int tagtransform::lua_filter_basic_tags(const OsmType type, keyval *tag
 
     lua_newtable(L);    /* key value table */
 
-    while( (item = tags->popItem()) != NULL ) {
-        lua_pushstring(L, item->key.c_str());
-        lua_pushstring(L, item->value.c_str());
+    for (taglist_t::const_iterator it = tags.begin(); it != tags.end(); ++it) {
+        lua_pushstring(L, it->key.c_str());
+        lua_pushstring(L, it->value.c_str());
         lua_rawset(L, -3);
-        delete(item);
-        count++;
     }
 
-    //printf("C count %i\n", count);
-    lua_pushinteger(L, count);
+    lua_pushinteger(L, tags.size());
 
     if (lua_pcall(L,2,type == OSMTYPE_WAY ? 4 : 2,0)) {
         fprintf(stderr, "Failed to execute lua function for basic tag processing: %s\n", lua_tostring(L, -1));
@@ -540,21 +514,23 @@ unsigned int tagtransform::lua_filter_basic_tags(const OsmType type, keyval *tag
     }
 
     if (type == OSMTYPE_WAY) {
+        assert(roads);
         *roads = lua_tointeger(L, -1);
         lua_pop(L,1);
+        assert(polygon);
         *polygon = lua_tointeger(L, -1);
         lua_pop(L,1);
     }
 
     lua_pushnil(L);
     while (lua_next(L,-2) != 0) {
-        key = lua_tostring(L,-2);
-        value = lua_tostring(L,-1);
-        tags->addItem(key, value, false);
+        const char *key = lua_tostring(L,-2);
+        const char *value = lua_tostring(L,-1);
+        out_tags.push_back(tag(key, value));
         lua_pop(L,1);
     }
 
-    filter = lua_tointeger(L, -2);
+    int filter = lua_tointeger(L, -2);
 
     lua_pop(L,2);
 
@@ -566,78 +542,69 @@ unsigned int tagtransform::lua_filter_basic_tags(const OsmType type, keyval *tag
 
 /* Go through the given tags and determine the union of flags. Also remove
  * any tags from the list that we don't know about */
-unsigned int tagtransform::c_filter_basic_tags(
-    const OsmType type, keyval *tags, int *polygon, int * roads,
-    const export_list *exlist, bool strict) {
-
+unsigned int tagtransform::c_filter_basic_tags(OsmType type, const taglist_t &tags, int *polygon,
+                                               int *roads, const export_list &exlist,
+                                               taglist_t &out_tags, bool strict)
+{
     //assume we dont like this set of tags
     int filter = 1;
 
     int flags = 0;
     int add_area_tag = 0;
 
-    //a place to keep the tags we like as we go
-    struct keyval temp;
-
-    enum OsmType export_type;
+    OsmType export_type;
     if (type == OSMTYPE_RELATION) {
         export_type = OSMTYPE_WAY;
     } else {
         export_type = type;
     }
+    const std::vector<taginfo> &infos = exlist.get(export_type);
 
-    /* We used to only go far enough to determine if it's a polygon or not, but now we go through and filter stuff we don't need */
-    //pop each tag off and keep it in the temp list if we like it
-    struct keyval *item;
-    while ((item = tags->popItem()) != NULL ) {
+    /* We used to only go far enough to determine if it's a polygon or not,
+       but now we go through and filter stuff we don't need
+       pop each tag off and keep it in the temp list if we like it */
+    for (taglist_t::const_iterator item = tags.begin(); item != tags.end(); ++item) {
         //if we want to do more than the export list says
         if(!strict) {
             if (type == OSMTYPE_RELATION && "type" == item->key) {
-                temp.pushItem(item);
-                item = NULL;
+                out_tags.push_back(*item);
                 filter = 0;
                 continue;
             }
             /* Allow named islands to appear as polygons */
             if ("natural" == item->key && "coastline" == item->value) {
                 add_area_tag = 1;
-            }
 
-            /* Discard natural=coastline tags (we render these from a shapefile instead) */
-            if (!options->keep_coastlines && "natural" == item->key
-                    && "coastline" == item->value) {
-                delete(item);
-                item = NULL;
-                continue;
+                /* Discard natural=coastline tags (we render these from a shapefile instead) */
+                if (!options->keep_coastlines) {
+                    continue;
+                }
             }
         }
 
         //go through the actual tags found on the item and keep the ones in the export list
-        const std::vector<taginfo> &infos = exlist->get(export_type);
         size_t i = 0;
         for (; i < infos.size(); i++) {
             const taginfo &info = infos[i];
             if (wildMatch(info.name.c_str(), item->key.c_str())) {
                 if (info.flags & FLAG_DELETE) {
-                    delete(item);
-                    item = NULL;
                     break;
                 }
 
                 filter = 0;
                 flags |= info.flags;
 
-                temp.pushItem(item);
-                item = NULL;
+                out_tags.push_back(*item);
                 break;
             }
         }
 
-        //if we didnt find any tags that we wanted to export and we aren't strictly adhering to the list
+        // if we didn't find any tags that we wanted to export
+        // and we aren't strictly adhering to the list
         if (i == infos.size() && !strict) {
             if (options->hstore_mode != HSTORE_NONE) {
                 /* with hstore, copy all tags... */
-                temp.pushItem(item);
+                out_tags.push_back(*item);
                 /* ... but if hstore_match_only is set then don't take this
                  as a reason for keeping the object */
                 if (!options->hstore_match_only && "osm_uid" != item->key
@@ -652,7 +619,7 @@ unsigned int tagtransform::c_filter_basic_tags(
                 for(; j < options->hstore_columns.size(); ++j) {
                     size_t pos = item->key.find(options->hstore_columns[j]);
                     if (pos == 0) {
-                        temp.pushItem(item);
+                        out_tags.push_back(*item);
                         /* ... but if hstore_match_only is set then don't take this
                          as a reason for keeping the object */
                         if (!options->hstore_match_only
@@ -665,40 +632,22 @@ unsigned int tagtransform::c_filter_basic_tags(
                         break;
                     }
                 }
-                /* if not, skip the tag */
-                if (j == options->hstore_columns.size()) {
-                    delete(item);
-                }
-            } else {
-                delete(item);
-            }
-            item = NULL;
+            } 
         }
     }
 
-    /* Move from temp list back to original list */
-    while ((item = temp.popItem()) != NULL )
-        tags->pushItem(item);
-
-    *polygon = flags & FLAG_POLYGON;
-
-    /* Special case allowing area= to override anything else */
-    const std::string *area;
-    if ((area = tags->getItem("area"))) {
-        if (*area == "yes" || *area == "true" || *area == "1")
-            *polygon = 1;
-        else if (*area == "no" || *area == "false" || *area == "0")
-            *polygon = 0;
-    } else {
-        /* If we need to force this as a polygon, append an area tag */
+    if (polygon) {
         if (add_area_tag) {
-            tags->addItem("area", "yes", false);
+            /* If we need to force this as a polygon, append an area tag */
+            out_tags.push_dedupe(tag("area", "yes"));
             *polygon = 1;
+        } else {
+            *polygon = tags.get_bool("area", flags & FLAG_POLYGON);
         }
     }
 
-    if (!filter && (type == OSMTYPE_WAY)) {
-        add_z_order(tags,roads);
+    if (roads && !filter && (type == OSMTYPE_WAY)) {
+        add_z_order(out_tags, roads);
     }
 
     return filter;
diff --git a/tagtransform.hpp b/tagtransform.hpp
index 7ad1b7b..a64b688 100644
--- a/tagtransform.hpp
+++ b/tagtransform.hpp
@@ -20,18 +20,29 @@ public:
 	tagtransform(const options_t *options_);
 	~tagtransform();
 
-	unsigned int filter_node_tags(keyval *tags, const export_list *exlist, bool strict = false);
-	unsigned int filter_way_tags(keyval *tags, int * polygon, int * roads, const export_list *exlist, bool strict = false);
-	unsigned int filter_rel_tags(keyval *tags, const export_list *exlist, bool strict = false);
-	unsigned int filter_rel_member_tags(keyval *rel_tags, int member_count,
-		keyval *member_tags, const char * const * member_roles, int * member_superseeded,
-		int * make_boundary, int * make_polygon, int * roads, const export_list *exlist,
-		bool allow_typeless = false);
+    unsigned filter_node_tags(const taglist_t &tags, const export_list &exlist,
+                              taglist_t &out_tags, bool strict = false);
+    unsigned filter_way_tags(const taglist_t &tags, int *polygon, int *roads,
+                             const export_list &exlist, taglist_t &out_tags, bool strict = false);
+    unsigned filter_rel_tags(const taglist_t &tags, const export_list &exlist,
+                             taglist_t &out_tags, bool strict = false);
+    unsigned filter_rel_member_tags(const taglist_t &rel_tags,
+        const multitaglist_t &member_tags, const rolelist_t &member_roles,
+        int *member_superseeded, int *make_boundary, int *make_polygon, int *roads,
+        const export_list &exlist, taglist_t &out_tags, bool allow_typeless = false);
 
 private:
-	unsigned int lua_filter_basic_tags(const OsmType type, keyval *tags, int * polygon, int * roads);
-	unsigned int c_filter_basic_tags(const OsmType type, keyval *tags, int *polygon, int * roads,
-	    const export_list *exlist, bool strict);
+    unsigned lua_filter_basic_tags(OsmType type, const taglist_t &tags,
+                                   int *polygon, int *roads, taglist_t &out_tags);
+    unsigned c_filter_basic_tags(OsmType type, const taglist_t &tags, int *polygon,
+                                 int *roads, const export_list &exlist,
+                                 taglist_t &out_tags, bool strict);
+    unsigned int lua_filter_rel_member_tags(const taglist_t &rel_tags,
+        const multitaglist_t &member_tags, const rolelist_t &member_roles,
+        int *member_superseeded, int *make_boundary, int *make_polygon, int *roads,
+        taglist_t &out_tags);
+    void check_lua_function_exists(const std::string &func_name);
+
 
 	const options_t* options;
 	const bool transform_method;
diff --git a/tests/middle-tests.cpp b/tests/middle-tests.cpp
index 17be1c2..0e20623 100644
--- a/tests/middle-tests.cpp
+++ b/tests/middle-tests.cpp
@@ -6,35 +6,36 @@
 #include <list>
 
 #include "osmtypes.hpp"
-#include "keyvals.hpp"
 #include "tests/middle-tests.hpp"
 
 int test_node_set(middle_t *mid)
 {
+  idlist_t ids;
   osmid_t id = 1234;
   double lat = 12.3456789;
   double lon = 98.7654321;
-  struct keyval tags;
-  struct osmNode node;
+  taglist_t tags;
+  nodelist_t nodes;
   int status = 0;
 
   // set the node
-  status = mid->nodes_set(id, lat, lon, &tags);
+  status = mid->nodes_set(id, lat, lon, tags);
   if (status != 0) { std::cerr << "ERROR: Unable to set node.\n"; return 1; }
 
   // get it back
-  int count = mid->nodes_get_list(&node, &id, 1);
+  ids.push_back(id);
+  int count = mid->nodes_get_list(nodes, ids);
   if (count != 1) { std::cerr << "ERROR: Unable to get node list.\n"; return 1; }
 
   // check that it's the same
-  if (node.lon != lon) {
+  if (nodes[0].lon != lon) {
     std::cerr << "ERROR: Node should have lon=" << lon << ", but got back "
-              << node.lon << " from middle.\n";
+              << nodes[0].lon << " from middle.\n";
     return 1;
   }
-  if (node.lat != lat) {
+  if (nodes[0].lat != lat) {
     std::cerr << "ERROR: Node should have lat=" << lat << ", but got back "
-              << node.lat << " from middle.\n";
+              << nodes[0].lat << " from middle.\n";
     return 1;
   }
 
@@ -43,8 +44,6 @@ int test_node_set(middle_t *mid)
     dynamic_cast<slim_middle_t *>(mid)->nodes_delete(id);
   }
 
-  tags.resetList();
-
   return 0;
 }
 
@@ -78,49 +77,52 @@ int test_way_set(middle_t *mid)
   osmid_t way_id = 1;
   double lat = 12.3456789;
   double lon = 98.7654321;
-  struct keyval tags;
+  taglist_t tags;
   struct osmNode *node_ptr = NULL;
-  osmid_t way_ids_ptr;
-  int node_count = 0;
   int status = 0;
-  osmid_t nds[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
-  const int nd_count = ((sizeof nds) / (sizeof nds[0]));
+  idlist_t nds;
+  for (osmid_t i = 1; i <= 10; ++i)
+      nds.push_back(i);
 
   // set the nodes
-  for (int i = 0; i < nd_count; ++i) {
-    status = mid->nodes_set(nds[i], lat, lon, &tags);
+  for (size_t i = 0; i < nds.size(); ++i) {
+    status = mid->nodes_set(nds[i], lat, lon, tags);
     if (status != 0) { std::cerr << "ERROR: Unable to set node " << nds[i] << ".\n"; return 1; }
   }
 
   // set the way
-  status = mid->ways_set(way_id, nds, nd_count, &tags);
+  status = mid->ways_set(way_id, nds, tags);
   if (status != 0) { std::cerr << "ERROR: Unable to set way.\n"; return 1; }
 
   // commit the setup data
   mid->commit();
 
   // get it back
-  int way_count = mid->ways_get_list(&way_id, 1, &way_ids_ptr, &tags, &node_ptr, &node_count);
+  idlist_t ways, xways;
+  ways.push_back(way_id);
+  std::vector<taglist_t> xtags;
+  multinodelist_t xnodes;
+  int way_count = mid->ways_get_list(ways, xways, xtags, xnodes);
   if (way_count != 1) { std::cerr << "ERROR: Unable to get way list.\n"; return 1; }
 
   // check that it's the same
-  if (node_count != nd_count) {
-    std::cerr << "ERROR: Way should have " << nd_count << " nodes, but got back "
-              << node_count << " from middle.\n";
+  if (xnodes[0].size() != nds.size()) {
+    std::cerr << "ERROR: Way should have " << nds.size() << " nodes, but got back "
+              << xnodes[0].size() << " from middle.\n";
     return 1;
   }
-  if (way_ids_ptr != way_id) {
+  if (xways[0] != way_id) {
     std::cerr << "ERROR: Way should have id=" << way_id << ", but got back "
-              << way_ids_ptr << " from middle.\n";
+              << xways[0] << " from middle.\n";
     return 1;
   }
-  for (int i = 0; i < nd_count; ++i) {
-    if (node_ptr[i].lon != lon) {
+  for (size_t i = 0; i < nds.size(); ++i) {
+    if (xnodes[0][i].lon != lon) {
       std::cerr << "ERROR: Way node should have lon=" << lon << ", but got back "
                 << node_ptr[i].lon << " from middle.\n";
       return 1;
     }
-    if (node_ptr[i].lat != lat) {
+    if (xnodes[0][i].lat != lat) {
       std::cerr << "ERROR: Way node should have lat=" << lat << ", but got back "
                 << node_ptr[i].lat << " from middle.\n";
       return 1;
@@ -155,14 +157,11 @@ int test_way_set(middle_t *mid)
       }
   }
 
-  tags.resetList();
-  free(node_ptr);
-
   // clean up for next test
   if (dynamic_cast<slim_middle_t *>(mid)) {
       slim_middle_t *slim = dynamic_cast<slim_middle_t *>(mid);
 
-      for (int i = 0; i < nd_count; ++i) {
+      for (size_t i = 0; i < nds.size(); ++i) {
           slim->nodes_delete(nds[i]);
       }
       slim->ways_delete(way_id);
diff --git a/tests/regression-test.py b/tests/regression-test.py
index fca7b33..9244b2a 100755
--- a/tests/regression-test.py
+++ b/tests/regression-test.py
@@ -34,9 +34,9 @@ sql_test_statements=[
     ( 15, 'Gazetteer place node count', 'SELECT count(*) FROM place WHERE osm_type = \'N\'', 759),
     ( 16, 'Gazetteer place way count', 'SELECT count(*) FROM place WHERE osm_type = \'W\'', 2059),
     ( 17, 'Gazetteer place rel count', 'SELECT count(*) FROM place WHERE osm_type = \'R\'', 19),
-    ( 18, 'Gazetteer post-diff place count', 'SELECT count(*) FROM place', 2879),
+    ( 18, 'Gazetteer post-diff place count', 'SELECT count(*) FROM place', 2878),
     ( 19, 'Gazetteer post-diff place node count', 'SELECT count(*) FROM place WHERE osm_type = \'N\'', 764),
-    ( 20, 'Gazetteer post-diff place way count', 'SELECT count(*) FROM place WHERE osm_type = \'W\'', 2096),
+    ( 20, 'Gazetteer post-diff place way count', 'SELECT count(*) FROM place WHERE osm_type = \'W\'', 2095),
     ( 21, 'Gazetteer post-diff place rel count', 'SELECT count(*) FROM place WHERE osm_type = \'R\'', 19),
     ( 22, 'Gazetteer housenumber count', 'SELECT count(*) FROM place WHERE housenumber is not null', 199),
     ( 23, 'Gazetteer post-diff housenumber count count', 'SELECT count(*) FROM place WHERE housenumber is not null', 199),
@@ -336,7 +336,7 @@ class BaseTestCase(unittest.TestCase):
             self.conn=psycopg2.connect("dbname='osm2pgsql-test'")
             self.conn.autocommit = True
             self.cur = self.conn.cursor()
-        except Exception, e:
+        except Exception as e:
             print "I am unable to connect to the database." + e
 
     def dbClose(self):
@@ -352,7 +352,7 @@ class BaseTestCase(unittest.TestCase):
                 try:
                     self.cur.execute(sql_test_statements[i][2])
                     res = self.cur.fetchall()
-                except Exception, e:
+                except Exception as e:
                     self.assertEqual(0, 1, str(sql_test_statements[i][0]) + ": Failed to execute " + sql_test_statements[i][1] +
                                      " (" + sql_test_statements[i][2] + ") {" + str(self.parameters) +"}")
                 if (res == None):
@@ -507,13 +507,13 @@ def setupDB():
     try:
         gen_conn=psycopg2.connect("dbname='template1'")
         gen_conn.autocommit = True
-    except Exception, e:
+    except Exception as e:
         print "I am unable to connect to the database."
         exit(1)
 
     try:
         gen_cur = gen_conn.cursor()
-    except Exception, e:
+    except Exception as e:
         gen_conn.close()
         print "I am unable to connect to the database."
         exit(1)
@@ -521,7 +521,7 @@ def setupDB():
     try:
         gen_cur.execute("""DROP DATABASE IF EXISTS \"osm2pgsql-test\"""")
         gen_cur.execute("""CREATE DATABASE \"osm2pgsql-test\" WITH ENCODING 'UTF8'""")
-    except Exception, e:
+    except Exception as e:
         print "Failed to create osm2pgsql-test db" + e.pgerror
         exit(1);
     finally:
@@ -531,13 +531,13 @@ def setupDB():
     try:
         test_conn=psycopg2.connect("dbname='osm2pgsql-test'")
         test_conn.autocommit = True
-    except Exception, e:
+    except Exception as e:
         print "I am unable to connect to the database." + e
         exit(1)
 
     try:
         test_cur = test_conn.cursor()
-    except Exception, e:
+    except Exception as e:
         print "I am unable to connect to the database." + e
         gen_conn.close()
         exit(1)
@@ -558,7 +558,7 @@ def setupDB():
                 print "  sudo /bin/chown postgres.postgres tmp/psql-tablespace"
                 print "  psql -c \"CREATE TABLESPACE tablespacetest LOCATION '/tmp/psql-tablespace'\" postgres"
                 exit(77)
-        except Exception, e:
+        except Exception as e:
             print "Failed to create directory for tablespace" + str(e)
 
         # Check for postgis
@@ -578,7 +578,7 @@ def setupDB():
         # Check for hstore support
         try:
             test_cur.execute("""CREATE EXTENSION hstore;""")
-        except Exception, e:
+        except Exception as e:
             hst = findContribSql('hstore.sql')
             pgscript = open(hst).read()
             test_cur.execute(pgscript)
@@ -593,7 +593,7 @@ def tearDownDB():
         gen_conn=psycopg2.connect("dbname='template1'")
         gen_conn.autocommit = True
         gen_cur = gen_conn.cursor()
-    except Exception, e:
+    except Exception as e:
         print "I am unable to connect to the database."
         exit(1)
 
@@ -601,7 +601,7 @@ def tearDownDB():
         gen_cur.execute("""DROP DATABASE IF EXISTS \"osm2pgsql-test\"""")
         if (created_tablespace == 1):
             gen_cur.execute("""DROP TABLESPACE IF EXISTS \"tablespacetest\"""")
-    except Exception, e:
+    except Exception as e:
         print "Failed to clean up osm2pgsql-test db" + e.pgerror
         exit(1);
 
diff --git a/tests/test-expire-tiles.cpp b/tests/test-expire-tiles.cpp
index 62f8fe5..4bc0b43 100644
--- a/tests/test-expire-tiles.cpp
+++ b/tests/test-expire-tiles.cpp
@@ -19,7 +19,7 @@ void run_test(const char* test_name, void (*testfunc)())
         fprintf(stderr, "%s\n", test_name);
         testfunc();
     }
-    catch(std::exception& e)
+    catch(const std::exception& e)
     {
         fprintf(stderr, "%s\n", e.what());
         fprintf(stderr, "FAIL\n");
diff --git a/tests/test-output-multi-line-storage.cpp b/tests/test-output-multi-line-storage.cpp
new file mode 100644
index 0000000..fa9d750
--- /dev/null
+++ b/tests/test-output-multi-line-storage.cpp
@@ -0,0 +1,112 @@
+#include <iostream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+#include <memory>
+
+#include "osmtypes.hpp"
+#include "middle.hpp"
+#include "output-multi.hpp"
+#include "options.hpp"
+#include "middle-pgsql.hpp"
+#include "taginfo_impl.hpp"
+#include "parse.hpp"
+
+#include <libpq-fe.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "tests/middle-tests.hpp"
+#include "tests/common-pg.hpp"
+
+void check_count(pg::conn_ptr &conn, int expected, const std::string &query) {
+    pg::result_ptr res = conn->exec(query);
+
+    int ntuples = PQntuples(res->get());
+    if (ntuples != 1) {
+        throw std::runtime_error((boost::format("Expected only one tuple from a query "
+                                                "to check COUNT(*), but got %1%. Query "
+                                                "was: %2%.")
+                                  % ntuples % query).str());
+    }
+
+    std::string numstr = PQgetvalue(res->get(), 0, 0);
+    int count = boost::lexical_cast<int>(numstr);
+
+    if (count != expected) {
+        throw std::runtime_error((boost::format("Expected %1%, but got %2%, when running "
+                                                "query: %3%.")
+                                  % expected % count % query).str());
+    }
+}
+
+int main(int argc, char *argv[]) {
+    boost::scoped_ptr<pg::tempdb> db;
+
+    try {
+        db.reset(new pg::tempdb);
+    } catch (const std::exception &e) {
+        std::cerr << "Unable to setup database: " << e.what() << "\n";
+        return 77; // <-- code to skip this test.
+    }
+
+    try {
+        options_t options;
+        options.conninfo = db->conninfo().c_str();
+        options.num_procs = 1;
+        options.slim = 1;
+
+        options.projection.reset(new reprojection(PROJ_LATLONG));
+
+        options.output_backend = "multi";
+        options.style = "tests/test_output_multi_line_trivial.style.json";
+
+        //setup the front (input)
+        parse_delegate_t parser(options.extra_attributes, options.bbox, options.projection);
+
+        //setup the middle
+        boost::shared_ptr<middle_t> middle = middle_t::create_middle(options.slim);
+
+        //setup the backend (output)
+        std::vector<boost::shared_ptr<output_t> > outputs = output_t::create_outputs(middle.get(), options);
+
+        //let osmdata orchestrate between the middle and the outs
+        osmdata_t osmdata(middle, outputs);
+
+        osmdata.start();
+
+        if (parser.streamFile("libxml2", "tests/test_output_multi_line_storage.osm", options.sanitize, &osmdata) != 0) {
+            throw std::runtime_error("Unable to read input file `tests/test_output_multi_line_storage.osm'.");
+        }
+
+        osmdata.stop();
+
+        // start a new connection to run tests on
+        pg::conn_ptr test_conn = pg::conn::connect(db->conninfo());
+
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'test_line'");
+        check_count(test_conn, 3, "select count(*) from test_line");
+
+        //check that we have the number of vertexes in each linestring
+        check_count(test_conn, 3, "SELECT ST_NumPoints(way) FROM test_line WHERE osm_id = 1");
+        check_count(test_conn, 2, "SELECT ST_NumPoints(way) FROM test_line WHERE osm_id = 2");
+        check_count(test_conn, 2, "SELECT ST_NumPoints(way) FROM test_line WHERE osm_id = 3");
+
+        check_count(test_conn, 3, "SELECT COUNT(*) FROM test_line WHERE foo = 'bar'");
+        return 0;
+
+    } catch (const std::exception &e) {
+        std::cerr << "ERROR: " << e.what() << std::endl;
+
+    } catch (...) {
+        std::cerr << "UNKNOWN ERROR" << std::endl;
+    }
+
+    return 1;
+}
diff --git a/tests/test-output-multi-poly-trivial.cpp b/tests/test-output-multi-poly-trivial.cpp
new file mode 100644
index 0000000..dc609cd
--- /dev/null
+++ b/tests/test-output-multi-poly-trivial.cpp
@@ -0,0 +1,137 @@
+#include <iostream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+#include <memory>
+
+#include "osmtypes.hpp"
+#include "middle.hpp"
+#include "output-multi.hpp"
+#include "options.hpp"
+#include "middle-pgsql.hpp"
+#include "taginfo_impl.hpp"
+#include "parse.hpp"
+
+#include <libpq-fe.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "tests/middle-tests.hpp"
+#include "tests/common-pg.hpp"
+
+void check_count(pg::conn_ptr &conn, int expected, const std::string &query) {
+    pg::result_ptr res = conn->exec(query);
+
+    int ntuples = PQntuples(res->get());
+    if (ntuples != 1) {
+        throw std::runtime_error((boost::format("Expected only one tuple from a query "
+                                                "to check COUNT(*), but got %1%. Query "
+                                                "was: %2%.")
+                                  % ntuples % query).str());
+    }
+
+    std::string numstr = PQgetvalue(res->get(), 0, 0);
+    int count = boost::lexical_cast<int>(numstr);
+
+    if (count != expected) {
+        throw std::runtime_error((boost::format("Expected %1%, but got %2%, when running "
+                                                "query: %3%.")
+                                  % expected % count % query).str());
+    }
+}
+
+void run_osm2pgsql(options_t &options) {
+  //setup the front (input)
+  parse_delegate_t parser(options.extra_attributes, options.bbox, options.projection);
+
+  //setup the middle
+  boost::shared_ptr<middle_t> middle = middle_t::create_middle(options.slim);
+
+  //setup the backend (output)
+  std::vector<boost::shared_ptr<output_t> > outputs = output_t::create_outputs(middle.get(), options);
+
+  //let osmdata orchestrate between the middle and the outs
+  osmdata_t osmdata(middle, outputs);
+
+  osmdata.start();
+
+  if (parser.streamFile("libxml2", "tests/test_output_multi_poly_trivial.osm", options.sanitize, &osmdata) != 0) {
+    throw std::runtime_error("Unable to read input file `tests/test_output_multi_poly_trivial.osm'.");
+  }
+
+  osmdata.stop();
+}
+
+void check_output_poly_trivial(int enable_multi, std::string conninfo) {
+  options_t options;
+  options.conninfo = conninfo.c_str();
+  options.num_procs = 1;
+  options.slim = 1;
+  options.enable_multi = enable_multi;
+
+  options.projection.reset(new reprojection(PROJ_LATLONG));
+
+  options.output_backend = "multi";
+  options.style = "tests/test_output_multi_poly_trivial.style.json";
+
+  run_osm2pgsql(options);
+
+  // start a new connection to run tests on
+  pg::conn_ptr test_conn = pg::conn::connect(conninfo);
+
+  // expect that the table exists
+  check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'test_poly'");
+
+  // expect 2 polygons if not in multi(geometry) mode, or 1 if multi(geometry)
+  // mode is enabled.
+  if (enable_multi) {
+    check_count(test_conn, 1, "select count(*) from test_poly");
+    check_count(test_conn, 1, "select count(*) from test_poly where foo='bar'");
+    check_count(test_conn, 1, "select count(*) from test_poly where bar='baz'");
+
+    // there should be two 5-pointed polygons in the multipolygon (note that
+    // it's 5 points including the duplicated first/last point)
+    check_count(test_conn, 2, "select count(*) from (select (st_dump(way)).geom as way from test_poly) x");
+    check_count(test_conn, 5, "select distinct st_numpoints(st_exteriorring(way)) from (select (st_dump(way)).geom as way from test_poly) x");
+
+  } else {
+    check_count(test_conn, 2, "select count(*) from test_poly");
+    check_count(test_conn, 2, "select count(*) from test_poly where foo='bar'");
+    check_count(test_conn, 2, "select count(*) from test_poly where bar='baz'");
+
+    // although there are 2 rows, they should both be 5-pointed polygons (note
+    // that it's 5 points including the duplicated first/last point)
+    check_count(test_conn, 5, "select distinct st_numpoints(st_exteriorring(way)) from test_poly");
+  }
+}
+
+int main(int argc, char *argv[]) {
+    boost::scoped_ptr<pg::tempdb> db;
+
+    try {
+        db.reset(new pg::tempdb);
+    } catch (const std::exception &e) {
+        std::cerr << "Unable to setup database: " << e.what() << "\n";
+        return 77; // <-- code to skip this test.
+    }
+
+    try {
+        check_output_poly_trivial(0, db->conninfo());
+        check_output_poly_trivial(1, db->conninfo());
+        return 0;
+
+    } catch (const std::exception &e) {
+        std::cerr << "ERROR: " << e.what() << std::endl;
+
+    } catch (...) {
+        std::cerr << "UNKNOWN ERROR" << std::endl;
+    }
+
+    return 1;
+}
diff --git a/tests/test-output-multi-tags.cpp b/tests/test-output-multi-tags.cpp
new file mode 100644
index 0000000..af9221b
--- /dev/null
+++ b/tests/test-output-multi-tags.cpp
@@ -0,0 +1,130 @@
+#include <iostream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+#include <memory>
+
+#include "osmtypes.hpp"
+#include "middle.hpp"
+#include "output-multi.hpp"
+#include "options.hpp"
+#include "middle-pgsql.hpp"
+#include "taginfo_impl.hpp"
+#include "parse.hpp"
+
+#include <libpq-fe.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "tests/middle-tests.hpp"
+#include "tests/common-pg.hpp"
+
+void check_count(pg::conn_ptr &conn, int expected, const std::string &query) {
+    pg::result_ptr res = conn->exec(query);
+
+    int ntuples = PQntuples(res->get());
+    if (ntuples != 1) {
+        throw std::runtime_error((boost::format("Expected only one tuple from a query "
+                                                "to check COUNT(*), but got %1%. Query "
+                                                "was: %2%.")
+                                  % ntuples % query).str());
+    }
+
+    std::string numstr = PQgetvalue(res->get(), 0, 0);
+    int count = boost::lexical_cast<int>(numstr);
+
+    if (count != expected) {
+        throw std::runtime_error((boost::format("Expected %1%, but got %2%, when running "
+                                                "query: %3%.")
+                                  % expected % count % query).str());
+    }
+}
+
+int main(int argc, char *argv[]) {
+    boost::scoped_ptr<pg::tempdb> db;
+
+    try {
+        db.reset(new pg::tempdb);
+    } catch (const std::exception &e) {
+        std::cerr << "Unable to setup database: " << e.what() << "\n";
+        return 77; // <-- code to skip this test.
+    }
+
+    try {
+        options_t options;
+        options.conninfo = db->conninfo().c_str();
+        options.num_procs = 1;
+        options.slim = 1;
+
+        options.projection.reset(new reprojection(PROJ_LATLONG));
+
+        options.output_backend = "multi";
+        options.style = "tests/test_output_multi_tags.json";
+
+        //setup the front (input)
+        parse_delegate_t parser(options.extra_attributes, options.bbox, options.projection);
+
+        //setup the middle
+        boost::shared_ptr<middle_t> middle = middle_t::create_middle(options.slim);
+
+        //setup the backend (output)
+        std::vector<boost::shared_ptr<output_t> > outputs = output_t::create_outputs(middle.get(), options);
+
+        //let osmdata orchestrate between the middle and the outs
+        osmdata_t osmdata(middle, outputs);
+
+        osmdata.start();
+
+        if (parser.streamFile("libxml2", "tests/test_output_multi_tags.osm", options.sanitize, &osmdata) != 0) {
+            throw std::runtime_error("Unable to read input file `tests/test_output_multi_line_storage.osm'.");
+        }
+
+        osmdata.stop();
+
+        // start a new connection to run tests on
+        pg::conn_ptr test_conn = pg::conn::connect(db->conninfo());
+
+        // Check we got the right tables
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'test_points_1'");
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'test_points_2'");
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'test_line_1'");
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'test_polygon_1'");
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'test_polygon_2'");
+
+        // Check we didn't get any extra in the tables
+        check_count(test_conn, 2, "select count(*) from test_points_1");
+        check_count(test_conn, 2, "select count(*) from test_points_2");
+        check_count(test_conn, 1, "select count(*) from test_line_1");
+        check_count(test_conn, 1, "select count(*) from test_line_2");
+        check_count(test_conn, 1, "select count(*) from test_polygon_1");
+        check_count(test_conn, 1, "select count(*) from test_polygon_2");
+
+        // Check that the first table for each type got the right transform
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_points_1 WHERE foo IS NULL and bar = 'n1' AND baz IS NULL");
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_points_1 WHERE foo IS NULL and bar = 'n2' AND baz IS NULL");
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_line_1 WHERE foo IS NULL and bar = 'w1' AND baz IS NULL");
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_polygon_1 WHERE foo IS NULL and bar = 'w2' AND baz IS NULL");
+
+        // Check that the second table also got the right transform
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_points_2 WHERE foo IS NULL and bar IS NULL AND baz = 'n1'");
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_points_2 WHERE foo IS NULL and bar IS NULL AND baz = 'n2'");
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_line_2 WHERE foo IS NULL and bar IS NULL AND baz = 'w1'");
+        check_count(test_conn, 1, "SELECT COUNT(*) FROM test_polygon_2 WHERE foo IS NULL and bar IS NULL AND baz = 'w2'");
+
+        return 0;
+
+    } catch (const std::exception &e) {
+        std::cerr << "ERROR: " << e.what() << std::endl;
+
+    } catch (...) {
+        std::cerr << "UNKNOWN ERROR" << std::endl;
+    }
+
+    return 1;
+}
diff --git a/tests/test-parse-options.cpp b/tests/test-parse-options.cpp
index 8cc57ee..bfe910a 100644
--- a/tests/test-parse-options.cpp
+++ b/tests/test-parse-options.cpp
@@ -24,7 +24,7 @@ void run_test(const char* test_name, void (*testfunc)())
         fprintf(stderr, "%s\n", test_name);
         testfunc();
     }
-    catch(std::exception& e)
+    catch(const std::exception& e)
     {
         fprintf(stderr, "%s\n", e.what());
         fprintf(stderr, "FAIL\n");
@@ -40,7 +40,7 @@ void parse_fail(const int argc, const char* argv[], const std::string& fail_mess
         options_t options = options_t::parse(argc, const_cast<char **>(argv));
         throw std::logic_error((boost::format("Expected '%1%'") % fail_message).str());
     }
-    catch(std::runtime_error& e)
+    catch(const std::runtime_error& e)
     {
         if(!alg::icontains(e.what(), fail_message))
             throw std::logic_error((boost::format("Expected '%1%' but instead got '%2%'") % fail_message % e.what()).str());
@@ -125,7 +125,7 @@ void test_outputs()
         out = outs.front().get();
         throw std::logic_error("Expected 'not recognised'");
     }
-    catch(std::runtime_error& e)
+    catch(const std::runtime_error& e)
     {
         if(!alg::icontains(e.what(), "not recognised"))
             throw std::logic_error((boost::format("Expected 'not recognised' but instead got '%2%'") % e.what()).str());
diff --git a/tests/test-parse-xml2.cpp b/tests/test-parse-xml2.cpp
index 52599f2..00b38af 100644
--- a/tests/test-parse-xml2.cpp
+++ b/tests/test-parse-xml2.cpp
@@ -10,7 +10,6 @@
 #include "parse-xml2.hpp"
 #include "output.hpp"
 #include "options.hpp"
-#include "keyvals.hpp"
 
 void exit_nicely()
 {
@@ -28,15 +27,17 @@ struct test_middle_t : public middle_t {
     void end(void) { }
     void commit(void) { }
 
-    int nodes_set(osmid_t id, double lat, double lon, struct keyval *tags) { return 0; }
-    int nodes_get_list(struct osmNode *out, const osmid_t *nds, int nd_count) const { return 0; }
+    int nodes_set(osmid_t id, double lat, double lon, const taglist_t &tags) { return 0; }
+    int nodes_get_list(nodelist_t &out, const idlist_t nds) const { return 0; }
 
-    int ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags) { return 0; }
-    int ways_get(osmid_t id, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const { return 0; }
-    int ways_get_list(const osmid_t *ids, int way_count, osmid_t *way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) const { return 0; }
+    int ways_set(osmid_t id, const idlist_t &nds, const taglist_t &tags) { return 0; }
+    int ways_get(osmid_t id, taglist_t &tags, nodelist_t &nodes) const { return 0; }
+    int ways_get_list(const idlist_t &ids, idlist_t &way_ids,
+                              std::vector<taglist_t> &tags,
+                              std::vector<nodelist_t> &nodes) const { return 0; }
 
-    int relations_set(osmid_t id, struct member *members, int member_count, struct keyval *tags) { return 0; }
-    int relations_get(osmid_t id, struct member **members, int *member_count, struct keyval *tags) const { return 0; }
+    int relations_set(osmid_t id, const memberlist_t &members, const taglist_t &tags) { return 0; }
+    int relations_get(osmid_t id, memberlist_t &members, taglist_t &tags) const { return 0; }
 
     void iterate_ways(pending_processor& pf) { }
     void iterate_relations(pending_processor& pf) { }
@@ -70,28 +71,28 @@ struct test_output_t : public output_t {
         return boost::shared_ptr<output_t>(clone);
     }
 
-    int node_add(osmid_t id, double lat, double lon, struct keyval *tags) {
+    int node_add(osmid_t id, double lat, double lon, const taglist_t &tags) {
         assert(id > 0);
         sum_ids += id;
         num_nodes += 1;
         return 0;
     }
 
-    int way_add(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) {
+    int way_add(osmid_t id, const idlist_t &nds, const taglist_t &tags) {
         assert(id > 0);
         sum_ids += id;
         num_ways += 1;
-        assert(node_count >= 0);
-        num_nds += uint64_t(node_count);
+        assert(nds.size() >= 0);
+        num_nds += uint64_t(nds.size());
         return 0;
     }
 
-    int relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags) {
+    int relation_add(osmid_t id, const memberlist_t &members, const taglist_t &tags) {
         assert(id > 0);
         sum_ids += id;
         num_relations += 1;
-        assert(member_count >= 0);
-        num_members += uint64_t(member_count);
+        assert(members.size() >= 0);
+        num_members += uint64_t(members.size());
         return 0;
     }
 
@@ -108,9 +109,9 @@ struct test_output_t : public output_t {
     void enqueue_relations(pending_queue_t &job_queue, osmid_t id, size_t output_id, size_t& added) { }
     int pending_relation(osmid_t id, int exists) { return 0; }
 
-    int node_modify(osmid_t id, double lat, double lon, struct keyval *tags) { return 0; }
-    int way_modify(osmid_t id, osmid_t *nodes, int node_count, struct keyval *tags) { return 0; }
-    int relation_modify(osmid_t id, struct member *members, int member_count, struct keyval *tags) { return 0; }
+    int node_modify(osmid_t id, double lat, double lon, const taglist_t &tags) { return 0; }
+    int way_modify(osmid_t id, const idlist_t &nds, const taglist_t &tags) { return 0; }
+    int relation_modify(osmid_t id, const memberlist_t &members, const taglist_t &tags) { return 0; }
 
     int node_delete(osmid_t id) { return 0; }
     int way_delete(osmid_t id) { return 0; }
diff --git a/tests/test-pgsql-escape.cpp b/tests/test-pgsql-escape.cpp
index 0fcd24e..477db7a 100644
--- a/tests/test-pgsql-escape.cpp
+++ b/tests/test-pgsql-escape.cpp
@@ -1,3 +1,4 @@
+#include <iostream>
 #include "pgsql.hpp"
 
 void test_escape(const char *in, const char *out) {
diff --git a/tests/test_output_multi_line_storage.osm b/tests/test_output_multi_line_storage.osm
new file mode 100644
index 0000000..425598e
--- /dev/null
+++ b/tests/test_output_multi_line_storage.osm
@@ -0,0 +1,35 @@
+<osm version="0.6">
+<!--
+  This file should create three ways which look like
+  6_____8
+
+  4___5
+
+        3
+       /
+  1___2
+
+  Node 7 is missing
+-->
+ <node id="1" lat="49.00" lon="-123.00"/>
+ <node id="2" lat="49.00" lon="-123.01"/>
+ <node id="3" lat="49.01" lon="-123.02"/>
+ <node id="4" lat="49.02" lon="-123.00"/>
+ <node id="5" lat="49.02" lon="-123.01"/>
+ <node id="6" lat="49.03" lon="-123.00"/>
+ <node id="8" lat="49.03" lon="-123.02"/>
+ <way id="1">
+  <nd ref="1"/>
+  <nd ref="2"/>
+  <nd ref="3"/>
+ </way>
+ <way id="2">
+  <nd ref="4"/>
+  <nd ref="5"/>
+ </way>
+ <way id="3">
+  <nd ref="6"/>
+  <nd ref="7"/>
+  <nd ref="8"/>
+ </way>
+</osm>
diff --git a/tests/test_output_multi_line_trivial.lua b/tests/test_output_multi_line_trivial.lua
new file mode 100644
index 0000000..7bd92bb
--- /dev/null
+++ b/tests/test_output_multi_line_trivial.lua
@@ -0,0 +1,10 @@
+function drop_all (...)
+  return 1, {}
+end
+
+-- A generic way to process ways, given a function which determines if tags are interesting
+-- Takes an optional function to process tags. Always says it's a polygon if there's matching tags
+function test_ways (kv, num_keys)
+  tags = {["foo"] = "bar"}
+  return 0, tags, 1, 0
+end
diff --git a/tests/test_output_multi_line_trivial.style.json b/tests/test_output_multi_line_trivial.style.json
new file mode 100644
index 0000000..05d7a32
--- /dev/null
+++ b/tests/test_output_multi_line_trivial.style.json
@@ -0,0 +1,14 @@
+[
+  {
+    "name": "test_line",
+    "type": "line",
+    "tagtransform": "tests/test_output_multi_line_trivial.lua",
+    "tagtransform-node-function": "drop_all",
+    "tagtransform-way-function": "test_ways",
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "foo", "type": "text"}
+    ]
+  }
+]
diff --git a/tests/test_output_multi_poly_trivial.lua b/tests/test_output_multi_poly_trivial.lua
new file mode 100644
index 0000000..3bf52a3
--- /dev/null
+++ b/tests/test_output_multi_poly_trivial.lua
@@ -0,0 +1,24 @@
+function drop_all (...)
+  return 1, {}
+end
+
+function drop_ways (...)
+  return 1, {}, 0, 0
+end
+
+function test_rels (kv, num_keys)
+  tags = {["foo"] = "bar"}
+  return 0, tags
+end
+
+function test_members (kv, member_tags, roles, num_members)
+  membersuperseeded = {}
+  for i = 1, num_members do
+    membersuperseeded[i] = 0
+  end
+
+  tags = kv
+  tags["bar"] = "baz"
+
+  return 0, tags, membersuperseeded, 0, 0, 0
+end
diff --git a/tests/test_output_multi_poly_trivial.osm b/tests/test_output_multi_poly_trivial.osm
new file mode 100644
index 0000000..3f82861
--- /dev/null
+++ b/tests/test_output_multi_poly_trivial.osm
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<osm version="0.6">
+<!--
+
+   1_____2     3_____4
+   |     |     |     |
+   |  w1 |     | w2  |
+   5_____6     7_____8
+
+this file defines the above nodes, ways and a relation
+which contains both w1 and w2.
+-->
+  <node id="1" lon="0" lat="2"/>
+  <node id="2" lon="1" lat="2"/>
+  <node id="3" lon="2" lat="2"/>
+  <node id="4" lon="3" lat="2"/>
+  <node id="5" lon="0" lat="1"/>
+  <node id="6" lon="1" lat="1"/>
+  <node id="7" lon="2" lat="1"/>
+  <node id="8" lon="3" lat="1"/>
+  <way id="1">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="6"/>
+    <nd ref="5"/>
+    <nd ref="1"/>
+  </way>
+  <way id="2">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="8"/>
+    <nd ref="7"/>
+    <nd ref="3"/>
+  </way>
+  <relation id="1">
+    <member type="way" ref="1" role=""/>
+    <member type="way" ref="2" role=""/>
+  </relation>
+</osm>
diff --git a/tests/test_output_multi_poly_trivial.style.json b/tests/test_output_multi_poly_trivial.style.json
new file mode 100644
index 0000000..e2b7160
--- /dev/null
+++ b/tests/test_output_multi_poly_trivial.style.json
@@ -0,0 +1,15 @@
+[
+  {
+    "name": "test_poly",
+    "type": "polygon",
+    "tagtransform": "tests/test_output_multi_poly_trivial.lua",
+    "tagtransform-node-function": "drop_all",
+    "tagtransform-way-function": "drop_ways",
+    "tagtransform-relation-function": "test_rels",
+    "tagtransform-relation-member-function": "test_members",
+    "tags": [
+      {"name": "foo", "type": "text"},
+      {"name": "bar", "type": "text"}
+    ]
+  }
+]
diff --git a/tests/test_output_multi_tags.json b/tests/test_output_multi_tags.json
new file mode 100644
index 0000000..ec86554
--- /dev/null
+++ b/tests/test_output_multi_tags.json
@@ -0,0 +1,99 @@
+/*
+These tables provide a test that tag processing works, and that one tag
+processing does not clobber another. They are not exhaustive,
+particularly with regards to interactions between way/area detection and
+relations.
+*/
+[
+  {
+    "name": "test_points_1",
+    "type": "point",
+    "tagtransform": "tests/test_output_multi_tags.lua",
+    "tagtransform-node-function": "test_nodes_1",
+    "tagtransform-way-function": "drop_all",
+    /* No relations in the file */
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "foo", "type": "text"},
+      {"name": "bar", "type": "text"},
+      {"name": "baz", "type": "text"} /* left empty by the transform */
+    ]
+  },
+  {
+    /* This second table will process the same data, but resulting in different tags */
+    "name": "test_points_2",
+    "type": "point",
+    "tagtransform": "tests/test_output_multi_tags.lua",
+    "tagtransform-node-function": "test_nodes_2",
+    "tagtransform-way-function": "drop_all",
+    /* No relations in the file */
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "foo", "type": "text"},
+      {"name": "bar", "type": "text"}, /* left empty by the transform */
+      {"name": "baz", "type": "text"}
+    ]
+  },
+  {
+    "name": "test_line_1",
+    "type": "line",
+    "tagtransform": "tests/test_output_multi_tags.lua",
+    "tagtransform-node-function": "drop_all",
+    "tagtransform-way-function": "test_line_1",
+    /* No relations in the file */
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "foo", "type": "text"},
+      {"name": "bar", "type": "text"},
+      {"name": "baz", "type": "text"} /* left empty by the transform */
+    ]
+  },
+  {
+    "name": "test_line_2",
+    "type": "line",
+    "tagtransform": "tests/test_output_multi_tags.lua",
+    "tagtransform-node-function": "drop_all",
+    "tagtransform-way-function": "test_line_2",
+    /* No relations in the file */
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "foo", "type": "text"},
+      {"name": "bar", "type": "text"}, /* left empty by the transform */
+      {"name": "baz", "type": "text"}
+    ]
+  },
+  {
+    "name": "test_polygon_1",
+    "type": "line",
+    "tagtransform": "tests/test_output_multi_tags.lua",
+    "tagtransform-node-function": "drop_all",
+    "tagtransform-way-function": "test_polygon_1",
+    /* No relations in the file */
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "foo", "type": "text"},
+      {"name": "bar", "type": "text"},
+      {"name": "baz", "type": "text"} /* left empty by the transform */
+    ]
+  },
+  {
+    "name": "test_polygon_2",
+    "type": "line",
+    "tagtransform": "tests/test_output_multi_tags.lua",
+    "tagtransform-node-function": "drop_all",
+    "tagtransform-way-function": "test_polygon_2",
+    /* No relations in the file */
+    "tagtransform-relation-function": "drop_all",
+    "tagtransform-relation-member-function": "drop_all",
+    "tags": [
+      {"name": "foo", "type": "text"},
+      {"name": "bar", "type": "text"},
+      {"name": "baz", "type": "text"} /* left empty by the transform */
+    ]
+  }
+]
diff --git a/tests/test_output_multi_tags.lua b/tests/test_output_multi_tags.lua
new file mode 100644
index 0000000..825f7c5
--- /dev/null
+++ b/tests/test_output_multi_tags.lua
@@ -0,0 +1,63 @@
+function drop_all (...)
+  return 1, {}
+end
+
+function test_nodes_1 (kv, num_tags)
+  if kv["foo"] then
+    tags = {}
+    tags["bar"] = kv["foo"]
+    return 0, tags
+  else
+    return 1, {}
+  end
+end
+
+function test_nodes_2 (kv, num_tags)
+  if kv["foo"] then
+    tags = {}
+    tags["baz"] = kv["foo"]
+    return 0, tags
+  else
+    return 1, {}
+  end
+end
+
+function test_line_1 (kv, num_tags)
+  if kv["foo"] and kv["area"] == "false" then
+    tags = {}
+    tags["bar"] = kv["foo"]
+    return 0, tags, 0, 0
+  else
+    return 1, {}, 0, 0
+  end
+end
+
+function test_line_2 (kv, num_tags)
+  if kv["foo"] and kv["area"] == "false" then
+    tags = {}
+    tags["baz"] = kv["foo"]
+    return 0, tags, 0, 0
+  else
+    return 1, {}, 0, 0
+  end
+end
+
+function test_polygon_1 (kv, num_tags)
+  if kv["foo"] and kv["area"] == "true" then
+    tags = {}
+    tags["bar"] = kv["foo"]
+    return 0, tags, 0, 0
+  else
+    return 1, {}, 0, 0
+  end
+end
+
+function test_polygon_2 (kv, num_tags)
+  if kv["foo"] and kv["area"] == "true" then
+    tags = {}
+    tags["baz"] = kv["foo"]
+    return 0, tags, 0, 0
+  else
+    return 1, {}, 0, 0
+  end
+end
diff --git a/tests/test_output_multi_tags.osm b/tests/test_output_multi_tags.osm
new file mode 100644
index 0000000..16a5088
--- /dev/null
+++ b/tests/test_output_multi_tags.osm
@@ -0,0 +1,28 @@
+<osm version="0.6">
+<!--
+ 43
+ 21
+-->
+ <node id="1" lat="49.00" lon="-123.00">
+ <tag k="foo" v="n1"/>
+ </node>
+ <node id="2" lat="49.00" lon="-123.01">
+ <tag k="foo" v="n2"/>
+ </node>
+ <node id="3" lat="49.01" lon="-123.00"/>
+ <node id="4" lat="49.01" lon="-123.01"/>
+ <way id="1">
+  <nd ref="1"/>
+  <nd ref="2"/>
+ <tag k="foo" v="w1"/>
+ <tag k="area" v="false"/>
+ </way>
+ <way id="2">
+  <nd ref="1"/>
+  <nd ref="3"/>
+  <nd ref="4"/>
+  <nd ref="1"/>
+ <tag k="foo" v="w2"/>
+ <tag k="area" v="true"/>
+ </way>
+</osm>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/osm2pgsql.git



More information about the Pkg-grass-devel mailing list