]> git.saurik.com Git - apple/syslog.git/blobdiff - libsystem_asl.tproj/src/asl_msg.c
syslog-385.tar.gz
[apple/syslog.git] / libsystem_asl.tproj / src / asl_msg.c
index f0b0c0d2c4b2f57a8c8ad33dc2152c1eefe4d86d..f9b100af992f573fa4b0907e2302187225c65100 100644 (file)
@@ -9,7 +9,7 @@
  * compliance with the License. Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this
  * file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -17,7 +17,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
 
 #include <stdarg.h>
 #include <regex.h>
 #include <syslog.h>
+#include <notify.h>
 #include <errno.h>
 #include <time.h>
 #include <sys/time.h>
 #include <asl.h>
+#include <asl_object.h>
 #include <asl_private.h>
 #include <asl_core.h>
+#include <asl_client.h>
 #include <sys/types.h>
 #include <libkern/OSAtomic.h>
-#include <assert.h>
-#include "asl_msg.h"
+#include <asl_msg.h>
+#include <asl_msg_list.h>
 
 #define TOKEN_NULL  0
 #define TOKEN_OPEN  1
 
 #define SEC_PER_HOUR 3600
 
-#define forever for(;;)
-
-#define streq(A, B) (strcmp(A, B) == 0)
-#define streq_len(A, B, C) (strncmp(A, B, C) == 0)
-#define strneq(A, B) (strcmp(A, B) != 0)
-#define strcaseeq(A, B) (strcasecmp(A, B) == 0)
-#define strcaseneq(A, B) (strcasecmp(A, B) != 0)
+#define PAGE_OBJECT 1
 
-#ifndef ASL_KEY_OPTION
-#define ASL_KEY_OPTION "ASLOption"
-#endif
+#define forever for(;;)
 
 #ifndef ASL_QUERY_OP_FALSE
 #define ASL_QUERY_OP_FALSE 0
 #define AUX_0_OPTION    0x00000200
 #define AUX_0_LEVEL     0x00000400
 
-extern time_t asl_parse_time(const char *in);
-
 /* from asl_util.c */
 int asl_is_utf8(const char *str);
 uint8_t *asl_b64_encode(const uint8_t *buf, size_t len);
 
+void _asl_msg_dump(FILE *f, const char *comment, asl_msg_t *msg);
+
+#pragma mark -
+#pragma mark standard message keys
+
 static const char *ASLStandardKey[] =
 {
        ASL_KEY_TIME,
@@ -109,7 +107,8 @@ static const char *ASLStandardKey[] =
        ASL_KEY_REF_PROC,
        ASL_KEY_MSG_ID,
        ASL_KEY_EXPIRE_TIME,
-       ASL_KEY_OPTION
+       ASL_KEY_OPTION,
+       ASL_KEY_FREE_NOTE
 };
 
 static const char *MTStandardKey[] =
@@ -201,6 +200,10 @@ _asl_msg_std_key(const char *s, uint32_t len)
                {
                        if streq(s, ASL_KEY_EXPIRE_TIME) return ASL_STD_KEY_EXPIRE;
                }
+               case 14:
+               {
+                       if streq(s, ASL_KEY_FREE_NOTE) return ASL_STD_KEY_FREE_NOTE;
+               }
                default:
                {
                        return 0;
@@ -210,51 +213,185 @@ _asl_msg_std_key(const char *s, uint32_t len)
        return 0;
 }
 
-static asl_msg_t *
-_asl_msg_make_page()
+#pragma mark -
+#pragma mark asl_msg
+
+static uint32_t
+_slot_count(asl_msg_t *m)
 {
-       asl_msg_t *out;
-       int i;
+       if (m == NULL) return 0;
+       if (m->asl_type == ASL_TYPE_MSG) return ASL_MSG_KVO_MSG_SLOTS;
+       if (m->asl_type == ASL_TYPE_QUERY) return ASL_MSG_KVO_QUERY_SLOTS;
+       return 0;
+}
+
+
+static uint16_t
+_get_slot_key(asl_msg_t *m, uint32_t slot)
+{
+       if (m == NULL) return 0;
+
+       if (m->asl_type == ASL_TYPE_MSG)
+       {
+               if (slot < ASL_MSG_KVO_MSG_SLOTS) return m->kvo[slot];
+               return 0;
+       }
+
+       if (m->asl_type == ASL_TYPE_QUERY)
+       {
+               if (slot < ASL_MSG_KVO_QUERY_SLOTS) return m->kvo[slot];
+               return 0;
+       }
+
+       return 0;
+}
+
+
+static void
+_set_slot_key(asl_msg_t *m, uint32_t slot, uint16_t x)
+{
+       if (m == NULL) return;
+
+       if (m->asl_type == ASL_TYPE_MSG)
+       {
+               if (slot < ASL_MSG_KVO_MSG_SLOTS) m->kvo[slot] = x;
+               return;
+       }
+
+       if (m->asl_type == ASL_TYPE_QUERY)
+       {
+               if (slot < ASL_MSG_KVO_QUERY_SLOTS) m->kvo[slot] = x;
+               return;
+       }
+}
 
-       out = calloc(1, sizeof(asl_msg_t));
-       if (out == NULL) return NULL;
 
-       for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
+static uint16_t
+_get_slot_val(asl_msg_t *m, uint32_t slot)
+{
+       if (m == NULL) return 0;
+       if (m->asl_type == ASL_TYPE_MSG)
+       {
+               if (slot < ASL_MSG_KVO_MSG_SLOTS) return m->kvo[slot + ASL_MSG_KVO_MSG_SLOTS];
+               return 0;
+       }
+
+       if (m->asl_type == ASL_TYPE_QUERY)
        {
-               out->key[i] = ASL_MSG_SLOT_FREE;
-               out->val[i] = ASL_MSG_SLOT_FREE;
+               if (slot < ASL_MSG_KVO_QUERY_SLOTS) return m->kvo[slot + ASL_MSG_KVO_QUERY_SLOTS];
+               return 0;
        }
 
+       return 0;
+}
+
+static void
+_set_slot_val(asl_msg_t *m, uint32_t slot, uint16_t x)
+{
+       if (m == NULL) return;
+
+       if (m->asl_type == ASL_TYPE_MSG)
+       {
+               if (slot < ASL_MSG_KVO_MSG_SLOTS) m->kvo[slot + ASL_MSG_KVO_MSG_SLOTS] = x;
+               return;
+       }
+
+       if (m->asl_type == ASL_TYPE_QUERY)
+       {
+               if (slot < ASL_MSG_KVO_QUERY_SLOTS) m->kvo[slot + ASL_MSG_KVO_QUERY_SLOTS] = x;
+               return;
+       }
+}
+
+static uint16_t
+_get_slot_op(asl_msg_t *m, uint32_t slot)
+{
+       if (m == NULL) return 0;
+
+       if (m->asl_type == ASL_TYPE_QUERY)
+       {
+               if (slot < ASL_MSG_KVO_QUERY_SLOTS) return m->kvo[slot + (ASL_MSG_KVO_QUERY_SLOTS * 2)];
+               return 0;
+       }
+
+       return 0;
+}
+
+static void
+_set_slot_op(asl_msg_t *m, uint32_t slot, uint16_t x)
+{
+       if (m == NULL) return;
+
+       if (m->asl_type == ASL_TYPE_QUERY)
+       {
+               if (slot < ASL_MSG_KVO_QUERY_SLOTS) m->kvo[slot + (ASL_MSG_KVO_QUERY_SLOTS * 2)] = x;
+       }
+}
+
+static asl_msg_t *
+_asl_msg_make_page(uint32_t type)
+{
+       uint32_t i, n = 0;
+       asl_msg_t *out = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
+       if (out == NULL) return NULL;
+
+       if (type == ASL_TYPE_MSG) n = ASL_MSG_KVO_MSG_SLOTS * 2;
+       else if (type == ASL_TYPE_QUERY) n = ASL_MSG_KVO_QUERY_SLOTS * 2;
+
+       for (i = 0; i < n; i++) out->kvo[i] = ASL_MSG_SLOT_FREE;
+
+       out->mem_size = sizeof(asl_msg_t);
+       out->asl_type = type;
+
        return out;
 }
 
