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