[osm2pgsql] 01/05: Imported Upstream version 0.87.1

Bas Couwenberg sebastic at xs4all.nl
Tue Dec 30 08:00:45 UTC 2014


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

sebastic-guest pushed a commit to branch master
in repository osm2pgsql.

commit 2808848afca1c2fb7a47fd84931cb155b21b9596
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Tue Dec 30 08:38:30 2014 +0100

    Imported Upstream version 0.87.1
---
 Makefile.am                                   |   4 -
 README.md                                     |   4 +-
 binarysearcharray.cpp                         | 118 ------
 binarysearcharray.hpp                         |  22 -
 building.lua                                  | 124 ++++++
 configure.ac                                  |   2 +-
 docs/multi.md                                 |  55 +++
 geometry-processor.cpp                        |   4 +-
 keyvals.cpp                                   | 240 +++--------
 keyvals.hpp                                   |  70 +--
 middle-pgsql.cpp                              |  56 ++-
 middle-pgsql.hpp                              |   1 -
 middle-ram.cpp                                |  25 +-
 middle-ram.hpp                                |   1 -
 middle.hpp                                    |   1 -
 node-persistent-cache-reader.cpp              |   1 -
 node-persistent-cache.cpp                     |  52 ++-
 node-persistent-cache.hpp                     |  32 +-
 options.cpp                                   |   6 +-
 osm2pgsql.cpp                                 |   1 -
 output-gazetteer.cpp                          | 590 +++++++++++++-------------
 output-gazetteer.hpp                          |   2 +-
 output-multi.cpp                              |  16 +-
 output-pgsql.cpp                              |  18 +-
 parse-o5m.cpp                                 |  16 +-
 parse-pbf.cpp                                 |  34 +-
 parse-xml2.cpp                                |  20 +-
 pgsql.cpp                                     |   8 +-
 sample.multi.json                             |  16 +
 table.cpp                                     |  21 +-
 taginfo.cpp                                   |   4 +-
 tagtransform.cpp                              | 364 +++++++---------
 tests/middle-tests.cpp                        |  13 +-
 tests/test-output-multi-line.cpp              |  40 +-
 tests/test-output-multi-point-multi-table.cpp |  11 +-
 tests/test-output-multi-point.cpp             |  11 +-
 tests/test-output-multi-polygon.cpp           |  41 +-
 tests/test-output-pgsql.cpp                   |   1 -
 tests/test-parse-xml2.cpp                     |   1 -
 tests/test-pgsql-escape.cpp                   |  18 +-
 text-tree.cpp                                 | 101 -----
 text-tree.hpp                                 |  21 -
 42 files changed, 974 insertions(+), 1212 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index cd733f7..2203d04 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,7 +5,6 @@ bin_PROGRAMS = osm2pgsql nodecachefilereader
 noinst_LTLIBRARIES = libosm2pgsql.la
 
 osm2pgsql_SOURCES = osm2pgsql.cpp \
-	binarysearcharray.hpp \
 	geometry-builder.hpp \
 	expire-tiles.hpp \
 	input.hpp \
@@ -32,14 +31,12 @@ osm2pgsql_SOURCES = osm2pgsql.cpp \
 	sanitizer.hpp \
 	sprompt.hpp \
 	table.hpp \
-	text-tree.hpp \
 	util.hpp
 
 osm2pgsql_LDADD = libosm2pgsql.la
 
 libosm2pgsql_la_SOURCES = \
 	UTF8sanitizer.cpp \
-	binarysearcharray.cpp \
 	expire-tiles.cpp \
 	geometry-builder.cpp \
 	geometry-processor.cpp \
@@ -73,7 +70,6 @@ libosm2pgsql_la_SOURCES = \
 	table.cpp \
 	taginfo.cpp \
 	tagtransform.cpp \
-	text-tree.cpp \
 	util.cpp \
 	wildcmp.cpp
 
diff --git a/README.md b/README.md
index 95be409..ce1b0cb 100644
--- a/README.md
+++ b/README.md
@@ -123,7 +123,9 @@ among others. It can also be used for [spatial analysis](docs/analysis.md) or
 In addition to the standard [pgsql](docs/pgsql.md) backend designed for
 rendering there is also the [gazetteer](docs/gazetteer.md) database for
 geocoding, principally with [Nominatim](http://www.nominatim.org/), and the
-null backend for testing.
+null backend for testing. For flexibility a new [multi](docs/multi.md)
+backend is also avialable which allows the configuration of custom
+postgres tables instead of those provided in the pgsql backend.
 
 Any questions should be directed at the osm dev list
 http://wiki.openstreetmap.org/index.php/Mailing_lists
diff --git a/binarysearcharray.cpp b/binarysearcharray.cpp
deleted file mode 100644
index e809c34..0000000
--- a/binarysearcharray.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "osmtypes.hpp"
-#include "binarysearcharray.hpp"
-
-static int binary_search_lookup(struct binary_search_array * array, int key)
-{
-    int a = 0;
-    int b = array->size - 1;
-    while (a <= b)
-    {
-        int pivot = ((b - a) >> 1) + a;
-        if (array->array[pivot].key == key)
-        {
-            return pivot;
-        }
-        else if (array->array[pivot].key > key)
-        {
-            b = pivot - 1;
-        }
-        else
-        {
-            a = pivot + 1;
-        }
-    }
-    if ((a < array->size) && (array->array[a].key < key))
-        a++;
-    return a | (1 << (sizeof(int) * 8 - 1));
-}
-
-osmid_t binary_search_get(struct binary_search_array * array, int key)
-{
-    int idx;
-    if (array->size == 0)
-        return -1;
-    idx = binary_search_lookup(array, key);
-    if (idx < 0)
-    {
-        return -1;
-    }
-    else
-    {
-        return array->array[idx].value;
-    }
-}
-
-void binary_search_remove(struct binary_search_array * array, int key)
-{
-    int idx = binary_search_lookup(array, key);
-    if (idx < 0)
-    {
-        return;
-    }
-    else
-    {
-        memmove(&(array->array[idx]), &(array->array[idx + 1]),
-                sizeof(struct key_val_tuple) * (array->capacity - idx - 1));
-        array->size--;
-    }
-}
-
-void binary_search_add(struct binary_search_array * array, int key,
-        osmid_t value)
-{
-    int idx;
-    if (array->size < array->capacity)
-    {
-        if (array->size == 0)
-        {
-            array->array[0].key = key;
-            array->array[0].value = value;
-            array->size++;
-            return;
-        }
-        idx = binary_search_lookup(array, key);
-        if (idx < 0)
-        {
-            idx = idx & (~(1 << (sizeof(int) * 8 - 1)));
-            memmove(&(array->array[idx + 1]), &(array->array[idx]),
-                    sizeof(struct key_val_tuple) * (array->capacity - idx - 1));
-            array->array[idx].key = key;
-            array->array[idx].value = value;
-            array->size++;
-        }
-        else
-        {
-            fprintf(stderr, "dupplicate!\n");
-            exit(1);
-        }
-    }
-}
-
-struct binary_search_array * init_search_array(int capacity)
-{
-    struct binary_search_array * array =
-        (struct binary_search_array *)
-        calloc(1, sizeof(struct binary_search_array));
-    array->array = (struct key_val_tuple *)calloc(capacity + 1, sizeof(struct key_val_tuple));
-    if (!array->array) {
-        fprintf(stderr, "Out of memory trying to allocate %li bytes for binary search array\n", ((capacity + 1) * sizeof(struct key_val_tuple)));
-	return NULL;
-    }
-    array->capacity = capacity;
-    array->size = 0;
-    return array;
-}
-
-void shutdown_search_array(struct binary_search_array ** array)
-{
-    free((*array)->array);
-    (*array)->array = NULL;
-    (*array)->capacity = 0;
-    free(*array);
-    *array = NULL;
-}
diff --git a/binarysearcharray.hpp b/binarysearcharray.hpp
deleted file mode 100644
index 8ff9f5b..0000000
--- a/binarysearcharray.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef BINARYSEARCHARRAY_H
-#define BINARYSEARCHARRAY_H
-
-
-struct key_val_tuple {
-	int key;
-	osmid_t value;
-};
-
-struct binary_search_array {
-	int capacity;
-	int size;
-	struct key_val_tuple * array;
-};
-
-void binary_search_remove(struct binary_search_array * array, int key);
-void binary_search_add(struct binary_search_array * array, int key, osmid_t value);
-osmid_t binary_search_get(struct binary_search_array * array, int key);
-struct binary_search_array *  init_search_array(int capacity);
-void shutdown_search_array(struct binary_search_array ** array);
-
-#endif
diff --git a/building.lua b/building.lua
new file mode 100644
index 0000000..93e002f
--- /dev/null
+++ b/building.lua
@@ -0,0 +1,124 @@
+tags = { 'building', 'shop', 'amenity' }
+
+function filter_tags_generic(keyvalues, nokeys)
+   filter = 0
+   tagcount = 0
+
+   --if there were no tags passed in, ie keyvalues is empty
+   if nokeys == 0 then
+      filter = 1
+      return filter, keyvalues
+   end
+
+   --remove anything we dont care about
+   for i,k in ipairs(tags) do
+      if keyvalues[k] then
+         tagcount = tagcount + 1
+      end
+   end
+
+   --if we didnt find any tags we care about
+   if tagcount == 0 then
+      filter = 1
+   end
+
+   --tell the caller whether we think we want this feature or not and give back the modified tags
+   return filter, keyvalues
+end
+
+function nodes_proc (keyvalues, nokeys)
+   --we dont care about nodes at all so filter all of them
+   filter = 1
+   return filter, keyvalues
+end
+
+function rels_proc (keyvalues, nokeys)
+   --let the generic filtering do its job
+   filter, keyvalues = filter_tags_generic(keyvalues, nokeys)
+   if filter == 1 then
+      return filter, keyvalues
+   end
+
+   --dont keep any relations with types other than multipolygon
+   if keyvalues["type"] ~= "multipolygon" then
+      filter = 1
+      return filter, keyvalues
+   end
+
+   --let the caller know if its a keeper or not and give back the modified tags
+   return filter, keyvalues
+end
+
+function ways_proc (keyvalues, nokeys)
+   filter = 0
+
+   --let the generic filtering do its job
+   filter, keyvalues = filter_tags_generic(keyvalues, nokeys)
+   poly = (filter + 1) % 2
+   roads = 0
+
+   --let the caller know if its a keeper or not and give back the  modified tags
+   --also tell it whether or not its a polygon or road
+   return filter, keyvalues, poly, roads
+end
+
+function rel_members_proc (keyvalues, keyvaluemembers, roles, membercount)
+   
+   filter = 0
+   boundary = 0
+   polygon = 0
+   roads = 0
+
+   --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
+   --we start by assuming each way will not be used as part of the relation
+   membersuperseeded = {}
+   for i = 1, membercount do
+      membersuperseeded[i] = 0
+   end
+
+   --remember the type on the relation and erase it from the tags
+   type = keyvalues["type"]
+   keyvalues["type"] = nil
+
+   if (type == "multipolygon") and keyvalues["boundary"] == nil then
+      --check if this relation has tags we care about
+      polygon = 1
+      filter, keyvalues = filter_tags_generic(keyvalues, 1)
+
+      --if the relation didn't have the tags we need go grab the tags from
+      --any members that are marked as outers of the multipolygon
+      if (filter == 1) then
+         for i = 1,membercount do
+            if (roles[i] == "outer") then
+               for j,k in ipairs(tags) do
+                  v = keyvaluemembers[i][k]
+                  if v then
+                     keyvalues[k] = v
+                     filter = 0
+                  end
+               end
+            end
+         end
+      end
+      if filter == 1 then
+         return filter, keyvalues, 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
+      --then we say the member will not be used in the relation and is there for not superseeded
+      --ie it is kept as a standalone way 
+      for i = 1,membercount do
+         superseeded = 1
+         for k,v in pairs(keyvaluemembers[i]) do
+            if ((keyvalues[k] == nil) or (keyvalues[k] ~= v)) then
+                superseeded = 0;
+                break
+            end
+         end
+         membersuperseeded[i] = superseeded
+      end
+   end
+
+   return filter, keyvalues, membersuperseeded, boundary, polygon, roads
+end
diff --git a/configure.ac b/configure.ac
index 31df268..8b6f9bc 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.0)
+AC_INIT(osm2pgsql, 0.87.1)
 
 dnl Required autoconf version
 AC_PREREQ(2.61)
diff --git a/docs/multi.md b/docs/multi.md
new file mode 100644
index 0000000..ebffd19
--- /dev/null
+++ b/docs/multi.md
@@ -0,0 +1,55 @@
+# Multi Backend #
+
+The multi backend is designed for custom table structures as an alternative
+to the standard [pgsql](pgsql.md) backend tables. It is intended to allow
+the configuration of a custom set of tables with hopefully fewer rows and fewer
+columns. This can be beneficial to queries in which some context (eg. zoom level)
+could limit the number of tables that need to be queried. Addtionaly it would
+allow more tables to be queried in parallel. 
+
+## Database Layout ##
+It connects to a PostgreSQL database and stores the data in one or more tables.
+Each table is configured in a way similar to that of the `pgsql` backend.
+That is essentially why it was named `multi` because it's basically multiple
+`pgsql` backends each with its own set of options and only a single table.
+
+## Table Configuration ##
+As sample configuration may resemble the following:
+
+    [
+      {
+        "name": "building",
+        "type": "polygon",
+        "tagtransform": "building.lua",
+        "tagtransform-node-function": "nodes_proc",
+        "tagtransform-way-function": "ways_proc",
+        "tagtransform-relation-function": "rels_proc",
+        "tagtransform-relation-member-function": "rel_members_proc",
+        "tags": [
+          {"name": "building", "type": "text"},
+          {"name": "shop", "type": "text"},
+          {"name": "amenity", "type": "text"}
+        ]
+      },
+      ...
+    ]
+
+Note that each table has a `name` and can target a single type of geometry
+by setting the `type` to one of `point`, `line` or `polygon`. `tagtransform`
+is used to set the name of the lua script to be used for custom tag processing.
+Within the lua script you may define several methods that will be called
+when processing various tags, these can be named via 
+`tagtransform-node-function`, `tagtransform-way-function`,
+`tagtransform-relation-function`, and `tagtransform-relation-member-function`.
+As with the normal top level options within osm2pgsql you can specify any of the
+following: `tablespace-index`, `tablespace-data`, `enable-hstore`,
+`enable-hstore-index`, `enable-multi`, `hstore-match-only`. Hstore colum names
+may be specified via an array of strings named `hstores`. Finally standard columns
+may be specified via an array of objects named `tags` with each object containing
+a `name` and a postgres `type`. Note you may also set `flags` on each tag as with
+the standard osm2pgsql style file.`flags` is formated exactly as in the style file
+as a string of flag names seprated by commas.
+
+## Importing ##
+
+See: [Importing](pgsql.md#importing).
diff --git a/geometry-processor.cpp b/geometry-processor.cpp
index 71ebee7..16e302b 100644
--- a/geometry-processor.cpp
+++ b/geometry-processor.cpp
@@ -91,7 +91,7 @@ relation_helper::~relation_helper()
     //clean up
     for(size_t i = 0; i < way_count; ++i)
     {
-        keyval::resetList(&(tags[i]));
+        tags[i].resetList();
         free(nodes[i]);
     }
 }