+asl_msg_t *
+asl_msg_retain(asl_msg_t *msg)
+{
+       if (msg == NULL) return NULL;
+       asl_retain((asl_object_t)msg);
+       return msg;
+}
+
+void
+asl_msg_release(asl_msg_t *msg)
+{
+       if (msg == NULL) return;
+       asl_release((asl_object_t)msg);
+}
+
 static const char *
 _asl_msg_slot_key(asl_msg_t *page, uint32_t slot)
 {
        const char *out;
-       uint16_t x;
+       uint16_t x, k;
 
        if (page == NULL) return NULL;
-       if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
 
-       if (page->key[slot] == ASL_MSG_SLOT_FREE) return NULL;
+       if ((page->asl_type == ASL_TYPE_MSG) && (slot >= ASL_MSG_KVO_MSG_SLOTS)) return NULL;
+       else if ((page->asl_type == ASL_TYPE_QUERY) && (slot >= ASL_MSG_KVO_QUERY_SLOTS)) return NULL;
 
-       switch (page->key[slot] & ASL_MSG_KV_MASK)
+       k = _get_slot_key(page, slot);
+
+       if (k == ASL_MSG_SLOT_FREE) return NULL;
+
+       switch (k & ASL_MSG_KV_MASK)
        {
                case ASL_MSG_KV_INLINE:
                {
-                       return page->data + page->key[slot];
+                       return page->data + k;
                }
                case ASL_MSG_KV_DICT:
                {
-                       if ((page->key[slot] > ASL_STD_KEY_BASE) && (page->key[slot] <= ASL_STD_KEY_LAST))
+                       if ((k > ASL_STD_KEY_BASE) && (k <= ASL_STD_KEY_LAST))
                        {
-                               x = page->key[slot] - ASL_STD_KEY_BASE - 1;
+                               x = k - ASL_STD_KEY_BASE - 1;
                                return ASLStandardKey[x];
                        }
-                       else if ((page->key[slot] > ASL_MT_KEY_BASE) && (page->key[slot] <= ASL_MT_KEY_LAST))
+                       else if ((k > ASL_MT_KEY_BASE) && (k <= ASL_MT_KEY_LAST))
                        {
-                               x = page->key[slot] - ASL_MT_KEY_BASE - 1;
+                               x = k - ASL_MT_KEY_BASE - 1;
                                return MTStandardKey[x];
                        }
 
@@ -262,7 +399,7 @@ _asl_msg_slot_key(asl_msg_t *page, uint32_t slot)
                }
                case ASL_MSG_KV_EXTERN:
                {
-                       x = page->key[slot] & ASL_MSG_OFFSET_MASK;
+                       x = k & ASL_MSG_OFFSET_MASK;
                        memcpy(&out, page->data + x, sizeof(char *));
                        return out;
                }
@@ -275,22 +412,26 @@ static const char *
 _asl_msg_slot_val(asl_msg_t *page, uint32_t slot)
 {
        const char *out;
-       uint16_t x, type;
+       uint16_t x, v, type;
 
        if (page == NULL) return NULL;
-       if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
 
-       if (page->val[slot] == ASL_MSG_SLOT_FREE) return NULL;
+       if ((page->asl_type == ASL_TYPE_MSG) && (slot >= ASL_MSG_KVO_MSG_SLOTS)) return NULL;
+       else if ((page->asl_type == ASL_TYPE_QUERY) && (slot >= ASL_MSG_KVO_QUERY_SLOTS)) return NULL;
+
+       v = _get_slot_val(page, slot);
 
-       type = page->val[slot] & ASL_MSG_KV_MASK;
+       if (v == ASL_MSG_SLOT_FREE) return NULL;
+
+       type = v & ASL_MSG_KV_MASK;
 
        if (type == ASL_MSG_KV_INLINE)
        {
-               return page->data + page->val[slot];
+               return page->data + v;
        }
        else if (type == ASL_MSG_KV_EXTERN)
        {
-               x = page->val[slot] & ASL_MSG_OFFSET_MASK;
+               x = v & ASL_MSG_OFFSET_MASK;
                memcpy(&out, page->data + x, sizeof(char *));
                return out;
        }
@@ -306,47 +447,45 @@ asl_msg_new(uint32_t type)
 {
        asl_msg_t *out;
 
-       out = _asl_msg_make_page();
+       out = _asl_msg_make_page(type);
        if (out == NULL) return NULL;
 
-       out->type = type;
+       out->asl_type = type;
        out->refcount = 1;
 
        return out;
 }
 
-asl_msg_t *
-asl_msg_retain(asl_msg_t *msg)
-{
-       int32_t new;
-
-       if (msg == NULL) return NULL;
-
-       new = OSAtomicIncrement32Barrier(&msg->refcount);
-       assert(new >= 1);
-
-       return msg;
-}
-
 static void
-_asl_msg_free(asl_msg_t *page)
+_asl_msg_free_page(asl_msg_t *page)
 {
-       uint32_t i;
+       uint32_t i, mslots;
        char *p;
 
        if (page == NULL) return;
 
-       for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
+       mslots = _slot_count(page);
+
+       for (i = 0; i < mslots; i++)
        {
-               if ((page->key[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+               uint16_t k = _get_slot_key(page, i);
+               uint16_t v = _get_slot_val(page, i);
+
+               if (k == ASL_STD_KEY_FREE_NOTE)
                {
-                       memcpy(&p, page->data + (page->key[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
+                       const char *x = _asl_msg_slot_val(page, i);
+                       if (x != NULL) notify_post(x);
+               }
+
+               if ((k & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+               {
+                       memcpy(&p, page->data + (k & ASL_MSG_OFFSET_MASK), sizeof(char *));
                        free(p);
                }
 
-               if ((page->val[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+               if ((v & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
                {
-                       memcpy(&p, page->data + (page->val[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
+                       memcpy(&p, page->data + (v & ASL_MSG_OFFSET_MASK), sizeof(char *));
                        free(p);
                }
        }
@@ -354,31 +493,142 @@ _asl_msg_free(asl_msg_t *page)
        free(page);
 }
 
-void
-asl_msg_release(asl_msg_t *msg)
+uint32_t
+asl_msg_type(asl_msg_t *msg)
 {
-       int32_t new;
-       asl_msg_t *next;
+       if (msg == NULL) return 0;
+       return msg->asl_type;
+}
 
-       if (msg == NULL) return;
+uint32_t
+asl_msg_count(asl_msg_t *msg)
+{
+       uint32_t total;
+
+       total = 0;
 
-       new = OSAtomicDecrement32Barrier(&msg->refcount);
-       assert(new >= 0);
+       for (; msg != NULL; msg = msg->next) total += msg->count;
+       return total;
+}
+
+static void
+_asl_msg_dump_kv(FILE *f, asl_msg_t *msg, uint16_t x)
+{
+       if (x == ASL_MSG_SLOT_FREE)
+       {
+               fprintf(f, "-free-");
+               return;
+       }
+
+       if ((x & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
+       {
+               switch (x)
+               {
+                       case ASL_STD_KEY_TIME: fprintf(f, "(dict: Time)"); return;
+                       case ASL_STD_KEY_NANO: fprintf(f, "(dict: Nano)"); return;
+                       case ASL_STD_KEY_HOST: fprintf(f, "(dict: Host)"); return;
+                       case ASL_STD_KEY_SENDER: fprintf(f, "(dict: Sender)"); return;
+                       case ASL_STD_KEY_FACILITY: fprintf(f, "(dict: Facility)"); return;
+                       case ASL_STD_KEY_PID: fprintf(f, "(dict: PID)"); return;
+                       case ASL_STD_KEY_UID: fprintf(f, "(dict: UID)"); return;
+                       case ASL_STD_KEY_GID: fprintf(f, "(dict: GID)"); return;
+                       case ASL_STD_KEY_LEVEL: fprintf(f, "(dict: Level)"); return;
+                       case ASL_STD_KEY_MESSAGE: fprintf(f, "(dict: Message)"); return;
+                       case ASL_STD_KEY_READ_UID: fprintf(f, "(dict: ReadUID)"); return;
+                       case ASL_STD_KEY_READ_GID: fprintf(f, "(dict: ReadGID)"); return;
+                       case ASL_STD_KEY_SESSION: fprintf(f, "(dict: Session)"); return;
+                       case ASL_STD_KEY_REF_PID: fprintf(f, "(dict: PID)"); return;
+                       case ASL_STD_KEY_REF_PROC: fprintf(f, "(dict: RefProc)"); return;
+                       case ASL_STD_KEY_MSG_ID: fprintf(f, "(dict: ASLMessageID)"); return;
+                       case ASL_STD_KEY_EXPIRE: fprintf(f, "(dict: Expire)"); return;
+                       case ASL_STD_KEY_OPTION: fprintf(f, "(dict: ASLOption)"); return;
+                       case ASL_MT_KEY_DOMAIN: fprintf(f, "(dict: com.apple.message.domain)"); return;
+                       case ASL_MT_KEY_SCOPE: fprintf(f, "(dict: com.apple.message.domain_scope)"); return;
+                       case ASL_MT_KEY_RESULT: fprintf(f, "(dict: com.apple.message.result)"); return;
+                       case ASL_MT_KEY_SIG: fprintf(f, "(dict: com.apple.message.signature)"); return;
+                       case ASL_MT_KEY_SIG2: fprintf(f, "(dict: com.apple.message.signature2)"); return;
+                       case ASL_MT_KEY_SIG3: fprintf(f, "(dict: com.apple.message.signature3)"); return;
+                       case ASL_MT_KEY_SUCCESS: fprintf(f, "(dict: com.apple.message.success)"); return;
+                       case ASL_MT_KEY_UUID: fprintf(f, "(dict: com.apple.message.uuid)"); return;
+                       case ASL_MT_KEY_VAL: fprintf(f, "(dict: com.apple.message.value)"); return;
+                       case ASL_MT_KEY_VAL2: fprintf(f, "(dict: com.apple.message.value2)"); return;
+                       case ASL_MT_KEY_VAL3: fprintf(f, "(dict: com.apple.message.value3)"); return;
+                       case ASL_MT_KEY_VAL4: fprintf(f, "(dict: com.apple.message.value4)"); return;
+                       case ASL_MT_KEY_VAL5: fprintf(f, "(dict: com.apple.message.value5)"); return;
+               }
+
+               fprintf(f, "(dict: -unknown-)");
+               return;
+       }
+
+       if ((x & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+       {
+               const char *c;
+               size_t z = x & ASL_MSG_OFFSET_MASK;
+               memcpy(&c, msg->data + z, sizeof(char *));
+               fprintf(f, "(extern: %s)", c);
+               return;
+       }
+
+       fprintf(f, "%s", msg->data + x);
+}
+
+void
+_asl_msg_dump(FILE *f, const char *comment, asl_msg_t *msg)
+{
+       uint32_t i, mslots, page1 = 1;
 
-       if (new > 0) return;
+       if (f == NULL) return;
+       if (msg == NULL)
+       {
+               fprintf(f, "asl_msg %s: NULL\n", comment);
+               return;
+       }
+
+       mslots = _slot_count(msg);
 
        while (msg != NULL)
        {
-               next = msg->next;
-               _asl_msg_free(msg);
-               msg = next;
+               if (page1 == 1)
+               {
+                       fprintf(f, "asl_msg %s: %p\n", comment, msg);
+                       fprintf(f, "    refcount: %u\n", msg->refcount);
+                       fprintf(f, "    asl_type: %u\n", msg->asl_type);
+                       page1 = 0;
+               }
+               else
+               {
+                       fprintf(f, "  page: %p\n", msg);
+               }
+
+               fprintf(f, "    count: %u\n", msg->count);
+               fprintf(f, "    data_size: %u\n", msg->data_size);
+               fprintf(f, "    mem_size: %llu\n", msg->mem_size);
+               fprintf(f, "    next: %p\n", msg->next);
+
+               for (i = 0; i < mslots; i++)
+               {
+                       fprintf(f, "    slot[%d]: ", i);
+                       _asl_msg_dump_kv(f, msg, _get_slot_key(msg, i));
+                       fprintf(f, " ");
+                       _asl_msg_dump_kv(f, msg, _get_slot_val(msg, i));
+                       if (msg->asl_type == ASL_TYPE_QUERY) fprintf(f, " 0x%04x\n", _get_slot_op(msg, i));
+               }
+
+               msg = msg->next;
        }
 }
 
+#pragma mark -
+#pragma mark fetching contents
+
+/*
+ * Find the slot and page for an input key.
+ */
 static uint32_t
 _asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opage)
 {
-       uint32_t i, len, slot;
+       uint32_t i, len, slot, mslots;
        uint16_t kx;
        asl_msg_t *page;
        const char *kp;
@@ -388,6 +638,8 @@ _asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opa
 
        i = 0;
        slot = 0;
+       mslots = _slot_count(msg);
+
        if (oslot != NULL) *oslot = slot;
 
        page = msg;
@@ -398,24 +650,24 @@ _asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opa
 
        forever
        {
-               if (page->key[slot] != ASL_MSG_SLOT_FREE)
+               if (_get_slot_key(page, slot) != ASL_MSG_SLOT_FREE)
                {
                        if (kx != 0)
                        {
-                               if (page->key[slot] == kx) return i;
+                               if (_get_slot_key(page, slot) == kx) return i;
                        }
-                       else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
+                       else if ((_get_slot_key(page, slot) & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
                        {
-                               /* page->key[slot] is a dictionary key, but key is not (kx == 0) so skip this slot */
+                               /* _get_slot_key(page, slot) is a dictionary key, but key is not (kx == 0) so skip this slot */
                        }
-                       else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+                       else if ((_get_slot_key(page, slot) & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
                        {
-                               memcpy(&kp, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
+                               memcpy(&kp, page->data + (_get_slot_key(page, slot) & ASL_MSG_OFFSET_MASK), sizeof(char *));
                                if (streq(key, kp)) return i;
                        }
                        else
                        {
-                               kp = page->data + page->key[slot];
+                               kp = page->data + _get_slot_key(page, slot);
                                if (streq(key, kp)) return i;
                        }
                }
@@ -424,7 +676,7 @@ _asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opa
                slot++;
                if (oslot != NULL) *oslot = slot;
 
-               if (slot >= ASL_MSG_PAGE_SLOTS)
+               if (slot >= mslots)
                {
                        if (page->next == NULL) return IndexNull;
 
@@ -440,78 +692,194 @@ _asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opa
 }
 
 /*
- * asl_msg_key: iterate over entries
- * initial value of n should be 0
- * after that, the value of n should be previously returned value
- * sets the pointers for the next key, value, and op in the msgionary
- * returns IndexNull when there are no more entries
+ * Find page and slot for an "index".
  */
-static uint32_t
-_asl_msg_fetch_internal(asl_msg_t *msg, uint32_t n, const char **keyout, const char **valout, uint32_t *opout, asl_msg_t **outpage, uint32_t *outslot)
+static int
+_asl_msg_resolve_index(asl_msg_t *msg, uint32_t n, asl_msg_t **page, uint32_t *slot)
 {
-       uint32_t slot;
-       asl_msg_t *page;
+       uint32_t i, sx, mslots;
+       asl_msg_t *px;
+
+       if (msg == NULL) return -1;
+
+       *slot = IndexNull;
+       *page = NULL;
+
+       mslots = _slot_count(msg);
+
+       sx = 0;
+
+       /* find page */
+       for (px = msg; px != NULL; px = px->next)
+       {
+               if (n > (sx + px->count))
+               {
+                       sx += px->count;
+                       continue;
+               }
+
+               *page = px;
+
+               /* find slot */
+               for (i = 0; i < mslots; i++)
+               {
+                       if (px->kvo[i] != ASL_MSG_SLOT_FREE)
+                       {
+                               if (sx == n)
+                               {
+                                       *slot = i;
+                                       return 0;
+                               }
+
+                               sx++;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+/*
+ * asl_msg_fetch: iterate over entries
+ * initial value of n should be 0.  Subseqent calls should use the last
+ * returned value.  Returns IndexNull when there are no more entries
+ * Sets the pointers for the next key, value, and op in the msg.
+ * The iterator encodes a page number and a slot number.
+ */
+
+uint32_t
+asl_msg_fetch(asl_msg_t *msg, uint32_t x, const char **keyout, const char **valout, uint16_t *opout)
+{
+       uint32_t p, xpn, xsn, mslots;
+       asl_msg_t *page = NULL;
 
        if (msg == NULL) return IndexNull;
-       if (outpage != NULL) *outpage = NULL;
-       if (outslot != NULL) *outslot = IndexNull;
 
-       slot = n;
-       page = msg;
+       mslots = _slot_count(msg);
+
+       xsn = x >> 24;
+       xpn = x & 0x00ffffff;
+
+       /* slot number 0xff means we have run out entries */
+       if (xsn == 0x000000ff) return IndexNull;
 
-       while (slot >= ASL_MSG_PAGE_SLOTS)
+       page = msg;
+       for (p = 0; p < xpn; p++)
        {
-               if (page->next == NULL) return IndexNull;
                page = page->next;
-               slot -= ASL_MSG_PAGE_SLOTS;
+               if (page == NULL) return IndexNull;
        }
 
-       while (page->key[slot] == ASL_MSG_SLOT_FREE)
+       if (keyout != NULL) *keyout = _asl_msg_slot_key(page, xsn);
+       if (valout != NULL) *valout = _asl_msg_slot_val(page, xsn);
+       if (opout != NULL) *opout = _get_slot_op(page, xsn);
+
+       /* advance to the next slot */
+       forever
        {
-               slot++;
-               n++;
+               xsn++;
 
-               if (slot >= ASL_MSG_PAGE_SLOTS)
+               if (xsn >= mslots)
                {
-                       if (page->next == NULL) return IndexNull;
+                       if (page->next == NULL) return 0xff000000;
+                       xsn = 0;
                        page = page->next;
-                       slot = 0;
+                       xpn++;
                }
+
+               if (page->kvo[xsn] != ASL_MSG_SLOT_FREE) return ((xsn << 24) | xpn);
        }
 
-       n++;
+       return IndexNull;
+}
+
+int
+asl_msg_lookup(asl_msg_t *msg, const char *key, const char **valout, uint16_t *opout)
+{
+       uint32_t i, slot;
+       asl_msg_t *page;
+
+       if (msg == NULL) return -1;
+       if (valout != NULL) *valout = NULL;
+       if (opout != NULL) *opout = 0;
+
+       slot = IndexNull;
+       page = NULL;
+
+       i = _asl_msg_index(msg, key, &slot, &page);
+       if (i == IndexNull) return -1;
 
-       if (keyout != NULL) *keyout = _asl_msg_slot_key(page, slot);
        if (valout != NULL) *valout = _asl_msg_slot_val(page, slot);
-       if (opout != NULL) *opout = page->op[slot];
+       if (opout != NULL) *opout = _get_slot_op(page, slot);
+
+       return 0;
+}
 
-       if (outpage != NULL) *outpage = page;
-       if (outslot != NULL) *outslot = slot;
+const char *
+asl_msg_get_val_for_key(asl_msg_t *msg, const char *key)
+{
+       uint32_t slot;
+       asl_msg_t *page;
+
+       if (msg == NULL) return NULL;
 
-       return n;
+       slot = IndexNull;
+       page = NULL;
+
+       if (_asl_msg_index(msg, key, &slot, &page) == IndexNull) return NULL;
+
+       return _asl_msg_slot_val(page, slot);
 }
 
-uint32_t
-asl_msg_fetch(asl_msg_t *msg, uint32_t n, const char **keyout, const char **valout, uint32_t *opout)
+const char *
+asl_msg_key(asl_msg_t *msg, uint32_t n)
 {
-       return _asl_msg_fetch_internal(msg, n, keyout, valout, opout, NULL, NULL);
+       uint32_t slot, i, mslots;
+       asl_msg_t *page;
+
+       if (msg == NULL) return NULL;
+
+       mslots = _slot_count(msg);
+
+       i = 0;
+       for (page = msg; page != NULL; page = page->next)
+       {
+               for (slot = 0; slot < mslots; slot++)
+               {
+                       if (_get_slot_key(page, slot) != ASL_MSG_SLOT_FREE)
+                       {
+                               if (i == n) return _asl_msg_slot_key(page, slot);
+                               i++;
+                       }
+               }
+       }
+
+       return NULL;
 }
 
+#pragma mark -
+#pragma mark adding and replacing contents
+
 static int
 _asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
 {
-       uint32_t slot, keylen, vallen, total;
-       uint16_t kx;
+       uint32_t slot, keylen, vallen, total, mslots;
+       uint64_t klen, vlen;
+       uint16_t kx, k, v, o;
        asl_msg_t *page, *last;
        char *extkey, *extval;
 
        if (msg == NULL) return -1;
        if (key == NULL) return -1;
 
+       mslots = _slot_count(msg);
+
+       o = op;
        extkey = NULL;
        extval = NULL;
 
        keylen = strlen(key);
+       klen = keylen;
        kx = _asl_msg_std_key(key, keylen);
 
        if (kx == 0) keylen++;
@@ -523,6 +891,7 @@ _asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32
        if (val != NULL)
        {
                vallen = strlen(val) + 1;
+               vlen = vallen;
                total += vallen;
        }
 
@@ -573,15 +942,15 @@ _asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32
                if (total <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
                {
                        /* check for a free slot */
-                       for (slot = 0; (slot < ASL_MSG_PAGE_SLOTS) && (page->key[slot] != ASL_MSG_SLOT_FREE); slot++);
-                       if (slot < ASL_MSG_PAGE_SLOTS) break;
+                       for (slot = 0; (slot < mslots) && (_get_slot_key(page, slot) != ASL_MSG_SLOT_FREE); slot++);
+                       if (slot < mslots) break;
                }
        }
 
        if (page == NULL)
        {
                /* allocate a new page and attach it */
-               page = _asl_msg_make_page();
+               page = _asl_msg_make_page(msg->asl_type);
                if (page == NULL)
                {
                        if (extkey != NULL) free(extkey);
@@ -596,42 +965,47 @@ _asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32
        /* copy key or external key pointer into page data */
        if (kx != 0)
        {
-               page->key[slot] = kx;
+               k = kx;
        }
        else if (extkey == NULL)
        {
-               page->key[slot] = page->data_size;
+               k = page->data_size;
                memcpy(page->data + page->data_size, key, keylen);
        }
        else
        {
-               page->key[slot] = page->data_size | ASL_MSG_KV_EXTERN;
+               k = page->data_size | ASL_MSG_KV_EXTERN;
                memcpy(page->data + page->data_size, &extkey, keylen);
+               page->mem_size += klen;
        }
 
+       _set_slot_key(page, slot, k);
        page->data_size += keylen;
 
        /* copy val or external val pointer into page data */
-       page->val[slot] = ASL_MSG_SLOT_FREE;
+
+       v = ASL_MSG_SLOT_FREE;
 
        if (val != NULL)
        {
                if (extval == NULL)
                {
-                       page->val[slot] = page->data_size;
+                       v = page->data_size;
                        memcpy(page->data + page->data_size, val, vallen);
                }
                else
                {
-                       page->val[slot] = page->data_size | ASL_MSG_KV_EXTERN;
+                       v = page->data_size | ASL_MSG_KV_EXTERN;
                        memcpy(page->data + page->data_size, &extval, vallen);
+                       page->mem_size += vlen;
                }
 
+               _set_slot_val(page, slot, v);
                page->data_size += vallen;
        }
 
        /* set op */
-       page->op[slot] = op;
+       _set_slot_op(page, slot, o);
 
        /* update page count */
        page->count++;
@@ -650,18 +1024,23 @@ _asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32
 static int
 _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
 {
-       uint32_t i, slot, newexternal;
+       uint32_t i, slot, mslots, newexternal;
        asl_msg_t *page;
        uint32_t intvallen, extvallen, newvallen;
        char *intval, *extval, *newval;
+       uint16_t k, v, o;
 
        if (msg == NULL) return -1;
        if (key == NULL) return -1;
 
+       mslots = _slot_count(msg);
+
        slot = IndexNull;
        page = NULL;
 
-       if ((msg->type == ASL_TYPE_QUERY) || (IndexNull == _asl_msg_index(msg, key, &slot, &page)))
+       o = op;
+
+       if ((msg->asl_type == ASL_TYPE_QUERY) || (IndexNull == _asl_msg_index(msg, key, &slot, &page)))
        {
                /* add key */
                return _asl_msg_new_key_val_op(msg, key, val, op);
@@ -673,17 +1052,19 @@ _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
        extval = NULL;
        extvallen = 0;
 
-       if (page->val[slot] != ASL_MSG_SLOT_FREE)
+       v = _get_slot_val(page, slot);
+
+       if (v != ASL_MSG_SLOT_FREE)
        {
-               if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+               if ((v & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
                {
-                       i = page->val[slot] & ASL_MSG_OFFSET_MASK;
+                       i = v & ASL_MSG_OFFSET_MASK;
                        memcpy(&extval, page->data + i, sizeof(char *));
                        extvallen = sizeof(char *);
                }
                else
                {
-                       intval = page->data + page->val[slot];
+                       intval = page->data + v;
                        intvallen = strlen(intval) + 1;
                }
        }
@@ -693,43 +1074,49 @@ _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
        /* easy case  - remove val */
        if (val == NULL)
        {
-               if (extval != NULL) free(extval);
-               page->val[slot] = ASL_MSG_SLOT_FREE;
-               if (op != IndexNull) page->op[slot] = op;
+               if (extval != NULL)
+               {
+                       page->mem_size -= (strlen(extval) + 1);
+                       free(extval);
+               }
+
+               _set_slot_val(page, slot, ASL_MSG_SLOT_FREE);
+               if (op != IndexNull) _set_slot_op(page, slot, o);
                return 0;
        }
 
        /* trivial case - internal val doesn't change */
        if ((intval != NULL) && (streq(val, intval)))
        {
-               if (op != IndexNull) page->op[slot] = op;
+               if (op != IndexNull) _set_slot_op(page, slot, o);
                return 0;
        }
 
        /* trivial case - external val doesn't change */
        if ((extval != NULL) && (streq(val, extval)))
        {
-               if (op != IndexNull) page->op[slot] = op;
+               if (op != IndexNull) _set_slot_op(page, slot, o);
                return 0;
        }
 
        /*
         * special case: we generally don't compress out holes in the data
         * space, but if this is the last string in the currently used data space
-        * we can just back up the data_size and reset page->val[slot]
+        * we can just back up the data_size and reset page->val[slot] (a.k.a. page->kvo[slot + mslots])
         */
-       i = page->val[slot] & ASL_MSG_OFFSET_MASK;
+       i = v & ASL_MSG_OFFSET_MASK;
        if ((intval != NULL) && ((i + intvallen) == page->data_size))
        {
-               page->val[slot] = ASL_MSG_SLOT_FREE;
+               _set_slot_val(page, slot, ASL_MSG_SLOT_FREE);
                page->data_size -= intvallen;
                intval = NULL;
                intvallen = 0;
        }
        else if ((extval != NULL) && ((i + extvallen) == page->data_size))
        {
-               page->val[slot] = ASL_MSG_SLOT_FREE;
+               _set_slot_val(page, slot, ASL_MSG_SLOT_FREE);
                page->data_size -= extvallen;
+               page->mem_size -= (strlen(extval) + 1);
                free(extval);
                extval = NULL;
                extvallen = 0;
@@ -748,11 +1135,16 @@ _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
        /* check if there is room to change val in place */
        if (((extval != NULL) && (newvallen <= extvallen)) || ((extval == NULL) && (newvallen <= intvallen)))
        {
-               if (extval != NULL) free(extval);
+               if (extval != NULL)
+               {
+                       page->mem_size -= (strlen(extval) + 1);
+                       free(extval);
+               }
+
                extval = NULL;
 
                /* we can re-use the space of the old value */
-               i = page->val[slot] & ASL_MSG_OFFSET_MASK;
+               i = v & ASL_MSG_OFFSET_MASK;
 
                if (newexternal == 1)
                {
@@ -760,22 +1152,28 @@ _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
                        newval = strdup(val);
                        if (newval == NULL) return -1;
 
-                       page->val[slot] = i | ASL_MSG_KV_EXTERN;
+                       page->mem_size += (strlen(newval) + 1);
+                       _set_slot_val(page, slot, i | ASL_MSG_KV_EXTERN);
                        memcpy(page->data + i, &newval, sizeof(char *));
                }
                else
                {
                        /* new internal value */
-                       page->val[slot] = i;
+                       _set_slot_val(page, slot, i);
                        memcpy(page->data + i, val, newvallen);
                }
 
-               if (op != IndexNull) page->op[slot] = op;
+               if (op != IndexNull) _set_slot_op(page, slot, o);
                return 0;
        }
 
        /* we're done with the old value if it is external - free it now */
-       if (extval != NULL) free(extval);
+       if (extval != NULL)
+       {
+               page->mem_size -= (strlen(extval) + 1);
+               free(extval);
+       }
+
        extval = NULL;
 
        if (newvallen <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
@@ -790,31 +1188,33 @@ _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
                        newval = strdup(val);
                        if (newval == NULL) return -1;
 
-                       page->val[slot] = i | ASL_MSG_KV_EXTERN;
+                       page->mem_size += (strlen(newval) + 1);
+                       _set_slot_val(page, slot, i | ASL_MSG_KV_EXTERN);
                        memcpy(page->data + i, &newval, sizeof(char *));
                }
                else
                {
                        /* new internal value */
-                       page->val[slot] = i;
+                       _set_slot_val(page, slot, i);
                        memcpy(page->data + i, val, newvallen);
                }
 
-               if (op != IndexNull) page->op[slot] = op;
+               if (op != IndexNull) _set_slot_op(page, slot, o);
                return 0;
 
        }
 
        /* no room on this page - free up existing entry and treat this as a new entry */
-       if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+       if ((k & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
        {
-               memcpy(&extval, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
+               memcpy(&extval, page->data + (k & ASL_MSG_OFFSET_MASK), sizeof(char *));
+               page->mem_size -= (strlen(extval) + 1);
                free(extval);
        }
 
-       page->key[slot] = ASL_MSG_SLOT_FREE;
-       page->val[slot] = ASL_MSG_SLOT_FREE;
-       page->op[slot] = 0;
+       _set_slot_key(page, slot, ASL_MSG_SLOT_FREE);
+       _set_slot_val(page, slot, ASL_MSG_SLOT_FREE);
+       _set_slot_op(page, slot, 0);
 
        return _asl_msg_new_key_val_op(msg, key, val, op);
 }
@@ -826,6 +1226,9 @@ asl_msg_set_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_
        uint32_t i, len;
        int status;
 
+       if (msg == NULL) return -1;
+       if (key == NULL) return -1;
+
        /* Special case handling */
        special = NULL;
 
@@ -887,13 +1290,48 @@ asl_msg_set_key_val(asl_msg_t *msg, const char *key, const char *val)
        return asl_msg_set_key_val_op(msg, key, val, 0);
 }
 
+static void
+_asl_msg_unset_page_slot(asl_msg_t *page, uint32_t slot)
+{
+       char *ext;
+       uint16_t k, v;
+
+       if (page == NULL) return;
+
+       if (slot >= _slot_count(page)) return;
+
+       k = _get_slot_key(page, slot);
+       v = _get_slot_val(page, slot);
+
+       if (k == ASL_MSG_SLOT_FREE) return;
+
+       if ((k & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+       {
+               memcpy(&ext, page->data + (k & ASL_MSG_OFFSET_MASK), sizeof(char *));
+               page->mem_size -= (strlen(ext) + 1);
+               free(ext);
+       }
+
+       if ((v & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+       {
+               memcpy(&ext, page->data + (v & ASL_MSG_OFFSET_MASK), sizeof(char *));
+               page->mem_size -= (strlen(ext) + 1);
+               free(ext);
+       }
+
+       _set_slot_key(page, slot, ASL_MSG_SLOT_FREE);
+       _set_slot_val(page, slot, ASL_MSG_SLOT_FREE);
+       _set_slot_op(page, slot, 0);
+
+       page->count--;
+}
+
 /*
- * Merge a key / val into a message (only ASL_TYPE_MSG).
- * Adds the key / val if the key is not found.
- * Does not replace the value if the key is found.
+ * asl_msg_unset
+ * Frees external key and val strings, but does not try to reclaim data space.
  */
-static void
-_asl_msg_merge_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
+void
+asl_msg_unset(asl_msg_t *msg, const char *key)
 {
        uint32_t i, slot;
        asl_msg_t *page;
@@ -905,135 +1343,129 @@ _asl_msg_merge_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint
        page = NULL;
 
        i = _asl_msg_index(msg, key, &slot, &page);
-       if (i != IndexNull) return;
+       if (i == IndexNull) return;
 
-       asl_msg_set_key_val_op(msg, key, val, op);
+       _asl_msg_unset_page_slot(page, slot);
+}
+
+void
+asl_msg_unset_index(asl_msg_t *msg, uint32_t n)
+{
+       uint32_t slot = IndexNull;
+       asl_msg_t *page = NULL;
+
+       if (msg == NULL) return;
+
+       if  (0 != _asl_msg_resolve_index(msg, n, &page, &slot)) return;
+       _asl_msg_unset_page_slot(page, slot);
 }
 
+#pragma mark -
+#pragma mark copy and merge
+
 /*
- * Merge msg into target (does not replace existing keys).
- * Creates a new asl_msg_t if target is NULL.
- * Returns target.
+ * Merge a key / val into a message (only ASL_TYPE_MSG).
+ * Adds the key / val if the key is not found.
+ * Does not replace the value if the key is found.
  */
-asl_msg_t *
-asl_msg_merge(asl_msg_t *target, asl_msg_t *msg)
+static void
+_asl_msg_merge_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint16_t op)
 {
-       uint32_t x, slot, op, isnew = 0;
-       const char *key, *val;
+       uint32_t i, slot;
        asl_msg_t *page;
 
-       if (msg == NULL) return target;
+       if (msg == NULL) return;
+       if (key == NULL) return;
 
-       if (target == NULL)
-       {
-               isnew = 1;
-               target = asl_msg_new(msg->type);
-       }
+       slot = IndexNull;
+       page = NULL;
 
-       for (x = _asl_msg_fetch_internal(msg, 0, &key, &val, &op, &page, &slot); x != IndexNull; x = _asl_msg_fetch_internal(msg, x, &key, &val, &op, &page, &slot))
-       {
-               if (msg->type == ASL_TYPE_MSG) op = 0;
-               if (isnew == 1) asl_msg_set_key_val_op(target, key, val, op);
-               else _asl_msg_merge_key_val_op(target, key, val, op);
-       }
+       i = _asl_msg_index(msg, key, &slot, &page);
+       if (i != IndexNull) return;
 
-       return target;
+       asl_msg_set_key_val_op(msg, key, val, op);
 }
 
 /*
- * Copy msg.
+ * Merge msg into target (does not replace existing keys).
+ * Creates a new asl_msg_t if target is NULL.
+ * Returns target.
  */
 asl_msg_t *
-asl_msg_copy(asl_msg_t *msg)
-{
-       return asl_msg_merge(NULL, msg);
-}
-
-/*
- * asl_msg_unset
- * Frees external key and val strings, but does not try to reclaim data space.
- */
-void
-asl_msg_unset(asl_msg_t *msg, const char *key)
+asl_msg_merge(asl_msg_t *target, asl_msg_t *msg)
 {
-       uint32_t i, slot;
-       asl_msg_t *page;
-       char *ext;
-
-       if (msg == NULL) return;
-       if (key == NULL) return;
+       uint32_t x, type, isnew = 0;
+       uint16_t op;
+       const char *key, *val;
 
-       slot = IndexNull;
-       page = NULL;
+       if (msg == NULL) return target;
 
-       i = _asl_msg_index(msg, key, &slot, &page);
-       if (i == IndexNull) return;
+       type = asl_get_type((asl_object_t)msg);
 
-       if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+       if (target == NULL)
        {
-               memcpy(&ext, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
-               free(ext);
+               isnew = 1;
+               target = asl_msg_new(type);
        }
 
-       if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
+       for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x =asl_msg_fetch(msg, x, &key, &val, &op))
        {
-               memcpy(&ext, page->data + (page->val[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
-               free(ext);
+               if (type == ASL_TYPE_MSG) op = 0;
+               if (isnew == 1) asl_msg_set_key_val_op(target, key, val, op);
+               else _asl_msg_merge_key_val_op(target, key, val, op);
        }
 
-       page->key[slot] = ASL_MSG_SLOT_FREE;
-       page->val[slot] = ASL_MSG_SLOT_FREE;
-       page->op[slot] = 0;
-
-       page->count--;
+       return target;
 }
 
-int
-asl_msg_lookup(asl_msg_t *msg, const char *key, const char **valout, uint32_t *opout)
+/*
+ * replace key/value pairs from msg in target
+ * Creates a new asl_msg_t if target is NULL.
+ * Returns target.
+ */
+static asl_msg_t *
+asl_msg_replace(asl_msg_t *target, asl_msg_t *msg)
 {
-       uint32_t i, slot;
-       asl_msg_t *page;
+       uint32_t x, type;
+       uint16_t op;
+       const char *key, *val;
 
-       if (valout != NULL) *valout = NULL;
-       if (opout != NULL) *opout = 0;
+       if (msg == NULL) return target;
 
-       slot = IndexNull;
-       page = NULL;
+       type = asl_get_type((asl_object_t)msg);
 
-       i = _asl_msg_index(msg, key, &slot, &page);
-       if (i == IndexNull) return -1;
+       if (target == NULL) target = asl_msg_new(type);
 
-       if (valout != NULL) *valout = _asl_msg_slot_val(page, slot);
-       if (opout != NULL) *opout = page->op[slot];
+       for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x =asl_msg_fetch(msg, x, &key, &val, &op))
+       {
+               if (type == ASL_TYPE_MSG) op = 0;
+               asl_msg_set_key_val_op(target, key, val, op);
+       }
 
-       return 0;
+       return target;
 }
 
-uint32_t
-asl_msg_type(asl_msg_t *msg)
-{
-       if (msg == NULL) return 0;
-       return msg->type;
-}
 
-uint32_t
-asl_msg_count(asl_msg_t *msg)
+/*
+ * Copy msg.
+ */
+asl_msg_t *
+asl_msg_copy(asl_msg_t *msg)
 {
-       uint32_t total;
-
-       total = 0;
-
-       for (; msg != NULL; msg = msg->next) total += msg->count;
-       return total;
+       return asl_msg_merge(NULL, msg);
 }
 
+#pragma mark -
+#pragma mark compare and test
+
 /*
  * Compare messages
  */
 static int
 _asl_msg_equal(asl_msg_t *a, asl_msg_t *b)
 {
-       uint32_t x, oa, ob;
+       uint32_t x;
+       uint16_t oa, ob;
        const char *key, *va, *vb;
 
        if (asl_msg_count(a) != asl_msg_count(b)) return 0;
@@ -1046,7 +1478,7 @@ _asl_msg_equal(asl_msg_t *a, asl_msg_t *b)
        {
                if (asl_msg_lookup(b, key, &vb, &ob) != 0) return 0;
                if (strcmp(va, vb)) return 0;
-               if ((a->type == ASL_TYPE_QUERY) && (oa != ob)) return 0;
+               if ((a->asl_type == ASL_TYPE_QUERY) && (oa != ob)) return 0;
        }
 
        return 1;
@@ -1282,10 +1714,10 @@ _asl_msg_test_time_expression(uint32_t op, const char *q, const char *m)
        if ((op & ASL_QUERY_OP_PREFIX) || (op & ASL_QUERY_OP_SUFFIX) || (op & ASL_QUERY_OP_REGEX)) return _asl_msg_test_expression(op, q, m);
        if ((q == NULL) || (m == NULL)) return _asl_msg_test_expression(op, q, m);
 
-       tq = asl_parse_time(q);
+       tq = asl_core_parse_time(q, NULL);
        if (tq < 0) return _asl_msg_test_expression(op, q, m);
 
-       tm = asl_parse_time(m);
+       tm = asl_core_parse_time(m, NULL);
        if (tm < 0) return _asl_msg_test_expression(op, q, m);
 
        t = op & ASL_QUERY_OP_TRUE;
@@ -1337,10 +1769,11 @@ _asl_msg_test_time_expression(uint32_t op, const char *q, const char *m)
 }
 
 /* test a query against a message */
-static int
+__private_extern__ int
 _asl_msg_test(asl_msg_t *q, asl_msg_t *m)
 {
-       uint32_t i, t, x, op;
+       uint32_t i, t, x;
+       uint16_t op;
        int cmp;
        const char *kq, *vq, *vm;
 
@@ -1398,6 +1831,7 @@ _asl_msg_test(asl_msg_t *q, asl_msg_t *m)
        return 1;
 }
 
+/* returns 1 if a and b match, 0 otherwise */
 int
 asl_msg_cmp(asl_msg_t *a, asl_msg_t *b)
 {
@@ -1405,11 +1839,35 @@ asl_msg_cmp(asl_msg_t *a, asl_msg_t *b)
        if (a == NULL) return 0;
        if (b == NULL) return 0;
 
-       if (a->type == b->type) return _asl_msg_equal(a, b);
-       if (a->type == ASL_TYPE_QUERY) return _asl_msg_test(a, b);
+       if (a->asl_type == b->asl_type) return _asl_msg_equal(a, b);
+       if (a->asl_type == ASL_TYPE_QUERY) return _asl_msg_test(a, b);
        return _asl_msg_test(b, a);
 }
 
+/*
+ * Test a message against a query list.
+ * Returns 1 if msg matches any query in the list, 0 otherwise.
+ * Returns 1 if the query list is NULL or empty.
+ */
+int
+asl_msg_cmp_list(asl_msg_t *msg, asl_msg_list_t *list)
+{
+       uint32_t i;
+
+       if (msg == NULL) return 0;
+       if (list == NULL) return 1;
+       if (list->count == 0) return 1;
+
+       for (i = 0; i < list->count; i++)
+       {
+               if (_asl_msg_test(list->msg[i], msg)) return 1;
+       }
+
+       return 0;
+}
+
+#pragma mark -
+#pragma mark string representation
 
 static char *
 _asl_time_string(const char *infmt, const char *str, const char *nano)
@@ -1468,7 +1926,7 @@ _asl_time_string(const char *infmt, const char *str, const char *nano)
        }
 
        tick = 0;
-       if (str != NULL) tick = asl_parse_time(str);
+       if (str != NULL) tick = asl_core_parse_time(str, NULL);
 
        if ((!strcasecmp(fmt, "lcl")) || (!strcasecmp(fmt, "local")))
        {
@@ -1492,8 +1950,8 @@ _asl_time_string(const char *infmt, const char *str, const char *nano)
                off %= 3600;
                zm = off / 60;
 
-               if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
-               else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
+               if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02lld", neg ? '-' : '+', (long long) zh);
+               else snprintf(zstr, sizeof(zstr), "%c%02lld:%02lld", neg ? '-' : '+', (long long) zh, (long long) zm);
 
                asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
                return out;
@@ -1509,8 +1967,8 @@ _asl_time_string(const char *infmt, const char *str, const char *nano)
                off %= 3600;
                zm = off / 60;
 
-               if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
-               else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
+               if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02lld", neg ? '-' : '+', (long long) zh);
+               else snprintf(zstr, sizeof(zstr), "%c%02lld:%02lld", neg ? '-' : '+', (long long) zh, (long long) zm);
 
                asprintf(&out, "%d%02d%02dT%02d%02d%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
                return out;
@@ -1518,7 +1976,7 @@ _asl_time_string(const char *infmt, const char *str, const char *nano)
 
        if ((!strcasecmp(fmt, "sec")) || (!strcasecmp(fmt, "raw")))
        {
-               asprintf(&out, "%lu%s", tick, nanobuf);
+               asprintf(&out, "%llu%s", (unsigned long long) tick, nanobuf);
                return out;
        }
 
@@ -1533,7 +1991,7 @@ _asl_time_string(const char *infmt, const char *str, const char *nano)
        {
                char sep = ' ';
                if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T';
-               
+
                if (NULL == gmtime_r(&tick, &stm)) return NULL;
                asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
                return out;
@@ -1576,8 +2034,8 @@ _asl_time_string(const char *infmt, const char *str, const char *nano)
                off = (zh * 3600) + (zm * 60);
                if (neg) off *= -1;
 
-               if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
-               else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
+               if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02lld", neg ? '-' : '+', (long long) zh);
+               else snprintf(zstr, sizeof(zstr), "%c%02lld:%02lld", neg ? '-' : '+', (long long) zh, (long long) zm);
        }
 
 
@@ -1602,6 +2060,7 @@ _asl_time_string(const char *infmt, const char *str, const char *nano)
        return out;
 }
 
+
 /* called from asl_format_message and _asl_send_message */
 __private_extern__ asl_string_t *
 asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, const char *tfmt)
@@ -1665,50 +2124,40 @@ asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, const char *tfmt)
        return str;
 }
 
-static asl_string_t *
-_asl_string_append_asl_msg(asl_string_t *str, asl_msg_t *msg)
+asl_string_t *
+asl_string_append_asl_msg(asl_string_t *str, asl_msg_t *msg)
 {
        const char *key, *val;
-       uint32_t i, op, n;
+       uint32_t i, x;
+       uint16_t op;
 
        if (msg == NULL) return str;
 
-       if (msg->type == ASL_TYPE_QUERY) asl_string_append(str, "Q ");
+       if (msg->asl_type == ASL_TYPE_QUERY) asl_string_append(str, "Q ");
 
        i = 0;
-       n = 0;
-
-       forever 
+       for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, &op))
        {
-               key = NULL;
-               val = NULL;
-
-               i = asl_msg_fetch(msg, i, &key, &val, &op);
-               if (key != NULL)
-               {
-                       if (n != 0)     asl_string_append_char_no_encoding(str, ' ');
-                       n++;
-
-                       asl_string_append_char_no_encoding(str, '[');
+               if (i != 0)     asl_string_append_char_no_encoding(str, ' ');
+               i++;
 
-                       if (msg->type == ASL_TYPE_QUERY)
-                       {
-                               asl_string_append_op(str, op);
-                               asl_string_append_char_no_encoding(str, ' ');
-                       }
+               asl_string_append_char_no_encoding(str, '[');
 
-                       asl_string_append_asl_key(str, key);
+               if (msg->asl_type == ASL_TYPE_QUERY)
+               {
+                       asl_string_append_op(str, op);
+                       asl_string_append_char_no_encoding(str, ' ');
+               }
 
-                       if (val != NULL)
-                       {
-                               asl_string_append_char_no_encoding(str, ' ');
-                               asl_string_append(str, val);
-                       }
+               asl_string_append_asl_key(str, key);
 
-                       asl_string_append_char_no_encoding(str, ']');
+               if (val != NULL)
+               {
+                       asl_string_append_char_no_encoding(str, ' ');
+                       asl_string_append(str, val);
                }
 
-               if (i == IndexNull) break;
+               asl_string_append_char_no_encoding(str, ']');
        }
 
        return str;
@@ -1721,9 +2170,9 @@ asl_msg_to_string(asl_msg_t *msg, uint32_t *len)
        asl_string_t *str = asl_string_new(ASL_ENCODE_ASL);
        if (str == NULL) return NULL;
 
-       str = _asl_string_append_asl_msg(str, msg);
+       str = asl_string_append_asl_msg(str, msg);
        *len = asl_string_length(str);
-       out = asl_string_free_return_bytes(str);
+       out = asl_string_release_return_bytes(str);
        return out;
 }
 
@@ -2021,7 +2470,7 @@ asl_msg_from_string(const char *buf)
        out = asl_msg_new(ASL_TYPE_MSG);
        if (out == NULL) return NULL;
 
-       out->type = type;
+       out->asl_type = type;
 
        /* OPEN WORD [WORD [WORD]] CLOSE */
        while (k != NULL)
@@ -2109,85 +2558,6 @@ asl_msg_from_string(const char *buf)
        return out;
 }
 
-char *
-asl_list_to_string(asl_search_result_t *list, uint32_t *len)
-{
-       uint32_t i;
-       char tmp[16];
-       char *out;
-       asl_string_t *str;
-
-       if (list == NULL) return NULL;
-       if (list->count == 0) return NULL;
-       if (list->msg == NULL) return NULL;
-
-       str = asl_string_new(ASL_ENCODE_ASL);
-       if (str == NULL) return NULL;
-
-       snprintf(tmp, sizeof(tmp), "%u", list->count);
-       asl_string_append(str, tmp);
-       asl_string_append_char_no_encoding(str, '\n');
-
-       for (i = 0; i < list->count; i++)
-       {
-               _asl_string_append_asl_msg(str, list->msg[i]);
-               asl_string_append_char_no_encoding(str, '\n');
-       }
-
-       *len = asl_string_length(str);
-       out = asl_string_free_return_bytes(str);
-       return out;
-}
-
-asl_search_result_t *
-asl_list_from_string(const char *buf)
-{
-       uint32_t i, n;
-       const char *p;
-       asl_search_result_t *out;
-       asl_msg_t *m;
-
-       if (buf == NULL) return NULL;
-       p = buf;
-
-       n = atoi(buf);
-       if (n == 0) return NULL;
-
-       out = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t));
-       if (out == NULL) return NULL;
-
-       out->msg = (asl_msg_t **)calloc(n, sizeof(asl_msg_t *));
-       if (out->msg == NULL)
-       {
-               free(out);
-               return NULL;
-       }
-
-       for (i = 0; i < n; i++)
-       {
-               p = strchr(p, '\n');
-               if (p == NULL)
-               {
-                       aslresponse_free((aslresponse)out);
-                       return NULL;
-               }
-
-               p++;
-
-               m = asl_msg_from_string(p);
-               if (m == NULL)
-               {
-                       aslresponse_free((aslresponse)out);
-                       return NULL;
-               }
-
-               out->msg[i] = (asl_msg_t *)m;
-               out->count += 1;
-       }
-
-       return out;
-}
-
 static const char *
 _asl_level_string(int level)
 {
@@ -2202,6 +2572,20 @@ _asl_level_string(int level)
        return "unknown";
 }
 
+static const char *
+_asl_level_char(int level)
+{
+       if (level == ASL_LEVEL_EMERG) return "P";
+       if (level == ASL_LEVEL_ALERT) return "A";
+       if (level == ASL_LEVEL_CRIT) return "C";
+       if (level == ASL_LEVEL_ERR) return "E";
+       if (level == ASL_LEVEL_WARNING) return "W";
+       if (level == ASL_LEVEL_NOTICE) return "N";
+       if (level == ASL_LEVEL_INFO) return "I";
+       if (level == ASL_LEVEL_DEBUG) return "D";
+       return "?";
+}
+
 /*
  * Find the value for a key in a message and append a formatted value to str.
  * kf may be a simple (no embedded white space) key, or one of (key) or ((key)(format)).
@@ -2284,6 +2668,11 @@ _asl_string_append_value_for_key_format(asl_string_t *str, asl_msg_t *msg, char
                        mval = _asl_level_string(atoi(mval));
                        asl_string_append_no_encoding(str, mval);
                }
+               else if (!strcmp(fmt, "char"))
+               {
+                       mval = _asl_level_char(atoi(mval));
+                       asl_string_append_no_encoding(str, mval);
+               }
                else
                {
                        asl_string_append_no_encoding(str, mval);
@@ -2360,7 +2749,7 @@ asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t
                asl_string_append_char_no_encoding(str, '\n');
 
                *len = asl_string_length(str);
-               out = asl_string_free_return_bytes(str);
+               out = asl_string_release_return_bytes(str);
                return out;
        }
 
@@ -2377,7 +2766,7 @@ asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t
                asl_string_append_char_no_encoding(str, '\n');
 
                *len = asl_string_length(str);
-               out = asl_string_free_return_bytes(str);
+               out = asl_string_release_return_bytes(str);
                return out;
        }
 
@@ -2473,7 +2862,7 @@ asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t
                asl_string_append_char_no_encoding(str, '\n');
 
                *len = asl_string_length(str);
-               out = asl_string_free_return_bytes(str);
+               out = asl_string_release_return_bytes(str);
                return out;
        }
 
@@ -2522,7 +2911,7 @@ asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t
                asl_string_append_char_no_encoding(str, '\n');
 
                *len = asl_string_length(str);
-               out = asl_string_free_return_bytes(str);
+               out = asl_string_release_return_bytes(str);
                return out;
        }
 
@@ -2638,115 +3027,269 @@ asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t
        asl_string_append_char_no_encoding(str, '\n');
 
        *len = asl_string_length(str);
-       out = asl_string_free_return_bytes(str);
+       out = asl_string_release_return_bytes(str);
        return out;
 }
 
-/*
- * OLD ASLMSG COMPATIBILITY
- */
-const char *
-asl_key(aslmsg msg, uint32_t n)
+#pragma mark -
+#pragma mark xpc conversion
+
+static void
+_asl_msg_to_xpc(asl_msg_t *msg, xpc_object_t dict)
 {
-       uint32_t slot, i;
-       asl_msg_t *page;
+       uint32_t x, len;
+       const char *key, *val, *nano;
+       uint16_t kx;
 
-       i = 0;
-       for (page = (asl_msg_t *)msg; page != NULL; page = page->next)
+       if (msg == NULL) return;
+       if (dict == NULL) return;
+
+       nano = NULL;
+       asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
+
+       for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
        {
-               for (slot = 0; slot < ASL_MSG_PAGE_SLOTS; slot++)
+               if (key == NULL) continue;
+
+               len = strlen(key);
+               kx = _asl_msg_std_key(key, len);
+
+               if (val == NULL)
+               {
+                       xpc_object_t obj = xpc_null_create();
+                       xpc_dictionary_set_value(dict, key, obj);
+                       xpc_release(obj);
+               }
+               else if (kx == 0)
                {
-                       if (page->key[slot] != ASL_MSG_SLOT_FREE)
+                       if (streq(key, ASL_KEY_SENDER_MACH_UUID))
                        {
-                               if (i == n) return _asl_msg_slot_key(page, slot);
-                               i++;
+                               uuid_t v;
+                               if (uuid_parse(val, v) == 0)
+                               {
+                                       xpc_object_t obj = xpc_uuid_create(v);
+                                       xpc_dictionary_set_value(dict, key, obj);
+                                       xpc_release(obj);
+                               }
+                       }
+                       else
+                       {
+                               xpc_object_t obj = xpc_string_create(val);
+                               xpc_dictionary_set_value(dict, key, obj);
+                               xpc_release(obj);
                        }
                }
+               else if (kx == ASL_STD_KEY_TIME)
+               {
+                       uint64_t t = NSEC_PER_SEC * asl_core_parse_time(val, NULL);
+                       if (nano != NULL) t += atoll(nano);
+                       xpc_object_t obj = xpc_date_create(t);
+                       xpc_dictionary_set_value(dict, key, obj);
+                       xpc_release(obj);
+               }
+               else if (kx == ASL_STD_KEY_NANO)
+               {
+                       /* handled with ASL_STD_KEY_TIME */
+               }
+               else if ((kx == ASL_STD_KEY_PID) || (kx == ASL_STD_KEY_REF_PID))
+               {
+                       int64_t v = atoll(val);
+                       xpc_object_t obj = xpc_int64_create(v);
+                       xpc_dictionary_set_value(dict, key, obj);
+                       xpc_release(obj);
+               }
+               else if ((kx == ASL_STD_KEY_UID) || (kx == ASL_STD_KEY_GID))
+               {
+                       int64_t v = atoll(val);
+                       xpc_object_t obj = xpc_int64_create(v);
+                       xpc_dictionary_set_value(dict, key, obj);
+                       xpc_release(obj);
+               }
+               else if (kx == ASL_STD_KEY_LEVEL)
+               {
+                       int64_t v = atoll(val);
+                       xpc_object_t obj = xpc_int64_create(v);
+                       xpc_dictionary_set_value(dict, key, obj);
+                       xpc_release(obj);
+               }
+               else if ((kx == ASL_STD_KEY_READ_UID) || (kx == ASL_STD_KEY_READ_GID))
+               {
+                       int64_t v = atoll(val);
+                       xpc_object_t obj = xpc_int64_create(v);
+                       xpc_dictionary_set_value(dict, key, obj);
+                       xpc_release(obj);
+               }
+               else if (kx == ASL_STD_KEY_MSG_ID)
+               {
+                       /* ignore */
+               }
+               else
+               {
+                       xpc_object_t obj = xpc_string_create(val);
+                       xpc_dictionary_set_value(dict, key, obj);
+                       xpc_release(obj);
+               }
        }
+}
 
-       return NULL;
+void
+_asl_log_args_to_xpc(asl_object_t client, asl_object_t msg, xpc_object_t dict)
+{
+       _asl_msg_to_xpc(asl_client_kvdict((asl_client_t *)client), dict);
+       _asl_msg_to_xpc((asl_msg_t *)msg, dict);
 }
 
-aslmsg
-asl_new(uint32_t type)
+#pragma mark -
+#pragma mark asl_object support
+
+static asl_object_private_t *
+_jump_alloc(uint32_t type)
 {
-       return (aslmsg)asl_msg_new(type);
+       return (asl_object_private_t *)asl_msg_new(type);
 }
 
-int
-asl_set(aslmsg msg, const char *key, const char *value)
+static void
+_jump_dealloc(asl_object_private_t *obj)
+{
+       asl_msg_t *msg = (asl_msg_t *)obj;
+       while (msg != NULL)
+       {
+               asl_msg_t *next = msg->next;
+               _asl_msg_free_page(msg);
+               msg = next;
+       }
+}
+
+static int
+_jump_set_key_val_op(asl_object_private_t *obj, const char *key, const char *val, uint16_t op)
 {
-       return asl_msg_set_key_val_op((asl_msg_t *)msg, key, value, IndexNull);
+       uint32_t op32 = op;
+       int status = asl_msg_set_key_val_op((asl_msg_t *)obj, key, val, op32);
+       return (status == ASL_STATUS_OK) ? 0 : -1;
 }
 
-int
-asl_set_query(aslmsg msg, const char *key, const char *value, uint32_t op)
+static void
+_jump_unset_key(asl_object_private_t *obj, const char *key)
 {
-       return asl_msg_set_key_val_op((asl_msg_t *)msg, key, value, op);
+       asl_msg_unset((asl_msg_t *)obj, key);
 }
 
-int
-asl_unset(aslmsg msg, const char *key)
+static int
+_jump_get_val_op_for_key(asl_object_private_t *obj, const char *key, const char **val, uint16_t *op)
 {
-       asl_msg_unset((asl_msg_t *)msg, key);
-       return 0;
+       return asl_msg_lookup((asl_msg_t *)obj, key, val, op);
 }
 
-const char *
-asl_get(aslmsg msg, const char *key)
+static int
+_jump_get_key_val_op_at_index(asl_object_private_t *obj, size_t n, const char **key, const char **val, uint16_t *op)
 {
-       const char *val;
-       int status;
+       uint32_t slot = IndexNull;
+       asl_msg_t *page = NULL;
 
-       val = NULL;
-       status = asl_msg_lookup((asl_msg_t *)msg, key, &val, NULL);
-       if (status != 0) return NULL;
-       return val;
+       if  (0 != _asl_msg_resolve_index((asl_msg_t *)obj, n, &page, &slot)) return -1;
+
+       if (key != NULL) *key = _asl_msg_slot_key(page, slot);
+       if (val != NULL) *val = _asl_msg_slot_val(page, slot);
+       if (op != NULL) *op = _get_slot_op(page, slot);
+
+       return 0;
 }
 
-void
-asl_free(aslmsg msg)
+static size_t
+_jump_count(asl_object_private_t *obj)
 {
-       asl_msg_release((asl_msg_t *)msg);
+       size_t count = asl_msg_count((asl_msg_t *)obj);
+       return count;
 }
 
-/* aslresponse */
+static void
+_jump_append(asl_object_private_t *obj, asl_object_private_t *newobj, void *addr)
+{
+       int type = asl_get_type((asl_object_t)newobj);
+       if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
 
-/*
- * aslresponse_next: Iterate over responses returned from asl_search()
- * a: a response returned from asl_search();
- * returns: The next log message (an aslmsg) or NULL on failure
- */
-aslmsg
-aslresponse_next(aslresponse r)
+       asl_msg_merge((asl_msg_t *)obj, (asl_msg_t *)newobj);
+}
+
+static void
+_jump_prepend(asl_object_private_t *obj, asl_object_private_t *newobj)
+{
+       if (obj == NULL) return;
+
+       int type = asl_get_type((asl_object_t)newobj);
+       if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
+
+       asl_msg_replace((asl_msg_t *)obj, (asl_msg_t *)newobj);
+}
+
+static asl_object_private_t *
+_jump_search(asl_object_private_t *obj, asl_object_private_t *query)
 {
-       asl_search_result_t *res;
-       asl_msg_t *m;
+       if (obj == NULL) return NULL;
+
+       if (query == NULL)
+       {
+               /* NULL matches any message */
+               asl_msg_list_t *out = asl_msg_list_new();
+               asl_msg_list_append(out, obj);
+               return (asl_object_private_t *)out;
+       }
 
-       res = (asl_search_result_t *)r;
-       if (res == NULL) return NULL;
+       if ((query->asl_type != ASL_TYPE_MSG) && (query->asl_type != ASL_TYPE_QUERY)) return NULL;
 
-       if (res->curr >= res->count) return NULL;
-       m = res->msg[res->curr];
-       res->curr++;
+       if (asl_msg_cmp((asl_msg_t *)obj, (asl_msg_t *)query) == 1)
+       {
+               asl_msg_list_t *out = asl_msg_list_new();
+               asl_msg_list_append(out, obj);
+               return (asl_object_private_t *)out;
+       }
 
-       return (aslmsg)m;
+       return NULL;
 }
 
-/*
- * aslresponse_free: Free a response returned from asl_search() 
- * a: a response returned from asl_search()
- */
-void
-aslresponse_free(aslresponse r)
+static asl_object_private_t *
+_jump_match(asl_object_private_t *obj, asl_object_private_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int32_t dir)
 {
-       asl_search_result_t *res;
-       uint32_t i;
+       if (obj == NULL) return NULL;
+
+       if (qlist == NULL)
+       {
+               /* NULL matches any message */
+               asl_msg_list_t *out = asl_msg_list_new();
+               asl_msg_list_append(out, obj);
+               return (asl_object_private_t *)out;
+       }
 
-       res = (asl_search_result_t *)r;
-       if (res == NULL) return;
+       if (asl_msg_cmp_list((asl_msg_t *)obj, (asl_msg_list_t *)qlist) == 0) return NULL;
 
-       for (i = 0; i < res->count; i++) asl_msg_release(res->msg[i]);
-       free(res->msg);
-       free(res);
+       asl_msg_list_t *out = asl_msg_list_new();
+       asl_msg_list_append(out, obj);
+       return (asl_object_private_t *)out;
+}
+
+
+__private_extern__ const asl_jump_table_t *
+asl_msg_jump_table()
+{
+       static const asl_jump_table_t jump =
+       {
+               .alloc = &_jump_alloc,
+               .dealloc = &_jump_dealloc,
+               .set_key_val_op = &_jump_set_key_val_op,
+               .unset_key = &_jump_unset_key,
+               .get_val_op_for_key = &_jump_get_val_op_for_key,
+               .get_key_val_op_at_index = &_jump_get_key_val_op_at_index,
+               .count = &_jump_count,
+               .next = NULL,
+               .prev = NULL,
+               .get_object_at_index = NULL,
+               .set_iteration_index = NULL,
+               .remove_object_at_index = NULL,
+               .append = &_jump_append,
+               .prepend = &_jump_prepend,
+               .search = &_jump_search,
+               .match = &_jump_match
+       };
+
+       return &jump;
 }