1/*
2 * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <string.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <ctype.h>
29#include <unistd.h>
30#include <stdarg.h>
31#include <regex.h>
32#include <syslog.h>
33#include <notify.h>
34#include <errno.h>
35#include <time.h>
36#include <sys/time.h>
37#include <asl.h>
38#include <asl_object.h>
39#include <asl_private.h>
40#include <asl_core.h>
41#include <sys/types.h>
42#include <libkern/OSAtomic.h>
43#include <asl_msg.h>
44#include <asl_msg_list.h>
45
46#define TOKEN_NULL  0
47#define TOKEN_OPEN  1
48#define TOKEN_CLOSE 2
49#define TOKEN_WORD  3
50#define TOKEN_INT   4
51
52#define MFMT_RAW 0
53#define MFMT_STD 1
54#define MFMT_BSD 2
55#define MFMT_XML 3
56#define MFMT_STR 4
57#define MFMT_MSG 5
58
59#define SEC_PER_HOUR 3600
60
61#define PAGE_OBJECT 1
62
63#define forever for(;;)
64
65#define streq(A, B) (strcmp(A, B) == 0)
66#define streq_len(A, B, C) (strncmp(A, B, C) == 0)
67#define strneq(A, B) (strcmp(A, B) != 0)
68#define strcaseeq(A, B) (strcasecmp(A, B) == 0)
69#define strcaseneq(A, B) (strcasecmp(A, B) != 0)
70
71#ifndef ASL_QUERY_OP_FALSE
72#define ASL_QUERY_OP_FALSE 0
73#endif
74
75#define AUX_0_TIME      0x00000001
76#define AUX_0_TIME_NSEC 0x00000002
77#define AUX_0_HOST      0x00000004
78#define AUX_0_SENDER    0x00000008
79#define AUX_0_FACILITY  0x00000010
80#define AUX_0_PID       0x00000020
81#define AUX_0_UID       0x00000040
82#define AUX_0_GID       0x00000080
83#define AUX_0_MSG       0x00000100
84#define AUX_0_OPTION    0x00000200
85#define AUX_0_LEVEL     0x00000400
86
87/* from asl_util.c */
88int asl_is_utf8(const char *str);
89uint8_t *asl_b64_encode(const uint8_t *buf, size_t len);
90
91void _asl_msg_dump(FILE *f, const char *comment, asl_msg_t *msg);
92
93#pragma mark -
94#pragma mark standard message keys
95
96static const char *ASLStandardKey[] =
97{
98	ASL_KEY_TIME,
99	ASL_KEY_TIME_NSEC,
100	ASL_KEY_HOST,
101	ASL_KEY_SENDER,
102	ASL_KEY_FACILITY,
103	ASL_KEY_PID,
104	ASL_KEY_UID,
105	ASL_KEY_GID,
106	ASL_KEY_LEVEL,
107	ASL_KEY_MSG,
108	ASL_KEY_READ_UID,
109	ASL_KEY_READ_GID,
110	ASL_KEY_SESSION,
111	ASL_KEY_REF_PID,
112	ASL_KEY_REF_PROC,
113	ASL_KEY_MSG_ID,
114	ASL_KEY_EXPIRE_TIME,
115	ASL_KEY_OPTION,
116	ASL_KEY_FREE_NOTE
117};
118
119static const char *MTStandardKey[] =
120{
121	"com.apple.message.domain",
122	"com.apple.message.domain_scope",
123	"com.apple.message.result",
124	"com.apple.message.signature",
125	"com.apple.message.signature2",
126	"com.apple.message.signature3",
127	"com.apple.message.success",
128	"com.apple.message.uuid",
129	"com.apple.message.value",
130	"com.apple.message.value2",
131	"com.apple.message.value3",
132	"com.apple.message.value4",
133	"com.apple.message.value5"
134};
135
136static uint16_t
137_asl_msg_std_key(const char *s, uint32_t len)
138{
139	if ((len > 18) && (streq_len(s, "com.apple.message.", 18)))
140	{
141		if (streq(s + 18, "domain")) return ASL_MT_KEY_DOMAIN;
142		else if (streq(s + 18, "domain_scope")) return ASL_MT_KEY_SCOPE;
143		else if (streq(s + 18, "result")) return ASL_MT_KEY_RESULT;
144		else if (streq(s + 18, "signature")) return ASL_MT_KEY_SIG;
145		else if (streq(s + 18, "signature2")) return ASL_MT_KEY_SIG2;
146		else if (streq(s + 18, "signature3")) return ASL_MT_KEY_SIG3;
147		else if (streq(s + 18, "success")) return ASL_MT_KEY_SUCCESS;
148		else if (streq(s + 18, "uuid")) return ASL_MT_KEY_UUID;
149		else if (streq(s + 18, "value")) return ASL_MT_KEY_VAL;
150		else if (streq(s + 18, "value2")) return ASL_MT_KEY_VAL2;
151		else if (streq(s + 18, "value3")) return ASL_MT_KEY_VAL3;
152		else if (streq(s + 18, "value4")) return ASL_MT_KEY_VAL4;
153		else if (streq(s + 18, "value5")) return ASL_MT_KEY_VAL5;
154
155		return 0;
156	}
157
158	switch (len)
159	{
160		case 3:
161		{
162			if streq(s, ASL_KEY_PID) return ASL_STD_KEY_PID;
163			else if streq(s, ASL_KEY_UID) return ASL_STD_KEY_UID;
164			else if streq(s, ASL_KEY_GID) return ASL_STD_KEY_GID;
165		}
166		case 4:
167		{
168			if streq(s, ASL_KEY_TIME) return ASL_STD_KEY_TIME;
169			else if streq(s, ASL_KEY_HOST) return ASL_STD_KEY_HOST;
170		}
171		case 5:
172		{
173			if streq(s, ASL_KEY_LEVEL) return ASL_STD_KEY_LEVEL;
174		}
175		case 6:
176		{
177			if streq(s, ASL_KEY_SENDER) return ASL_STD_KEY_SENDER;
178			else if streq(s, ASL_KEY_REF_PID) return ASL_STD_KEY_REF_PID;
179		}
180		case 7:
181		{
182			if streq(s, ASL_KEY_MSG) return ASL_STD_KEY_MESSAGE;
183			else if streq(s, ASL_KEY_SESSION) return ASL_STD_KEY_SESSION;
184			else if streq(s, ASL_KEY_READ_UID) return ASL_STD_KEY_READ_UID;
185			else if streq(s, ASL_KEY_READ_GID) return ASL_STD_KEY_READ_GID;
186			else if streq(s, ASL_KEY_REF_PROC) return ASL_STD_KEY_REF_PROC;
187		}
188		case 8:
189		{
190			if streq(s, ASL_KEY_FACILITY) return ASL_STD_KEY_FACILITY;
191		}
192		case 9:
193		{
194			if streq(s, ASL_KEY_OPTION) return ASL_STD_KEY_OPTION;
195		}
196		case 11:
197		{
198			if streq(s, ASL_KEY_TIME_NSEC) return ASL_STD_KEY_NANO;
199		}
200		case 12:
201		{
202			if streq(s, ASL_KEY_MSG_ID) return ASL_STD_KEY_MSG_ID;
203		}
204		case 13:
205		{
206			if streq(s, ASL_KEY_EXPIRE_TIME) return ASL_STD_KEY_EXPIRE;
207		}
208		case 14:
209		{
210			if streq(s, ASL_KEY_FREE_NOTE) return ASL_STD_KEY_FREE_NOTE;
211		}
212		default:
213		{
214			return 0;
215		}
216	}
217
218	return 0;
219}
220
221#pragma mark -
222#pragma mark asl_msg
223
224static asl_msg_t *
225_asl_msg_make_page(void)
226{
227	int i;
228	asl_msg_t *out = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
229
230	if (out == NULL) return NULL;
231
232	for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
233	{
234		out->key[i] = ASL_MSG_SLOT_FREE;
235		out->val[i] = ASL_MSG_SLOT_FREE;
236	}
237
238	out->mem_size = sizeof(asl_msg_t);
239
240	return out;
241}
242
243asl_msg_t *
244asl_msg_retain(asl_msg_t *msg)
245{
246	if (msg == NULL) return NULL;
247	asl_retain((asl_object_t)msg);
248	return msg;
249}
250
251void
252asl_msg_release(asl_msg_t *msg)
253{
254	if (msg == NULL) return;
255	asl_release((asl_object_t)msg);
256}
257
258static const char *
259_asl_msg_slot_key(asl_msg_t *page, uint32_t slot)
260{
261	const char *out;
262	uint16_t x;
263
264	if (page == NULL) return NULL;
265	if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
266	if (page->key[slot] == ASL_MSG_SLOT_FREE) return NULL;
267
268	switch (page->key[slot] & ASL_MSG_KV_MASK)
269	{
270		case ASL_MSG_KV_INLINE:
271		{
272			return page->data + page->key[slot];
273		}
274		case ASL_MSG_KV_DICT:
275		{
276			if ((page->key[slot] > ASL_STD_KEY_BASE) && (page->key[slot] <= ASL_STD_KEY_LAST))
277			{
278				x = page->key[slot] - ASL_STD_KEY_BASE - 1;
279				return ASLStandardKey[x];
280			}
281			else if ((page->key[slot] > ASL_MT_KEY_BASE) && (page->key[slot] <= ASL_MT_KEY_LAST))
282			{
283				x = page->key[slot] - ASL_MT_KEY_BASE - 1;
284				return MTStandardKey[x];
285			}
286
287			return NULL;
288		}
289		case ASL_MSG_KV_EXTERN:
290		{
291			x = page->key[slot] & ASL_MSG_OFFSET_MASK;
292			memcpy(&out, page->data + x, sizeof(char *));
293			return out;
294		}
295	}
296
297	return NULL;
298}
299
300static const char *
301_asl_msg_slot_val(asl_msg_t *page, uint32_t slot)
302{
303	const char *out;
304	uint16_t x, type;
305
306	if (page == NULL) return NULL;
307	if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
308
309	if (page->val[slot] == ASL_MSG_SLOT_FREE) return NULL;
310
311	type = page->val[slot] & ASL_MSG_KV_MASK;
312
313	if (type == ASL_MSG_KV_INLINE)
314	{
315		return page->data + page->val[slot];
316	}
317	else if (type == ASL_MSG_KV_EXTERN)
318	{
319		x = page->val[slot] & ASL_MSG_OFFSET_MASK;
320		memcpy(&out, page->data + x, sizeof(char *));
321		return out;
322	}
323
324	return NULL;
325}
326
327/*
328 * asl_new: create a new log message.
329 */
330asl_msg_t *
331asl_msg_new(uint32_t type)
332{
333	asl_msg_t *out;
334
335	out = _asl_msg_make_page();
336	if (out == NULL) return NULL;
337
338	out->asl_type = type;
339	out->refcount = 1;
340
341	return out;
342}
343
344static void
345_asl_msg_free_page(asl_msg_t *page)
346{
347	uint32_t i;
348	char *p;
349
350	if (page == NULL) return;
351
352	for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
353	{
354		if (page->key[i] == ASL_STD_KEY_FREE_NOTE)
355		{
356			const char *x = _asl_msg_slot_val(page, i);
357			if (x != NULL) notify_post(x);
358		}
359
360		if ((page->key[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
361		{
362			memcpy(&p, page->data + (page->key[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
363			free(p);
364		}
365
366		if ((page->val[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
367		{
368			memcpy(&p, page->data + (page->val[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
369			free(p);
370		}
371	}
372
373	free(page);
374}
375
376uint32_t
377asl_msg_type(asl_msg_t *msg)
378{
379	if (msg == NULL) return 0;
380	return msg->asl_type;
381}
382
383uint32_t
384asl_msg_count(asl_msg_t *msg)
385{
386	uint32_t total;
387
388	total = 0;
389
390	for (; msg != NULL; msg = msg->next) total += msg->count;
391	return total;
392}
393
394static void
395_asl_msg_dump_kv(FILE *f, asl_msg_t *msg, uint16_t x)
396{
397	if (x == ASL_MSG_SLOT_FREE)
398	{
399		fprintf(f, "-free-");
400		return;
401	}
402
403	if ((x & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
404	{
405		switch (x)
406		{
407			case ASL_STD_KEY_TIME: fprintf(f, "(dict: Time)"); return;
408			case ASL_STD_KEY_NANO: fprintf(f, "(dict: Nano)"); return;
409			case ASL_STD_KEY_HOST: fprintf(f, "(dict: Host)"); return;
410			case ASL_STD_KEY_SENDER: fprintf(f, "(dict: Sender)"); return;
411			case ASL_STD_KEY_FACILITY: fprintf(f, "(dict: Facility)"); return;
412			case ASL_STD_KEY_PID: fprintf(f, "(dict: PID)"); return;
413			case ASL_STD_KEY_UID: fprintf(f, "(dict: UID)"); return;
414			case ASL_STD_KEY_GID: fprintf(f, "(dict: GID)"); return;
415			case ASL_STD_KEY_LEVEL: fprintf(f, "(dict: Level)"); return;
416			case ASL_STD_KEY_MESSAGE: fprintf(f, "(dict: Message)"); return;
417			case ASL_STD_KEY_READ_UID: fprintf(f, "(dict: ReadUID)"); return;
418			case ASL_STD_KEY_READ_GID: fprintf(f, "(dict: ReadGID)"); return;
419			case ASL_STD_KEY_SESSION: fprintf(f, "(dict: Session)"); return;
420			case ASL_STD_KEY_REF_PID: fprintf(f, "(dict: PID)"); return;
421			case ASL_STD_KEY_REF_PROC: fprintf(f, "(dict: RefProc)"); return;
422			case ASL_STD_KEY_MSG_ID: fprintf(f, "(dict: ASLMessageID)"); return;
423			case ASL_STD_KEY_EXPIRE: fprintf(f, "(dict: Expire)"); return;
424			case ASL_STD_KEY_OPTION: fprintf(f, "(dict: ASLOption)"); return;
425			case ASL_MT_KEY_DOMAIN: fprintf(f, "(dict: com.apple.message.domain)"); return;
426			case ASL_MT_KEY_SCOPE: fprintf(f, "(dict: com.apple.message.domain_scope)"); return;
427			case ASL_MT_KEY_RESULT: fprintf(f, "(dict: com.apple.message.result)"); return;
428			case ASL_MT_KEY_SIG: fprintf(f, "(dict: com.apple.message.signature)"); return;
429			case ASL_MT_KEY_SIG2: fprintf(f, "(dict: com.apple.message.signature2)"); return;
430			case ASL_MT_KEY_SIG3: fprintf(f, "(dict: com.apple.message.signature3)"); return;
431			case ASL_MT_KEY_SUCCESS: fprintf(f, "(dict: com.apple.message.success)"); return;
432			case ASL_MT_KEY_UUID: fprintf(f, "(dict: com.apple.message.uuid)"); return;
433			case ASL_MT_KEY_VAL: fprintf(f, "(dict: com.apple.message.value)"); return;
434			case ASL_MT_KEY_VAL2: fprintf(f, "(dict: com.apple.message.value2)"); return;
435			case ASL_MT_KEY_VAL3: fprintf(f, "(dict: com.apple.message.value3)"); return;
436			case ASL_MT_KEY_VAL4: fprintf(f, "(dict: com.apple.message.value4)"); return;
437			case ASL_MT_KEY_VAL5: fprintf(f, "(dict: com.apple.message.value5)"); return;
438		}
439
440		fprintf(f, "(dict: -unknown-)");
441		return;
442	}
443
444	if ((x & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
445	{
446		const char *c;
447		size_t z = x & ASL_MSG_OFFSET_MASK;
448		memcpy(&c, msg->data + z, sizeof(char *));
449		fprintf(f, "(extern: %s)", c);
450		return;
451	}
452
453	fprintf(f, "%s", msg->data + x);
454}
455
456void
457_asl_msg_dump(FILE *f, const char *comment, asl_msg_t *msg)
458{
459	int i, page1 = 1;
460
461	if (f == NULL) return;
462	if (msg == NULL)
463	{
464		fprintf(f, "asl_msg %s: NULL\n", comment);
465		return;
466	}
467
468	while (msg != NULL)
469	{
470		if (page1 == 1)
471		{
472			fprintf(f, "asl_msg %s: %p\n", comment, msg);
473			fprintf(f, "    refcount: %u\n", msg->refcount);
474			fprintf(f, "    asl_type: %u\n", msg->asl_type);
475			page1 = 0;
476		}
477		else
478		{
479			fprintf(f, "  page: %p\n", msg);
480		}
481
482		fprintf(f, "    count: %u\n", msg->count);
483		fprintf(f, "    data_size: %u\n", msg->data_size);
484		fprintf(f, "    mem_size: %llu\n", msg->mem_size);
485		fprintf(f, "    next: %p\n", msg->next);
486
487		for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
488		{
489			fprintf(f, "    slot[%d]: ", i);
490			_asl_msg_dump_kv(f, msg, msg->key[i]);
491			fprintf(f, " ");
492			_asl_msg_dump_kv(f, msg, msg->val[i]);
493			fprintf(f, " 0x%04x\n", msg->op[i]);
494		}
495
496		msg = msg->next;
497	}
498}
499
500#pragma mark -
501#pragma mark fetching contents
502
503/*
504 * Find the slot and page for an input key.
505 */
506static uint32_t
507_asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opage)
508{
509	uint32_t i, len, slot;
510	uint16_t kx;
511	asl_msg_t *page;
512	const char *kp;
513
514	if (msg == NULL) return IndexNull;
515	if (key == NULL) return IndexNull;
516
517	i = 0;
518	slot = 0;
519	if (oslot != NULL) *oslot = slot;
520
521	page = msg;
522	if (opage != NULL) *opage = page;
523
524	len = strlen(key);
525	kx = _asl_msg_std_key(key, len);
526
527	forever
528	{
529		if (page->key[slot] != ASL_MSG_SLOT_FREE)
530		{
531			if (kx != 0)
532			{
533				if (page->key[slot] == kx) return i;
534			}
535			else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
536			{
537				/* page->key[slot] is a dictionary key, but key is not (kx == 0) so skip this slot */
538			}
539			else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
540			{
541				memcpy(&kp, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
542				if (streq(key, kp)) return i;
543			}
544			else
545			{
546				kp = page->data + page->key[slot];
547				if (streq(key, kp)) return i;
548			}
549		}
550
551		i++;
552		slot++;
553		if (oslot != NULL) *oslot = slot;
554
555		if (slot >= ASL_MSG_PAGE_SLOTS)
556		{
557			if (page->next == NULL) return IndexNull;
558
559			slot = 0;
560			if (oslot != NULL) *oslot = slot;
561
562			page = page->next;
563			if (opage != NULL) *opage = page;
564		}
565	}
566
567	return IndexNull;
568}
569
570/*
571 * Find page and slot for an "index".
572 */
573static int
574_asl_msg_resolve_index(asl_msg_t *msg, uint32_t n, asl_msg_t **page, uint32_t *slot)
575{
576	uint32_t i, sx;
577	asl_msg_t *px;
578
579	if (msg == NULL) return -1;
580
581	*slot = IndexNull;
582	*page = NULL;
583
584	sx = 0;
585
586	/* find page */
587	for (px = msg; px != NULL; px = px->next)
588	{
589		if (n > (sx + px->count))
590		{
591			sx += px->count;
592			continue;
593		}
594
595		*page = px;
596
597		/* find slot */
598		for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
599		{
600			if (px->key[i] != ASL_MSG_SLOT_FREE)
601			{
602				if (sx == n)
603				{
604					*slot = i;
605					return 0;
606				}
607
608				sx++;
609			}
610		}
611	}
612
613	return -1;
614}
615
616/*
617 * asl_msg_fetch: iterate over entries
618 * initial value of n should be 0.  Subseqent calls should use the last
619 * returned value.  Returns IndexNull when there are no more entries
620 * Sets the pointers for the next key, value, and op in the msg.
621 * The iterator encodes a page number and a slot number.
622 */
623
624uint32_t
625asl_msg_fetch(asl_msg_t *msg, uint32_t x, const char **keyout, const char **valout, uint16_t *opout)
626{
627	uint32_t p, xpn, xsn;
628	asl_msg_t *page = NULL;
629
630	if (msg == NULL) return IndexNull;
631
632	xsn = x >> 24;
633	xpn = x & 0x00ffffff;
634
635	/* slot number 0xff means we have run out entries */
636	if (xsn == 0x000000ff) return IndexNull;
637
638	page = msg;
639	for (p = 0; p < xpn; p++)
640	{
641		page = page->next;
642		if (page == NULL) return IndexNull;
643	}
644
645	if (keyout != NULL) *keyout = _asl_msg_slot_key(page, xsn);
646	if (valout != NULL) *valout = _asl_msg_slot_val(page, xsn);
647	if (opout != NULL) *opout = (uint32_t)(page->op[xsn]);
648
649	/* advance to the next slot */
650	forever
651	{
652		xsn++;
653
654		if (xsn >= ASL_MSG_PAGE_SLOTS)
655		{
656			if (page->next == NULL) return 0xff000000;
657			xsn = 0;
658			page = page->next;
659			xpn++;
660		}
661
662		if (page->key[xsn] != ASL_MSG_SLOT_FREE) return ((xsn << 24) | xpn);
663	}
664
665	return IndexNull;
666}
667
668int
669asl_msg_lookup(asl_msg_t *msg, const char *key, const char **valout, uint16_t *opout)
670{
671	uint32_t i, slot;
672	asl_msg_t *page;
673
674	if (msg == NULL) return -1;
675	if (valout != NULL) *valout = NULL;
676	if (opout != NULL) *opout = 0;
677
678	slot = IndexNull;
679	page = NULL;
680
681	i = _asl_msg_index(msg, key, &slot, &page);
682	if (i == IndexNull) return -1;
683
684	if (valout != NULL) *valout = _asl_msg_slot_val(page, slot);
685	if (opout != NULL) *opout = (uint32_t)(page->op[slot]);
686
687	return 0;
688}
689
690const char *
691asl_msg_get_val_for_key(asl_msg_t *msg, const char *key)
692{
693	uint32_t slot;
694	asl_msg_t *page;
695
696	if (msg == NULL) return NULL;
697
698	slot = IndexNull;
699	page = NULL;
700
701	if (_asl_msg_index(msg, key, &slot, &page) == IndexNull) return NULL;
702
703	return _asl_msg_slot_val(page, slot);
704}
705
706const char *
707asl_msg_key(asl_msg_t *msg, uint32_t n)
708{
709	uint32_t slot, i;
710	asl_msg_t *page;
711
712	if (msg == NULL) return NULL;
713
714	i = 0;
715	for (page = msg; page != NULL; page = page->next)
716	{
717		for (slot = 0; slot < ASL_MSG_PAGE_SLOTS; slot++)
718		{
719			if (page->key[slot] != ASL_MSG_SLOT_FREE)
720			{
721				if (i == n) return _asl_msg_slot_key(page, slot);
722				i++;
723			}
724		}
725	}
726
727	return NULL;
728}
729
730#pragma mark -
731#pragma mark adding and replacing contents
732
733static int
734_asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
735{
736	uint32_t slot, keylen, vallen, total;
737	uint64_t klen, vlen;
738	uint16_t kx;
739	asl_msg_t *page, *last;
740	char *extkey, *extval;
741
742	if (msg == NULL) return -1;
743	if (key == NULL) return -1;
744
745	extkey = NULL;
746	extval = NULL;
747
748	keylen = strlen(key);
749	klen = keylen;
750	kx = _asl_msg_std_key(key, keylen);
751
752	if (kx == 0) keylen++;
753	else keylen = 0;
754
755	total = keylen;
756
757	vallen = 0;
758	if (val != NULL)
759	{
760		vallen = strlen(val) + 1;
761		vlen = vallen;
762		total += vallen;
763	}
764
765	/* check if one or both of key and value must be "external" (in its own malloced space) */
766	if (keylen > ASL_MSG_PAGE_DATA_SIZE)
767	{
768		extkey = strdup(key);
769		keylen = sizeof(char *);
770	}
771
772	if (vallen > ASL_MSG_PAGE_DATA_SIZE)
773	{
774		extval = strdup(val);
775		vallen = sizeof(char *);
776	}
777
778	total = keylen + vallen;
779	if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extval == NULL) && (keylen > 0))
780	{
781		extval = strdup(val);
782		vallen = sizeof(char *);
783		total = keylen + vallen;
784	}
785
786	if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extkey == NULL))
787	{
788		extkey = strdup(key);
789		keylen = sizeof(char *);
790		total = keylen + vallen;
791	}
792
793	if (total > ASL_MSG_PAGE_DATA_SIZE)
794	{
795		/* can't happen, but... */
796		if (extkey != NULL) free(extkey);
797		if (extval != NULL) free(extval);
798		return -1;
799	}
800
801	/* find a page with space for the key and value and a free slot*/
802	slot = 0;
803	last = msg;
804
805	for (page = msg; page != NULL; page = page->next)
806	{
807		last = page;
808
809		if (total <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
810		{
811			/* check for a free slot */
812			for (slot = 0; (slot < ASL_MSG_PAGE_SLOTS) && (page->key[slot] != ASL_MSG_SLOT_FREE); slot++);
813			if (slot < ASL_MSG_PAGE_SLOTS) break;
814		}
815	}
816
817	if (page == NULL)
818	{
819		/* allocate a new page and attach it */
820		page = _asl_msg_make_page();
821		if (page == NULL)
822		{
823			if (extkey != NULL) free(extkey);
824			if (extval != NULL) free(extval);
825			return -1;
826		}
827
828		last->next = page;
829		slot = 0;
830	}
831
832	/* copy key or external key pointer into page data */
833	if (kx != 0)
834	{
835		page->key[slot] = kx;
836	}
837	else if (extkey == NULL)
838	{
839		page->key[slot] = page->data_size;
840		memcpy(page->data + page->data_size, key, keylen);
841	}
842	else
843	{
844		page->key[slot] = page->data_size | ASL_MSG_KV_EXTERN;
845		memcpy(page->data + page->data_size, &extkey, keylen);
846		page->mem_size += klen;
847	}
848
849	page->data_size += keylen;
850
851	/* copy val or external val pointer into page data */
852	page->val[slot] = ASL_MSG_SLOT_FREE;
853
854	if (val != NULL)
855	{
856		if (extval == NULL)
857		{
858			page->val[slot] = page->data_size;
859			memcpy(page->data + page->data_size, val, vallen);
860		}
861		else
862		{
863			page->val[slot] = page->data_size | ASL_MSG_KV_EXTERN;
864			memcpy(page->data + page->data_size, &extval, vallen);
865			page->mem_size += vlen;
866		}
867
868		page->data_size += vallen;
869	}
870
871	/* set op */
872	page->op[slot] = (uint16_t)op;
873
874	/* update page count */
875	page->count++;
876
877	return 0;
878}
879
880/*
881 * Most of the code in asl_msg_set_key_val_op is concerned with trying to re-use
882 * space in an asl_msg_t page when given a new value for an existing key.
883 * If the key is new, we just call _asl_msg_new_key_val_op.
884 *
885 * Note that queries can have duplicate keys, so for ASL_TYPE_QUERY we just
886 * call through to _asl_msg_new_key_val_op.
887 */
888static int
889_asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
890{
891	uint32_t i, slot, newexternal;
892	asl_msg_t *page;
893	uint32_t intvallen, extvallen, newvallen;
894	char *intval, *extval, *newval;
895
896	if (msg == NULL) return -1;
897	if (key == NULL) return -1;
898
899	slot = IndexNull;
900	page = NULL;
901
902	if ((msg->asl_type == ASL_TYPE_QUERY) || (IndexNull == _asl_msg_index(msg, key, &slot, &page)))
903	{
904		/* add key */
905		return _asl_msg_new_key_val_op(msg, key, val, op);
906	}
907
908	intval = NULL;
909	intvallen = 0;
910
911	extval = NULL;
912	extvallen = 0;
913
914	if (page->val[slot] != ASL_MSG_SLOT_FREE)
915	{
916		if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
917		{
918			i = page->val[slot] & ASL_MSG_OFFSET_MASK;
919			memcpy(&extval, page->data + i, sizeof(char *));
920			extvallen = sizeof(char *);
921		}
922		else
923		{
924			intval = page->data + page->val[slot];
925			intvallen = strlen(intval) + 1;
926		}
927	}
928
929	/* replace val and op for existing entry */
930
931	/* easy case  - remove val */
932	if (val == NULL)
933	{
934		if (extval != NULL)
935		{
936			page->mem_size -= (strlen(extval) + 1);
937			free(extval);
938		}
939
940		page->val[slot] = ASL_MSG_SLOT_FREE;
941		if (op != IndexNull) page->op[slot] = (uint16_t)op;
942		return 0;
943	}
944
945	/* trivial case - internal val doesn't change */
946	if ((intval != NULL) && (streq(val, intval)))
947	{
948		if (op != IndexNull) page->op[slot] = (uint16_t)op;
949		return 0;
950	}
951
952	/* trivial case - external val doesn't change */
953	if ((extval != NULL) && (streq(val, extval)))
954	{
955		if (op != IndexNull) page->op[slot] = (uint16_t)op;
956		return 0;
957	}
958
959	/*
960	 * special case: we generally don't compress out holes in the data
961	 * space, but if this is the last string in the currently used data space
962	 * we can just back up the data_size and reset page->val[slot]
963	 */
964	i = page->val[slot] & ASL_MSG_OFFSET_MASK;
965	if ((intval != NULL) && ((i + intvallen) == page->data_size))
966	{
967		page->val[slot] = ASL_MSG_SLOT_FREE;
968		page->data_size -= intvallen;
969		intval = NULL;
970		intvallen = 0;
971	}
972	else if ((extval != NULL) && ((i + extvallen) == page->data_size))
973	{
974		page->val[slot] = ASL_MSG_SLOT_FREE;
975		page->data_size -= extvallen;
976		page->mem_size -= (strlen(extval) + 1);
977		free(extval);
978		extval = NULL;
979		extvallen = 0;
980	}
981
982	/* val changes - see if it needs to be external */
983	newvallen = strlen(val) + 1;
984	newexternal = 0;
985
986	if (newvallen > ASL_MSG_PAGE_DATA_SIZE)
987	{
988		newexternal = 1;
989		newvallen = sizeof(char *);
990	}
991
992	/* check if there is room to change val in place */
993	if (((extval != NULL) && (newvallen <= extvallen)) || ((extval == NULL) && (newvallen <= intvallen)))
994	{
995		if (extval != NULL)
996		{
997			page->mem_size -= (strlen(extval) + 1);
998			free(extval);
999		}
1000
1001		extval = NULL;
1002
1003		/* we can re-use the space of the old value */
1004		i = page->val[slot] & ASL_MSG_OFFSET_MASK;
1005
1006		if (newexternal == 1)
1007		{
1008			/* create an external val and copy in the new pointer */
1009			newval = strdup(val);
1010			if (newval == NULL) return -1;
1011
1012			page->mem_size += (strlen(newval) + 1);
1013			page->val[slot] = i | ASL_MSG_KV_EXTERN;
1014			memcpy(page->data + i, &newval, sizeof(char *));
1015		}
1016		else
1017		{
1018			/* new internal value */
1019			page->val[slot] = i;
1020			memcpy(page->data + i, val, newvallen);
1021		}
1022
1023		if (op != IndexNull) page->op[slot] = (uint16_t)op;
1024		return 0;
1025	}
1026
1027	/* we're done with the old value if it is external - free it now */
1028	if (extval != NULL)
1029	{
1030		page->mem_size -= (strlen(extval) + 1);
1031		free(extval);
1032	}
1033
1034	extval = NULL;
1035
1036	if (newvallen <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
1037	{
1038		/* can't re-use the old space, but there's room on the page */
1039		i = page->data_size;
1040		page->data_size += newvallen;
1041
1042		if (newexternal == 1)
1043		{
1044			/* create an external val and copy in the new pointer */
1045			newval = strdup(val);
1046			if (newval == NULL) return -1;
1047
1048			page->mem_size += (strlen(newval) + 1);
1049			page->val[slot] = i | ASL_MSG_KV_EXTERN;
1050			memcpy(page->data + i, &newval, sizeof(char *));
1051		}
1052		else
1053		{
1054			/* new internal value */
1055			page->val[slot] = i;
1056			memcpy(page->data + i, val, newvallen);
1057		}
1058
1059		if (op != IndexNull) page->op[slot] = (uint16_t)op;
1060		return 0;
1061
1062	}
1063
1064	/* no room on this page - free up existing entry and treat this as a new entry */
1065	if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
1066	{
1067		memcpy(&extval, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
1068		page->mem_size -= (strlen(extval) + 1);
1069		free(extval);
1070	}
1071
1072	page->key[slot] = ASL_MSG_SLOT_FREE;
1073	page->val[slot] = ASL_MSG_SLOT_FREE;
1074	page->op[slot] = 0;
1075
1076	return _asl_msg_new_key_val_op(msg, key, val, op);
1077}
1078
1079int
1080asl_msg_set_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
1081{
1082	char *special, buf[512];
1083	uint32_t i, len;
1084	int status;
1085
1086	/* Special case handling */
1087	special = NULL;
1088
1089	/* if query modifier is set but op is zero, default to equality test */
1090	if ((op != 0) && ((op & ASL_QUERY_OP_TRUE) == 0)) op |= ASL_QUERY_OP_EQUAL;
1091
1092	/* convert "Level" values to "0" through "7" */
1093	if (streq(key, ASL_KEY_LEVEL))
1094	{
1095		if (val == NULL) val = "7";
1096		else if ((val[0] >= '0') && (val[0] <= '7') && (val[1] == '\0')) /* do nothing */;
1097		else if (strcaseeq(val, ASL_STRING_EMERG)) val = "0";
1098		else if (strcaseeq(val, ASL_STRING_ALERT)) val = "1";
1099		else if (strcaseeq(val, ASL_STRING_CRIT)) val = "2";
1100		else if (strcaseeq(val, ASL_STRING_ERR)) val = "3";
1101		else if (strcaseeq(val, ASL_STRING_WARNING)) val = "4";
1102		else if (strcaseeq(val, ASL_STRING_NOTICE)) val = "5";
1103		else if (strcaseeq(val, ASL_STRING_INFO)) val = "6";
1104		else if (strcaseeq(val, ASL_STRING_DEBUG)) val = "7";
1105		else val = "7";
1106	}
1107
1108	/* strip trailing newlines from "Message" values */
1109	if ((streq(key, ASL_KEY_MSG)) && (val != NULL))
1110	{
1111		len = strlen(val);
1112		i = len;
1113		while ((i > 0) && (val[i - 1] == '\n')) i--;
1114		if (i == 0) val = NULL;
1115		else if (i < len)
1116		{
1117			/* use buf if it is big enough, else malloc a temporary buffer */
1118			if (i < sizeof(buf))
1119			{
1120				memcpy(buf, val, i);
1121				buf[i] = 0;
1122				val = (const char *)buf;
1123			}
1124			else
1125			{
1126				special = malloc(i + 1);
1127				if (special == NULL) return -1;
1128				memcpy(special, val, i);
1129				special[i] = 0;
1130				val = (const char *)special;
1131			}
1132		}
1133	}
1134
1135	status = _asl_msg_set_kvo(msg, key, val, op);
1136
1137	if (special != NULL) free(special);
1138	return status;
1139}
1140
1141int
1142asl_msg_set_key_val(asl_msg_t *msg, const char *key, const char *val)
1143{
1144	return asl_msg_set_key_val_op(msg, key, val, 0);
1145}
1146
1147static void
1148_asl_msg_unset_page_slot(asl_msg_t *page, uint32_t slot)
1149{
1150	char *ext;
1151
1152	if (page == NULL) return;
1153	if (slot >= ASL_MSG_PAGE_SLOTS) return;
1154	if (page->key[slot] == ASL_MSG_SLOT_FREE) return;
1155
1156	if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
1157	{
1158		memcpy(&ext, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
1159		page->mem_size -= (strlen(ext) + 1);
1160		free(ext);
1161	}
1162
1163	if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
1164	{
1165		memcpy(&ext, page->data + (page->val[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
1166		page->mem_size -= (strlen(ext) + 1);
1167		free(ext);
1168	}
1169
1170	page->key[slot] = ASL_MSG_SLOT_FREE;
1171	page->val[slot] = ASL_MSG_SLOT_FREE;
1172	page->op[slot] = 0;
1173
1174	page->count--;
1175}
1176
1177/*
1178 * asl_msg_unset
1179 * Frees external key and val strings, but does not try to reclaim data space.
1180 */
1181void
1182asl_msg_unset(asl_msg_t *msg, const char *key)
1183{
1184	uint32_t i, slot;
1185	asl_msg_t *page;
1186
1187	if (msg == NULL) return;
1188	if (key == NULL) return;
1189
1190	slot = IndexNull;
1191	page = NULL;
1192
1193	i = _asl_msg_index(msg, key, &slot, &page);
1194	if (i == IndexNull) return;
1195
1196	_asl_msg_unset_page_slot(page, slot);
1197}
1198
1199void
1200asl_msg_unset_index(asl_msg_t *msg, uint32_t n)
1201{
1202	uint32_t slot = IndexNull;
1203	asl_msg_t *page = NULL;
1204
1205	if (msg == NULL) return;
1206
1207	if  (0 != _asl_msg_resolve_index(msg, n, &page, &slot)) return;
1208	_asl_msg_unset_page_slot(page, slot);
1209}
1210
1211#pragma mark -
1212#pragma mark copy and merge
1213
1214/*
1215 * Merge a key / val into a message (only ASL_TYPE_MSG).
1216 * Adds the key / val if the key is not found.
1217 * Does not replace the value if the key is found.
1218 */
1219static void
1220_asl_msg_merge_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint16_t op)
1221{
1222	uint32_t i, slot;
1223	asl_msg_t *page;
1224
1225	if (msg == NULL) return;
1226	if (key == NULL) return;
1227
1228	slot = IndexNull;
1229	page = NULL;
1230
1231	i = _asl_msg_index(msg, key, &slot, &page);
1232	if (i != IndexNull) return;
1233
1234	asl_msg_set_key_val_op(msg, key, val, op);
1235}
1236
1237/*
1238 * Merge msg into target (does not replace existing keys).
1239 * Creates a new asl_msg_t if target is NULL.
1240 * Returns target.
1241 */
1242asl_msg_t *
1243asl_msg_merge(asl_msg_t *target, asl_msg_t *msg)
1244{
1245	uint32_t x, type, isnew = 0;
1246	uint16_t op;
1247	const char *key, *val;
1248
1249	if (msg == NULL) return target;
1250
1251	type = asl_get_type((asl_object_t)msg);
1252
1253	if (target == NULL)
1254	{
1255		isnew = 1;
1256		target = asl_msg_new(type);
1257	}
1258
1259	for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x =asl_msg_fetch(msg, x, &key, &val, &op))
1260	{
1261		if (type == ASL_TYPE_MSG) op = 0;
1262		if (isnew == 1) asl_msg_set_key_val_op(target, key, val, op);
1263		else _asl_msg_merge_key_val_op(target, key, val, op);
1264	}
1265
1266	return target;
1267}
1268
1269/*
1270 * replace key/value pairs from msg in target
1271 * Creates a new asl_msg_t if target is NULL.
1272 * Returns target.
1273 */
1274static asl_msg_t *
1275asl_msg_replace(asl_msg_t *target, asl_msg_t *msg)
1276{
1277	uint32_t x, type;
1278	uint16_t op;
1279	const char *key, *val;
1280
1281	if (msg == NULL) return target;
1282
1283	type = asl_get_type((asl_object_t)msg);
1284
1285	if (target == NULL) target = asl_msg_new(type);
1286
1287	for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x =asl_msg_fetch(msg, x, &key, &val, &op))
1288	{
1289		if (type == ASL_TYPE_MSG) op = 0;
1290		asl_msg_set_key_val_op(target, key, val, op);
1291	}
1292
1293	return target;
1294}
1295
1296
1297/*
1298 * Copy msg.
1299 */
1300asl_msg_t *
1301asl_msg_copy(asl_msg_t *msg)
1302{
1303	return asl_msg_merge(NULL, msg);
1304}
1305
1306#pragma mark -
1307#pragma mark compare and test
1308
1309/*
1310 * Compare messages
1311 */
1312static int
1313_asl_msg_equal(asl_msg_t *a, asl_msg_t *b)
1314{
1315	uint32_t x;
1316	uint16_t oa, ob;
1317	const char *key, *va, *vb;
1318
1319	if (asl_msg_count(a) != asl_msg_count(b)) return 0;
1320
1321	key = NULL;
1322	va = NULL;
1323	oa = 0;
1324
1325	for (x = asl_msg_fetch(a, 0, &key, &va, &oa); x != IndexNull; x = asl_msg_fetch(a, x, &key, &va, &oa))
1326	{
1327		if (asl_msg_lookup(b, key, &vb, &ob) != 0) return 0;
1328		if (strcmp(va, vb)) return 0;
1329		if ((a->asl_type == ASL_TYPE_QUERY) && (oa != ob)) return 0;
1330	}
1331
1332	return 1;
1333}
1334
1335static int
1336_asl_isanumber(const char *s)
1337{
1338	int i;
1339
1340	if (s == NULL) return 0;
1341
1342	i = 0;
1343	if ((s[0] == '-') || (s[0] == '+')) i = 1;
1344
1345	if (s[i] == '\0') return 0;
1346
1347	for (; s[i] != '\0'; i++)
1348	{
1349		if (!isdigit(s[i])) return 0;
1350	}
1351
1352	return 1;
1353}
1354
1355static int
1356_asl_msg_basic_test(uint32_t op, const char *q, const char *m, uint32_t n)
1357{
1358	int cmp;
1359	uint32_t t;
1360	int64_t nq, nm;
1361	int rflags;
1362	regex_t rex;
1363
1364	t = op & ASL_QUERY_OP_TRUE;
1365
1366	/* NULL value from query or message string fails */
1367	if ((q == NULL) || (m == NULL)) return (t & ASL_QUERY_OP_NOT_EQUAL);
1368
1369	if (op & ASL_QUERY_OP_REGEX)
1370	{
1371		/* greater than or less than make no sense in substring search */
1372		if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0;
1373
1374		memset(&rex, 0, sizeof(regex_t));
1375
1376		rflags = REG_EXTENDED | REG_NOSUB;
1377		if (op & ASL_QUERY_OP_CASEFOLD) rflags |= REG_ICASE;
1378
1379		/* A bad reqular expression matches nothing */
1380		if (regcomp(&rex, q, rflags) != 0) return (t & ASL_QUERY_OP_NOT_EQUAL);
1381
1382		cmp = regexec(&rex, m, 0, NULL, 0);
1383		regfree(&rex);
1384
1385		if (t == ASL_QUERY_OP_NOT_EQUAL) return (cmp != 0);
1386		return (cmp == 0);
1387	}
1388
1389	if (op & ASL_QUERY_OP_NUMERIC)
1390	{
1391		if (_asl_isanumber(q) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL);
1392		if (_asl_isanumber(m) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL);
1393
1394		nq = atoll(q);
1395		nm = atoll(m);
1396
1397		switch (t)
1398		{
1399			case ASL_QUERY_OP_EQUAL: return (nm == nq);
1400			case ASL_QUERY_OP_GREATER: return (nm > nq);
1401			case ASL_QUERY_OP_GREATER_EQUAL: return (nm >= nq);
1402			case ASL_QUERY_OP_LESS: return (nm < nq);
1403			case ASL_QUERY_OP_LESS_EQUAL: return (nm <= nq);
1404			case ASL_QUERY_OP_NOT_EQUAL: return (nm != nq);
1405			default: return (t == ASL_QUERY_OP_NOT_EQUAL);
1406		}
1407	}
1408
1409	cmp = 0;
1410	if (op & ASL_QUERY_OP_CASEFOLD)
1411	{
1412		if (n == 0) cmp = strcasecmp(m, q);
1413		else cmp = strncasecmp(m, q, n);
1414	}
1415	else
1416	{
1417		if (n == 0) cmp = strcmp(m, q);
1418		else cmp = strncmp(m, q, n);
1419	}
1420
1421	switch (t)
1422	{
1423		case ASL_QUERY_OP_EQUAL: return (cmp == 0);
1424		case ASL_QUERY_OP_GREATER: return (cmp > 0);
1425		case ASL_QUERY_OP_GREATER_EQUAL: return (cmp >= 0);
1426		case ASL_QUERY_OP_LESS: return (cmp < 0);
1427		case ASL_QUERY_OP_LESS_EQUAL: return (cmp <= 0);
1428		case ASL_QUERY_OP_NOT_EQUAL: return (cmp != 0);
1429	}
1430
1431	return (t == ASL_QUERY_OP_NOT_EQUAL);
1432}
1433
1434static int
1435_asl_msg_test_substring(uint32_t op, const char *q, const char *m)
1436{
1437	uint32_t t, i, d, lm, lq, match, newop;
1438
1439	t = op & ASL_QUERY_OP_TRUE;
1440
1441	lm = 0;
1442	if (m != NULL) lm = strlen(m);
1443
1444	lq = 0;
1445	if (q != NULL) lq = strlen(q);
1446
1447	/* NULL is a substring of any string */
1448	if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1449
1450	/* A long string is defined to be not equal to a short string */
1451	if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1452
1453	/* greater than or less than make no sense in substring search */
1454	if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0;
1455
1456	/*
1457	 * We scan the string doing an equality test.
1458	 * If the input test is equality, we stop as soon as we hit a match.
1459	 * Otherwise we keep scanning the whole message string.
1460	 */
1461	newop = op & 0xff0;
1462	newop |= ASL_QUERY_OP_EQUAL;
1463
1464	match = 0;
1465	d = lm - lq;
1466	for (i = 0; i <= d; i++)
1467	{
1468		if (_asl_msg_basic_test(newop, q, m + i, lq) != 0)
1469		{
1470			if (t & ASL_QUERY_OP_EQUAL) return 1;
1471			match++;
1472		}
1473	}
1474
1475	/* If the input test was for equality, no matches were found */
1476	if (t & ASL_QUERY_OP_EQUAL) return 0;
1477
1478	/* The input test was for not equal.  Return true if no matches were found */
1479	return (match == 0);
1480}
1481
1482static int
1483_asl_msg_test_prefix(uint32_t op, const char *q, const char *m)
1484{
1485	uint32_t lm, lq, t;
1486
1487	t = op & ASL_QUERY_OP_TRUE;
1488
1489	lm = 0;
1490	if (m != NULL) lm = strlen(m);
1491
1492	lq = 0;
1493	if (q != NULL) lq = strlen(q);
1494
1495	/* NULL is a prefix of any string */
1496	if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1497
1498	/* A long string is defined to be not equal to a short string */
1499	if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1500
1501	/* Compare two equal-length strings */
1502	return _asl_msg_basic_test(op, q, m, lq);
1503}
1504
1505static int
1506_asl_msg_test_suffix(uint32_t op, const char *q, const char *m)
1507{
1508	uint32_t lm, lq, d, t;
1509
1510	t = op & ASL_QUERY_OP_TRUE;
1511
1512	lm = 0;
1513	if (m != NULL) lm = strlen(m);
1514
1515	lq = 0;
1516	if (q != NULL) lq = strlen(q);
1517
1518	/* NULL is a suffix of any string */
1519	if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1520
1521	/* A long string is defined to be not equal to a short string */
1522	if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1523
1524	/* Compare two equal-length strings */
1525	d = lm - lq;
1526	return _asl_msg_basic_test(op, q, m + d, lq);
1527}
1528
1529/*
1530 * Splits out prefix, suffix, and substring tests.
1531 * Sends the rest to _asl_msg_basic_test().
1532 */
1533static int
1534_asl_msg_test_expression(uint32_t op, const char *q, const char *m)
1535{
1536	uint32_t t;
1537
1538	t = op & ASL_QUERY_OP_TRUE;
1539	if (t == ASL_QUERY_OP_TRUE) return 1;
1540
1541	if (op & ASL_QUERY_OP_PREFIX)
1542	{
1543		if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_substring(op, q, m);
1544		return _asl_msg_test_prefix(op, q, m);
1545	}
1546	if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_suffix(op, q, m);
1547
1548	return _asl_msg_basic_test(op, q, m, 0);
1549}
1550
1551/*
1552 * Special case for comparing time values.
1553 * If both inputs are time strings, this compares the time
1554 * value in seconds.  Otherwise it just does normal matching.
1555 */
1556static int
1557_asl_msg_test_time_expression(uint32_t op, const char *q, const char *m)
1558{
1559	time_t tq, tm;
1560	uint32_t t;
1561
1562	if ((op & ASL_QUERY_OP_PREFIX) || (op & ASL_QUERY_OP_SUFFIX) || (op & ASL_QUERY_OP_REGEX)) return _asl_msg_test_expression(op, q, m);
1563	if ((q == NULL) || (m == NULL)) return _asl_msg_test_expression(op, q, m);
1564
1565	tq = asl_core_parse_time(q, NULL);
1566	if (tq < 0) return _asl_msg_test_expression(op, q, m);
1567
1568	tm = asl_core_parse_time(m, NULL);
1569	if (tm < 0) return _asl_msg_test_expression(op, q, m);
1570
1571	t = op & ASL_QUERY_OP_TRUE;
1572
1573	switch (t)
1574	{
1575		case ASL_QUERY_OP_FALSE:
1576		{
1577			return 0;
1578		}
1579		case ASL_QUERY_OP_EQUAL:
1580		{
1581			if (tm == tq) return 1;
1582			return 0;
1583		}
1584		case ASL_QUERY_OP_GREATER:
1585		{
1586			if (tm > tq) return 1;
1587			return 0;
1588		}
1589		case ASL_QUERY_OP_GREATER_EQUAL:
1590		{
1591			if (tm >= tq) return 1;
1592			return 0;
1593		}
1594		case ASL_QUERY_OP_LESS:
1595		{
1596			if (tm < tq) return 1;
1597			return 0;
1598		}
1599		case ASL_QUERY_OP_LESS_EQUAL:
1600		{
1601			if (tm <= tq) return 1;
1602			return 0;
1603		}
1604		case ASL_QUERY_OP_NOT_EQUAL:
1605		{
1606			if (tm != tq) return 1;
1607			return 0;
1608		}
1609		case ASL_QUERY_OP_TRUE:
1610		{
1611			return 1;
1612		}
1613	}
1614
1615	/* NOTREACHED */
1616	return 0;
1617}
1618
1619/* test a query against a message */
1620__private_extern__ int
1621_asl_msg_test(asl_msg_t *q, asl_msg_t *m)
1622{
1623	uint32_t i, t, x;
1624	uint16_t op;
1625	int cmp;
1626	const char *kq, *vq, *vm;
1627
1628	/*
1629	 * Check each simple expression (key op val) separately.
1630	 * The query suceeds (returns 1) if all simple expressions
1631	 * succeed (i.e. AND the simple expressions).
1632	 */
1633
1634	kq = NULL;
1635	vq = NULL;
1636	op = 0;
1637
1638	for (x = asl_msg_fetch(q, 0, &kq, &vq, &op); x != IndexNull; x = asl_msg_fetch(q, x, &kq, &vq, &op))
1639	{
1640		/* Find query key in the message */
1641		vm = NULL;
1642		i = asl_msg_lookup(m, kq, &vm, NULL);
1643
1644		/* ASL_QUERY_OP_TRUE tests if key is present in the message */
1645		t = op & ASL_QUERY_OP_TRUE;
1646		if (t == ASL_QUERY_OP_TRUE)
1647		{
1648			if (i != 0) return 0;
1649			continue;
1650		}
1651
1652		/* ASL_QUERY_OP_FALSE tests if the key is NOT present in the message */
1653		if (t == ASL_QUERY_OP_FALSE)
1654		{
1655			if (i == 0) return 0;
1656			continue;
1657		}
1658
1659		if (i != 0)
1660		{
1661			/* the message does NOT have query key - fail unless we are testing not equal */
1662			if (t == ASL_QUERY_OP_NOT_EQUAL) continue;
1663			return 0;
1664		}
1665
1666		cmp = 1;
1667		if (streq(kq, ASL_KEY_TIME))
1668		{
1669			cmp = _asl_msg_test_time_expression(op, vq, vm);
1670		}
1671		else
1672		{
1673			cmp = _asl_msg_test_expression(op, vq, vm);
1674		}
1675
1676		if (cmp == 0) return 0;
1677	}
1678
1679	return 1;
1680}
1681
1682/* returns 1 if a and b match, 0 otherwise */
1683int
1684asl_msg_cmp(asl_msg_t *a, asl_msg_t *b)
1685{
1686
1687	if (a == NULL) return 0;
1688	if (b == NULL) return 0;
1689
1690	if (a->asl_type == b->asl_type) return _asl_msg_equal(a, b);
1691	if (a->asl_type == ASL_TYPE_QUERY) return _asl_msg_test(a, b);
1692	return _asl_msg_test(b, a);
1693}
1694
1695/*
1696 * Test a message against a query list.
1697 * Returns 1 if msg matches any query in the list, 0 otherwise.
1698 * Returns 1 if the query list is NULL or empty.
1699 */
1700int
1701asl_msg_cmp_list(asl_msg_t *msg, asl_msg_list_t *list)
1702{
1703	uint32_t i;
1704
1705	if (msg == NULL) return 0;
1706	if (list == NULL) return 1;
1707	if (list->count == 0) return 1;
1708
1709	for (i = 0; i < list->count; i++)
1710	{
1711		if (_asl_msg_test(list->msg[i], msg)) return 1;
1712	}
1713
1714	return 0;
1715}
1716
1717#pragma mark -
1718#pragma mark string representation
1719
1720static char *
1721_asl_time_string(const char *infmt, const char *str, const char *nano)
1722{
1723	time_t tick, off;
1724	struct tm stm;
1725	char *ltime, *out, *p, *q;
1726	char ltbuf[32], nanobuf[16], fmt[32], zstr[8];
1727	time_t zh, zm;
1728	uint32_t subsec = 0;
1729	bool neg;
1730
1731	out = NULL;
1732	memset(zstr, 0, sizeof(zstr));
1733	zh = 0;
1734	zm = 0;
1735	off = 0;
1736	neg = false;
1737
1738	/*
1739	 * The longest infmt string we understand is "-hh:mm.N" (8 chars), so
1740	 * it is safe to ignore the input if it doesn't fit in the temp buffer.
1741	 * The default is local time zone format.
1742	 */
1743	if (infmt == NULL)
1744	{
1745		snprintf(fmt, sizeof(fmt), "local");
1746	}
1747	else if (strlen(infmt) >= sizeof (fmt))
1748	{
1749		snprintf(fmt, sizeof(fmt), "local");
1750	}
1751	else
1752	{
1753		snprintf(fmt, sizeof(fmt), "%s", infmt);
1754
1755		p = fmt;
1756		q = strchr(fmt, '.');
1757		if (q != NULL)
1758		{
1759			*q = '\0';
1760			q++;
1761			if (q != '\0') subsec = atoi(q);
1762		}
1763	}
1764
1765	nanobuf[0] = '\0';
1766
1767	if (subsec > 0)
1768	{
1769		uint32_t nsec = 0;
1770		if (nano != NULL) nsec = atoi(nano);
1771		snprintf(nanobuf, sizeof(nanobuf), ".%09u", nsec);
1772		if (subsec > 9) subsec = 9;
1773		nanobuf[subsec + 1] = '\0';
1774	}
1775
1776	tick = 0;
1777	if (str != NULL) tick = asl_core_parse_time(str, NULL);
1778
1779	if ((!strcasecmp(fmt, "lcl")) || (!strcasecmp(fmt, "local")))
1780	{
1781		ltime = ctime_r(&tick, ltbuf);
1782		if (ltime == NULL) return NULL;
1783		ltime[19] = '\0';
1784		asprintf(&out, "%s%s", ltime + 4, nanobuf);
1785		return out;
1786	}
1787
1788	if ((!strcasecmp(fmt, "jz")) || (!strcasecmp(fmt, "iso8601")) || (!strcasecmp(fmt, "iso8601e")))
1789	{
1790		char sep = ' ';
1791		if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T';
1792
1793		if (NULL == localtime_r(&tick, &stm)) return NULL;
1794
1795		off = stm.tm_gmtoff;
1796		if ((neg = (off < 0))) off *= -1;
1797		zh = off / 3600;
1798		off %= 3600;
1799		zm = off / 60;
1800
1801		if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1802		else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1803
1804		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);
1805		return out;
1806	}
1807
1808	if (!strcasecmp(fmt, "iso8601b"))
1809	{
1810		if (NULL == localtime_r(&tick, &stm)) return NULL;
1811
1812		off = stm.tm_gmtoff;
1813		if ((neg = (off < 0))) off *= -1;
1814		zh = off / 3600;
1815		off %= 3600;
1816		zm = off / 60;
1817
1818		if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1819		else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1820
1821		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);
1822		return out;
1823	}
1824
1825	if ((!strcasecmp(fmt, "sec")) || (!strcasecmp(fmt, "raw")))
1826	{
1827		asprintf(&out, "%lu%s", tick, nanobuf);
1828		return out;
1829	}
1830
1831	if (!strcasecmp(fmt, "j"))
1832	{
1833		if (NULL == localtime_r(&tick, &stm)) return NULL;
1834		asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1835		return out;
1836	}
1837
1838	if ((!strcasecmp(fmt, "utc")) || (!strcasecmp(fmt, "zulu")) || (!strcasecmp(fmt, "iso8601z")) || (!strcasecmp(fmt, "iso8601ez")))
1839	{
1840		char sep = ' ';
1841		if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T';
1842
1843		if (NULL == gmtime_r(&tick, &stm)) return NULL;
1844		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);
1845		return out;
1846	}
1847
1848	if (!strcasecmp(fmt, "iso8601bz"))
1849	{
1850		if (NULL == gmtime_r(&tick, &stm)) return NULL;
1851		asprintf(&out, "%d%02d%02dT%02d%02d%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1852		return out;
1853	}
1854
1855	if ((fmt[1] == '\0') && (((fmt[0] >= 'a') && (fmt[0] <= 'z')) || ((fmt[0] >= 'A') && (fmt[0] <= 'Z'))))
1856	{
1857		char z = fmt[0];
1858		if (z >= 'a') z -= 32;
1859
1860		if (z == 'Z') off = 0;
1861		else if ((z >= 'A') && (z <= 'I')) off = ((z - 'A') + 1) * SEC_PER_HOUR;
1862		else if ((z >= 'K') && (z <= 'M')) off = (z - 'A') * SEC_PER_HOUR;
1863		else if ((z >= 'N') && (z <= 'Y')) off = ('M' - z) * SEC_PER_HOUR;
1864		else return NULL;
1865	}
1866	else
1867	{
1868		if (fmt[0] == '-') neg = true;
1869		if ((*p == '-') || (*p == '+')) p++;
1870		if ((*p) >= '0' && (*p <= '9'))
1871		{
1872			zh = atoi(p);
1873
1874			p = strchr(p, ':');
1875			if (p != NULL) zm = atoi(p + 1);
1876		}
1877		else
1878		{
1879			return NULL;
1880		}
1881
1882		off = (zh * 3600) + (zm * 60);
1883		if (neg) off *= -1;
1884
1885		if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1886		else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1887	}
1888
1889
1890	tick += off;
1891
1892	memset(&stm, 0, sizeof (struct tm));
1893	if (NULL == gmtime_r(&tick, &stm)) return NULL;
1894
1895	if ((fmt[0] >= 'A') && (fmt[0] <= 'Z'))
1896	{
1897		asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0]);
1898	}
1899	else if ((fmt[0] >= 'a') && (fmt[0] <= 'z'))
1900	{
1901		asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0] - 32);
1902	}
1903	else
1904	{
1905		asprintf(&out, "%d-%02d-%02d %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);
1906	}
1907
1908	return out;
1909}
1910
1911
1912/* called from asl_format_message and _asl_send_message */
1913__private_extern__ asl_string_t *
1914asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, const char *tfmt)
1915{
1916	uint32_t i, x, count;
1917	const char *key, *val, *nano;
1918	asl_string_t *str;
1919
1920	if (msg == NULL) return NULL;
1921
1922	count = asl_msg_count(msg);
1923	if (count == 0) return NULL;
1924
1925	str = asl_string_new(encoding);
1926	if (str == NULL) return NULL;
1927
1928	key = NULL;
1929	val = NULL;
1930	i = 0;
1931
1932	nano = NULL;
1933	asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
1934
1935	for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
1936	{
1937		if (key == NULL) continue;
1938
1939		if (i > 0) asl_string_append_char_no_encoding(str, ' ');
1940
1941		asl_string_append_char_no_encoding(str, '[');
1942		asl_string_append_asl_key(str, key);
1943
1944		if (!strcmp(key, ASL_KEY_TIME))
1945		{
1946			char *vtime = NULL;
1947			asl_string_append_char_no_encoding(str, ' ');
1948
1949			if (val != NULL) vtime = _asl_time_string(tfmt, val, nano);
1950
1951			if (vtime != NULL)
1952			{
1953				asl_string_append_no_encoding(str, vtime);
1954				free(vtime);
1955			}
1956			else
1957			{
1958				asl_string_append_char_no_encoding(str, '0');
1959			}
1960		}
1961		else if (val != NULL)
1962		{
1963			asl_string_append_char_no_encoding(str, ' ');
1964			asl_string_append(str, val);
1965		}
1966
1967		asl_string_append_char_no_encoding(str, ']');
1968
1969		i++;
1970	}
1971
1972	return str;
1973}
1974
1975asl_string_t *
1976asl_string_append_asl_msg(asl_string_t *str, asl_msg_t *msg)
1977{
1978	const char *key, *val;
1979	uint32_t i, x;
1980	uint16_t op;
1981
1982	if (msg == NULL) return str;
1983
1984	if (msg->asl_type == ASL_TYPE_QUERY) asl_string_append(str, "Q ");
1985
1986	i = 0;
1987	for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, &op))
1988	{
1989		if (i != 0)	asl_string_append_char_no_encoding(str, ' ');
1990		i++;
1991
1992		asl_string_append_char_no_encoding(str, '[');
1993
1994		if (msg->asl_type == ASL_TYPE_QUERY)
1995		{
1996			asl_string_append_op(str, op);
1997			asl_string_append_char_no_encoding(str, ' ');
1998		}
1999
2000		asl_string_append_asl_key(str, key);
2001
2002		if (val != NULL)
2003		{
2004			asl_string_append_char_no_encoding(str, ' ');
2005			asl_string_append(str, val);
2006		}
2007
2008		asl_string_append_char_no_encoding(str, ']');
2009	}
2010
2011	return str;
2012}
2013
2014char *
2015asl_msg_to_string(asl_msg_t *msg, uint32_t *len)
2016{
2017	char *out;
2018	asl_string_t *str = asl_string_new(ASL_ENCODE_ASL);
2019	if (str == NULL) return NULL;
2020
2021	str = asl_string_append_asl_msg(str, msg);
2022	*len = asl_string_length(str);
2023	out = asl_string_release_return_bytes(str);
2024	return out;
2025}
2026
2027static uint32_t
2028_asl_msg_op_from_string(char *o)
2029{
2030	uint32_t op, i;
2031
2032	op = ASL_QUERY_OP_NULL;
2033
2034	if (o == NULL) return op;
2035
2036	for (i = 0; o[i] != '\0'; i++)
2037	{
2038		if (o[i] == '.') return ASL_QUERY_OP_NULL;
2039		if (o[i] == 'C') op |= ASL_QUERY_OP_CASEFOLD;
2040		if (o[i] == 'R') op |= ASL_QUERY_OP_REGEX;
2041		if (o[i] == 'N') op |= ASL_QUERY_OP_NUMERIC;
2042		if (o[i] == 'S') op |= ASL_QUERY_OP_SUBSTRING;
2043		if (o[i] == 'A') op |= ASL_QUERY_OP_PREFIX;
2044		if (o[i] == 'Z') op |= ASL_QUERY_OP_SUFFIX;
2045		if (o[i] == '<') op |= ASL_QUERY_OP_LESS;
2046		if (o[i] == '>') op |= ASL_QUERY_OP_GREATER;
2047		if (o[i] == '=') op |= ASL_QUERY_OP_EQUAL;
2048		if (o[i] == '!') op |= ASL_QUERY_OP_NOT_EQUAL;
2049		if (o[i] == 'T') op |= ASL_QUERY_OP_TRUE;
2050	}
2051
2052	return op;
2053}
2054
2055static char *
2056_asl_msg_get_next_word(char **p, uint32_t *tt, uint32_t spacedel)
2057{
2058	char *str, *out, c, oval;
2059	uint32_t i, len, n, outlen;
2060
2061	*tt = TOKEN_NULL;
2062
2063	if (p == NULL) return NULL;
2064	if (*p == NULL) return NULL;
2065	if (**p == '\0') return NULL;
2066
2067	/* skip one space if it's there (word separator) */
2068	if (**p == ' ') (*p)++;
2069
2070	/* skip leading white space */
2071	if (spacedel != 0)
2072	{
2073		while ((**p == ' ') || (**p == '\t')) (*p)++;
2074	}
2075
2076	if (**p == '\0') return NULL;
2077	if (**p == '\n') return NULL;
2078
2079	str = *p;
2080
2081	/* opening [ */
2082	if (**p == '[')
2083	{
2084		*tt = TOKEN_OPEN;
2085
2086		(*p)++;
2087		out = malloc(2);
2088		if (out == NULL) return NULL;
2089
2090		out[0] = '[';
2091		out[1] = '\0';
2092		return out;
2093	}
2094
2095	/* scan for token and calulate it's length (input and decoded output len) */
2096	len = 0;
2097	outlen = 0;
2098
2099	forever
2100	{
2101		c = str[len];
2102
2103		/* stop scanning when we hit a delimiter */
2104		if (((spacedel != 0) && (c == ' ')) || (c == ']') || (c == '\0')) break;
2105
2106		if (c == '\\')
2107		{
2108			len++;
2109			c = str[len];
2110			if ((c == 'a') || (c == 'b') || (c == 't') || (c == 'n') || (c == 'v') || (c == 'f') || (c == 'r') || (c == 's') || (c == '[') || (c == '\\') || (c == ']'))
2111			{
2112			}
2113			else if (c == '^')
2114			{
2115				if (str[++len] == '\0') return NULL;
2116			}
2117			else if (c == 'M')
2118			{
2119				if (str[++len] == '\0') return NULL;
2120				if (str[++len] == '\0') return NULL;
2121			}
2122			else if ((c >= '0') && (c <= '3'))
2123			{
2124				if (str[++len] == '\0') return NULL;
2125				if (str[++len] == '\0') return NULL;
2126			}
2127			else
2128			{
2129				return NULL;
2130			}
2131		}
2132
2133		len++;
2134		outlen++;
2135	}
2136
2137	(*p) += len;
2138
2139	if ((len == 0) && (**p == ']'))
2140	{
2141		*tt = TOKEN_CLOSE;
2142		(*p)++;
2143		out = malloc(2);
2144		if (out == NULL) return NULL;
2145
2146		out[0] = ']';
2147		out[1] = '\0';
2148		return out;
2149	}
2150
2151	*tt = TOKEN_INT;
2152
2153	out = malloc(outlen + 1);
2154	if (out == NULL) return NULL;
2155
2156	n = 0;
2157	for (i = 0; i < len; i++)
2158	{
2159		c = str[i];
2160
2161		if (c == '\\')
2162		{
2163			*tt = TOKEN_WORD;
2164
2165			i++;
2166			c = str[i];
2167			if (c == 'a')
2168			{
2169				out[n++] = '\a';
2170			}
2171			else if (c == 'b')
2172			{
2173				out[n++] = '\b';
2174			}
2175			else if (c == 't')
2176			{
2177				out[n++] = '\t';
2178			}
2179			else if (c == 'n')
2180			{
2181				out[n++] = '\n';
2182			}
2183			else if (c == 'v')
2184			{
2185				out[n++] = '\v';
2186			}
2187			else if (c == 'f')
2188			{
2189				out[n++] = '\f';
2190			}
2191			else if (c == 'r')
2192			{
2193				out[n++] = '\r';
2194			}
2195			else if (c == 's')
2196			{
2197				out[n++] = ' ';
2198			}
2199			else if (c == '[')
2200			{
2201				out[n++] = '[';
2202			}
2203			else if (c == '\\')
2204			{
2205				out[n++] = '\\';
2206			}
2207			else if (c == ']')
2208			{
2209				out[n++] = ']';
2210			}
2211			else if (c == '^')
2212			{
2213				i++;
2214				if (str[i] == '?') out[n++] = 127;
2215				else out[n++] = str[i] - 64;
2216			}
2217			else if (c == 'M')
2218			{
2219				i++;
2220				c = str[i];
2221				if (c == '^')
2222				{
2223					i++;
2224					if (str[i] == '?') out[n++] = 255;
2225					else out[n++] = str[i] + 64;
2226				}
2227				else if (c == '-')
2228				{
2229					i++;
2230					out[n++] = str[i] + 128;
2231				}
2232				else
2233				{
2234					*tt = TOKEN_NULL;
2235					free(out);
2236					return NULL;
2237				}
2238
2239			}
2240			else if ((c >= '0') && (c <= '3'))
2241			{
2242				oval = (c - '0') * 64;
2243
2244				i++;
2245				c = str[i];
2246				if ((c < '0') || (c > '7'))
2247				{
2248					*tt = TOKEN_NULL;
2249					free(out);
2250					return NULL;
2251				}
2252
2253				oval += ((c - '0') * 8);
2254
2255				i++;
2256				c = str[i];
2257				if ((c < '0') || (c > '7'))
2258				{
2259					*tt = TOKEN_NULL;
2260					free(out);
2261					return NULL;
2262				}
2263
2264				oval += (c - '0');
2265
2266				out[n++] = oval;
2267			}
2268			else
2269			{
2270				*tt = TOKEN_NULL;
2271				free(out);
2272				return NULL;
2273			}
2274		}
2275		else
2276		{
2277
2278			if ((c < '0') || (c > '9')) *tt = TOKEN_WORD;
2279			out[n++] = c;
2280		}
2281	}
2282
2283	out[n] = '\0';
2284
2285	return out;
2286}
2287
2288asl_msg_t *
2289asl_msg_from_string(const char *buf)
2290{
2291	uint32_t tt, type, op;
2292	char *k, *v, *o, *p;
2293	asl_msg_t *out;
2294
2295	if (buf == NULL) return NULL;
2296
2297	type = ASL_TYPE_MSG;
2298	p = (char *)buf;
2299
2300	k = _asl_msg_get_next_word(&p, &tt, 1);
2301	if (k == NULL) return NULL;
2302
2303	if (streq(k, "Q"))
2304	{
2305		type = ASL_TYPE_QUERY;
2306		free(k);
2307
2308		k = _asl_msg_get_next_word(&p, &tt, 1);
2309	}
2310	else if (tt == TOKEN_INT)
2311	{
2312		/* Leading integer is a string length - skip it */
2313		free(k);
2314		k = _asl_msg_get_next_word(&p, &tt, 1);
2315		if (k == NULL) return NULL;
2316	}
2317
2318	out = asl_msg_new(ASL_TYPE_MSG);
2319	if (out == NULL) return NULL;
2320
2321	out->asl_type = type;
2322
2323	/* OPEN WORD [WORD [WORD]] CLOSE */
2324	while (k != NULL)
2325	{
2326		op = ASL_QUERY_OP_NULL;
2327
2328		if (tt != TOKEN_OPEN)
2329		{
2330			asl_msg_release(out);
2331			return NULL;
2332		}
2333
2334		free(k);
2335
2336		/* get op for query type */
2337		if (type == ASL_TYPE_QUERY)
2338		{
2339			o = _asl_msg_get_next_word(&p, &tt, 1);
2340			if ((o == NULL) || (tt != TOKEN_WORD))
2341			{
2342				if (o != NULL) free(o);
2343				asl_msg_release(out);
2344				return NULL;
2345			}
2346
2347			op = _asl_msg_op_from_string(o);
2348			free(o);
2349		}
2350
2351		k = _asl_msg_get_next_word(&p, &tt, 1);
2352		if (tt == TOKEN_INT) tt = TOKEN_WORD;
2353		if ((k == NULL) || (tt != TOKEN_WORD))
2354		{
2355			if (k != NULL) free(k);
2356			asl_msg_release(out);
2357			return NULL;
2358		}
2359
2360		v = _asl_msg_get_next_word(&p, &tt, 0);
2361		if (tt == TOKEN_INT) tt = TOKEN_WORD;
2362		if (v == NULL)
2363		{
2364			asl_msg_set_key_val_op(out, k, NULL, op);
2365			free(k);
2366			break;
2367		}
2368
2369		if (tt == TOKEN_CLOSE)
2370		{
2371			asl_msg_set_key_val_op(out, k, NULL, op);
2372		}
2373		else if (tt == TOKEN_WORD)
2374		{
2375			asl_msg_set_key_val_op(out, k, v, op);
2376		}
2377		else
2378		{
2379			if (k != NULL) free(k);
2380			if (v != NULL) free(v);
2381			asl_msg_release(out);
2382			return NULL;
2383		}
2384
2385		if (k != NULL) free(k);
2386		if (v != NULL) free(v);
2387
2388		if (tt != TOKEN_CLOSE)
2389		{
2390			k = _asl_msg_get_next_word(&p, &tt, 1);
2391			if (k == NULL) break;
2392
2393			if (tt != TOKEN_CLOSE)
2394			{
2395				asl_msg_release(out);
2396				return NULL;
2397			}
2398
2399			free(k);
2400		}
2401
2402		k = _asl_msg_get_next_word(&p, &tt, 1);
2403		if (k == NULL) break;
2404	}
2405
2406	return out;
2407}
2408
2409static const char *
2410_asl_level_string(int level)
2411{
2412	if (level == ASL_LEVEL_EMERG) return ASL_STRING_EMERG;
2413	if (level == ASL_LEVEL_ALERT) return ASL_STRING_ALERT;
2414	if (level == ASL_LEVEL_CRIT) return ASL_STRING_CRIT;
2415	if (level == ASL_LEVEL_ERR) return ASL_STRING_ERR;
2416	if (level == ASL_LEVEL_WARNING) return ASL_STRING_WARNING;
2417	if (level == ASL_LEVEL_NOTICE) return ASL_STRING_NOTICE;
2418	if (level == ASL_LEVEL_INFO) return ASL_STRING_INFO;
2419	if (level == ASL_LEVEL_DEBUG) return ASL_STRING_DEBUG;
2420	return "unknown";
2421}
2422
2423static const char *
2424_asl_level_char(int level)
2425{
2426	if (level == ASL_LEVEL_EMERG) return "P";
2427	if (level == ASL_LEVEL_ALERT) return "A";
2428	if (level == ASL_LEVEL_CRIT) return "C";
2429	if (level == ASL_LEVEL_ERR) return "E";
2430	if (level == ASL_LEVEL_WARNING) return "W";
2431	if (level == ASL_LEVEL_NOTICE) return "N";
2432	if (level == ASL_LEVEL_INFO) return "I";
2433	if (level == ASL_LEVEL_DEBUG) return "D";
2434	return "?";
2435}
2436
2437/*
2438 * Find the value for a key in a message and append a formatted value to str.
2439 * kf may be a simple (no embedded white space) key, or one of (key) or ((key)(format)).
2440 * WARNING: modifies kf!
2441 */
2442static asl_string_t *
2443_asl_string_append_value_for_key_format(asl_string_t *str, asl_msg_t *msg, char *kf, const char *tfmt)
2444{
2445	uint32_t i, get_fmt;
2446	int status;
2447	char *key, *fmt;
2448	const char *mval, *nano;
2449
2450	if (str == NULL) return NULL;
2451	if (msg == NULL) return str;
2452	if (kf == NULL) return str;
2453
2454	key = NULL;
2455	fmt = NULL;
2456	get_fmt = 0;
2457
2458	for (i = 0; kf[i] != '\0'; i++)
2459	{
2460		if (kf[i] == ')')
2461		{
2462			kf[i] = '\0';
2463			get_fmt = 1;
2464		}
2465		else if (kf[i] != '(')
2466		{
2467			if (key == NULL) key = kf + i;
2468			else if ((get_fmt == 1) && (fmt == NULL)) fmt = kf + i;
2469		}
2470	}
2471
2472	if (key == NULL) return str;
2473
2474	nano = NULL;
2475	asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
2476
2477	status = asl_msg_lookup(msg, key, &mval, NULL);
2478	if ((status != 0) || (mval == NULL)) return str;
2479
2480	if (!strcmp(key, ASL_KEY_TIME))
2481	{
2482		char *fval = NULL;
2483
2484		/* format in $((Time)(fmt)) overrides tfmt */
2485		if (fmt == NULL)
2486		{
2487			fval = _asl_time_string(tfmt, mval, nano);
2488		}
2489		else
2490		{
2491			fval = _asl_time_string(fmt, mval, nano);
2492		}
2493
2494		if (fval != NULL)
2495		{
2496			asl_string_append_no_encoding(str, fval);
2497			free(fval);
2498		}
2499		else
2500		{
2501			asl_string_append_char_no_encoding(str, '0');
2502		}
2503
2504		return str;
2505	}
2506
2507	/* Level: num str */
2508	if (!strcmp(key, ASL_KEY_LEVEL))
2509	{
2510		if (fmt == NULL)
2511		{
2512			asl_string_append_no_encoding(str, mval);
2513		}
2514		else if (!strcmp(fmt, "str"))
2515		{
2516			mval = _asl_level_string(atoi(mval));
2517			asl_string_append_no_encoding(str, mval);
2518		}
2519		else if (!strcmp(fmt, "char"))
2520		{
2521			mval = _asl_level_char(atoi(mval));
2522			asl_string_append_no_encoding(str, mval);
2523		}
2524		else
2525		{
2526			asl_string_append_no_encoding(str, mval);
2527		}
2528
2529		return str;
2530	}
2531
2532	return asl_string_append(str, mval);
2533}
2534
2535/*
2536 * format a message for printing
2537 * out parameter len returns string length including trailing NUL
2538 */
2539char *
2540asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t text_encoding, uint32_t *len)
2541{
2542	char *out, *vtime, *k, c, skey[512], tfmt_ext[16];
2543	const char *vhost, *vpid, *vsender, *vmessage, *vlevel, *vrefproc, *vrefpid, *v, *key, *val, *nano;
2544	int i, j, l, mf, paren, oval, level;
2545	uint32_t x, cursor;
2546	asl_string_t *str;
2547	uint8_t *b64;
2548
2549	out = NULL;
2550	*len = 0;
2551
2552	if (msg == NULL) return NULL;
2553
2554	mf = MFMT_RAW;
2555
2556	if (mfmt == NULL) mf = MFMT_RAW;
2557	else if (!strcmp(mfmt, ASL_MSG_FMT_RAW)) mf = MFMT_RAW;
2558	else if (!strcmp(mfmt, ASL_MSG_FMT_STD)) mf = MFMT_STD;
2559	else if (!strcmp(mfmt, ASL_MSG_FMT_BSD)) mf = MFMT_BSD;
2560	else if (!strcmp(mfmt, ASL_MSG_FMT_XML)) mf = MFMT_XML;
2561	else if (!strcmp(mfmt, ASL_MSG_FMT_MSG)) mf = MFMT_MSG;
2562	else if ((!strncmp(mfmt, ASL_MSG_FMT_RAW, 3)) && (mfmt[3] == '.'))
2563	{
2564		mf = MFMT_RAW;
2565		if ((tfmt == NULL) && (mfmt[4] != '\0'))
2566		{
2567			snprintf(tfmt_ext, sizeof(tfmt_ext), "sec.%s", mfmt + 4);
2568			tfmt = (const char *)tfmt_ext;
2569		}
2570	}
2571	else if ((!strncmp(mfmt, ASL_MSG_FMT_STD, 3)) && (mfmt[3] == '.'))
2572	{
2573		mf = MFMT_STD;
2574		if ((tfmt == NULL) && (mfmt[4] != '\0'))
2575		{
2576			snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4);
2577			tfmt = (const char *)tfmt_ext;
2578		}
2579	}
2580	else if ((!strncmp(mfmt, ASL_MSG_FMT_BSD, 3)) && (mfmt[3] == '.'))
2581	{
2582		mf = MFMT_BSD;
2583		if ((tfmt == NULL) && (mfmt[4] != '\0'))
2584		{
2585			snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4);
2586			tfmt = (const char *)tfmt_ext;
2587		}
2588	}
2589	else mf = MFMT_STR;
2590
2591	nano = NULL;
2592	asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
2593
2594	if (mf == MFMT_RAW)
2595	{
2596		str = asl_msg_to_string_raw(text_encoding, msg, tfmt);
2597		asl_string_append_char_no_encoding(str, '\n');
2598
2599		*len = asl_string_length(str);
2600		out = asl_string_release_return_bytes(str);
2601		return out;
2602	}
2603
2604	if (mf == MFMT_MSG)
2605	{
2606		vmessage = NULL;
2607
2608		if (asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL) != 0) return NULL;
2609
2610		str = asl_string_new(text_encoding);
2611		if (str == NULL) return NULL;
2612
2613		asl_string_append(str, vmessage);
2614		asl_string_append_char_no_encoding(str, '\n');
2615
2616		*len = asl_string_length(str);
2617		out = asl_string_release_return_bytes(str);
2618		return out;
2619	}
2620
2621	if ((mf == MFMT_STD) || (mf == MFMT_BSD))
2622	{
2623		/* COMMON:  Mth dd hh:mm:ss host sender[pid] (refproc[refpid])*/
2624		/* BSD:  <COMMON>: message */
2625		/* STD:  <COMMON> <Level>: message */
2626
2627		v = NULL;
2628		vtime = NULL;
2629		vhost = NULL;
2630		vsender = NULL;
2631		vpid = NULL;
2632		vmessage = NULL;
2633		vlevel = NULL;
2634		vrefproc = NULL;
2635		vrefpid = NULL;
2636
2637		if (asl_msg_lookup(msg, ASL_KEY_TIME, &v, NULL) == 0)
2638		{
2639			vtime = _asl_time_string(tfmt, v, nano);
2640		}
2641
2642		level = 7;
2643		if (asl_msg_lookup(msg, ASL_KEY_LEVEL, &vlevel, NULL) == 0)
2644		{
2645			if (vlevel != NULL) level = atoi(vlevel);
2646		}
2647
2648		if (asl_msg_lookup(msg, ASL_KEY_HOST, &vhost, NULL) == 0)
2649		{
2650			if (vhost == NULL) vhost = "unknown";
2651		}
2652
2653		if (asl_msg_lookup(msg, ASL_KEY_SENDER, &vsender, NULL) == 0)
2654		{
2655			if (vsender == NULL) vsender = "unknown";
2656		}
2657
2658		asl_msg_lookup(msg, ASL_KEY_PID, &vpid, NULL);
2659		asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL);
2660		asl_msg_lookup(msg, ASL_KEY_REF_PROC, &vrefproc, NULL);
2661		asl_msg_lookup(msg, ASL_KEY_REF_PID, &vrefpid, NULL);
2662
2663		/* COMMON */
2664		str = asl_string_new(text_encoding);
2665		if (str == NULL) return NULL;
2666
2667		if (vtime != NULL)
2668		{
2669			asl_string_append(str, vtime);
2670			free(vtime);
2671		}
2672		else
2673		{
2674			asl_string_append_char_no_encoding(str, '0');
2675		}
2676
2677		asl_string_append_char_no_encoding(str, ' ');
2678		asl_string_append(str, vhost);
2679		asl_string_append_char_no_encoding(str, ' ');
2680		asl_string_append(str, vsender);
2681
2682		if ((vpid != NULL) && (strcmp(vpid, "-1")))
2683		{
2684			asl_string_append_char_no_encoding(str, '[');
2685			asl_string_append(str, vpid);
2686			asl_string_append_char_no_encoding(str, ']');
2687		}
2688
2689		if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_no_encoding(str, " (");
2690
2691		if (vrefproc != NULL) asl_string_append(str, vrefproc);
2692		if (vrefpid != NULL)
2693		{
2694			asl_string_append_char_no_encoding(str, '[');
2695			asl_string_append(str, vrefpid);
2696			asl_string_append_char_no_encoding(str, ']');
2697		}
2698
2699		if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_char_no_encoding(str, ')');
2700
2701		if (mf == MFMT_STD)
2702		{
2703			asl_string_append_no_encoding(str, " <");
2704			asl_string_append(str, _asl_level_string(level));
2705			asl_string_append_char_no_encoding(str, '>');
2706		}
2707
2708		asl_string_append_no_encoding(str, ": ");
2709		if (vmessage != NULL) asl_string_append(str, vmessage);
2710		asl_string_append_char_no_encoding(str, '\n');
2711
2712		*len = asl_string_length(str);
2713		out = asl_string_release_return_bytes(str);
2714		return out;
2715	}
2716
2717	if (mf == MFMT_XML)
2718	{
2719		str = asl_string_new(text_encoding);
2720		if (str == NULL) return NULL;
2721
2722		asl_string_append_char_no_encoding(str, '\t');
2723		asl_string_append_no_encoding(str, "<dict>");
2724		asl_string_append_char_no_encoding(str, '\n');
2725
2726		for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
2727		{
2728			if (asl_is_utf8(key) == 1)
2729			{
2730				asl_string_append_xml_tag(str, "key", key);
2731				if (!strcmp(key, ASL_KEY_TIME))
2732				{
2733					vtime = _asl_time_string(tfmt, val, nano);
2734					if (vtime != NULL)
2735					{
2736						asl_string_append_xml_tag(str, "string", vtime);
2737						free(vtime);
2738					}
2739					else
2740					{
2741						asl_string_append_xml_tag(str, "string", "0");
2742					}
2743				}
2744				else
2745				{
2746					if (asl_is_utf8(val) == 1) asl_string_append_xml_tag(str, "string", val);
2747					else
2748					{
2749						b64 = asl_b64_encode((uint8_t *)val, strlen(val));
2750						asl_string_append_xml_tag(str, "data", (char *)b64);
2751						free(b64);
2752					}
2753				}
2754			}
2755		}
2756
2757		asl_string_append_char_no_encoding(str, '\t');
2758		asl_string_append_no_encoding(str, "</dict>");
2759		asl_string_append_char_no_encoding(str, '\n');
2760
2761		*len = asl_string_length(str);
2762		out = asl_string_release_return_bytes(str);
2763		return out;
2764	}
2765
2766	/*
2767	 * Custom format
2768	 * The format string may contain arbitrary characters.
2769	 * Keys are identified by $Key or $(Key).  The value for
2770	 * that key is substituted.  If there are alterate formats
2771	 * for the value (for example a time may be formatted as
2772	 * raw seconds, in UTC, or a local timezone), then the
2773	 * key may be $((Key)(Format)).  "\$" prints a plain "$".
2774	 */
2775
2776	str = asl_string_new(text_encoding);
2777	if (str == NULL) return NULL;
2778
2779	/*
2780	 * We need enough space to copy any keys found in mfmt.
2781	 * The key obviously can't be longer than strlen(mfmt),
2782	 * in fact, keys must be shorter, since there's at least a '$'
2783	 * in front of the key, so we allocate a buffer with strlen(mfmt).
2784	 * If strlen(mfmt) <= sizeof(skey), we use skey to avoid a malloc.
2785	 */
2786
2787	x = strlen(mfmt);
2788	if (x <= sizeof(skey))
2789	{
2790		k = skey;
2791	}
2792	else
2793	{
2794		k = malloc(x);
2795		if (k == NULL) return NULL;
2796	}
2797
2798	cursor = 0;
2799
2800	for (i = 0; mfmt[i] != '\0'; i++)
2801	{
2802		if (mfmt[i] == '$')
2803		{
2804			paren = 0;
2805
2806			/* scan key, (key) or ((key)(format)) */
2807			for (j = i + 1; mfmt[j] != 0; j++)
2808			{
2809				if (mfmt[j] == '(')
2810				{
2811					paren++;
2812				}
2813				else if (mfmt[j] == ')')
2814				{
2815					if (paren > 0) paren--;
2816					if (paren == 0)
2817					{
2818						j++;
2819						break;
2820					}
2821				}
2822				else if (((mfmt[j] == ' ') || (mfmt[j] == '\t')) && (paren == 0)) break;
2823			}
2824
2825			/* mfmt[i + 1] is the first char of the key or a '('. mfmt[j] is one char past the end. */
2826			l = j - (i + 1);
2827			memcpy(k, mfmt+i+1, l);
2828			k[l] = '\0';
2829			_asl_string_append_value_for_key_format(str, msg, k, tfmt);
2830
2831			i = j - 1;
2832			continue;
2833		}
2834
2835		if (mfmt[i] == '\\')
2836		{
2837			i++;
2838			if (mfmt[i] == '$') asl_string_append_char_no_encoding(str, '$');
2839			else if (mfmt[i] == 'e') asl_string_append_char_no_encoding(str, '\e');
2840			else if (mfmt[i] == 's') asl_string_append_char_no_encoding(str, ' ');
2841			else if (mfmt[i] == 'a') asl_string_append_char_no_encoding(str, '\a');
2842			else if (mfmt[i] == 'b') asl_string_append_char_no_encoding(str, '\b');
2843			else if (mfmt[i] == 'f') asl_string_append_char_no_encoding(str, '\f');
2844			else if (mfmt[i] == 'n') asl_string_append_char_no_encoding(str, '\n');
2845			else if (mfmt[i] == 'r') asl_string_append_char_no_encoding(str, '\r');
2846			else if (mfmt[i] == 't') asl_string_append_char_no_encoding(str, '\t');
2847			else if (mfmt[i] == 'v') asl_string_append_char_no_encoding(str, '\v');
2848			else if (mfmt[i] == '\'') asl_string_append_char_no_encoding(str, '\'');
2849			else if (mfmt[i] == '\\') asl_string_append_char_no_encoding(str, '\\');
2850			else if (isdigit(mfmt[i]))
2851			{
2852				oval = mfmt[i] - '0';
2853				if (isdigit(mfmt[i+1]))
2854				{
2855					i++;
2856					oval = (oval * 8) + (mfmt[i] - '0');
2857					if (isdigit(mfmt[i+1]))
2858					{
2859						i++;
2860						oval = (oval * 8) + (mfmt[i] - '0');
2861					}
2862				}
2863				c = oval;
2864				asl_string_append_char_no_encoding(str, c);
2865			}
2866			continue;
2867		}
2868
2869		if (mfmt[i] == '\0') break;
2870		asl_string_append_char_no_encoding(str, mfmt[i]);
2871	}
2872
2873	if (k != skey) free(k);
2874
2875	asl_string_append_char_no_encoding(str, '\n');
2876
2877	*len = asl_string_length(str);
2878	out = asl_string_release_return_bytes(str);
2879	return out;
2880}
2881
2882#pragma mark -
2883#pragma mark asl_object support
2884
2885static asl_object_private_t *
2886_jump_alloc(uint32_t type)
2887{
2888	return (asl_object_private_t *)asl_msg_new(type);
2889}
2890
2891static void
2892_jump_dealloc(asl_object_private_t *obj)
2893{
2894	asl_msg_t *msg = (asl_msg_t *)obj;
2895	while (msg != NULL)
2896	{
2897		asl_msg_t *next = msg->next;
2898		_asl_msg_free_page(msg);
2899		msg = next;
2900	}
2901}
2902
2903static int
2904_jump_set_key_val_op(asl_object_private_t *obj, const char *key, const char *val, uint16_t op)
2905{
2906	uint32_t op32 = op;
2907	int status = asl_msg_set_key_val_op((asl_msg_t *)obj, key, val, op32);
2908	return (status == ASL_STATUS_OK) ? 0 : -1;
2909}
2910
2911static void
2912_jump_unset_key(asl_object_private_t *obj, const char *key)
2913{
2914	asl_msg_unset((asl_msg_t *)obj, key);
2915}
2916
2917static int
2918_jump_get_val_op_for_key(asl_object_private_t *obj, const char *key, const char **val, uint16_t *op)
2919{
2920	return asl_msg_lookup((asl_msg_t *)obj, key, val, op);
2921}
2922
2923static int
2924_jump_get_key_val_op_at_index(asl_object_private_t *obj, size_t n, const char **key, const char **val, uint16_t *op)
2925{
2926	uint32_t slot = IndexNull;
2927	asl_msg_t *page = NULL;
2928
2929	if  (0 != _asl_msg_resolve_index((asl_msg_t *)obj, n, &page, &slot)) return -1;
2930
2931	if (key != NULL) *key = _asl_msg_slot_key(page, slot);
2932	if (val != NULL) *val = _asl_msg_slot_val(page, slot);
2933	if (op != NULL) *op = page->op[slot];
2934	return 0;
2935}
2936
2937static size_t
2938_jump_count(asl_object_private_t *obj)
2939{
2940	size_t count = asl_msg_count((asl_msg_t *)obj);
2941	return count;
2942}
2943
2944static void
2945_jump_append(asl_object_private_t *obj, asl_object_private_t *newobj)
2946{
2947	int type = asl_get_type((asl_object_t)newobj);
2948	if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
2949
2950	asl_msg_merge((asl_msg_t *)obj, (asl_msg_t *)newobj);
2951}
2952
2953static void
2954_jump_prepend(asl_object_private_t *obj, asl_object_private_t *newobj)
2955{
2956	if (obj == NULL) return;
2957
2958	int type = asl_get_type((asl_object_t)newobj);
2959	if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
2960
2961	asl_msg_replace((asl_msg_t *)obj, (asl_msg_t *)newobj);
2962}
2963
2964static asl_object_private_t *
2965_jump_search(asl_object_private_t *obj, asl_object_private_t *query)
2966{
2967	if (obj == NULL) return NULL;
2968
2969	if (query == NULL)
2970	{
2971		/* NULL matches any message */
2972		asl_msg_list_t *out = asl_msg_list_new();
2973		asl_msg_list_append(out, obj);
2974		return (asl_object_private_t *)out;
2975	}
2976
2977	if ((query->asl_type != ASL_TYPE_MSG) && (query->asl_type != ASL_TYPE_QUERY)) return NULL;
2978
2979	if (asl_msg_cmp((asl_msg_t *)obj, (asl_msg_t *)query) == 1)
2980	{
2981		asl_msg_list_t *out = asl_msg_list_new();
2982		asl_msg_list_append(out, obj);
2983		return (asl_object_private_t *)out;
2984	}
2985
2986	return NULL;
2987}
2988
2989static asl_object_private_t *
2990_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)
2991{
2992	if (obj == NULL) return NULL;
2993
2994	if (qlist == NULL)
2995	{
2996		/* NULL matches any message */
2997		asl_msg_list_t *out = asl_msg_list_new();
2998		asl_msg_list_append(out, obj);
2999		return (asl_object_private_t *)out;
3000	}
3001
3002	if (asl_msg_cmp_list((asl_msg_t *)obj, (asl_msg_list_t *)qlist) == 0) return NULL;
3003
3004	asl_msg_list_t *out = asl_msg_list_new();
3005	asl_msg_list_append(out, obj);
3006	return (asl_object_private_t *)out;
3007}
3008
3009
3010__private_extern__ const asl_jump_table_t *
3011asl_msg_jump_table()
3012{
3013	static const asl_jump_table_t jump =
3014	{
3015		.alloc = &_jump_alloc,
3016		.dealloc = &_jump_dealloc,
3017		.set_key_val_op = &_jump_set_key_val_op,
3018		.unset_key = &_jump_unset_key,
3019		.get_val_op_for_key = &_jump_get_val_op_for_key,
3020		.get_key_val_op_at_index = &_jump_get_key_val_op_at_index,
3021		.count = &_jump_count,
3022		.next = NULL,
3023		.prev = NULL,
3024		.get_object_at_index = NULL,
3025		.set_iteration_index = NULL,
3026		.remove_object_at_index = NULL,
3027		.append = &_jump_append,
3028		.prepend = &_jump_prepend,
3029		.search = &_jump_search,
3030		.match = &_jump_match
3031	};
3032
3033	return &jump;
3034}
3035