@@ -101,7 +101,7 @@ size_t& relation_helper::set(const member* member_list, const int member_list_le
     //clean up
     for(size_t i = 0; i < way_count; ++i)
     {
-        keyval::resetList(&(tags[i]));
+        tags[i].resetList();
         free(nodes[i]);
     }
 
diff --git a/keyvals.cpp b/keyvals.cpp
index 92f98f3..8c5b9cd 100644
--- a/keyvals.cpp
+++ b/keyvals.cpp
@@ -13,75 +13,37 @@
 
 #include <algorithm>
 
-keyval::keyval()
-{
-    tree_ctx.reset(new text_tree());
-    keyval::initList(this);
-}
 
-keyval::~keyval()
+keyval::keyval(const keyval &other)
 {
-    //keyval::resetList(this);
+    key = other.key;
+    value = other.value;
+    // the list cannot be copied, so initialise as empty
+    next = this;
+    prev = this;
+    has_column = other.has_column;
 }
 
-void keyval::initList(struct keyval *head)
-{
-    assert(head);
-
-    head->next = head;
-    head->prev = head;
-    head->key = NULL;
-    head->value = NULL;
-    head->has_column = 0;
-}
 
-void keyval::freeItem(struct keyval *p)
+unsigned int keyval::countList() const
 {
-    if (!p)
-        return;
-
-    p->tree_ctx->text_release(p->key);
-    p->tree_ctx->text_release(p->value);
-    delete p;
-}
-
-
-unsigned int keyval::countList(const struct keyval *head)
-{
-    struct keyval *p;
     unsigned int count = 0;
 
-    if (!head)
-        return 0;
-
-    p = head->next;
-    while(p != head) {
+    keyval *p = next;
+    while(p != this) {
         count++;
         p = p->next;
     }
     return count;
 }
 
-int keyval::listHasData(struct keyval *head)
-{
-    if (!head)
-        return 0;
-
-    return (head->next != head);
-}
-
 
-char *keyval::getItem(const struct keyval *head, const char *name)
+const std::string *keyval::getItem(const std::string &name) const
 {
-    struct keyval *p;
-
-    if (!head)
-        return NULL;
-
-    p = head->next;
-    while(p != head) {
-        if (!strcmp(p->key, name))
-            return p->value;
+    keyval *p = next;
+    while(p != this) {
+        if (!p->key.compare(name))
+            return &(p->value);
         p = p->next;
     }
     return NULL;
@@ -91,112 +53,33 @@ char *keyval::getItem(const struct keyval *head, const char *name)
    list item which can be used to remove the tag from the linked list
    with the removeTag function
 */
-struct keyval *keyval::getTag(struct keyval *head, const char *name)
+struct keyval *keyval::getTag(const std::string &name)
 {
-    struct keyval *p;
-
-    if (!head)
-        return NULL;
-
-    p = head->next;
-    while(p != head) {
-        if (!strcmp(p->key, name))
+    keyval *p = next;
+    while(p != this) {
+        if (!p->key.compare(name))
             return p;
         p = p->next;
     }
     return NULL;
 }
 
-void keyval::removeTag(struct keyval *tag)
+void keyval::removeTag()
 {
-  tag->prev->next=tag->next;
-  tag->next->prev=tag->prev;
-  freeItem(tag);
+  prev->next = next;
+  next->prev = prev;
+  delete(this);
 }
 
-struct keyval *keyval::firstItem(struct keyval *head)
-{
-    if (head == NULL || head == head->next)
-        return NULL;
-
-    return head->next;
-}
 
-struct keyval *keyval::nextItem(struct keyval *head, struct keyval *item)
+struct keyval *keyval::popItem()
 {
-    if (item->next == head)
+    keyval *p = next;
+    if (p == this)
         return NULL;
 
-    return item->next;
-}
-
-/* Pulls all items from list which match this prefix
- * note: they are removed from the original list an returned in a new one
- */
-struct keyval *keyval::getMatches(struct keyval *head, const char *name)
-{
-    struct keyval *out = NULL;
-    struct keyval *p;
-
-    if (!head)
-        return NULL;
-
-    //TODO: properly copy the tree_ctx from the keyval passed in
-    out = new keyval();
-    if (!out)
-        return NULL;
-
-    p = head->next;
-    while(p != head) {
-        struct keyval *next = p->next;
-        if (!strncmp(p->key, name, strlen(name))) {
-            p->next->prev = p->prev;
-            p->prev->next = p->next;
-            pushItem(out, p);
-        }
-        p = next;
-    }
-
-    if (listHasData(out))
-        return out;
-
-    delete out;
-    return NULL;
-}
-
-void keyval::updateItem(struct keyval *head, const char *name, const char *value)
-{
-    struct keyval *item;
-
-    if (!head)
-        return;
-
-    item = head->next;
-    while(item != head) {
-        if (!strcmp(item->key, name)) {
-            head->tree_ctx->text_release(item->value);
-            item->value = (char *)head->tree_ctx->text_get(value);
-            return;
-        }
-        item = item->next;
-    }
-    addItem(head, name, value, 0);
-}
-
-
-struct keyval *keyval::popItem(struct keyval *head)
-{
-    struct keyval *p;
-
-    if (!head)
-        return NULL;
-
-    p = head->next;
-    if (p == head)
-        return NULL;
-
-    head->next = p->next;
-    p->next->prev = head;
+    next = p->next;
+    p->next->prev = this;
 
     p->next = NULL;
     p->prev = NULL;
@@ -205,62 +88,57 @@ struct keyval *keyval::popItem(struct keyval *head)
 }
 
 
-void keyval::pushItem(struct keyval *head, struct keyval *item)
+void keyval::pushItem(struct keyval *item)
 {
-
-    assert(head);
     assert(item);
 
-    item->next = head;
-    item->prev = head->prev;
-    head->prev->next = item;
-    head->prev = item;
+    item->next = this;
+    item->prev = prev;
+    prev->next = item;
+    prev = item;
 }
 
-int keyval::addItem(struct keyval *head, const char *name, const char *value, int noDupe)
+int keyval::addItem(const std::string &name, const std::string &value, bool noDupe)
 {
-    struct keyval *item;
-
-    assert(head);
-    assert(name);
-    assert(value);
-
     if (noDupe) {
-        item = head->next;
-        while (item != head) {
-            if (!strcmp(item->value, value) && !strcmp(item->key, name))
+        keyval *item = next;
+        while (item != this) {
+            if (!value.compare(item->value) && !name.compare(item->key))
                 return 1;
             item = item->next;
         }
     }
 
-    //TODO: properly implement a copy constructor and do the
-    //shallow copy there instead of relying on implicit one?
-    item = new keyval(*head);
-
-    item->key   = (char *)head->tree_ctx->text_get(name);
-    item->value = (char *)head->tree_ctx->text_get(value);
-    item->has_column=0;
+    new keyval(name, value, this);
 
-    /* Add to head */
-    item->next = head->next;
-    item->prev = head;
-    head->next->prev = item;
-    head->next = item;
     return 0;
 }
 
-void keyval::resetList(struct keyval *head)
+void keyval::resetList()
 {
     struct keyval *item;
 
-    while((item = popItem(head)))
-        freeItem(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::cloneList( struct keyval *target, struct keyval *source )
+void keyval::moveList(keyval *target)
 {
-  struct keyval *ptr;
-  for( ptr = source->next; ptr != source; ptr=ptr->next )
-    addItem( target, ptr->key, ptr->value, 0 );
+    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
index 12db037..2e63115 100644
--- a/keyvals.hpp
+++ b/keyvals.hpp
@@ -8,42 +8,56 @@
 #ifndef KEYVAL_H
 #define KEYVAL_H
 
-#include "text-tree.hpp"
-
 #include <boost/shared_ptr.hpp>
 
 struct keyval {
-    char *key;
-    char *value;
+    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;
-    struct keyval *next;
-    struct keyval *prev;
-    boost::shared_ptr<text_tree> tree_ctx;
-
-    keyval();
-    ~keyval();
-
-    static void initList(struct keyval *head);
-    static void freeItem(struct keyval *p);
-    static unsigned int countList(const struct keyval *head);
-    static int listHasData(struct keyval *head);
-    static char *getItem(const struct keyval *head, const char *name);
-    static struct keyval *getTag(struct keyval *head, const char *name);
-    static const struct keyval *getTag(const struct keyval *head, const char *name);
-    static void removeTag(struct keyval *tag);
-    static struct keyval *firstItem(struct keyval *head);
-    static struct keyval *nextItem(struct keyval *head, struct keyval *item);
-    static struct keyval *popItem(struct keyval *head);
-    static void pushItem(struct keyval *head, struct keyval *item);
-    static int addItem(struct keyval *head, const char *name, const char *value, int noDupe);
-    static void resetList(struct keyval *head);
-    static struct keyval *getMatches(struct keyval *head, const char *name);
-    static void updateItem(struct keyval *head, const char *name, const char *value);
-    static void cloneList( struct keyval *target, struct keyval *source );
+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;
+    }
+
 };
 
 
diff --git a/middle-pgsql.cpp b/middle-pgsql.cpp
index 2065edb..6377047 100644
--- a/middle-pgsql.cpp
+++ b/middle-pgsql.cpp
@@ -189,10 +189,9 @@ const char *pgsql_store_tags(const struct keyval *tags, const int& escape)
   static int buflen;
 
   char *ptr;
-  struct keyval *i;
   int first;
 
-  int countlist = keyval::countList(tags);
+  int countlist = tags->countList();
   if( countlist == 0 )
   {
     if( escape )
@@ -211,10 +210,10 @@ _restart:
   ptr = buffer;
   first = 1;
   *ptr++ = '{';
-  // The lists are circular, exit when we reach the head again */
-  for( i=tags->next; i->key; i = i->next )
+
+  for(keyval* i = tags->firstItem(); i; i = tags->nextItem(i))
   {
-    int maxlen = (strlen(i->key) + strlen(i->value)) * 4;
+    int maxlen = (i->key.length() + i->value.length()) * 4;
     if( (ptr+maxlen-buffer) > (buflen-20) ) // Almost overflowed? */
     {
       buflen <<= 1;
@@ -224,11 +223,11 @@ _restart:
     }
     if( !first ) *ptr++ = ',';
     *ptr++ = '"';
-    ptr = escape_tag( ptr, i->key, escape );
+    ptr = escape_tag( ptr, i->key.c_str(), escape );
     *ptr++ = '"';
     *ptr++ = ',';
     *ptr++ = '"';
-    ptr = escape_tag( ptr, i->value, escape );
+    ptr = escape_tag( ptr, i->value.c_str(), escape );
     *ptr++ = '"';
 
     first=0;
@@ -284,7 +283,7 @@ void pgsql_parse_tags( const char *string, struct keyval *tags )
     string++;
     string = decode_upto( string, val );
     // String points to the comma or closing '}' */
-    keyval::addItem( tags, key, val, 0 );
+    tags->addItem( key, val, false );
     if( *string == ',' )
       string++;
   }
@@ -468,17 +467,6 @@ int middle_pgsql_t::local_nodes_get_list(struct osmNode *nodes, const osmid_t *n
     return count;
 }
 
-void middle_pgsql_t::cleanup(void)
-{
-    int i;
-
-    for (i=0; i<num_tables; i++) {
-        if (tables[i].sql_conn) {
-            PQfinish(tables[i].sql_conn);
-            tables[i].sql_conn = NULL;
-        }
-    }
-}
 
 int middle_pgsql_t::nodes_set(osmid_t id, double lat, double lon, struct keyval *tags) {
     cache->set( id, lat, lon, tags );
@@ -651,7 +639,6 @@ int middle_pgsql_t::ways_get_list(const osmid_t *ids, int way_count, osmid_t *wa
     // Match the list of ways coming from postgres in a different order
     //   back to the list of ways given by the caller */
     count = 0;
-    keyval::initList(&(tags[count]));
     for (i = 0; i < way_count; i++) {
         for (j = 0; j < countPG; j++) {
             if (ids[i] == wayidspg[j]) {
@@ -666,11 +653,13 @@ int middle_pgsql_t::ways_get_list(const osmid_t *ids, int way_count, osmid_t *wa
                 count_ptr[count] = nodes_get_list(nodes_ptr[count], list, num_nodes);
 
                 count++;
-                keyval::initList(&(tags[count]));
+                break;
             }
         }
     }
 
+    assert(count<=way_count);
+
     PQclear(res);
     free(tmp2);
     free(wayidspg);
@@ -754,8 +743,6 @@ int middle_pgsql_t::relations_set(osmid_t id, struct member *members, int member
     way_parts.reserve(member_count);
     rel_parts.reserve(member_count);
 
-    keyval::initList( &member_list );
-
     for( i=0; i<member_count; i++ )
     {
       char tag = 0;
@@ -767,7 +754,7 @@ int middle_pgsql_t::relations_set(osmid_t id, struct member *members, int member
         default: fprintf( stderr, "Internal error: Unknown member type %d\n", members[i].type ); util::exit_nicely();
       }
       sprintf( buf, "%c%" PRIdOSMID, tag, members[i].id );
-      keyval::addItem( &member_list, buf, members[i].role, 0 );
+      member_list.addItem( buf, members[i].role, false );
     }
 
     int all_count = 0;
@@ -787,7 +774,7 @@ int middle_pgsql_t::relations_set(osmid_t id, struct member *members, int member
               id, node_count, node_count+way_count, parts_buf, member_buf, tag_buf ) > (length-10) )
       { fprintf( stderr, "buffer overflow relation id %" PRIdOSMID "\n", id); return 1; }
       free(tag_buf);
-      keyval::resetList(&member_list);
+      member_list.resetList();
       pgsql_CopyData(__FUNCTION__, rel_table->sql_conn, buffer);
       return 0;
     }
@@ -807,7 +794,7 @@ int middle_pgsql_t::relations_set(osmid_t id, struct member *members, int member
     pgsql_execPrepared(rel_table->sql_conn, "insert_rel", 6, (const char * const *)paramValues, PGRES_COMMAND_OK);
     if( paramValues[4] )
         free((void *)paramValues[4]);
-    keyval::resetList(&member_list);
+    member_list.resetList();
     return 0;
 }
 
@@ -823,7 +810,6 @@ int middle_pgsql_t::relations_get(osmid_t id, struct member **members, int *memb
     int num_members;
     struct member *list;
     int i=0;
-    struct keyval *item;
 
     // Make sure we're out of copy mode */
     pgsql_endCopy( rel_table );
@@ -840,13 +826,13 @@ int middle_pgsql_t::relations_get(osmid_t id, struct member **members, int *memb
     }
 
     pgsql_parse_tags( PQgetvalue(res, 0, 1), tags );
-    keyval::initList(&member_temp);
     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 );
 
-    while( (item = keyval::popItem(&member_temp)) )
+    keyval *item;
+    while((item = member_temp.popItem()))
     {
         if( i >= num_members )
         {
@@ -855,9 +841,9 @@ int middle_pgsql_t::relations_get(osmid_t id, struct member **members, int *memb
         }
         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+1, NULL, 10 );
-        list[i].role = strdup( item->value );
-        keyval::freeItem(item);
+        list[i].id = strtoosmid(item->key.c_str()+1, NULL, 10 );
+        list[i].role = strdup( item->value.c_str() );
+        delete(item);
         i++;
     }
     *members = list;
@@ -1417,6 +1403,12 @@ middle_pgsql_t::middle_pgsql_t()
 }
 
 middle_pgsql_t::~middle_pgsql_t() {
+    for (int i=0; i < num_tables; i++) {
+        if (tables[i].sql_conn) {
+            PQfinish(tables[i].sql_conn);
+        }
+    }
+
 }
 
 boost::shared_ptr<const middle_query_t> middle_pgsql_t::get_instance() const {
diff --git a/middle-pgsql.hpp b/middle-pgsql.hpp
index 377ab26..3f7ef03 100644
--- a/middle-pgsql.hpp
+++ b/middle-pgsql.hpp
@@ -23,7 +23,6 @@ struct middle_pgsql_t : public slim_middle_t {
 
     int start(const options_t *out_options_);
     void stop(void);
-    void cleanup(void);
     void analyze(void);
     void end(void);
     void commit(void);
diff --git a/middle-ram.cpp b/middle-ram.cpp
index 89a544f..01deb43 100644
--- a/middle-ram.cpp
+++ b/middle-ram.cpp
@@ -89,11 +89,10 @@ int middle_ram_t::ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval
 
     if (!ways[block][offset].tags) {
         ways[block][offset].tags = new keyval();
-        keyval::initList(ways[block][offset].tags);
     } else
-        keyval::resetList(ways[block][offset].tags);
+        ways[block][offset].tags->resetList();
 
-    keyval::cloneList(ways[block][offset].tags, tags);
+    tags->cloneList(ways[block][offset].tags);
 
     return 0;
 }
@@ -113,11 +112,10 @@ int middle_ram_t::relations_set(osmid_t id, struct member *members, int member_c
 
     if (!rels[block][offset].tags) {
         rels[block][offset].tags = new keyval();
-        keyval::initList(rels[block][offset].tags);
     } else
-        keyval::resetList(rels[block][offset].tags);
+        rels[block][offset].tags->resetList();
 
-    keyval::cloneList(rels[block][offset].tags, tags);
+    tags->cloneList(rels[block][offset].tags);
 
     free( rels[block][offset].members );
     rels[block][offset].members = NULL;
@@ -188,7 +186,7 @@ void middle_ram_t::release_relations()
             if (rels[block][offset].members) {
                 free(rels[block][offset].members);
                 rels[block][offset].members = NULL;
-                keyval::resetList(rels[block][offset].tags);
+                rels[block][offset].tags->resetList();
                 delete rels[block][offset].tags;
                 rels[block][offset].tags=NULL;
             }
@@ -206,7 +204,7 @@ void middle_ram_t::release_ways()
         if (ways[i]) {
             for (j=0; j<PER_BLOCK; j++) {
                 if (ways[i][j].tags) {
-                    keyval::resetList(ways[i][j].tags);
+                    ways[i][j].tags->resetList();
                     delete ways[i][j].tags;
                 }
                 if (ways[i][j].ndids)
@@ -236,7 +234,7 @@ int middle_ram_t::ways_get(osmid_t id, struct keyval *tags_ptr, struct osmNode *
         int ndCount = nodes_get_list(nodes, ways[block][offset].ndids+1, ways[block][offset].ndids[0]);
 
         if (ndCount) {
-            keyval::cloneList( tags_ptr, ways[block][offset].tags );
+            ways[block][offset].tags->cloneList(tags_ptr);
             *nodes_ptr = nodes;
             *count_ptr = ndCount;
             return 0;
@@ -250,13 +248,11 @@ int middle_ram_t::ways_get_list(const osmid_t *ids, int way_count, osmid_t *way_
     int count = 0;
     int i;
 
-    keyval::initList(&(tag_ptr[count]));
     for (i = 0; i < way_count; i++) {
 
         if (ways_get(ids[i], &(tag_ptr[count]), &(node_ptr[count]), &(count_ptr[count])) == 0) {
             way_ids[count] = ids[i];
             count++;
-            keyval::initList(&(tag_ptr[count]));
         }
     }
     return count;
@@ -278,7 +274,7 @@ int middle_ram_t::relations_get(osmid_t id, struct member **members_ptr, int *me
         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);
-        keyval::cloneList( tags_ptr, rels[block][offset].tags );
+        rels[block][offset].tags->cloneList(tags_ptr);
 
         *members_ptr = members;
         *member_count = rels[block][offset].member_count;
@@ -292,11 +288,6 @@ void middle_ram_t::analyze(void)
     /* No need */
 }
 
-void middle_ram_t::cleanup(void)
-{
-    /* No need */
-}
-
 void middle_ram_t::end(void)
 {
     /* No need */
diff --git a/middle-ram.hpp b/middle-ram.hpp
index 70c3303..d9c7fc3 100644
--- a/middle-ram.hpp
+++ b/middle-ram.hpp
@@ -20,7 +20,6 @@ struct middle_ram_t : public middle_t {
 
     int start(const options_t *out_options_);
     void stop(void);
-    void cleanup(void);
     void analyze(void);
     void end(void);
     void commit(void);
diff --git a/middle.hpp b/middle.hpp
index d5fdda8..e880413 100644
--- a/middle.hpp
+++ b/middle.hpp
@@ -37,7 +37,6 @@ struct middle_t : public middle_query_t {
 
     virtual int start(const options_t *out_options_) = 0;
     virtual void stop(void) = 0;
-    virtual void cleanup(void) = 0;
     virtual void analyze(void) = 0;
     virtual void end(void) = 0;
     virtual void commit(void) = 0;
diff --git a/node-persistent-cache-reader.cpp b/node-persistent-cache-reader.cpp
index 4693880..9fb8ec0 100644
--- a/node-persistent-cache-reader.cpp
+++ b/node-persistent-cache-reader.cpp
@@ -16,7 +16,6 @@
 #include "options.hpp"
 #include "node-persistent-cache.hpp"
 #include "node-ram-cache.hpp"
-#include "binarysearcharray.hpp"
 
 void test_get_node_list(boost::shared_ptr<node_persistent_cache> cache,
                         int itterations, int max_size, int process_number) {
diff --git a/node-persistent-cache.cpp b/node-persistent-cache.cpp
index 2c26c7f..0b37718 100644
--- a/node-persistent-cache.cpp
+++ b/node-persistent-cache.cpp
@@ -17,10 +17,10 @@
 #include "output.hpp"
 #include "options.hpp"
 #include "node-persistent-cache.hpp"
-#include "binarysearcharray.hpp"
 #include "util.hpp"
 
 #include <stdexcept>
+#include <algorithm>
 
 #ifdef _WIN32
  #include "win_fsync.h"
@@ -171,8 +171,33 @@ int node_persistent_cache::replace_block()
  */
 int node_persistent_cache::find_block(osmid_t block_offset)
 {
-    int idx = binary_search_get(readNodeBlockCacheIdx, block_offset);
-    return idx;
+    cache_index::iterator it = std::lower_bound(readNodeBlockCacheIdx.begin(),
+                                                readNodeBlockCacheIdx.end(),
+                                                block_offset);
+    if (it != readNodeBlockCacheIdx.end() && it->key == block_offset)
+        return it->value;
+
+    return -1;
+}
+
+void node_persistent_cache::remove_from_cache_idx(osmid_t block_offset)
+{
+    cache_index::iterator it = std::lower_bound(readNodeBlockCacheIdx.begin(),
+                                                readNodeBlockCacheIdx.end(),
+                                                block_offset);
+
+    if (it == readNodeBlockCacheIdx.end() || it->key != block_offset)
+        return;
+
+    readNodeBlockCacheIdx.erase(it);
+}
+
+void node_persistent_cache::add_to_cache_idx(cache_index_entry const &entry)
+{
+    cache_index::iterator it = std::lower_bound(readNodeBlockCacheIdx.begin(),
+                                                readNodeBlockCacheIdx.end(),
+                                                entry);
+    readNodeBlockCacheIdx.insert(it, entry);
 }
 
 /**
@@ -232,7 +257,7 @@ void node_persistent_cache::nodes_prefetch_async(osmid_t id)
 #ifdef HAVE_POSIX_FADVISE
     osmid_t block_offset = id >> READ_NODE_BLOCK_SHIFT;
 
-    osmid_t block_id = find_block(block_offset);
+    int block_id = find_block(block_offset);
 
     if (block_id < 0)
         {   /* The needed block isn't in cache already, so initiate loading */
@@ -282,8 +307,7 @@ int node_persistent_cache::load_block(osmid_t block_offset)
         readNodeBlockCache[block_id].dirty = 0;
     }
 
-    binary_search_remove(readNodeBlockCacheIdx,
-            readNodeBlockCache[block_id].block_offset);
+    remove_from_cache_idx(readNodeBlockCache[block_id].block_offset);
     ramNodes_clear(readNodeBlockCache[block_id].nodes, READ_NODE_BLOCK_SIZE);
     readNodeBlockCache[block_id].block_offset = block_offset;
     readNodeBlockCache[block_id].used = READ_NODE_CACHE_SIZE;
@@ -311,8 +335,8 @@ int node_persistent_cache::load_block(osmid_t block_offset)
                 strerror(errno));
         exit(1);
     }
-    binary_search_add(readNodeBlockCacheIdx,
-            readNodeBlockCache[block_id].block_offset, block_id);
+    add_to_cache_idx(cache_index_entry(readNodeBlockCache[block_id].block_offset,
+                                       block_id));
 
     return block_id;
 }
@@ -461,7 +485,7 @@ int node_persistent_cache::get(struct osmNode *out, osmid_t id)
 {
     osmid_t block_offset = id >> READ_NODE_BLOCK_SHIFT;
 
-    osmid_t block_id = find_block(block_offset);
+    int block_id = find_block(block_offset);
 
     if (block_id < 0)
     {
@@ -564,7 +588,7 @@ int node_persistent_cache::get_list(struct osmNode *nodes, const osmid_t *ndids,
 node_persistent_cache::node_persistent_cache(const options_t *options, int append,
                                              boost::shared_ptr<node_ram_cache> ptr)
     : node_cache_fd(0), node_cache_fname(NULL), append_mode(0), cacheHeader(),
-      writeNodeBlock(), readNodeBlockCache(NULL), readNodeBlockCacheIdx(NULL),
+      writeNodeBlock(), readNodeBlockCache(NULL),
       scale_(0), cache_already_written(0), ram_cache(ptr)
 {
     int i, err;
@@ -579,12 +603,7 @@ node_persistent_cache::node_persistent_cache(const options_t *options, int appen
     fprintf(stderr, "Mid: loading persistent node cache from %s\n",
             node_cache_fname);
 
-    readNodeBlockCacheIdx = init_search_array(READ_NODE_CACHE_SIZE);
-    if (readNodeBlockCacheIdx == NULL)
-    {
-	fprintf(stderr, "Unable to initialise binary search array\n");
-	util::exit_nicely();
-    }
+    readNodeBlockCacheIdx.reserve(READ_NODE_CACHE_SIZE);
 
     /* Setup the file for the node position cache */
     if (append_mode)
@@ -746,7 +765,6 @@ node_persistent_cache::~node_persistent_cache()
     {
         free(readNodeBlockCache[i].nodes);
     }
-    shutdown_search_array(&readNodeBlockCacheIdx);
     free(readNodeBlockCache);
     readNodeBlockCache = NULL;
 }
diff --git a/node-persistent-cache.hpp b/node-persistent-cache.hpp
index 0c9ee9b..11722e8 100644
--- a/node-persistent-cache.hpp
+++ b/node-persistent-cache.hpp
@@ -4,6 +4,8 @@
 #include "node-ram-cache.hpp"
 #include <boost/shared_ptr.hpp>
 
+#include <vector>
+
 #define MAXIMUM_INITIAL_ID 2600000000
 
 #define READ_NODE_CACHE_SIZE 10000
@@ -23,6 +25,29 @@ struct persistentCacheHeader {
     osmid_t max_initialised_id;
 };
 
+struct cache_index_entry {
+    osmid_t key;
+    int value;
+
+    cache_index_entry(osmid_t k, int v) : key(k), value(v) {}
+    cache_index_entry() {}
+};
+
+inline bool operator<(cache_index_entry const &a, cache_index_entry const &b)
+{
+    return a.key < b.key;
+}
+
+inline bool operator<(cache_index_entry const &a, osmid_t b)
+{
+    return a.key < b;
+}
+
+inline bool operator<(osmid_t a, cache_index_entry const &b)
+{
+    return a < b.key;
+}
+
 struct node_persistent_cache : public boost::noncopyable
 {
     node_persistent_cache(const struct options_t *options, const int append,
@@ -46,6 +71,9 @@ private:
     int load_block(osmid_t block_offset);
     void nodes_set_create_writeout_block();
 
+    void remove_from_cache_idx(osmid_t block_offset);
+    void add_to_cache_idx(cache_index_entry const &entry);
+
     int node_cache_fd;
     const char * node_cache_fname;
     int append_mode;
@@ -53,7 +81,9 @@ private:
     struct persistentCacheHeader cacheHeader;
     struct ramNodeBlock writeNodeBlock; /* larger node block for more efficient initial sequential writing of node cache */
     struct ramNodeBlock * readNodeBlockCache;
-    struct binary_search_array * readNodeBlockCacheIdx;
+
+    typedef std::vector<cache_index_entry> cache_index;
+    cache_index readNodeBlockCacheIdx;
 
     int scale_;
     int cache_already_written;
diff --git a/options.cpp b/options.cpp
index 3a0a877..6691ca2 100644
--- a/options.cpp
+++ b/options.cpp
@@ -184,9 +184,11 @@ namespace
     #endif
         printf("\
        -O|--output      Output backend.\n\
-                        pgsql - Output to a PostGIS database. (default)\n\
+                        pgsql - Output to a PostGIS database (default)\n\
+                        multi - Multiple Custom Table Output to a PostGIS \n\
+                            database (requires style file for configuration)\n\
                         gazetteer - Output to a PostGIS database for Nominatim\n\
-                        null - No output. Useful for testing.\n");
+                        null - No output. Useful for testing\n");
     #ifdef HAVE_LUA
         printf("\
           --tag-transform-script  Specify a lua script to handle tag filtering and normalisation\n\
diff --git a/osm2pgsql.cpp b/osm2pgsql.cpp
index 99a0f39..59a1915 100644
--- a/osm2pgsql.cpp
+++ b/osm2pgsql.cpp
@@ -30,7 +30,6 @@
 #include "output.hpp"
 #include "osmdata.hpp"
 #include "util.hpp"
-#include "text-tree.hpp"
 
 #include <unistd.h>
 #include <assert.h>
diff --git a/output-gazetteer.cpp b/output-gazetteer.cpp
index 552b517..d2d4757 100644
--- a/output-gazetteer.cpp
+++ b/output-gazetteer.cpp
@@ -249,340 +249,334 @@ static int split_tags(struct keyval *tags, unsigned int flags,
    conscriptionnumber = 0;
    streetnumber = 0;
 
-   /* Initialise the result lists */
-   keyval::initList(names);
-   keyval::initList(places);
-   keyval::initList(extratags);
-
    /* Loop over the tags */
-   while ((item = keyval::popItem(tags)) != NULL)
+   while ((item = tags->popItem()) != NULL)
    {
 
       /* If this is a name tag, add it to the name list */
-      if (strcmp(item->key, "ref") == 0 ||
-          strcmp(item->key, "int_ref") == 0 ||
-          strcmp(item->key, "nat_ref") == 0 ||
-          strcmp(item->key, "reg_ref") == 0 ||
-          strcmp(item->key, "loc_ref") == 0 ||
-          strcmp(item->key, "old_ref") == 0 ||
-          strcmp(item->key, "ncn_ref") == 0 ||
-          strcmp(item->key, "rcn_ref") == 0 ||
-          strcmp(item->key, "lcn_ref") == 0 ||
-          strcmp(item->key, "iata") == 0 ||
-          strcmp(item->key, "icao") == 0 ||
-          strcmp(item->key, "pcode:1") == 0 ||
-          strcmp(item->key, "pcode:2") == 0 ||
-          strcmp(item->key, "pcode:3") == 0 ||
-          strcmp(item->key, "un:pcode:1") == 0 ||
-          strcmp(item->key, "un:pcode:2") == 0 ||
-          strcmp(item->key, "un:pcode:3") == 0 ||
-          strcmp(item->key, "name") == 0 ||
-          (strncmp(item->key, "name:", 5) == 0) ||
-          strcmp(item->key, "int_name") == 0 ||
-          (strncmp(item->key, "int_name:", 9) == 0) ||
-          strcmp(item->key, "nat_name") == 0 ||
-          (strncmp(item->key, "nat_name:", 9) == 0) ||
-          strcmp(item->key, "reg_name") == 0 ||
-          (strncmp(item->key, "reg_name:", 9) == 0) ||
-          strcmp(item->key, "loc_name") == 0 ||
-          (strncmp(item->key, "loc_name:", 9) == 0) ||
-          strcmp(item->key, "old_name") == 0 ||
-          (strncmp(item->key, "old_name:", 9) == 0) ||
-          strcmp(item->key, "alt_name") == 0 ||
-          (strncmp(item->key, "alt_name_", 9) == 0) ||
-          (strncmp(item->key, "alt_name:", 9) == 0) ||
-          strcmp(item->key, "official_name") == 0 ||
-          (strncmp(item->key, "official_name:", 14) == 0) ||
-          strcmp(item->key, "commonname") == 0 ||
-          (strncmp(item->key, "commonname:", 11) == 0) ||
-          strcmp(item->key, "common_name") == 0 ||
-          (strncmp(item->key, "common_name:", 12) == 0) ||
-          strcmp(item->key, "place_name") == 0 ||
-          (strncmp(item->key, "place_name:", 11) == 0) ||
-          strcmp(item->key, "short_name") == 0 ||
-          (strncmp(item->key, "short_name:", 11) == 0) ||
-          strcmp(item->key, "operator") == 0) /* operator is a bit of an oddity */
+      if (item->key == "ref" ||
+          item->key == "int_ref" ||
+          item->key == "nat_ref" ||
+          item->key == "reg_ref" ||
+          item->key == "loc_ref" ||
+          item->key == "old_ref" ||
+          item->key == "ncn_ref" ||
+          item->key == "rcn_ref" ||
+          item->key == "lcn_ref" ||
+          item->key == "iata" ||
+          item->key == "icao" ||
+          item->key == "pcode:1" ||
+          item->key == "pcode:2" ||
+          item->key == "pcode:3" ||
+          item->key == "un:pcode:1" ||
+          item->key == "un:pcode:2" ||
+          item->key == "un:pcode:3" ||
+          item->key == "name" ||
+          (strncmp(item->key.c_str(), "name:", 5) == 0) ||
+          item->key == "int_name" ||
+          (strncmp(item->key.c_str(), "int_name:", 9) == 0) ||
+          item->key == "nat_name" ||
+          (strncmp(item->key.c_str(), "nat_name:", 9) == 0) ||
+          item->key == "reg_name" ||
+          (strncmp(item->key.c_str(), "reg_name:", 9) == 0) ||
+          item->key == "loc_name" ||
+          (strncmp(item->key.c_str(), "loc_name:", 9) == 0) ||
+          item->key == "old_name" ||
+          (strncmp(item->key.c_str(), "old_name:", 9) == 0) ||
+          item->key == "alt_name" ||
+          (strncmp(item->key.c_str(), "alt_name_", 9) == 0) ||
+          (strncmp(item->key.c_str(), "alt_name:", 9) == 0) ||
+          item->key == "official_name" ||
+          (strncmp(item->key.c_str(), "official_name:", 14) == 0) ||
+          item->key == "commonname" ||
+          (strncmp(item->key.c_str(), "commonname:", 11) == 0) ||
+          item->key == "common_name" ||
+          (strncmp(item->key.c_str(), "common_name:", 12) == 0) ||
+          item->key == "place_name" ||
+          (strncmp(item->key.c_str(), "place_name:", 11) == 0) ||
+          item->key == "short_name" ||
+          (strncmp(item->key.c_str(), "short_name:", 11) == 0) ||
+          item->key == "operator") /* operator is a bit of an oddity */
       {
-         if (strcmp(item->key, "name:prefix") == 0)
+         if (item->key == "name:prefix")
          {
-            keyval::pushItem(extratags, item);
+            extratags->pushItem(item);
          }
          else
          {
-            keyval::pushItem(names, item);
+            names->pushItem(item);
          }
       }
-      else if (strcmp(item->key, "emergency") == 0 ||
-               strcmp(item->key, "tourism") == 0 ||
-               strcmp(item->key, "historic") == 0 ||
-               strcmp(item->key, "military") == 0 ||
-               strcmp(item->key, "natural") == 0)
+      else if (item->key == "emergency" ||
+               item->key == "tourism" ||
+               item->key == "historic" ||
+               item->key == "military" ||
+               item->key == "natural")
       {
-         if (strcmp(item->value, "no") && strcmp(item->value, "yes"))
+         if (item->value != "no" && item->value != "yes")
          {
-            keyval::pushItem(places, item);
+            places->pushItem(item);
          }
          else
          {
-            keyval::freeItem(item);
+            delete(item);
          }
       }
-      else if (strcmp(item->key, "highway") == 0)
+      else if (item->key == "highway")
       {
-         if (strcmp(item->value, "no") &&
-             strcmp(item->value, "turning_circle") &&
-             strcmp(item->value, "traffic_signals") &&
-             strcmp(item->value, "mini_roundabout") &&
-             strcmp(item->value, "noexit") &&
-             strcmp(item->value, "crossing"))
+         if (item->value != "no" &&
+             item->value != "turning_circle" &&
+             item->value != "traffic_signals" &&
+             item->value != "mini_roundabout" &&
+             item->value != "noexit" &&
+             item->value != "crossing")
          {
-             keyval::pushItem(places, item);
+             places->pushItem(item);
          }
          else
          {
-             keyval::freeItem(item);
+             delete(item);
          }
       }
-      else if (strcmp(item->key, "aerialway") == 0 ||
-               strcmp(item->key, "aeroway") == 0 ||
-               strcmp(item->key, "amenity") == 0 ||
-               strcmp(item->key, "boundary") == 0 ||
-               strcmp(item->key, "bridge") == 0 ||
-               strcmp(item->key, "craft") == 0 ||
-               strcmp(item->key, "leisure") == 0 ||
-               strcmp(item->key, "office") == 0 ||
-               strcmp(item->key, "railway") == 0 ||
-               strcmp(item->key, "shop") == 0 ||
-               strcmp(item->key, "tunnel") == 0 )
+      else if (item->key == "aerialway" ||
+               item->key == "aeroway" ||
+               item->key == "amenity" ||
+               item->key == "boundary" ||
+               item->key == "bridge" ||
+               item->key == "craft" ||
+               item->key == "leisure" ||
+               item->key == "office" ||
+               item->key == "railway" ||
+               item->key == "shop" ||
+               item->key == "tunnel" )
       {
-         if (strcmp(item->value, "no"))
+         if (item->value != "no")
          {
-            keyval::pushItem(places, item);
-            if (strcmp(item->key, "boundary") == 0 && strcmp(item->value, "administrative") == 0)
+            places->pushItem(item);
+            if (item->key == "boundary" && item->value == "administrative")
             {
                placeadmin = 1;
             }
          }
          else
          {
-            keyval::freeItem(item);
+            delete(item);
          }
       }
-      else if (strcmp(item->key, "waterway") == 0 &&
-               strcmp(item->value, "riverbank") != 0)
+      else if (item->key == "waterway" && item->value != "riverbank")
       {
-            keyval::pushItem(places, item);
+            places->pushItem(item);
       }
-      else if (strcmp(item->key, "place") == 0)
+      else if (item->key == "place")
       {
          place = item;
       }
-      else if (strcmp(item->key, "addr:housename") == 0)
+      else if (item->key == "addr:housename")
       {
-         keyval::pushItem(names, item);
+         names->pushItem(item);
          placehouse = 1;
       }
-      else if (strcmp(item->key, "landuse") == 0)
+      else if (item->key == "landuse")
       {
-         if (strcmp(item->value, "cemetery") == 0)
-            keyval::pushItem(places, item);
+         if (item->value == "cemetery")
+            places->pushItem(item);
          else
             landuse = item;
       }
-      else if (strcmp(item->key, "postal_code") == 0 ||
-          strcmp(item->key, "post_code") == 0 ||
-          strcmp(item->key, "postcode") == 0 ||
-          strcmp(item->key, "addr:postcode") == 0 ||
-          strcmp(item->key, "tiger:zip_left") == 0 ||
-          strcmp(item->key, "tiger:zip_right") == 0)
+      else if (item->key == "postal_code" ||
+          item->key == "post_code" ||
+          item->key == "postcode" ||
+          item->key == "addr:postcode" ||
+          item->key == "tiger:zip_left" ||
+          item->key == "tiger:zip_right")
       {
          if (*postcode)
-	        keyval::freeItem(item);
+	        delete(item);
          else
             *postcode = item;
       }
-      else if (strcmp(item->key, "addr:street") == 0)
+      else if (item->key == "addr:street")
       {
          *street = item;
       }
-      else if (strcmp(item->key, "addr:place") == 0)
+      else if (item->key == "addr:place")
       {
          *addr_place = item;
       }
-      else if ((strcmp(item->key, "country_code_iso3166_1_alpha_2") == 0 ||
-                strcmp(item->key, "country_code_iso3166_1") == 0 ||
-                strcmp(item->key, "country_code_iso3166") == 0 ||
-                strcmp(item->key, "country_code") == 0 ||
-                strcmp(item->key, "iso3166-1:alpha2") == 0 ||
-                strcmp(item->key, "iso3166-1") == 0 ||
-                strcmp(item->key, "ISO3166-1") == 0 ||
-                strcmp(item->key, "iso3166") == 0 ||
-                strcmp(item->key, "is_in:country_code") == 0 ||
-                strcmp(item->key, "addr:country") == 0 ||
-                strcmp(item->key, "addr:country_code") == 0)
-                && strlen(item->value) == 2)
+      else if ((item->key == "country_code_iso3166_1_alpha_2" ||
+                item->key == "country_code_iso3166_1" ||
+                item->key == "country_code_iso3166" ||
+                item->key == "country_code" ||
+                item->key == "iso3166-1:alpha2" ||
+                item->key == "iso3166-1" ||
+                item->key == "ISO3166-1" ||
+                item->key == "iso3166" ||
+                item->key == "is_in:country_code" ||
+                item->key == "addr:country" ||
+                item->key == "addr:country_code")
+                && item->value.length() == 2)
       {
          *countrycode = item;
       }
-      else if (strcmp(item->key, "addr:housenumber") == 0)
+      else if (item->key == "addr:housenumber")
       {
           /* house number can be far more complex than just a single house number - leave for postgresql to deal with */
          if (*housenumber)
-             keyval::freeItem(item);
+             delete(item);
          else {
              *housenumber = item;
              placehouse = 1;
          }
       }
-      else if (strcmp(item->key, "addr:conscriptionnumber") == 0)
+      else if (item->key == "addr:conscriptionnumber")
       {
          if (conscriptionnumber)
-             keyval::freeItem(item);
+             delete(item);
          else {
              conscriptionnumber = item;
              placehouse = 1;
          }
       }
-      else if (strcmp(item->key, "addr:streetnumber") == 0)
+      else if (item->key == "addr:streetnumber")
       {
          if (streetnumber)
-             keyval::freeItem(item);
+             delete(item);
          else {
              streetnumber = item;
              placehouse = 1;
          }
       }
-      else if (strcmp(item->key, "addr:interpolation") == 0)
+      else if (item->key == "addr:interpolation")
       {
           /* house number can be far more complex than just a single house number - leave for postgresql to deal with */
           if (*housenumber) {
-              keyval::freeItem(item);
+              delete(item);
           } else {
              *housenumber = item;
-             keyval::addItem(places, "place", "houses", 1);
+             places->addItem("place", "houses", true);
           }
       }
-      else if (strcmp(item->key, "tiger:county") == 0)
+      else if (item->key == "tiger:county")
       {
          /* strip the state and replace it with a county suffix to ensure that
             the tag only matches against counties and not against some town
             with the same name.
           */
-         subval = strcspn(item->value, ",");
+         subval = strcspn(item->value.c_str(), ",");
          *isin = (char *)realloc(*isin, isinsize + 9 + subval);
          *(*isin+isinsize) = ',';
-         strncpy(*isin+1+isinsize, item->value, subval);
+         strncpy(*isin+1+isinsize, item->value.c_str(), subval);
          strcpy(*isin+1+isinsize+subval, " county");
          isinsize += 8 + subval;
-         keyval::freeItem(item);
+         delete(item);
       }
-      else if (strcmp(item->key, "is_in") == 0 ||
-          (strncmp(item->key, "is_in:", 5) == 0) ||
-          strcmp(item->key, "addr:suburb")== 0 ||
-          strcmp(item->key, "addr:county")== 0 ||
-          strcmp(item->key, "addr:city") == 0 ||
-          strcmp(item->key, "addr:state_code") == 0 ||
-          strcmp(item->key, "addr:state") == 0)
+      else if (item->key == "is_in" ||
+          (strncmp(item->key.c_str(), "is_in:", 5) == 0) ||
+          item->key == "addr:suburb" ||
+          item->key == "addr:county" ||
+          item->key == "addr:city" ||
+          item->key == "addr:state_code" ||
+          item->key == "addr:state")
       {
-          *isin = (char *)realloc(*isin, isinsize + 2 + strlen(item->value));
+          *isin = (char *)realloc(*isin, isinsize + 2 + item->value.length());
          *(*isin+isinsize) = ',';
-         strcpy(*isin+1+isinsize, item->value);
-         isinsize += 1 + strlen(item->value);
-         keyval::freeItem(item);
+         strcpy(*isin+1+isinsize, item->value.c_str());
+         isinsize += 1 + item->value.length();
+         delete(item);
       }
-      else if (strcmp(item->key, "admin_level") == 0)
+      else if (item->key == "admin_level")
       {
-         *admin_level = atoi(item->value);
-         keyval::freeItem(item);
+         *admin_level = atoi(item->value.c_str());
+         delete(item);
       }
-      else if (strcmp(item->key, "tracktype") == 0 ||
-               strcmp(item->key, "traffic_calming") == 0 ||
-               strcmp(item->key, "service") == 0 ||
-               strcmp(item->key, "cuisine") == 0 ||
-               strcmp(item->key, "capital") == 0 ||
-               strcmp(item->key, "dispensing") == 0 ||
-               strcmp(item->key, "religion") == 0 ||
-               strcmp(item->key, "denomination") == 0 ||
-               strcmp(item->key, "sport") == 0 ||
-               strcmp(item->key, "internet_access") == 0 ||
-               strcmp(item->key, "lanes") == 0 ||
-               strcmp(item->key, "surface") == 0 ||
-               strcmp(item->key, "smoothness") == 0 ||
-               strcmp(item->key, "width") == 0 ||
-               strcmp(item->key, "est_width") == 0 ||
-               strcmp(item->key, "incline") == 0 ||
-               strcmp(item->key, "opening_hours") == 0 ||
-               strcmp(item->key, "food_hours") == 0 ||
-               strcmp(item->key, "collection_times") == 0 ||
-               strcmp(item->key, "service_times") == 0 ||
-               strcmp(item->key, "smoking_hours") == 0 ||
-               strcmp(item->key, "disused") == 0 ||
-               strcmp(item->key, "wheelchair") == 0 ||
-               strcmp(item->key, "sac_scale") == 0 ||
-               strcmp(item->key, "trail_visibility") == 0 ||
-               strcmp(item->key, "mtb:scale") == 0 ||
-               strcmp(item->key, "mtb:description") == 0 ||
-               strcmp(item->key, "wood") == 0 ||
-               strcmp(item->key, "drive_thru") == 0 ||
-               strcmp(item->key, "drive_in") == 0 ||
-               strcmp(item->key, "access") == 0 ||
-               strcmp(item->key, "vehicle") == 0 ||
-               strcmp(item->key, "bicyle") == 0 ||
-               strcmp(item->key, "foot") == 0 ||
-               strcmp(item->key, "goods") == 0 ||
-               strcmp(item->key, "hgv") == 0 ||
-               strcmp(item->key, "motor_vehicle") == 0 ||
-               strcmp(item->key, "motor_car") == 0 ||
-               (strncmp(item->key, "access:", 7) == 0) ||
-               (strncmp(item->key, "contact:", 8) == 0) ||
-               (strncmp(item->key, "drink:", 6) == 0) ||
-               strcmp(item->key, "oneway") == 0 ||
-               strcmp(item->key, "date_on") == 0 ||
-               strcmp(item->key, "date_off") == 0 ||
-               strcmp(item->key, "day_on") == 0 ||
-               strcmp(item->key, "day_off") == 0 ||
-               strcmp(item->key, "hour_on") == 0 ||
-               strcmp(item->key, "hour_off") == 0 ||
-               strcmp(item->key, "maxweight") == 0 ||
-               strcmp(item->key, "maxheight") == 0 ||
-               strcmp(item->key, "maxspeed") == 0 ||
-               strcmp(item->key, "disused") == 0 ||
-               strcmp(item->key, "toll") == 0 ||
-               strcmp(item->key, "charge") == 0 ||
-               strcmp(item->key, "population") == 0 ||
-               strcmp(item->key, "description") == 0 ||
-               strcmp(item->key, "image") == 0 ||
-               strcmp(item->key, "attribution") == 0 ||
-               strcmp(item->key, "fax") == 0 ||
-               strcmp(item->key, "email") == 0 ||
-               strcmp(item->key, "url") == 0 ||
-               strcmp(item->key, "website") == 0 ||
-               strcmp(item->key, "phone") == 0 ||
-               strcmp(item->key, "tel") == 0 ||
-               strcmp(item->key, "real_ale") == 0 ||
-               strcmp(item->key, "smoking") == 0 ||
-               strcmp(item->key, "food") == 0 ||
-               strcmp(item->key, "camera") == 0 ||
-               strcmp(item->key, "brewery") == 0 ||
-               strcmp(item->key, "locality") == 0 ||
-               strcmp(item->key, "wikipedia") == 0 ||
-               (strncmp(item->key, "wikipedia:", 10) == 0)
+      else if (item->key == "tracktype" ||
+               item->key == "traffic_calming" ||
+               item->key == "service" ||
+               item->key == "cuisine" ||
+               item->key == "capital" ||
+               item->key == "dispensing" ||
+               item->key == "religion" ||
+               item->key == "denomination" ||
+               item->key == "sport" ||
+               item->key == "internet_access" ||
+               item->key == "lanes" ||
+               item->key == "surface" ||
+               item->key == "smoothness" ||
+               item->key == "width" ||
+               item->key == "est_width" ||
+               item->key == "incline" ||
+               item->key == "opening_hours" ||
+               item->key == "food_hours" ||
+               item->key == "collection_times" ||
+               item->key == "service_times" ||
+               item->key == "smoking_hours" ||
+               item->key == "disused" ||
+               item->key == "wheelchair" ||
+               item->key == "sac_scale" ||
+               item->key == "trail_visibility" ||
+               item->key == "mtb:scale" ||
+               item->key == "mtb:description" ||
+               item->key == "wood" ||
+               item->key == "drive_thru" ||
+               item->key == "drive_in" ||
+               item->key == "access" ||
+               item->key == "vehicle" ||
+               item->key == "bicyle" ||
+               item->key == "foot" ||
+               item->key == "goods" ||
+               item->key == "hgv" ||
+               item->key == "motor_vehicle" ||
+               item->key == "motor_car" ||
+               (strncmp(item->key.c_str(), "access:", 7) == 0) ||
+               (strncmp(item->key.c_str(), "contact:", 8) == 0) ||
+               (strncmp(item->key.c_str(), "drink:", 6) == 0) ||
+               item->key == "oneway" ||
+               item->key == "date_on" ||
+               item->key == "date_off" ||
+               item->key == "day_on" ||
+               item->key == "day_off" ||
+               item->key == "hour_on" ||
+               item->key == "hour_off" ||
+               item->key == "maxweight" ||
+               item->key == "maxheight" ||
+               item->key == "maxspeed" ||
+               item->key == "disused" ||
+               item->key == "toll" ||
+               item->key == "charge" ||
+               item->key == "population" ||
+               item->key == "description" ||
+               item->key == "image" ||
+               item->key == "attribution" ||
+               item->key == "fax" ||
+               item->key == "email" ||
+               item->key == "url" ||
+               item->key == "website" ||
+               item->key == "phone" ||
+               item->key == "tel" ||
+               item->key == "real_ale" ||
+               item->key == "smoking" ||
+               item->key == "food" ||
+               item->key == "camera" ||
+               item->key == "brewery" ||
+               item->key == "locality" ||
+               item->key == "wikipedia" ||
+               (strncmp(item->key.c_str(), "wikipedia:", 10) == 0)
                )
       {
-          keyval::pushItem(extratags, item);
+          extratags->pushItem(item);
       }
-      else if (strcmp(item->key, "building") == 0)
+      else if (item->key == "building")
       {
           placebuilding = 1;
-          keyval::freeItem(item);
+          delete(item);
       }
-      else if (strcmp(item->key, "mountain_pass") == 0)
+      else if (item->key == "mountain_pass")
       {
-          keyval::pushItem(places, item);
+          places->pushItem(item);
       }
       else
       {
-         keyval::freeItem(item);
+         delete(item);
       }
    }
 
@@ -596,32 +590,27 @@ static int split_tags(struct keyval *tags, unsigned int flags,
    {
       if (*housenumber)
       {
-         keyval::freeItem(*housenumber);
+         delete(*housenumber);
       }
       if (!conscriptionnumber)
       {
-         keyval::addItem(tags, "addr:housenumber", streetnumber->value, 0);
-         keyval::freeItem(streetnumber);
-         *housenumber = keyval::popItem(tags);
+         streetnumber->key.assign("addr:housenumber");
+         *housenumber = streetnumber;
       }
       if (!streetnumber)
       {
-         keyval::addItem(tags, "addr:housenumber", conscriptionnumber->value, 10);
-         keyval::freeItem(conscriptionnumber);
-         *housenumber = keyval::popItem(tags);
+         conscriptionnumber->key.assign("addr:housenumber");
+         *housenumber = conscriptionnumber;
       }
       if (conscriptionnumber && streetnumber)
       {
-         char * completenumber = strdup(conscriptionnumber->value);
-         size_t completenumberlength = strlen(completenumber);
-         completenumber = (char *)realloc(completenumber, completenumberlength + 2 + strlen(streetnumber->value));
-         *(completenumber + completenumberlength) = '/';
-         strcpy(completenumber + completenumberlength + 1, streetnumber->value);
-         keyval::freeItem(conscriptionnumber);
-         keyval::freeItem(streetnumber);
-         keyval::addItem(tags, "addr:housenumber", completenumber, 0);
-         *housenumber = keyval::popItem(tags);
-         free(completenumber);
+         conscriptionnumber->key.assign("addr:housenumber");
+         conscriptionnumber->value.reserve(conscriptionnumber->value.size() + 1
+                                           + streetnumber->value.size());
+         conscriptionnumber->value += '/';
+         conscriptionnumber->value += streetnumber->value;
+         delete(streetnumber);
+         *housenumber = conscriptionnumber;
       }
     }
 
@@ -629,40 +618,40 @@ static int split_tags(struct keyval *tags, unsigned int flags,
    {
       if (placeadmin)
       {
-         keyval::pushItem(extratags, place);
+         extratags->pushItem(place);
       }
       else
       {
-         keyval::pushItem(places, place);
+         places->pushItem(place);
       }
    }
 
-   if (placehouse && !keyval::listHasData(places))
+   if (placehouse && !places->listHasData())
    {
-      keyval::addItem(places, "place", "house", 1);
+      places->addItem("place", "house", false);
    }
 
    /* Fallback place types - only used if we didn't create something more specific already */
-   if (placebuilding && !keyval::listHasData(places) && (keyval::listHasData(names) || *housenumber || *postcode))
+   if (placebuilding && !places->listHasData() && (names->listHasData() || *housenumber || *postcode))
    {
-      keyval::addItem(places, "building", "yes", 1);
+      places->addItem("building", "yes", false);
    }
 
    if (landuse)
    {
-      if (!keyval::listHasData(places) && keyval::listHasData(names))
+      if (!places->listHasData() && names->listHasData())
       {
-          keyval::pushItem(places, landuse);
+          places->pushItem(landuse);
       }
       else
       {
-          keyval::freeItem(landuse);
+          delete(landuse);
       }
    }
 
-   if (*postcode && !keyval::listHasData(places))
+   if (*postcode && !places->listHasData())
    {
-      keyval::addItem(places, "place", "postcode", 1);
+      places->addItem("place", "postcode", false);
    }
 
    /* Try to convert everything to an area */
@@ -719,7 +708,7 @@ void output_gazetteer_t::delete_unused_classes(char osm_type, osmid_t osm_id, st
     } else {
         for (i = 0; i < sz; i++) {
             cls = PQgetvalue(res, i, 0);
-            if (!keyval::getItem(places, cls)) {
+            if (!places->getItem(cls)) {
                 if (!clslist) {
                     clslist = (char *)malloc(strlen(cls)+3);
                     sprintf(clslist, "'%s'", cls);
@@ -745,7 +734,7 @@ void output_gazetteer_t::delete_unused_classes(char osm_type, osmid_t osm_id, st
     }
 }
 
-void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *key_class, const char *type, struct keyval *names, struct keyval *extratags,
+void output_gazetteer_t::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 char *wkt)
 {
    int first;
@@ -756,31 +745,31 @@ void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *ke
    sprintf(sql, "%c\t%" PRIdOSMID "\t", osm_type, osm_id);
    copy_data(sql);
 
-   escape(sql, sizeof(sql), key_class);
+   escape(sql, sizeof(sql), key_class.c_str());
    copy_data(sql);
    copy_data("\t");
 
-   escape(sql, sizeof(sql), type);
+   escape(sql, sizeof(sql), type.c_str());
    copy_data(sql);
    copy_data("\t");
 
    /* start name array */
-   if (keyval::listHasData(names))
+   if (names->listHasData())
    {
       first = 1;
-      for (name = keyval::firstItem(names); name; name = keyval::nextItem(names, name))
+      for (name = names->firstItem(); name; name = names->nextItem(name))
       {
          if (first) first = 0;
          else copy_data(", ");
 
          copy_data("\"");
 
-         escape_array_record(sql, sizeof(sql), name->key);
+         escape_array_record(sql, sizeof(sql), name->key.c_str());
          copy_data(sql);
 
          copy_data("\"=>\"");
 
-         escape_array_record(sql, sizeof(sql), name->value);
+         escape_array_record(sql, sizeof(sql), name->value.c_str());
          copy_data(sql);
 
          copy_data("\"");
@@ -797,7 +786,7 @@ void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *ke
 
    if (housenumber)
    {
-      escape(sql, sizeof(sql), housenumber->value);
+      escape(sql, sizeof(sql), housenumber->value.c_str());
       copy_data(sql);
       copy_data("\t");
    }
@@ -808,7 +797,7 @@ void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *ke
 
    if (street)
    {
-      escape(sql, sizeof(sql), street->value);
+      escape(sql, sizeof(sql), street->value.c_str());
       copy_data(sql);
       copy_data("\t");
    }
@@ -819,7 +808,7 @@ void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *ke
 
    if (addr_place)
    {
-      escape(sql, sizeof(sql), addr_place->value);
+      escape(sql, sizeof(sql), addr_place->value.c_str());
       copy_data(sql);
       copy_data("\t");
    }
@@ -842,7 +831,7 @@ void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *ke
 
    if (postcode)
    {
-      escape(sql, sizeof(sql), postcode->value);
+      escape(sql, sizeof(sql), postcode->value.c_str());
       copy_data(sql);
       copy_data("\t");
    }
@@ -853,7 +842,7 @@ void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *ke
 
    if (countrycode)
    {
-      escape(sql, sizeof(sql), countrycode->value);
+      escape(sql, sizeof(sql), countrycode->value.c_str());
       copy_data(sql);
       copy_data("\t");
    }
@@ -863,22 +852,22 @@ void output_gazetteer_t::add_place(char osm_type, osmid_t osm_id, const char *ke
    }
 
    /* extra tags array */
-   if (keyval::listHasData(extratags))
+   if (extratags->listHasData())
    {
       first = 1;
-      for (name = keyval::firstItem(extratags); name; name = keyval::nextItem(extratags, name))
+      for (name = extratags->firstItem(); name; name = extratags->nextItem(name))
       {
          if (first) first = 0;
          else copy_data(", ");
 
          copy_data("\"");
 
-         escape_array_record(sql, sizeof(sql), name->key);
+         escape_array_record(sql, sizeof(sql), name->key.c_str());
          copy_data(sql);
 
          copy_data("\"=>\"");
 
-         escape_array_record(sql, sizeof(sql), name->value);
+         escape_array_record(sql, sizeof(sql), name->value.c_str());
          copy_data(sql);
 
          copy_data("\"");
@@ -1122,26 +1111,26 @@ int output_gazetteer_t::gazetteer_process_node(osmid_t id, double lat, double lo
        delete_unused_classes('N', id, &places);
 
    /* Are we interested in this item? */
-   if (keyval::listHasData(&places))
+   if (places.listHasData())
    {
       sprintf(wkt, "POINT(%.15g %.15g)", lon, lat);
-      for (place = keyval::firstItem(&places); place; place = keyval::nextItem(&places, place))
+      for (place = places.firstItem(); place; place = places.nextItem(place))
       {
          add_place('N', id, place->key, place->value, &names, &extratags, adminlevel, housenumber, street, addr_place, isin, postcode, countrycode, wkt);
       }
    }
 
-   if (housenumber) keyval::freeItem(housenumber);
-   if (street) keyval::freeItem(street);
-   if (addr_place) keyval::freeItem(addr_place);
+   if (housenumber) delete(housenumber);
+   if (street) delete(street);
+   if (addr_place) delete(addr_place);
    if (isin) free(isin);
-   if (postcode) keyval::freeItem(postcode);
-   if (countrycode) keyval::freeItem(countrycode);
+   if (postcode) delete(postcode);
+   if (countrycode) delete(countrycode);
 
    /* Free tag lists */
-   keyval::resetList(&names);
-   keyval::resetList(&places);
-   keyval::resetList(&extratags);
+   names.resetList();
+   places.resetList();
+   extratags.resetList();
 
    return 0;
 }
@@ -1174,7 +1163,7 @@ int output_gazetteer_t::gazetteer_process_way(osmid_t id, osmid_t *ndv, int ndc,
        delete_unused_classes('W', id, &places);
 
    /* Are we interested in this item? */
-   if (keyval::listHasData(&places))
+   if (places.listHasData())
    {
       struct osmNode *nodev;
       int nodec;
@@ -1187,7 +1176,7 @@ int output_gazetteer_t::gazetteer_process_way(osmid_t id, osmid_t *ndv, int ndc,
       geometry_builder::maybe_wkt_t wkt = builder.get_wkt_simple(nodev, nodec, area);
       if (wkt)
       {
-         for (place = keyval::firstItem(&places); place; place = keyval::nextItem(&places, place))
+         for (place = places.firstItem(); place; place = places.nextItem(place))
          {
             add_place('W', id, place->key, place->value, &names, &extratags, adminlevel,
                       housenumber, street, addr_place, isin, postcode, countrycode, wkt->geom.c_str());
@@ -1198,17 +1187,17 @@ int output_gazetteer_t::gazetteer_process_way(osmid_t id, osmid_t *ndv, int ndc,
       free(nodev);
    }
 
-   if (housenumber) keyval::freeItem(housenumber);
-   if (street) keyval::freeItem(street);
-   if (addr_place) keyval::freeItem(addr_place);
+   if (housenumber) delete(housenumber);
+   if (street) delete(street);
+   if (addr_place) delete(addr_place);
    if (isin) free(isin);
-   if (postcode) keyval::freeItem(postcode);
-   if (countrycode) keyval::freeItem(countrycode);
+   if (postcode) delete(postcode);
+   if (countrycode) delete(countrycode);
 
    /* Free tag lists */
-   keyval::resetList(&names);
-   keyval::resetList(&places);
-   keyval::resetList(&extratags);
+   names.resetList();
+   places.resetList();
+   extratags.resetList();
 
    return 0;
 }
@@ -1231,24 +1220,23 @@ int output_gazetteer_t::gazetteer_process_relation(osmid_t id, struct member *me
    char * isin;
    struct keyval * postcode;
    struct keyval * countrycode;
-   const char *type;
    int cmp_waterway;
 
-   type = keyval::getItem(tags, "type");
+   const std::string *type = tags->getItem("type");
    if (!type) {
       if (delete_old) delete_unused_classes('R', id, 0);
       return 0;
    }
 
-   cmp_waterway = strcmp(type, "waterway");
+   cmp_waterway = type->compare("waterway");
 
-   if (!strcmp(type, "associatedStreet"))
+   if (!type->compare("associatedStreet"))
    {
       if (delete_old) delete_unused_classes('R', id, 0);
       return 0;
    }
 
-   if (strcmp(type, "boundary") && strcmp(type, "multipolygon") && cmp_waterway) {
+   if (type->compare("boundary") && type->compare("multipolygon") && cmp_waterway) {
       if (delete_old) delete_unused_classes('R', id, 0);
       return 0;
    }
@@ -1264,7 +1252,7 @@ int output_gazetteer_t::gazetteer_process_relation(osmid_t id, struct member *me
    if (delete_old)
        delete_unused_classes('R', id, &places);
 
-   if (keyval::listHasData(&places))
+   if (places.listHasData())
    {
       /* get the boundary path (ways) */
       int i, count;
@@ -1306,7 +1294,7 @@ int output_gazetteer_t::gazetteer_process_relation(osmid_t id, struct member *me
          {
             if ((boost::starts_with(wkt->geom,  "POLYGON") || boost::starts_with(wkt->geom,  "MULTIPOLYGON")))
             {
-                for (place = keyval::firstItem(&places); place; place = keyval::nextItem(&places, place))
+                for (place = places.firstItem(); place; place = places.nextItem(place))
                 {
                    add_place('R', id, place->key, place->value, &names, &extratags, adminlevel, housenumber, street, addr_place,
                              isin, postcode, countrycode, wkt->geom.c_str());
@@ -1323,7 +1311,7 @@ int output_gazetteer_t::gazetteer_process_relation(osmid_t id, struct member *me
          geometry_builder::maybe_wkt_t wkt = builder.build_multilines(xnodes, xcount, id);
          if ((wkt->geom).length() > 0)
          {
-            for (place = keyval::firstItem(&places); place; place = keyval::nextItem(&places, place))
+            for (place = places.firstItem(); place; place = places.nextItem(place))
             {
                add_place('R', id, place->key, place->value, &names, &extratags, adminlevel, housenumber, street, addr_place,
                          isin, postcode, countrycode, wkt->geom.c_str());
@@ -1332,7 +1320,7 @@ int output_gazetteer_t::gazetteer_process_relation(osmid_t id, struct member *me
       }
       for( i=0; i<count; i++ )
       {
-         keyval::resetList( &(xtags[i]) );
+         xtags[i].resetList();
          free( xnodes[i] );
       }
 
@@ -1343,17 +1331,17 @@ int output_gazetteer_t::gazetteer_process_relation(osmid_t id, struct member *me
       free(xnodes);
    }
 
-   if (housenumber) keyval::freeItem(housenumber);
-   if (street) keyval::freeItem(street);
-   if (addr_place) keyval::freeItem(addr_place);
+   if (housenumber) delete(housenumber);
+   if (street) delete(street);
+   if (addr_place) delete(addr_place);
    if (isin) free(isin);
-   if (postcode) keyval::freeItem(postcode);
-   if (countrycode) keyval::freeItem(countrycode);
+   if (postcode) delete(postcode);
+   if (countrycode) delete(countrycode);
 
    /* Free tag lists */
-   keyval::resetList(&names);
-   keyval::resetList(&places);
-   keyval::resetList(&extratags);
+   names.resetList();
+   places.resetList();
+   extratags.resetList();
 
    return 0;
 }
diff --git a/output-gazetteer.hpp b/output-gazetteer.hpp
index af18a6d..caa3a1b 100644
--- a/output-gazetteer.hpp
+++ b/output-gazetteer.hpp
@@ -44,7 +44,7 @@ private:
     void copy_data(const char *sql);
     void stop_copy(void);
     void delete_unused_classes(char osm_type, osmid_t osm_id, struct keyval *places);
-    void add_place(char osm_type, osmid_t osm_id, const char *key_class, const char *type,
+    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,
diff --git a/output-multi.cpp b/output-multi.cpp
index eae1799..9a0fbd9 100644
--- a/output-multi.cpp
+++ b/output-multi.cpp
@@ -7,14 +7,6 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <vector>
 
-namespace {
-
-std::string mk_column_name(const std::string &name, const options_t &options) {
-    return (boost::format("%1%_%2%") % options.prefix % name).str();
-}
-
-} // anonymous namespace
-
 output_multi_t::output_multi_t(const std::string &name,
                                boost::shared_ptr<geometry_processor> processor_,
                                const struct export_list &export_list_,
@@ -25,7 +17,7 @@ output_multi_t::output_multi_t(const std::string &name,
       m_processor(processor_),
       //TODO: we could in fact have something that is interested in nodes and ways..
       m_osm_type(m_processor->interests(geometry_processor::interest_node) ? OSMTYPE_NODE : OSMTYPE_WAY),
-      m_table(new table_t(m_options.conninfo, mk_column_name(name, m_options), m_processor->column_type(),
+      m_table(new table_t(m_options.conninfo, name, m_processor->column_type(),
                           m_export_list->normal_columns(m_osm_type),
                           m_options.hstore_columns, m_processor->srid(), m_options.scale,
                           m_options.append, m_options.slim, m_options.droptemp,
@@ -101,14 +93,13 @@ int output_multi_t::pending_way(osmid_t id, int exists) {
     int count_int;
     int ret = 0;
 
-    keyval::initList(&tags_int);
     // Try to fetch the way from the DB
     if (!m_mid->ways_get(id, &tags_int, &nodes_int, &count_int)) {
         // Output the way
         ret = reprocess_way(id, nodes_int, count_int, &tags_int, exists);
         free(nodes_int);
     }
-    keyval::resetList(&tags_int);
+    tags_int.resetList();
 
     return ret;
 }
@@ -148,13 +139,12 @@ int output_multi_t::pending_relation(osmid_t id, int exists) {
     int count_int;
     int ret = 0;
 
-    keyval::initList(&tags_int);
     // 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);
     }
-    keyval::resetList(&tags_int);
+    tags_int.resetList();
 
     return ret;
 }
diff --git a/output-pgsql.cpp b/output-pgsql.cpp
index f72d739..8add8a0 100644
--- a/output-pgsql.cpp
+++ b/output-pgsql.cpp
@@ -136,7 +136,7 @@ int output_pgsql_t::pgsql_out_way(osmid_t id, struct keyval *tags, const struct
             if ((wkt->area > 0.0) && m_enable_way_area) {
                 char tmp[32];
                 snprintf(tmp, sizeof(tmp), "%g", wkt->area);
-                keyval::addItem(tags, "way_area", tmp, 0);
+                tags->addItem("way_area", tmp, false);
             }
             m_tables[t_poly]->write_wkt(id, tags, wkt->geom.c_str());
         } else {
@@ -192,7 +192,7 @@ 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);
-                keyval::addItem(rel_tags, "way_area", tmp, 0);
+                rel_tags->addItem("way_area", tmp, false);
             }
             m_tables[t_poly]->write_wkt(-id, rel_tags, wkt->geom.c_str());
         } else {
@@ -230,7 +230,7 @@ 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);
-                keyval::addItem(rel_tags, "way_area", tmp, 0);
+                rel_tags->addItem("way_area", tmp, false);
             }
             m_tables[t_poly]->write_wkt(-id, rel_tags, wkt->geom.c_str());
         }
@@ -292,7 +292,6 @@ int output_pgsql_t::pending_way(osmid_t id, int exists) {
     int count_int;
     int ret = 0;
 
-    keyval::initList(&tags_int);
     // Try to fetch the way from the DB
     if (!m_mid->ways_get(id, &tags_int, &nodes_int, &count_int)) {
         // Output the way
@@ -300,7 +299,7 @@ int output_pgsql_t::pending_way(osmid_t id, int exists) {
         ret = pgsql_out_way(id, &tags_int, nodes_int, count_int, exists);
         free(nodes_int);
     }
-    keyval::resetList(&tags_int);
+    tags_int.resetList();
 
     return ret;
 }
@@ -340,13 +339,12 @@ int output_pgsql_t::pending_relation(osmid_t id, int exists) {
     int count_int;
     int ret = 0;
 
-    keyval::initList(&tags_int);
     // 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);
     }
-    keyval::resetList(&tags_int);
+    tags_int.resetList();
 
     return ret;
 }
@@ -495,7 +493,7 @@ int output_pgsql_t::pgsql_process_relation(osmid_t id, const struct member *memb
 
   for( i=0; i<count2; i++ )
   {
-    keyval::resetList( &(xtags[i]) );
+    xtags[i].resetList();
     free( xnodes[i] );
   }
 
@@ -510,14 +508,14 @@ int output_pgsql_t::pgsql_process_relation(osmid_t id, const struct member *memb
 
 int output_pgsql_t::relation_add(osmid_t id, struct member *members, int member_count, struct keyval *tags)
 {
-  const char *type = keyval::getItem(tags, "type");
+  const std::string *type = tags->getItem("type");
 
   /* Must have a type field or we ignore it */
   if (!type)
       return 0;
 
   /* Only a limited subset of type= is supported, ignore other */
-  if ( (strcmp(type, "route") != 0) && (strcmp(type, "multipolygon") != 0) && (strcmp(type, "boundary") != 0))
+  if ( (*type != "route") && (*type != "multipolygon") && (*type != "boundary"))
     return 0;
 
 
diff --git a/parse-o5m.cpp b/parse-o5m.cpp
index 17eb378..b6fc9de 100644
--- a/parse-o5m.cpp
+++ b/parse-o5m.cpp
@@ -809,18 +809,18 @@ return 1;
 
       hisver= pbf_uint32(&bufp);
       uint32toa(hisver,tmpstr);
-      keyval::addItem(&(tags),"osm_version",tmpstr,0);
+      tags.addItem("osm_version",tmpstr,false);
       if(hisver!=0) {  /* history information available */
         histime= o5histime+= pbf_sint64(&bufp);
         createtimestamp(histime,tmpstr);
-        keyval::addItem(&(tags),"osm_timestamp",tmpstr, 0);
+        tags.addItem("osm_timestamp",tmpstr, false);
         if(histime!=0) {
             hiscset= o5hiscset+= pbf_sint32(&bufp);  /* (not used) */
           str_read(&bufp,&sp,&hisuser);
           hisuid= pbf_uint64((byte**)&sp);
           uint32toa(hisuid,tmpstr);
-          keyval::addItem(&(tags),"osm_uid",tmpstr,0);
-          keyval::addItem(&(tags),"osm_user",hisuser,0);
+          tags.addItem("osm_uid",tmpstr,false);
+          tags.addItem("osm_user",hisuser,false);
           }
       }  /* end   history information available */
     }  /* end   read history */
@@ -841,7 +841,7 @@ return 1;
         break;
       default: ;
         }
-      keyval::resetList(&(tags));
+      tags.resetList();
       continue;  /* end processing for this object */
     }  /* end   delete request */
     else {  /* not a delete request */
@@ -858,7 +858,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)) {
-          keyval::resetList(&(tags));
+          tags.resetList();
   continue;
           }
         proj->reproject(&(node_lat),&(node_lon));
@@ -920,7 +920,7 @@ return 1;
             /* replace all blanks in key by underlines */
             p++;
             }
-          keyval::addItem(&(tags),k,v,0);
+          tags.addItem(k,v,false);
           }
       }  /* end   for all tags of this object */
 
@@ -954,7 +954,7 @@ return 1;
         }
 
       /* reset temporary storage lists */
-      keyval::resetList(&(tags));
+      tags.resetList();
 
     }  /* end   not a delete request */
 
diff --git a/parse-pbf.cpp b/parse-pbf.cpp
index 348236a..051894d 100644
--- a/parse-pbf.cpp
+++ b/parse-pbf.cpp
@@ -172,27 +172,19 @@ static size_t uncompress_blob(Blob *bmsg, void *buf, int32_t max_size)
 
 int addProtobufItem(struct keyval *head, ProtobufCBinaryData key, ProtobufCBinaryData val, int noDupe)
 {
-  char *keystr, *valstr;
-  int retval;
+  std::string keystr((const char *) key.data, key.len);
 
-  keystr = (char *)calloc(key.len + 1, 1);
-  memcpy(keystr, key.data, key.len);
+  assert(keystr.find('\0') == std::string::npos);
 
   /* drop certain keys (matching parse-xml2) */
-  if ((strcmp(keystr, "created_by") == 0) || (strcmp(keystr, "source") == 0)) {
-    free(keystr);
+  if ((keystr == "created_by") || (keystr == "source"))
     return 0;
-  }
-
-  valstr = (char *)calloc(val.len + 1, 1);
-  memcpy(valstr, val.data, val.len);
 
-  retval = keyval::addItem(head, keystr, valstr, noDupe);
+  std::string valstr((const char *) val.data, val.len);
 
-  free(keystr);
-  free(valstr);
+  assert(valstr.find('\0') == std::string::npos);
 
-  return retval;
+  return head->addItem(keystr, valstr, noDupe);
 }
 
 int addIntItem(struct keyval *head, const char *key, int val, int noDupe)
@@ -200,7 +192,7 @@ int addIntItem(struct keyval *head, const char *key, int val, int noDupe)
   char buf[100];
 
   sprintf(buf, "%d", val);
-  return keyval::addItem(head, key, buf, noDupe);
+  return head->addItem(key, buf, noDupe);
 }
 
 int addInfoItems(struct keyval *head, Info *info, StringTable *string_table)
@@ -221,7 +213,7 @@ int addInfoItems(struct keyval *head, Info *info, StringTable *string_table)
         username = (char *)calloc(user.len + 1, 1);
 	memcpy(username, user.data, user.len);
 
-	keyval::addItem(head, "osm_user", username, 0);
+	head->addItem("osm_user", username, false);
     free(username);
       }
 
@@ -250,7 +242,7 @@ int parse_pbf_t::processOsmDataNodes(struct osmdata_t *osmdata, PrimitiveGroup *
     Node *node = group->nodes[node_id];
     double lat, lon;
 
-    keyval::resetList(&(tags));
+    tags.resetList();
 
     if (node->info && extra_attributes) {
       addInfoItems(&(tags), node->info, string_table);
@@ -303,7 +295,7 @@ int parse_pbf_t::processOsmDataDenseNodes(struct osmdata_t *osmdata, PrimitiveGr
         DenseNodes *dense = group->dense;
 
         for (node_id = 0; node_id < dense->n_id; node_id++) {
-            keyval::resetList(&(tags));
+            tags.resetList();
 
             deltaid += dense->id[node_id];
             deltalat += dense->lat[node_id];
@@ -325,7 +317,7 @@ int parse_pbf_t::processOsmDataDenseNodes(struct osmdata_t *osmdata, PrimitiveGr
                     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);
-                    keyval::addItem(&(tags), "osm_user", valstr, 0);
+                    tags.addItem("osm_user", valstr, false);
                     free(valstr);
                 }
             }
@@ -373,7 +365,7 @@ int parse_pbf_t::processOsmDataWays(struct osmdata_t *osmdata, PrimitiveGroup *g
     Way *way = group->ways[way_id];
     osmid_t deltaref = 0;
 
-    keyval::resetList(&(tags));
+    tags.resetList();
 
     if (way->info && extra_attributes) {
       addInfoItems(&(tags), way->info, string_table);
@@ -424,7 +416,7 @@ int parse_pbf_t::processOsmDataRelations(struct osmdata_t *osmdata, PrimitiveGro
     Relation *relation = group->relations[rel_id];
     osmid_t deltamemids = 0;
 
-    keyval::resetList(&(tags));
+    tags.resetList();
 
     member_count = 0;
 
diff --git a/parse-xml2.cpp b/parse-xml2.cpp
index fc118ae..2c4f58d 100644
--- a/parse-xml2.cpp
+++ b/parse-xml2.cpp
@@ -181,7 +181,7 @@ void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, st
       while ((p = strchr(k, ' ')))
         *p = '_';
 
-      keyval::addItem(&(tags), k, (char *)xv, 0);
+      tags.addItem(k, (char *)xv, 0);
       xmlFree(k);
       xmlFree(xv);
     }
@@ -246,31 +246,31 @@ void parse_xml2_t::StartElement(xmlTextReaderPtr reader, const xmlChar *name, st
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "user");
       if (xtmp) {
-    keyval::addItem(&(tags), "osm_user", (char *)xtmp, 0);
+          tags.addItem("osm_user", (char *)xtmp, false);
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "uid");
       if (xtmp) {
-    keyval::addItem(&(tags), "osm_uid", (char *)xtmp, 0);
+          tags.addItem("osm_uid", (char *)xtmp, false);
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "version");
       if (xtmp) {
-    keyval::addItem(&(tags), "osm_version", (char *)xtmp, 0);
+          tags.addItem("osm_version", (char *)xtmp, false);
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp");
       if (xtmp) {
-    keyval::addItem(&(tags), "osm_timestamp", (char *)xtmp, 0);
+          tags.addItem("osm_timestamp", (char *)xtmp, false);
           xmlFree(xtmp);
       }
 
       xtmp = xmlTextReaderGetAttribute(reader, BAD_CAST "changeset");
       if (xtmp) {
-    keyval::addItem(&(tags), "osm_changeset", (char *)xtmp, 0);
+          tags.addItem("osm_changeset", (char *)xtmp, false);
           xmlFree(xtmp);
       }
   }
@@ -294,7 +294,7 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
                 util::exit_nicely();
             }
         }
-        keyval::resetList(&(tags));
+        tags.resetList();
     } else if (xmlStrEqual(name, BAD_CAST "way")) {
         if( action == ACTION_CREATE )
 	    osmdata->way_add(osm_id, nds, nd_count, &(tags) );
@@ -307,7 +307,7 @@ 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();
         }
-        keyval::resetList(&(tags));
+        tags.resetList();
     } else if (xmlStrEqual(name, BAD_CAST "relation")) {
         if( action == ACTION_CREATE )
 	    osmdata->relation_add(osm_id, members, member_count, &(tags));
@@ -320,7 +320,7 @@ 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();
         }
-        keyval::resetList(&(tags));
+        tags.resetList();
         resetMembers();
     } else if (xmlStrEqual(name, BAD_CAST "tag")) {
         /* ignore */
@@ -343,7 +343,7 @@ void parse_xml2_t::EndElement(const xmlChar *name, struct osmdata_t *osmdata)
         /* ignore */
     } else if (xmlStrEqual(name, BAD_CAST "changeset")) {
         /* ignore */
-	keyval::resetList(&(tags)); /* We may have accumulated some tags even if we ignored the changeset */
+    tags.resetList(); /* 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")) {
diff --git a/pgsql.cpp b/pgsql.cpp
index a7eaea4..ed3435a 100644
--- a/pgsql.cpp
+++ b/pgsql.cpp
@@ -10,11 +10,9 @@
 
 void escape(const char* src, std::string& dst)
 {
-    //loop over the chars in the input
-    size_t length = strlen(src);
-    for(size_t i = 0; i < length; ++i)
+    for(; *src; ++src)
     {
-        switch(src[i]) {
+        switch(*src) {
             case '\\':  dst.append("\\\\"); break;
             //case 8:   dst.append("\\\b"); break;
             //case 12:  dst.append("\\\f"); break;
@@ -22,7 +20,7 @@ void escape(const char* src, std::string& dst)
             case '\r':  dst.append("\\\r"); break;
             case '\t':  dst.append("\\\t"); break;
             //case 11:  dst.append("\\\v"); break;
-            default:    dst.push_back(src[i]); break;
+            default:    dst.push_back(*src); break;
         }
     }
 }
diff --git a/sample.multi.json b/sample.multi.json
new file mode 100644
index 0000000..d671a91
--- /dev/null
+++ b/sample.multi.json
@@ -0,0 +1,16 @@
+    [
+      {
+        "name": "building",
+        "type": "polygon",
+        "tagtransform": "building.lua",
+        "tagtransform-node-function": "nodes_proc",
+        "tagtransform-way-function": "ways_proc",
+        "tagtransform-relation-function": "rels_proc",
+        "tagtransform-relation-member-function": "rel_members_proc",
+        "tags": [
+          {"name": "building", "type": "text"},
+          {"name": "shop", "type": "text"},
+          {"name": "amenity", "type": "text"}
+        ]
+      }
+    ]
diff --git a/table.cpp b/table.cpp
index 515dce2..7e40f7d 100644
--- a/table.cpp
+++ b/table.cpp
@@ -356,9 +356,9 @@ void table_t::write_columns(keyval *tags, string& values)
     for(columns_t::const_iterator column = columns.begin(); column != columns.end(); ++column)
     {
         keyval *tag = NULL;
-        if ((tag = keyval::getTag(tags, column->first.c_str())))
+        if ((tag = tags->getTag(column->first)))
         {
-            escape_type(tag->value, column->second.c_str(), values);
+            escape_type(tag->value.c_str(), column->second.c_str(), 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;
@@ -373,18 +373,18 @@ void table_t::write_tags_column(keyval *tags, std::string& values)
 {
     //iterate through the list of tags, first one is always null
     bool added = false;
-    for (keyval* xtags = tags->next; xtags->key != NULL; xtags = xtags->next)
+    for (keyval* xtags = tags->firstItem(); xtags; xtags = tags->nextItem(xtags))
     {
         //skip z_order tag and keys which have their own column
-        if ((xtags->has_column) || (strcmp("z_order", xtags->key) == 0))
+        if (xtags->has_column || ("z_order" == xtags->key))
             continue;
 
         //hstore ASCII representation looks like "key"=>"value"
         if(added)
             values.push_back(',');
-        escape4hstore(xtags->key, values);
+        escape4hstore(xtags->key.c_str(), values);
         values.append("=>");
-        escape4hstore(xtags->value, values);
+        escape4hstore(xtags->value.c_str(), values);
 
         //we did at least one so we need commas from here on out
         added = true;
@@ -400,17 +400,16 @@ void table_t::write_hstore_columns(keyval *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)
     {
-        //a clone of the tags pointer
         bool added = false;
 
         //iterate through the list of tags, first one is always null
-        for (keyval* xtags = tags->next; xtags->key != NULL; xtags = xtags->next)
+        for (keyval* xtags = tags->firstItem(); xtags; xtags = tags->nextItem(xtags))
         {
             //check if the tag's key starts with the name of the hstore column
-            if(hstore_column->find(xtags->key) == 0)
+            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...
-                char* shortkey = xtags->key + hstore_column->size();
+                const char* shortkey = xtags->key.c_str() + hstore_column->size();
 
                 //and pack the shortkey with its value into the hstore
                 //hstore ASCII representation looks like "key"=>"value"
@@ -418,7 +417,7 @@ void table_t::write_hstore_columns(keyval *tags, std::string& values)
                     values.push_back(',');
                 escape4hstore(shortkey, values);
                 values.append("=>");
-                escape4hstore(xtags->value, values);
+                escape4hstore(xtags->value.c_str(), values);
 
                 //we did at least one so we need commas from here on out
                 added = true;
diff --git a/taginfo.cpp b/taginfo.cpp
index 1f91472..a00141a 100644
--- a/taginfo.cpp
+++ b/taginfo.cpp
@@ -22,9 +22,9 @@
  */
 struct flagsname {
     flagsname(const char *name_, int flag_)
-        : name(strdup(name_)), flag(flag_) {
+        : name(name_), flag(flag_) {
     }
-    char *name;
+    const char *name;
     int flag;
 };
 
diff --git a/tagtransform.cpp b/tagtransform.cpp
index 119f4f2..581f604 100644
--- a/tagtransform.cpp
+++ b/tagtransform.cpp
@@ -38,25 +38,23 @@ static const unsigned int nLayers = (sizeof(layers)/sizeof(*layers));
 
 namespace {
 int add_z_order(keyval *tags, int *roads) {
-    const char *layer = keyval::getItem(tags, "layer");
-    const char *highway = keyval::getItem(tags, "highway");
-    const char *bridge = keyval::getItem(tags, "bridge");
-    const char *tunnel = keyval::getItem(tags, "tunnel");
-    const char *railway = keyval::getItem(tags, "railway");
-    const char *boundary = keyval::getItem(tags, "boundary");
+    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");
 
     int z_order = 0;
-    int l;
-    unsigned int i;
     char z[13];
 
-    l = layer ? strtol(layer, NULL, 10) : 0;
+    int l = layer ? strtol(layer->c_str(), NULL, 10) : 0;
     z_order = 10 * l;
     *roads = 0;
 
     if (highway) {
-        for (i = 0; i < nLayers; i++) {
-            if (!strcmp(layers[i].highway, highway)) {
+        for (unsigned i = 0; i < nLayers; i++) {
+            if (!strcmp(layers[i].highway, highway->c_str())) {
                 z_order += layers[i].offset;
                 *roads = layers[i].roads;
                 break;
@@ -64,26 +62,22 @@ int add_z_order(keyval *tags, int *roads) {
         }
     }
 
-    if (railway && strlen(railway)) {
+    if (railway && !railway->empty()) {
         z_order += 5;
         *roads = 1;
     }
     /* Administrative boundaries are rendered at low zooms so we prefer to use the roads table */
-    if (boundary && !strcmp(boundary, "administrative"))
+    if (boundary && *boundary == "administrative")
         *roads = 1;
 
-    if (bridge
-            && (!strcmp(bridge, "true") || !strcmp(bridge, "yes")
-                    || !strcmp(bridge, "1")))
+    if (bridge  && (*bridge == "true" || *bridge == "yes" || *bridge == "1"))
         z_order += 10;
 
-    if (tunnel
-            && (!strcmp(tunnel, "true") || !strcmp(tunnel, "yes")
-                    || !strcmp(tunnel, "1")))
+    if (tunnel && (*tunnel == "true" || *tunnel == "yes" || *tunnel == "1"))
         z_order -= 10;
 
     snprintf(z, sizeof(z), "%d", z_order);
-    keyval::addItem(tags, "z_order", z, 0);
+    tags->addItem("z_order", z, 0);
 
     return 0;
 }
@@ -94,134 +88,95 @@ unsigned int c_filter_rel_member_tags(
         int * member_superseeded, int * make_boundary, int * make_polygon, int * roads,
         const export_list *exlist, bool allow_typeless) {
 
-    char *type;
-    struct keyval tags, *p, *q, *qq, poly_tags;
-    int i, j;
+    struct keyval tags, *q, poly_tags;
     int first_outerway, contains_tag;
 
     //if it has a relation figure out what kind it is
-    type = keyval::getItem(rel_tags, "type");
+    const std::string *type = rel_tags->getItem("type");
     bool is_route = false, is_boundary = false, is_multipolygon = false;
-    if(type)
+    if (type)
     {
         //what kind of relation is it
-        is_route = strcmp(type, "route") == 0;
-        is_boundary = strcmp(type, "boundary") == 0;
-        is_multipolygon = strcmp(type, "multipolygon") == 0;
+        is_route = *type == "route";
+        is_boundary = *type == "boundary";
+        is_multipolygon = *type == "multipolygon";
     }//you didnt have a type and it was required
-    else if(!allow_typeless)
+    else if (!allow_typeless)
     {
         return 1;
     }
 
     /* Clone tags from relation */
-    keyval::initList(&tags);
-    keyval::initList(&poly_tags);
-    p = rel_tags->next;
-    while (p != rel_tags) {
+    for (keyval *p = rel_tags->firstItem(); p; p = rel_tags->nextItem(p)) {
         //copy the name tag as "route_name"
-        if (is_route && (strcmp(p->key, "name") == 0))
-            keyval::addItem(&tags, "route_name", p->value, 1);
+        if (is_route && (p->key == "name"))
+            tags.addItem("route_name", p->value, true);
         //copy all other tags except for "type"
-        else if (strcmp(p->key, "type"))
-            keyval::addItem(&tags, p->key, p->value, 1);
-        p = p->next;
+        else if (p->key != "type")
+            tags.addItem(p->key, p->value, true);
     }
 
     if (is_route) {
-        const char *state = keyval::getItem(rel_tags, "state");
-        const char *netw = keyval::getItem(rel_tags, "network");
+        const std::string *netw = rel_tags->getItem("network");
         int networknr = -1;
 
-        if (state == NULL ) {
-            state = "";
-        }
-
-        if (netw != NULL ) {
-            if (strcmp(netw, "lcn") == 0) {
+        if (netw != NULL) {
+            const std::string *state = rel_tags->getItem("state");
+            std::string statetype("yes");
+            if (state) {
+                if (*state == "alternate")
+                    statetype = "alternate";
+                else if (*state == "connection")
+                    statetype = "connection";
+            }
+            if (*netw == "lcn") {
                 networknr = 10;
-                if (strcmp(state, "alternate") == 0) {
-                    keyval::addItem(&tags, "lcn", "alternate", 1);
-                } else if (strcmp(state, "connection") == 0) {
-                    keyval::addItem(&tags, "lcn", "connection", 1);
-                } else {
-                    keyval::addItem(&tags, "lcn", "yes", 1);
-                }
-            } else if (strcmp(netw, "rcn") == 0) {
+                tags.addItem("lcn", statetype, true);
+            } else if (*netw == "rcn") {
                 networknr = 11;
-                if (strcmp(state, "alternate") == 0) {
-                    keyval::addItem(&tags, "rcn", "alternate", 1);
-                } else if (strcmp(state, "connection") == 0) {
-                    keyval::addItem(&tags, "rcn", "connection", 1);
-                } else {
-                    keyval::addItem(&tags, "rcn", "yes", 1);
-                }
-            } else if (strcmp(netw, "ncn") == 0) {
+                tags.addItem("rcn", statetype, true);
+            } else if (*netw == "ncn") {
                 networknr = 12;
-                if (strcmp(state, "alternate") == 0) {
-                    keyval::addItem(&tags, "ncn", "alternate", 1);
-                } else if (strcmp(state, "connection") == 0) {
-                    keyval::addItem(&tags, "ncn", "connection", 1);
-                } else {
-                    keyval::addItem(&tags, "ncn", "yes", 1);
-                }
-
-            } else if (strcmp(netw, "lwn") == 0) {
+                tags.addItem("ncn", statetype, true);
+            } else if (*netw == "lwn") {
                 networknr = 20;
-                if (strcmp(state, "alternate") == 0) {
-                    keyval::addItem(&tags, "lwn", "alternate", 1);
-                } else if (strcmp(state, "connection") == 0) {
-                    keyval::addItem(&tags, "lwn", "connection", 1);
-                } else {
-                    keyval::addItem(&tags, "lwn", "yes", 1);
-                }
-            } else if (strcmp(netw, "rwn") == 0) {
+                tags.addItem("lwn", statetype, true);
+            } else if (*netw == "rwn") {
                 networknr = 21;
-                if (strcmp(state, "alternate") == 0) {
-                    keyval::addItem(&tags, "rwn", "alternate", 1);
-                } else if (strcmp(state, "connection") == 0) {
-                    keyval::addItem(&tags, "rwn", "connection", 1);
-                } else {
-                    keyval::addItem(&tags, "rwn", "yes", 1);
-                }
-            } else if (strcmp(netw, "nwn") == 0) {
+                tags.addItem("rwn", statetype, true);
+            } else if (*netw == "nwn") {
                 networknr = 22;
-                if (strcmp(state, "alternate") == 0) {
-                    keyval::addItem(&tags, "nwn", "alternate", 1);
-                } else if (strcmp(state, "connection") == 0) {
-                    keyval::addItem(&tags, "nwn", "connection", 1);
-                } else {
-                    keyval::addItem(&tags, "nwn", "yes", 1);
-                }
+                tags.addItem("nwn", statetype, true);
             }
         }
 
-        if (keyval::getItem(rel_tags, "preferred_color") != NULL ) {
-            const char *a = keyval::getItem(rel_tags, "preferred_color");
-            if (strcmp(a, "0") == 0 || strcmp(a, "1") == 0
-                    || strcmp(a, "2") == 0 || strcmp(a, "3") == 0
-                    || strcmp(a, "4") == 0) {
-                keyval::addItem(&tags, "route_pref_color", a, 1);
+        const std::string *prefcol = rel_tags->getItem("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);
             } else {
-                keyval::addItem(&tags, "route_pref_color", "0", 1);
+                tags.addItem("route_pref_color", "0", true);
             }
         } else {
-            keyval::addItem(&tags, "route_pref_color", "0", 1);
+            tags.addItem("route_pref_color", "0", true);
         }
 
-        if (keyval::getItem(rel_tags, "ref") != NULL ) {
+        const std::string *relref = rel_tags->getItem("ref");
+        if (relref != NULL ) {
             if (networknr == 10) {
-                keyval::addItem(&tags, "lcn_ref", keyval::getItem(rel_tags, "ref"), 1);
+                tags.addItem("lcn_ref", *relref, true);
             } else if (networknr == 11) {
-                keyval::addItem(&tags, "rcn_ref", keyval::getItem(rel_tags, "ref"), 1);
+                tags.addItem("rcn_ref", *relref, true);
             } else if (networknr == 12) {
-                keyval::addItem(&tags, "ncn_ref", keyval::getItem(rel_tags, "ref"), 1);
+                tags.addItem("ncn_ref", *relref, true);
             } else if (networknr == 20) {
-                keyval::addItem(&tags, "lwn_ref", keyval::getItem(rel_tags, "ref"), 1);
+                tags.addItem("lwn_ref", *relref, true);
             } else if (networknr == 21) {
-                keyval::addItem(&tags, "rwn_ref", keyval::getItem(rel_tags, "ref"), 1);
+                tags.addItem("rwn_ref", *relref, true);
             } else if (networknr == 22) {
-                keyval::addItem(&tags, "nwn_ref", keyval::getItem(rel_tags, "ref"), 1);
+                tags.addItem("nwn_ref", *relref, true);
             }
         }
     } else if (is_boundary) {
@@ -230,7 +185,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 && keyval::getItem(&tags, "boundary")) {
+    } else if (is_multipolygon && tags.getItem("boundary")) {
         /* Treat type=multipolygon exactly like type=boundary if it has a boundary tag. */
         *make_boundary = 1;
     } else if (is_multipolygon) {
@@ -238,53 +193,48 @@ 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 */
-        p = tags.next;
-        while (p != &tags) {
-            if (!strcmp(p->key, "area")) {
-                keyval::addItem(&poly_tags, p->key, p->value, 1);
+        for (keyval *p = tags.firstItem(); p; p = tags.nextItem(p)) {
+            if (p->key == "area") {
+                poly_tags.addItem(p->key, p->value, true);
             } else {
                 const std::vector<taginfo> &infos = exlist->get(OSMTYPE_WAY);
-                for (i = 0; i < infos.size(); i++) {
+                for (unsigned i = 0; i < infos.size(); i++) {
                     const taginfo &info = infos[i];
-                    if (strcmp(info.name.c_str(), p->key) == 0) {
+                    if (info.name == p->key) {
                         if (info.flags & FLAG_POLYGON) {
-                            keyval::addItem(&poly_tags, p->key, p->value, 1);
+                            poly_tags.addItem(p->key, p->value, true);
                         }
                         break;
                     }
                 }
             }
-            p = p->next;
         }
 
         /* 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 (!keyval::listHasData(&poly_tags)) {
+        if (!poly_tags.listHasData()) {
             first_outerway = 1;
-            for (i = 0; i < member_count; i++) {
+            for (int i = 0; i < member_count; i++) {
                 if (member_roles[i] && !strcmp(member_roles[i], "inner"))
                     continue;
 
                 /* insert all tags of the first outerway to the potential list of copied tags. */
                 if (first_outerway) {
-                    p = member_tags[i].next;
-                    while (p != &(member_tags[i])) {
-                        keyval::addItem(&poly_tags, p->key, p->value, 1);
-                        p = p->next;
-                    }
+                    for (keyval *p = member_tags[i].firstItem(); p; p = member_tags[i].nextItem(p))
+                        poly_tags.addItem(p->key, p->value, true);
                 } 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.next;
-                    while (q != &poly_tags) {
-                        p = keyval::getTag(&(member_tags[i]), q->key);
-                        if ((p != NULL) && (strcmp(q->value, p->value) == 0)) {
-                            q = q->next;
+                    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 {
                             /* This tag is not present on all member outer ways, so don't copy it over to relation */
-                            qq = q->next;
-                            keyval::removeTag(q);
+                            keyval *qq = poly_tags.nextItem(q);
+                            q->removeTag();
                             q = qq;
                         }
                     }
@@ -292,20 +242,17 @@ unsigned int c_filter_rel_member_tags(
                 first_outerway = 0;
             }
             /* Copy the list identified outer way tags over to the relation */
-            q = poly_tags.next;
-            while (q != &poly_tags) {
-                keyval::addItem(&tags, q->key, q->value, 1);
-                q = q->next;
-            }
+            for (q = poly_tags.firstItem(); q; q = poly_tags.nextItem(q))
+                tags.addItem(q->key, q->value, true);
 
             /* We need to re-check and only keep polygon tags in the list of polytags */
-            q = poly_tags.next;
-            while (q != &poly_tags) {
+            q = poly_tags.firstItem();
+            while (q) {
                 contains_tag = 0;
                 const std::vector<taginfo> &infos = exlist->get(OSMTYPE_WAY);
-                for (j = 0; j < infos.size(); j++) {
+                for (unsigned j = 0; j < infos.size(); j++) {
                     const taginfo &info = infos[j];
-                    if (strcmp(info.name.c_str(), q->key) == 0) {
+                    if (info.name == q->key) {
                         if (info.flags & FLAG_POLYGON) {
                             contains_tag = 1;
                             break;
@@ -313,25 +260,25 @@ unsigned int c_filter_rel_member_tags(
                     }
                 }
                 if (contains_tag == 0) {
-                    qq = q->next;
-                    keyval::removeTag(q);
+                    keyval *qq = poly_tags.nextItem(q);
+                    q->removeTag();
                     q = qq;
                 } else {
-                    q = q->next;
+                    q = poly_tags.nextItem(q);
                 }
             }
         }
-        keyval::resetList(&poly_tags);
+        poly_tags.resetList();
     } else if(!allow_typeless) {
         /* Unknown type, just exit */
-        keyval::resetList(&tags);
-        keyval::resetList(&poly_tags);
+        tags.resetList();
+        poly_tags.resetList();
         return 1;
     }
 
-    if (!keyval::listHasData(&tags)) {
-        keyval::resetList(&tags);
-        keyval::resetList(&poly_tags);
+    if (!tags.listHasData()) {
+        tags.resetList();
+        poly_tags.resetList();
         return 1;
     }
 
@@ -339,21 +286,19 @@ 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 (i = 0; i < member_count; i++) {
+        for (int i = 0; i < member_count; i++) {
             int match = 1;
-            struct keyval *p = member_tags[i].next;
-            while (p != &(member_tags[i])) {
-                const char *v = keyval::getItem(&tags, p->key);
-                if (!v || strcmp(v, p->value)) {
+            for (const keyval *p = member_tags[i].firstItem(); p; p = member_tags[i].nextItem(p)) {
+                const std::string *v = tags.getItem(p->key);
+                if (!v || *v != p->value) {
                     /* z_order and osm_ are automatically generated tags, so ignore them */
-                    if ((strcmp(p->key, "z_order") != 0) && (strcmp(p->key, "osm_user") != 0) &&
-                        (strcmp(p->key, "osm_version") != 0) && (strcmp(p->key, "osm_uid") != 0) &&
-                        (strcmp(p->key, "osm_changeset")) && (strcmp(p->key, "osm_timestamp") != 0)) {
+                    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;
                         break;
                     }
                 }
-                p = p->next;
             }
             if (match) {
                 member_superseeded[i] = 1;
@@ -363,9 +308,7 @@ unsigned int c_filter_rel_member_tags(
         }
     }
 
-    keyval::resetList(rel_tags);
-    keyval::cloneList(rel_tags, &tags);
-    keyval::resetList(&tags);
+    tags.moveList(rel_tags);
 
     add_z_order(rel_tags, roads);
 
@@ -387,11 +330,11 @@ unsigned int lua_filter_rel_member_tags(lua_State* L, const char* rel_mem_func,
 
     lua_newtable(L);    /* relations key value table */
 
-    while( (item = keyval::popItem(rel_tags)) != NULL ) {
-        lua_pushstring(L, item->key);
-        lua_pushstring(L, item->value);
+    while( (item = rel_tags->popItem()) != NULL ) {
+        lua_pushstring(L, item->key.c_str());
+        lua_pushstring(L, item->value.c_str());
         lua_rawset(L, -3);
-        keyval::freeItem(item);
+        delete(item);
         count++;
     }
 
@@ -400,11 +343,11 @@ unsigned int lua_filter_rel_member_tags(lua_State* L, const char* rel_mem_func,
     for (i = 1; i <= member_count; i++) {
         lua_pushnumber(L, i);
         lua_newtable(L);    /* member key value table */
-        while( (item = keyval::popItem(&(member_tags[i - 1]))) != NULL ) {
-            lua_pushstring(L, item->key);
-            lua_pushstring(L, item->value);
+        while( (item = member_tags[i - 1].popItem()) != NULL ) {
+            lua_pushstring(L, item->key.c_str());
+            lua_pushstring(L, item->value.c_str());
             lua_rawset(L, -3);
-            keyval::freeItem(item);
+            delete(item);
             count++;
         }
         lua_rawset(L, -3);
@@ -448,7 +391,7 @@ unsigned int lua_filter_rel_member_tags(lua_State* L, const char* rel_mem_func,
     while (lua_next(L,-2) != 0) {
         key = lua_tostring(L,-2);
         value = lua_tostring(L,-1);
-        keyval::addItem(rel_tags, key, value, 0);
+        rel_tags->addItem(key, value, false);
         lua_pop(L,1);
     }
     lua_pop(L,1);
@@ -579,11 +522,11 @@ unsigned int tagtransform::lua_filter_basic_tags(const OsmType type, keyval *tag
 
     lua_newtable(L);    /* key value table */
 
-    while( (item = keyval::popItem(tags)) != NULL ) {
-        lua_pushstring(L, item->key);
-        lua_pushstring(L, item->value);
+    while( (item = tags->popItem()) != NULL ) {
+        lua_pushstring(L, item->key.c_str());
+        lua_pushstring(L, item->value.c_str());
         lua_rawset(L, -3);
-        keyval::freeItem(item);
+        delete(item);
         count++;
     }
 
@@ -607,7 +550,7 @@ unsigned int tagtransform::lua_filter_basic_tags(const OsmType type, keyval *tag
     while (lua_next(L,-2) != 0) {
         key = lua_tostring(L,-2);
         value = lua_tostring(L,-1);
-        keyval::addItem(tags, key, value, 0);
+        tags->addItem(key, value, false);
         lua_pop(L,1);
     }
 
@@ -635,7 +578,6 @@ unsigned int tagtransform::c_filter_basic_tags(
 
     //a place to keep the tags we like as we go
     struct keyval temp;
-    keyval::initList(&temp);
 
     enum OsmType export_type;
     if (type == OSMTYPE_RELATION) {
@@ -647,25 +589,24 @@ unsigned int tagtransform::c_filter_basic_tags(
     /* 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 = keyval::popItem(tags)) != NULL ) {
+    while ((item = tags->popItem()) != NULL ) {
         //if we want to do more than the export list says
         if(!strict) {
-            if (type == OSMTYPE_RELATION && !strcmp("type", item->key)) {
-                keyval::pushItem(&temp, item);
+            if (type == OSMTYPE_RELATION && "type" == item->key) {
+                temp.pushItem(item);
                 item = NULL;
                 filter = 0;
                 continue;
             }
             /* Allow named islands to appear as polygons */
-            if (!strcmp("natural", item->key)
-                    && !strcmp("coastline", item->value)) {
+            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 && !strcmp("natural", item->key)
-                    && !strcmp("coastline", item->value)) {
-                keyval::freeItem(item);
+            if (!options->keep_coastlines && "natural" == item->key
+                    && "coastline" == item->value) {
+                delete(item);
                 item = NULL;
                 continue;
             }
@@ -676,9 +617,9 @@ unsigned int tagtransform::c_filter_basic_tags(
         size_t i = 0;
         for (; i < infos.size(); i++) {
             const taginfo &info = infos[i];
-            if (wildMatch(info.name.c_str(), item->key)) {
+            if (wildMatch(info.name.c_str(), item->key.c_str())) {
                 if (info.flags & FLAG_DELETE) {
-                    keyval::freeItem(item);
+                    delete(item);
                     item = NULL;
                     break;
                 }
@@ -686,7 +627,7 @@ unsigned int tagtransform::c_filter_basic_tags(
                 filter = 0;
                 flags |= info.flags;
 
-                keyval::pushItem(&temp, item);
+                temp.pushItem(item);
                 item = NULL;
                 break;
             }
@@ -696,63 +637,62 @@ unsigned int tagtransform::c_filter_basic_tags(
         if (i == infos.size() && !strict) {
             if (options->hstore_mode != HSTORE_NONE) {
                 /* with hstore, copy all tags... */
-                keyval::pushItem(&temp, item);
+                temp.pushItem(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 && strcmp("osm_uid", item->key)
-                        && strcmp("osm_user", item->key)
-                        && strcmp("osm_timestamp", item->key)
-                        && strcmp("osm_version", item->key)
-                        && strcmp("osm_changeset", item->key))
+                if (!options->hstore_match_only && "osm_uid" != item->key
+                        && "osm_user" != item->key
+                        && "osm_timestamp" != item->key
+                        && "osm_version" != item->key
+                        && "osm_changeset" != item->key)
                     filter = 0;
             } else if (options->hstore_columns.size() > 0) {
                 /* does this column match any of the hstore column prefixes? */
                 size_t j = 0;
                 for(; j < options->hstore_columns.size(); ++j) {
-                    char *pos = strstr(item->key, options->hstore_columns[j].c_str());
-                    if (pos == item->key) {
-                        keyval::pushItem(&temp, item);
+                    size_t pos = item->key.find(options->hstore_columns[j]);
+                    if (pos == 0) {
+                        temp.pushItem(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
-                                && strcmp("osm_uid", item->key)
-                                && strcmp("osm_user", item->key)
-                                && strcmp("osm_timestamp", item->key)
-                                && strcmp("osm_version", item->key)
-                                && strcmp("osm_changeset", item->key))
+                                && "osm_uid" != item->key
+                                && "osm_user" != item->key
+                                && "osm_timestamp" != item->key
+                                && "osm_version" != item->key
+                                && "osm_changeset" != item->key)
                             filter = 0;
                         break;
                     }
                 }
                 /* if not, skip the tag */
                 if (j == options->hstore_columns.size()) {
-                    keyval::freeItem(item);
+                    delete(item);
                 }
             } else {
-                keyval::freeItem(item);
+                delete(item);
             }
             item = NULL;
         }
     }
 
     /* Move from temp list back to original list */
-    while ((item = keyval::popItem(&temp)) != NULL )
-        keyval::pushItem(tags, item);
+    while ((item = temp.popItem()) != NULL )
+        tags->pushItem(item);
 
     *polygon = flags & FLAG_POLYGON;
 
     /* Special case allowing area= to override anything else */
-    const char *area;
-    if ((area = keyval::getItem(tags, "area"))) {
-        if (!strcmp(area, "yes") || !strcmp(area, "true") || !strcmp(area, "1"))
+    const std::string *area;
+    if ((area = tags->getItem("area"))) {
+        if (*area == "yes" || *area == "true" || *area == "1")
             *polygon = 1;
-        else if (!strcmp(area, "no") || !strcmp(area, "false")
-                || !strcmp(area, "0"))
+        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 (add_area_tag) {
-            keyval::addItem(tags, "area", "yes", 0);
+            tags->addItem("area", "yes", false);
             *polygon = 1;
         }
     }
diff --git a/tests/middle-tests.cpp b/tests/middle-tests.cpp
index dc0df94..17be1c2 100644
--- a/tests/middle-tests.cpp
+++ b/tests/middle-tests.cpp
@@ -43,7 +43,7 @@ int test_node_set(middle_t *mid)
     dynamic_cast<slim_middle_t *>(mid)->nodes_delete(id);
   }
 
-  keyval::resetList(&tags);
+  tags.resetList();
 
   return 0;
 }
@@ -78,8 +78,7 @@ int test_way_set(middle_t *mid)
   osmid_t way_id = 1;
   double lat = 12.3456789;
   double lon = 98.7654321;
-  struct keyval tags[2]; /* <-- this is needed because the ways_get_list method calls
-                          * keyval::initList() on the `count + 1`th tags element. */
+  struct keyval tags;
   struct osmNode *node_ptr = NULL;
   osmid_t way_ids_ptr;
   int node_count = 0;
@@ -89,19 +88,19 @@ int test_way_set(middle_t *mid)
 
   // set the nodes
   for (int i = 0; i < nd_count; ++i) {
-    status = mid->nodes_set(nds[i], lat, lon, &tags[0]);
+    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[0]);
+  status = mid->ways_set(way_id, nds, nd_count, &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[0], &node_ptr, &node_count);
+  int way_count = mid->ways_get_list(&way_id, 1, &way_ids_ptr, &tags, &node_ptr, &node_count);
   if (way_count != 1) { std::cerr << "ERROR: Unable to get way list.\n"; return 1; }
 
   // check that it's the same
@@ -156,7 +155,7 @@ int test_way_set(middle_t *mid)
       }
   }
 
-  keyval::resetList(&tags[0]);
+  tags.resetList();
   free(node_ptr);
 
   // clean up for next test
diff --git a/tests/test-output-multi-line.cpp b/tests/test-output-multi-line.cpp
index c55fd1c..b740a97 100644
--- a/tests/test-output-multi-line.cpp
+++ b/tests/test-output-multi-line.cpp
@@ -14,7 +14,6 @@
 #include "middle-pgsql.hpp"
 #include "taginfo_impl.hpp"
 #include "parse.hpp"
-#include "text-tree.hpp"
 
 #include <libpq-fe.h>
 #include <sys/types.h>
@@ -62,7 +61,6 @@ int main(int argc, char *argv[]) {
         options_t options;
         options.conninfo = db->conninfo().c_str();
         options.num_procs = 1;
-        options.prefix = "osm2pgsql_test";
         options.tblsslim_index = "tablespacetest";
         options.tblsslim_data = "tablespacetest";
         options.slim = 1;
@@ -92,27 +90,27 @@ int main(int argc, char *argv[]) {
         // 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 = 'osm2pgsql_test_foobar_highways'");
-        check_count(test_conn, 2753, "select count(*) from osm2pgsql_test_foobar_highways");
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'foobar_highways'");
+        check_count(test_conn, 2753, "select count(*) from foobar_highways");
 
         //check that we have the right spread
-        check_count(test_conn, 13, "select count(*) from osm2pgsql_test_foobar_highways where highway='bridleway'");
-        check_count(test_conn, 3, "select count(*) from osm2pgsql_test_foobar_highways where highway='construction'");
-        check_count(test_conn, 96, "select count(*) from osm2pgsql_test_foobar_highways where highway='cycleway'");
-        check_count(test_conn, 249, "select count(*) from osm2pgsql_test_foobar_highways where highway='footway'");
-        check_count(test_conn, 18, "select count(*) from osm2pgsql_test_foobar_highways where highway='living_street'");
-        check_count(test_conn, 171, "select count(*) from osm2pgsql_test_foobar_highways where highway='path'");
-        check_count(test_conn, 6, "select count(*) from osm2pgsql_test_foobar_highways where highway='pedestrian'");
-        check_count(test_conn, 81, "select count(*) from osm2pgsql_test_foobar_highways where highway='primary'");
-        check_count(test_conn, 842, "select count(*) from osm2pgsql_test_foobar_highways where highway='residential'");
-        check_count(test_conn, 3, "select count(*) from osm2pgsql_test_foobar_highways where highway='road'");
-        check_count(test_conn, 90, "select count(*) from osm2pgsql_test_foobar_highways where highway='secondary'");
-        check_count(test_conn, 1, "select count(*) from osm2pgsql_test_foobar_highways where highway='secondary_link'");
-        check_count(test_conn, 352, "select count(*) from osm2pgsql_test_foobar_highways where highway='service'");
-        check_count(test_conn, 34, "select count(*) from osm2pgsql_test_foobar_highways where highway='steps'");
-        check_count(test_conn, 33, "select count(*) from osm2pgsql_test_foobar_highways where highway='tertiary'");
-        check_count(test_conn, 597, "select count(*) from osm2pgsql_test_foobar_highways where highway='track'");
-        check_count(test_conn, 164, "select count(*) from osm2pgsql_test_foobar_highways where highway='unclassified'");
+        check_count(test_conn, 13, "select count(*) from foobar_highways where highway='bridleway'");
+        check_count(test_conn, 3, "select count(*) from foobar_highways where highway='construction'");
+        check_count(test_conn, 96, "select count(*) from foobar_highways where highway='cycleway'");
+        check_count(test_conn, 249, "select count(*) from foobar_highways where highway='footway'");
+        check_count(test_conn, 18, "select count(*) from foobar_highways where highway='living_street'");
+        check_count(test_conn, 171, "select count(*) from foobar_highways where highway='path'");
+        check_count(test_conn, 6, "select count(*) from foobar_highways where highway='pedestrian'");
+        check_count(test_conn, 81, "select count(*) from foobar_highways where highway='primary'");
+        check_count(test_conn, 842, "select count(*) from foobar_highways where highway='residential'");
+        check_count(test_conn, 3, "select count(*) from foobar_highways where highway='road'");
+        check_count(test_conn, 90, "select count(*) from foobar_highways where highway='secondary'");
+        check_count(test_conn, 1, "select count(*) from foobar_highways where highway='secondary_link'");
+        check_count(test_conn, 352, "select count(*) from foobar_highways where highway='service'");
+        check_count(test_conn, 34, "select count(*) from foobar_highways where highway='steps'");
+        check_count(test_conn, 33, "select count(*) from foobar_highways where highway='tertiary'");
+        check_count(test_conn, 597, "select count(*) from foobar_highways where highway='track'");
+        check_count(test_conn, 164, "select count(*) from foobar_highways where highway='unclassified'");
         return 0;
 
     } catch (const std::exception &e) {
diff --git a/tests/test-output-multi-point-multi-table.cpp b/tests/test-output-multi-point-multi-table.cpp
index ee43b9e..b904075 100644
--- a/tests/test-output-multi-point-multi-table.cpp
+++ b/tests/test-output-multi-point-multi-table.cpp
@@ -14,7 +14,6 @@
 #include "middle-pgsql.hpp"
 #include "taginfo_impl.hpp"
 #include "parse.hpp"
-#include "text-tree.hpp"
 
 #include <libpq-fe.h>
 #include <sys/types.h>
@@ -111,25 +110,25 @@ int main(int argc, char *argv[]) {
 
             check_count(test_conn, 1,
                         (boost::format("select count(*) from pg_catalog.pg_class "
-                                       "where relname = 'osm2pgsql_test_foobar_%d'")
+                                       "where relname = 'foobar_%d'")
                          % i).str());
 
             check_count(test_conn, 244,
-                        (boost::format("select count(*) from osm2pgsql_test_foobar_%d")
+                        (boost::format("select count(*) from foobar_%d")
                          % i).str());
 
             check_count(test_conn, 36,
-                        (boost::format("select count(*) from osm2pgsql_test_foobar_%d "
+                        (boost::format("select count(*) from foobar_%d "
                                        "where amenity='parking'")
                          % i).str());
 
             check_count(test_conn, 34,
-                        (boost::format("select count(*) from osm2pgsql_test_foobar_%d "
+                        (boost::format("select count(*) from foobar_%d "
                                        "where amenity='bench'")
                          % i).str());
 
             check_count(test_conn, 1,
-                        (boost::format("select count(*) from osm2pgsql_test_foobar_%d "
+                        (boost::format("select count(*) from foobar_%d "
                                        "where amenity='vending_machine'")
                          % i).str());
         }
diff --git a/tests/test-output-multi-point.cpp b/tests/test-output-multi-point.cpp
index 7996922..809717d 100644
--- a/tests/test-output-multi-point.cpp
+++ b/tests/test-output-multi-point.cpp
@@ -14,7 +14,6 @@
 #include "middle-pgsql.hpp"
 #include "taginfo_impl.hpp"
 #include "parse.hpp"
-#include "text-tree.hpp"
 
 #include <libpq-fe.h>
 #include <sys/types.h>
@@ -94,19 +93,19 @@ int main(int argc, char *argv[]) {
 
         check_count(test_conn, 1,
                     "select count(*) from pg_catalog.pg_class "
-                    "where relname = 'osm2pgsql_test_foobar_amenities'");
+                    "where relname = 'foobar_amenities'");
 
         check_count(test_conn, 244,
-                    "select count(*) from osm2pgsql_test_foobar_amenities");
+                    "select count(*) from foobar_amenities");
 
         check_count(test_conn, 36,
-                    "select count(*) from osm2pgsql_test_foobar_amenities where amenity='parking'");
+                    "select count(*) from foobar_amenities where amenity='parking'");
 
         check_count(test_conn, 34,
-                    "select count(*) from osm2pgsql_test_foobar_amenities where amenity='bench'");
+                    "select count(*) from foobar_amenities where amenity='bench'");
 
         check_count(test_conn, 1,
-                    "select count(*) from osm2pgsql_test_foobar_amenities where amenity='vending_machine'");
+                    "select count(*) from foobar_amenities where amenity='vending_machine'");
 
         return 0;
 
diff --git a/tests/test-output-multi-polygon.cpp b/tests/test-output-multi-polygon.cpp
index 63eeac1..067e759 100644
--- a/tests/test-output-multi-polygon.cpp
+++ b/tests/test-output-multi-polygon.cpp
@@ -14,7 +14,6 @@
 #include "middle-pgsql.hpp"
 #include "taginfo_impl.hpp"
 #include "parse.hpp"
-#include "text-tree.hpp"
 
 #include <libpq-fe.h>
 #include <sys/types.h>
@@ -91,28 +90,28 @@ int main(int argc, char *argv[]) {
         // 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 = 'osm2pgsql_test_foobar_buildings'");
-        check_count(test_conn, 0, "select count(*) from osm2pgsql_test_foobar_buildings where building is null");
-        check_count(test_conn, 3723, "select count(*) from osm2pgsql_test_foobar_buildings");
+        check_count(test_conn, 1, "select count(*) from pg_catalog.pg_class where relname = 'foobar_buildings'");
+        check_count(test_conn, 0, "select count(*) from foobar_buildings where building is null");
+        check_count(test_conn, 3723, "select count(*) from foobar_buildings");
 
         //check that we have the right spread
-        check_count(test_conn, 1, "select count(*) from osm2pgsql_test_foobar_buildings where building='barn'");
-        check_count(test_conn, 1, "select count(*) from osm2pgsql_test_foobar_buildings where building='chapel'");
-        check_count(test_conn, 5, "select count(*) from osm2pgsql_test_foobar_buildings where building='church'");
-        check_count(test_conn, 3, "select count(*) from osm2pgsql_test_foobar_buildings where building='commercial'");
-        check_count(test_conn, 6, "select count(*) from osm2pgsql_test_foobar_buildings where building='farm'");
-        check_count(test_conn, 1, "select count(*) from osm2pgsql_test_foobar_buildings where building='garage'");
-        check_count(test_conn, 2, "select count(*) from osm2pgsql_test_foobar_buildings where building='glasshouse'");
-        check_count(test_conn, 1, "select count(*) from osm2pgsql_test_foobar_buildings where building='greenhouse'");
-        check_count(test_conn, 153, "select count(*) from osm2pgsql_test_foobar_buildings where building='house'");
-        check_count(test_conn, 4, "select count(*) from osm2pgsql_test_foobar_buildings where building='hut'");
-        check_count(test_conn, 8, "select count(*) from osm2pgsql_test_foobar_buildings where building='industrial'");
-        check_count(test_conn, 200, "select count(*) from osm2pgsql_test_foobar_buildings where building='residential'");
-        check_count(test_conn, 6, "select count(*) from osm2pgsql_test_foobar_buildings where building='roof'");
-        check_count(test_conn, 4, "select count(*) from osm2pgsql_test_foobar_buildings where building='school'");
-        check_count(test_conn, 2, "select count(*) from osm2pgsql_test_foobar_buildings where building='station'");
-        check_count(test_conn, 3, "select count(*) from osm2pgsql_test_foobar_buildings where building='warehouse'");
-        check_count(test_conn, 3323, "select count(*) from osm2pgsql_test_foobar_buildings where building='yes'");
+        check_count(test_conn, 1, "select count(*) from foobar_buildings where building='barn'");
+        check_count(test_conn, 1, "select count(*) from foobar_buildings where building='chapel'");
+        check_count(test_conn, 5, "select count(*) from foobar_buildings where building='church'");
+        check_count(test_conn, 3, "select count(*) from foobar_buildings where building='commercial'");
+        check_count(test_conn, 6, "select count(*) from foobar_buildings where building='farm'");
+        check_count(test_conn, 1, "select count(*) from foobar_buildings where building='garage'");
+        check_count(test_conn, 2, "select count(*) from foobar_buildings where building='glasshouse'");
+        check_count(test_conn, 1, "select count(*) from foobar_buildings where building='greenhouse'");
+        check_count(test_conn, 153, "select count(*) from foobar_buildings where building='house'");
+        check_count(test_conn, 4, "select count(*) from foobar_buildings where building='hut'");
+        check_count(test_conn, 8, "select count(*) from foobar_buildings where building='industrial'");
+        check_count(test_conn, 200, "select count(*) from foobar_buildings where building='residential'");
+        check_count(test_conn, 6, "select count(*) from foobar_buildings where building='roof'");
+        check_count(test_conn, 4, "select count(*) from foobar_buildings where building='school'");
+        check_count(test_conn, 2, "select count(*) from foobar_buildings where building='station'");
+        check_count(test_conn, 3, "select count(*) from foobar_buildings where building='warehouse'");
+        check_count(test_conn, 3323, "select count(*) from foobar_buildings where building='yes'");
         return 0;
 
     } catch (const std::exception &e) {
diff --git a/tests/test-output-pgsql.cpp b/tests/test-output-pgsql.cpp
index cf773cf..1ef7eba 100644
--- a/tests/test-output-pgsql.cpp
+++ b/tests/test-output-pgsql.cpp
@@ -15,7 +15,6 @@
 #include "middle-ram.hpp"
 #include "taginfo_impl.hpp"
 #include "parse.hpp"
-#include "text-tree.hpp"
 
 #include <libpq-fe.h>
 #include <sys/types.h>
diff --git a/tests/test-parse-xml2.cpp b/tests/test-parse-xml2.cpp
index 44ce7d9..52599f2 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 "text-tree.hpp"
 #include "keyvals.hpp"
 
 void exit_nicely()
diff --git a/tests/test-pgsql-escape.cpp b/tests/test-pgsql-escape.cpp
index 6c2fbdd..0fcd24e 100644
--- a/tests/test-pgsql-escape.cpp
+++ b/tests/test-pgsql-escape.cpp
@@ -1,7 +1,21 @@
 #include "pgsql.hpp"
 
+void test_escape(const char *in, const char *out) {
+    std::string sql;
+    escape(in, sql);
+    if (sql.compare(out) != 0) {
+        std::cerr << "Expected " << out << ", but got " << sql << " for " << in <<".\n";
+        exit(1);
+    }
+}
+
 int main(int argc, char *argv[]) {
     std::string sql;
-    escape("farmland", sql);
-    return sql.compare("farmland") != 0;
+    test_escape("farmland", "farmland");
+    test_escape("", "");
+    test_escape("\\", "\\\\");
+    test_escape("foo\nbar", "foo\\\nbar");
+    test_escape("\t\r\n", "\\\t\\\r\\\n");
+
+    return 0;
 }
diff --git a/text-tree.cpp b/text-tree.cpp
deleted file mode 100644
index 36616b4..0000000
--- a/text-tree.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/* text-tree.c
- *
- * Storage of reference counted text strings
- * used by keyvals.c to store the key/value strings
- */
-
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "text-tree.hpp"
-
-namespace
-{
-    int text_compare(const void *pa, const void *pb, void *rb_param)
-    {
-        struct text_node *a = (struct text_node *)pa;
-        struct text_node *b = (struct text_node *)pb;
-
-        rb_param = NULL;
-        return strcmp(a->str, b->str);
-    }
-
-    void text_free(void *pa, void *rb_param)
-    {
-        struct text_node *a = (struct text_node *)pa;
-        rb_param = NULL;
-        free(a->str);
-        free(a);
-    }
-}
-
-text_tree::text_tree()
-{
-    table = rb_create (text_compare, NULL, NULL);
-    assert(table);
-}
-
-const char *text_tree::text_get(const char *text)
-{
-    struct text_node *node, *dupe;
-
-    node = (struct text_node *)malloc(sizeof(*node));
-    assert(node);
-
-    node->str = strdup(text);
-    assert(node->str);
-    node->ref = 0;
-    dupe = (struct text_node *)rb_insert(table, (void *)node);
-    if (dupe) {
-        free(node->str);
-        free(node);
-        dupe->ref++;
-        return dupe->str;
-    } else {
-        node->ref++;
-        return node->str;
-    }
-}
-
-void text_tree::text_release(const char *text)
-{
-    struct text_node *node, find;
-
-    find.str = (char *)text;
-    find.ref = 0;
-    node = (struct text_node *)rb_find(table, (void *)&find);
-    if (!node) {
-        fprintf(stderr, "failed to find '%s'\n", text);
-        return;
-    }
-    node->ref--;
-    if (!node->ref) {
-        rb_delete (table, &find);
-        free(node->str);
-        free(node);
-    }
-}
-
-text_tree::~text_tree()
-{
-    rb_destroy(table, text_free);
-}
-#if 0
-int main(int argc, char **argv)
-{
-    struct tree_context *ctx = text_init();
-
-    printf("%1$p %1$s\n", text_get(ctx, "Hello"));
-    printf("%1$p %1$s\n", text_get(ctx, "Hello"));
-    printf("%1$p %1$s\n", text_get(ctx, "World"));
-
-    text_release(ctx,"Hello");
-    text_release(ctx,"Hello");
-    text_release(ctx,"World");
-    text_release(ctx,"Hello");
-
-    text_exit(ctx);
-    return 0;
-}
-#endif
diff --git a/text-tree.hpp b/text-tree.hpp
deleted file mode 100644
index 51473d5..0000000
--- a/text-tree.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef TEXT_TREE_H
-#define TEXT_TREE_H
-
-#include "rb.hpp"
-
-struct text_tree {
-    struct rb_table *table;
-
-    text_tree();
-    ~text_tree();
-    const char *text_get(const char *text);
-    void text_release(const char *text);
-};
-
-struct text_node {
-    char *str;
-    int ref;
-};
-
-
-#endif

-- 
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