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