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