2 * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
37 #include <asl_private.h>
39 #include <sys/types.h>
40 #include <libkern/OSAtomic.h>
57 #define SEC_PER_HOUR 3600
59 #define forever for(;;)
61 #define streq(A, B) (strcmp(A, B) == 0)
62 #define streq_len(A, B, C) (strncmp(A, B, C) == 0)
63 #define strneq(A, B) (strcmp(A, B) != 0)
64 #define strcaseeq(A, B) (strcasecmp(A, B) == 0)
65 #define strcaseneq(A, B) (strcasecmp(A, B) != 0)
67 #ifndef ASL_KEY_OPTION
68 #define ASL_KEY_OPTION "ASLOption"
71 #ifndef ASL_QUERY_OP_FALSE
72 #define ASL_QUERY_OP_FALSE 0
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
87 extern time_t asl_parse_time(const char *in
);
90 int asl_is_utf8(const char *str
);
91 uint8_t *asl_b64_encode(const uint8_t *buf
, size_t len
);
93 static const char *ASLStandardKey
[] =
115 static const char *MTStandardKey
[] =
117 "com.apple.message.domain",
118 "com.apple.message.domain_scope",
119 "com.apple.message.result",
120 "com.apple.message.signature",
121 "com.apple.message.signature2",
122 "com.apple.message.signature3",
123 "com.apple.message.success",
124 "com.apple.message.uuid",
125 "com.apple.message.value",
126 "com.apple.message.value2",
127 "com.apple.message.value3",
128 "com.apple.message.value4",
129 "com.apple.message.value5"
133 _asl_msg_std_key(const char *s
, uint32_t len
)
135 if ((len
> 18) && (streq_len(s
, "com.apple.message.", 18)))
137 if (streq(s
+ 18, "domain")) return ASL_MT_KEY_DOMAIN
;
138 else if (streq(s
+ 18, "domain_scope")) return ASL_MT_KEY_SCOPE
;
139 else if (streq(s
+ 18, "result")) return ASL_MT_KEY_RESULT
;
140 else if (streq(s
+ 18, "signature")) return ASL_MT_KEY_SIG
;
141 else if (streq(s
+ 18, "signature2")) return ASL_MT_KEY_SIG2
;
142 else if (streq(s
+ 18, "signature3")) return ASL_MT_KEY_SIG3
;
143 else if (streq(s
+ 18, "success")) return ASL_MT_KEY_SUCCESS
;
144 else if (streq(s
+ 18, "uuid")) return ASL_MT_KEY_UUID
;
145 else if (streq(s
+ 18, "value")) return ASL_MT_KEY_VAL
;
146 else if (streq(s
+ 18, "value2")) return ASL_MT_KEY_VAL2
;
147 else if (streq(s
+ 18, "value3")) return ASL_MT_KEY_VAL3
;
148 else if (streq(s
+ 18, "value4")) return ASL_MT_KEY_VAL4
;
149 else if (streq(s
+ 18, "value5")) return ASL_MT_KEY_VAL5
;
158 if streq(s
, ASL_KEY_PID
) return ASL_STD_KEY_PID
;
159 else if streq(s
, ASL_KEY_UID
) return ASL_STD_KEY_UID
;
160 else if streq(s
, ASL_KEY_GID
) return ASL_STD_KEY_GID
;
164 if streq(s
, ASL_KEY_TIME
) return ASL_STD_KEY_TIME
;
165 else if streq(s
, ASL_KEY_HOST
) return ASL_STD_KEY_HOST
;
169 if streq(s
, ASL_KEY_LEVEL
) return ASL_STD_KEY_LEVEL
;
173 if streq(s
, ASL_KEY_SENDER
) return ASL_STD_KEY_SENDER
;
174 else if streq(s
, ASL_KEY_REF_PID
) return ASL_STD_KEY_REF_PID
;
178 if streq(s
, ASL_KEY_MSG
) return ASL_STD_KEY_MESSAGE
;
179 else if streq(s
, ASL_KEY_SESSION
) return ASL_STD_KEY_SESSION
;
180 else if streq(s
, ASL_KEY_READ_UID
) return ASL_STD_KEY_READ_UID
;
181 else if streq(s
, ASL_KEY_READ_GID
) return ASL_STD_KEY_READ_GID
;
182 else if streq(s
, ASL_KEY_REF_PROC
) return ASL_STD_KEY_REF_PROC
;
186 if streq(s
, ASL_KEY_FACILITY
) return ASL_STD_KEY_FACILITY
;
190 if streq(s
, ASL_KEY_OPTION
) return ASL_STD_KEY_OPTION
;
194 if streq(s
, ASL_KEY_TIME_NSEC
) return ASL_STD_KEY_NANO
;
198 if streq(s
, ASL_KEY_MSG_ID
) return ASL_STD_KEY_MSG_ID
;
202 if streq(s
, ASL_KEY_EXPIRE_TIME
) return ASL_STD_KEY_EXPIRE
;
219 out
= calloc(1, sizeof(asl_msg_t
));
220 if (out
== NULL
) return NULL
;
222 for (i
= 0; i
< ASL_MSG_PAGE_SLOTS
; i
++)
224 out
->key
[i
] = ASL_MSG_SLOT_FREE
;
225 out
->val
[i
] = ASL_MSG_SLOT_FREE
;
232 _asl_msg_slot_key(asl_msg_t
*page
, uint32_t slot
)
237 if (page
== NULL
) return NULL
;
238 if (slot
>= ASL_MSG_PAGE_SLOTS
) return NULL
;
240 if (page
->key
[slot
] == ASL_MSG_SLOT_FREE
) return NULL
;
242 switch (page
->key
[slot
] & ASL_MSG_KV_MASK
)
244 case ASL_MSG_KV_INLINE
:
246 return page
->data
+ page
->key
[slot
];
248 case ASL_MSG_KV_DICT
:
250 if ((page
->key
[slot
] > ASL_STD_KEY_BASE
) && (page
->key
[slot
] <= ASL_STD_KEY_LAST
))
252 x
= page
->key
[slot
] - ASL_STD_KEY_BASE
- 1;
253 return ASLStandardKey
[x
];
255 else if ((page
->key
[slot
] > ASL_MT_KEY_BASE
) && (page
->key
[slot
] <= ASL_MT_KEY_LAST
))
257 x
= page
->key
[slot
] - ASL_MT_KEY_BASE
- 1;
258 return MTStandardKey
[x
];
263 case ASL_MSG_KV_EXTERN
:
265 x
= page
->key
[slot
] & ASL_MSG_OFFSET_MASK
;
266 memcpy(&out
, page
->data
+ x
, sizeof(char *));
275 _asl_msg_slot_val(asl_msg_t
*page
, uint32_t slot
)
280 if (page
== NULL
) return NULL
;
281 if (slot
>= ASL_MSG_PAGE_SLOTS
) return NULL
;
283 if (page
->val
[slot
] == ASL_MSG_SLOT_FREE
) return NULL
;
285 type
= page
->val
[slot
] & ASL_MSG_KV_MASK
;
287 if (type
== ASL_MSG_KV_INLINE
)
289 return page
->data
+ page
->val
[slot
];
291 else if (type
== ASL_MSG_KV_EXTERN
)
293 x
= page
->val
[slot
] & ASL_MSG_OFFSET_MASK
;
294 memcpy(&out
, page
->data
+ x
, sizeof(char *));
302 * asl_new: create a new log message.
305 asl_msg_new(uint32_t type
)
309 out
= _asl_msg_make_page();
310 if (out
== NULL
) return NULL
;
319 asl_msg_retain(asl_msg_t
*msg
)
323 if (msg
== NULL
) return NULL
;
325 new = OSAtomicIncrement32Barrier(&msg
->refcount
);
332 _asl_msg_free(asl_msg_t
*page
)
337 if (page
== NULL
) return;
339 for (i
= 0; i
< ASL_MSG_PAGE_SLOTS
; i
++)
341 if ((page
->key
[i
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_EXTERN
)
343 memcpy(&p
, page
->data
+ (page
->key
[i
] & ASL_MSG_OFFSET_MASK
), sizeof(char *));
347 if ((page
->val
[i
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_EXTERN
)
349 memcpy(&p
, page
->data
+ (page
->val
[i
] & ASL_MSG_OFFSET_MASK
), sizeof(char *));
358 asl_msg_release(asl_msg_t
*msg
)
363 if (msg
== NULL
) return;
365 new = OSAtomicDecrement32Barrier(&msg
->refcount
);
379 _asl_msg_index(asl_msg_t
*msg
, const char *key
, uint32_t *oslot
, asl_msg_t
**opage
)
381 uint32_t i
, len
, slot
;
386 if (msg
== NULL
) return IndexNull
;
387 if (key
== NULL
) return IndexNull
;
391 if (oslot
!= NULL
) *oslot
= slot
;
394 if (opage
!= NULL
) *opage
= page
;
397 kx
= _asl_msg_std_key(key
, len
);
401 if (page
->key
[slot
] != ASL_MSG_SLOT_FREE
)
405 if (page
->key
[slot
] == kx
) return i
;
407 else if ((page
->key
[slot
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_DICT
)
409 /* page->key[slot] is a dictionary key, but key is not (kx == 0) so skip this slot */
411 else if ((page
->key
[slot
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_EXTERN
)
413 memcpy(&kp
, page
->data
+ (page
->key
[slot
] & ASL_MSG_OFFSET_MASK
), sizeof(char *));
414 if (streq(key
, kp
)) return i
;
418 kp
= page
->data
+ page
->key
[slot
];
419 if (streq(key
, kp
)) return i
;
425 if (oslot
!= NULL
) *oslot
= slot
;
427 if (slot
>= ASL_MSG_PAGE_SLOTS
)
429 if (page
->next
== NULL
) return IndexNull
;
432 if (oslot
!= NULL
) *oslot
= slot
;
435 if (opage
!= NULL
) *opage
= page
;
443 * asl_msg_key: iterate over entries
444 * initial value of n should be 0
445 * after that, the value of n should be previously returned value
446 * sets the pointers for the next key, value, and op in the msgionary
447 * returns IndexNull when there are no more entries
450 _asl_msg_fetch_internal(asl_msg_t
*msg
, uint32_t n
, const char **keyout
, const char **valout
, uint32_t *opout
, asl_msg_t
**outpage
, uint32_t *outslot
)
455 if (msg
== NULL
) return IndexNull
;
456 if (outpage
!= NULL
) *outpage
= NULL
;
457 if (outslot
!= NULL
) *outslot
= IndexNull
;
462 while (slot
>= ASL_MSG_PAGE_SLOTS
)
464 if (page
->next
== NULL
) return IndexNull
;
466 slot
-= ASL_MSG_PAGE_SLOTS
;
469 while (page
->key
[slot
] == ASL_MSG_SLOT_FREE
)
474 if (slot
>= ASL_MSG_PAGE_SLOTS
)
476 if (page
->next
== NULL
) return IndexNull
;
484 if (keyout
!= NULL
) *keyout
= _asl_msg_slot_key(page
, slot
);
485 if (valout
!= NULL
) *valout
= _asl_msg_slot_val(page
, slot
);
486 if (opout
!= NULL
) *opout
= page
->op
[slot
];
488 if (outpage
!= NULL
) *outpage
= page
;
489 if (outslot
!= NULL
) *outslot
= slot
;
495 asl_msg_fetch(asl_msg_t
*msg
, uint32_t n
, const char **keyout
, const char **valout
, uint32_t *opout
)
497 return _asl_msg_fetch_internal(msg
, n
, keyout
, valout
, opout
, NULL
, NULL
);
501 _asl_msg_new_key_val_op(asl_msg_t
*msg
, const char *key
, const char *val
, uint32_t op
)
503 uint32_t slot
, keylen
, vallen
, total
;
505 asl_msg_t
*page
, *last
;
506 char *extkey
, *extval
;
508 if (msg
== NULL
) return -1;
509 if (key
== NULL
) return -1;
514 keylen
= strlen(key
);
515 kx
= _asl_msg_std_key(key
, keylen
);
517 if (kx
== 0) keylen
++;
525 vallen
= strlen(val
) + 1;
529 /* check if one or both of key and value must be "external" (in its own malloced space) */
530 if (keylen
> ASL_MSG_PAGE_DATA_SIZE
)
532 extkey
= strdup(key
);
533 keylen
= sizeof(char *);
536 if (vallen
> ASL_MSG_PAGE_DATA_SIZE
)
538 extval
= strdup(val
);
539 vallen
= sizeof(char *);
542 total
= keylen
+ vallen
;
543 if ((total
> ASL_MSG_PAGE_DATA_SIZE
) && (extval
== NULL
) && (keylen
> 0))
545 extval
= strdup(val
);
546 vallen
= sizeof(char *);
547 total
= keylen
+ vallen
;
550 if ((total
> ASL_MSG_PAGE_DATA_SIZE
) && (extkey
== NULL
))
552 extkey
= strdup(key
);
553 keylen
= sizeof(char *);
554 total
= keylen
+ vallen
;
557 if (total
> ASL_MSG_PAGE_DATA_SIZE
)
559 /* can't happen, but... */
560 if (extkey
!= NULL
) free(extkey
);
561 if (extval
!= NULL
) free(extval
);
565 /* find a page with space for the key and value and a free slot*/
569 for (page
= msg
; page
!= NULL
; page
= page
->next
)
573 if (total
<= (ASL_MSG_PAGE_DATA_SIZE
- page
->data_size
))
575 /* check for a free slot */
576 for (slot
= 0; (slot
< ASL_MSG_PAGE_SLOTS
) && (page
->key
[slot
] != ASL_MSG_SLOT_FREE
); slot
++);
577 if (slot
< ASL_MSG_PAGE_SLOTS
) break;
583 /* allocate a new page and attach it */
584 page
= _asl_msg_make_page();
587 if (extkey
!= NULL
) free(extkey
);
588 if (extval
!= NULL
) free(extval
);
596 /* copy key or external key pointer into page data */
599 page
->key
[slot
] = kx
;
601 else if (extkey
== NULL
)
603 page
->key
[slot
] = page
->data_size
;
604 memcpy(page
->data
+ page
->data_size
, key
, keylen
);
608 page
->key
[slot
] = page
->data_size
| ASL_MSG_KV_EXTERN
;
609 memcpy(page
->data
+ page
->data_size
, &extkey
, keylen
);
612 page
->data_size
+= keylen
;
614 /* copy val or external val pointer into page data */
615 page
->val
[slot
] = ASL_MSG_SLOT_FREE
;
621 page
->val
[slot
] = page
->data_size
;
622 memcpy(page
->data
+ page
->data_size
, val
, vallen
);
626 page
->val
[slot
] = page
->data_size
| ASL_MSG_KV_EXTERN
;
627 memcpy(page
->data
+ page
->data_size
, &extval
, vallen
);
630 page
->data_size
+= vallen
;
636 /* update page count */
643 * Most of the code in asl_msg_set_key_val_op is concerned with trying to re-use
644 * space in an asl_msg_t page when given a new value for an existing key.
645 * If the key is new, we just call _asl_msg_new_key_val_op.
647 * Note that queries can have duplicate keys, so for ASL_TYPE_QUERY we just
648 * call through to _asl_msg_new_key_val_op.
651 _asl_msg_set_kvo(asl_msg_t
*msg
, const char *key
, const char *val
, uint32_t op
)
653 uint32_t i
, slot
, newexternal
;
655 uint32_t intvallen
, extvallen
, newvallen
;
656 char *intval
, *extval
, *newval
;
658 if (msg
== NULL
) return -1;
659 if (key
== NULL
) return -1;
664 if ((msg
->type
== ASL_TYPE_QUERY
) || (IndexNull
== _asl_msg_index(msg
, key
, &slot
, &page
)))
667 return _asl_msg_new_key_val_op(msg
, key
, val
, op
);
676 if (page
->val
[slot
] != ASL_MSG_SLOT_FREE
)
678 if ((page
->val
[slot
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_EXTERN
)
680 i
= page
->val
[slot
] & ASL_MSG_OFFSET_MASK
;
681 memcpy(&extval
, page
->data
+ i
, sizeof(char *));
682 extvallen
= sizeof(char *);
686 intval
= page
->data
+ page
->val
[slot
];
687 intvallen
= strlen(intval
) + 1;
691 /* replace val and op for existing entry */
693 /* easy case - remove val */
696 if (extval
!= NULL
) free(extval
);
697 page
->val
[slot
] = ASL_MSG_SLOT_FREE
;
698 if (op
!= IndexNull
) page
->op
[slot
] = op
;
702 /* trivial case - internal val doesn't change */
703 if ((intval
!= NULL
) && (streq(val
, intval
)))
705 if (op
!= IndexNull
) page
->op
[slot
] = op
;
709 /* trivial case - external val doesn't change */
710 if ((extval
!= NULL
) && (streq(val
, extval
)))
712 if (op
!= IndexNull
) page
->op
[slot
] = op
;
717 * special case: we generally don't compress out holes in the data
718 * space, but if this is the last string in the currently used data space
719 * we can just back up the data_size and reset page->val[slot]
721 i
= page
->val
[slot
] & ASL_MSG_OFFSET_MASK
;
722 if ((intval
!= NULL
) && ((i
+ intvallen
) == page
->data_size
))
724 page
->val
[slot
] = ASL_MSG_SLOT_FREE
;
725 page
->data_size
-= intvallen
;
729 else if ((extval
!= NULL
) && ((i
+ extvallen
) == page
->data_size
))
731 page
->val
[slot
] = ASL_MSG_SLOT_FREE
;
732 page
->data_size
-= extvallen
;
738 /* val changes - see if it needs to be external */
739 newvallen
= strlen(val
) + 1;
742 if (newvallen
> ASL_MSG_PAGE_DATA_SIZE
)
745 newvallen
= sizeof(char *);
748 /* check if there is room to change val in place */
749 if (((extval
!= NULL
) && (newvallen
<= extvallen
)) || ((extval
== NULL
) && (newvallen
<= intvallen
)))
751 if (extval
!= NULL
) free(extval
);
754 /* we can re-use the space of the old value */
755 i
= page
->val
[slot
] & ASL_MSG_OFFSET_MASK
;
757 if (newexternal
== 1)
759 /* create an external val and copy in the new pointer */
760 newval
= strdup(val
);
761 if (newval
== NULL
) return -1;
763 page
->val
[slot
] = i
| ASL_MSG_KV_EXTERN
;
764 memcpy(page
->data
+ i
, &newval
, sizeof(char *));
768 /* new internal value */
770 memcpy(page
->data
+ i
, val
, newvallen
);
773 if (op
!= IndexNull
) page
->op
[slot
] = op
;
777 /* we're done with the old value if it is external - free it now */
778 if (extval
!= NULL
) free(extval
);
781 if (newvallen
<= (ASL_MSG_PAGE_DATA_SIZE
- page
->data_size
))
783 /* can't re-use the old space, but there's room on the page */
785 page
->data_size
+= newvallen
;
787 if (newexternal
== 1)
789 /* create an external val and copy in the new pointer */
790 newval
= strdup(val
);
791 if (newval
== NULL
) return -1;
793 page
->val
[slot
] = i
| ASL_MSG_KV_EXTERN
;
794 memcpy(page
->data
+ i
, &newval
, sizeof(char *));
798 /* new internal value */
800 memcpy(page
->data
+ i
, val
, newvallen
);
803 if (op
!= IndexNull
) page
->op
[slot
] = op
;
808 /* no room on this page - free up existing entry and treat this as a new entry */
809 if ((page
->key
[slot
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_EXTERN
)
811 memcpy(&extval
, page
->data
+ (page
->key
[slot
] & ASL_MSG_OFFSET_MASK
), sizeof(char *));
815 page
->key
[slot
] = ASL_MSG_SLOT_FREE
;
816 page
->val
[slot
] = ASL_MSG_SLOT_FREE
;
819 return _asl_msg_new_key_val_op(msg
, key
, val
, op
);
823 asl_msg_set_key_val_op(asl_msg_t
*msg
, const char *key
, const char *val
, uint32_t op
)
825 char *special
, buf
[512];
829 /* Special case handling */
832 /* if query modifier is set but op is zero, default to equality test */
833 if ((op
!= 0) && ((op
& ASL_QUERY_OP_TRUE
) == 0)) op
|= ASL_QUERY_OP_EQUAL
;
835 /* convert "Level" values to "0" through "7" */
836 if (streq(key
, ASL_KEY_LEVEL
))
838 if (val
== NULL
) val
= "7";
839 else if ((val
[0] >= '0') && (val
[0] <= '7') && (val
[1] == '\0')) /* do nothing */;
840 else if (strcaseeq(val
, ASL_STRING_EMERG
)) val
= "0";
841 else if (strcaseeq(val
, ASL_STRING_ALERT
)) val
= "1";
842 else if (strcaseeq(val
, ASL_STRING_CRIT
)) val
= "2";
843 else if (strcaseeq(val
, ASL_STRING_ERR
)) val
= "3";
844 else if (strcaseeq(val
, ASL_STRING_WARNING
)) val
= "4";
845 else if (strcaseeq(val
, ASL_STRING_NOTICE
)) val
= "5";
846 else if (strcaseeq(val
, ASL_STRING_INFO
)) val
= "6";
847 else if (strcaseeq(val
, ASL_STRING_DEBUG
)) val
= "7";
851 /* strip trailing newlines from "Message" values */
852 if ((streq(key
, ASL_KEY_MSG
)) && (val
!= NULL
))
856 while ((i
> 0) && (val
[i
- 1] == '\n')) i
--;
857 if (i
== 0) val
= NULL
;
860 /* use buf if it is big enough, else malloc a temporary buffer */
865 val
= (const char *)buf
;
869 special
= malloc(i
+ 1);
870 if (special
== NULL
) return -1;
871 memcpy(special
, val
, i
);
873 val
= (const char *)special
;
878 status
= _asl_msg_set_kvo(msg
, key
, val
, op
);
880 if (special
!= NULL
) free(special
);
885 asl_msg_set_key_val(asl_msg_t
*msg
, const char *key
, const char *val
)
887 return asl_msg_set_key_val_op(msg
, key
, val
, 0);
891 * Merge a key / val into a message (only ASL_TYPE_MSG).
892 * Adds the key / val if the key is not found.
893 * Does not replace the value if the key is found.
896 _asl_msg_merge_key_val_op(asl_msg_t
*msg
, const char *key
, const char *val
, uint32_t op
)
901 if (msg
== NULL
) return;
902 if (key
== NULL
) return;
907 i
= _asl_msg_index(msg
, key
, &slot
, &page
);
908 if (i
!= IndexNull
) return;
910 asl_msg_set_key_val_op(msg
, key
, val
, op
);
914 * Merge msg into target (does not replace existing keys).
915 * Creates a new asl_msg_t if target is NULL.
919 asl_msg_merge(asl_msg_t
*target
, asl_msg_t
*msg
)
921 uint32_t x
, slot
, op
, isnew
= 0;
922 const char *key
, *val
;
925 if (msg
== NULL
) return target
;
930 target
= asl_msg_new(msg
->type
);
933 for (x
= _asl_msg_fetch_internal(msg
, 0, &key
, &val
, &op
, &page
, &slot
); x
!= IndexNull
; x
= _asl_msg_fetch_internal(msg
, x
, &key
, &val
, &op
, &page
, &slot
))
935 if (msg
->type
== ASL_TYPE_MSG
) op
= 0;
936 if (isnew
== 1) asl_msg_set_key_val_op(target
, key
, val
, op
);
937 else _asl_msg_merge_key_val_op(target
, key
, val
, op
);
947 asl_msg_copy(asl_msg_t
*msg
)
949 return asl_msg_merge(NULL
, msg
);
954 * Frees external key and val strings, but does not try to reclaim data space.
957 asl_msg_unset(asl_msg_t
*msg
, const char *key
)
963 if (msg
== NULL
) return;
964 if (key
== NULL
) return;
969 i
= _asl_msg_index(msg
, key
, &slot
, &page
);
970 if (i
== IndexNull
) return;
972 if ((page
->key
[slot
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_EXTERN
)
974 memcpy(&ext
, page
->data
+ (page
->key
[slot
] & ASL_MSG_OFFSET_MASK
), sizeof(char *));
978 if ((page
->val
[slot
] & ASL_MSG_KV_MASK
) == ASL_MSG_KV_EXTERN
)
980 memcpy(&ext
, page
->data
+ (page
->val
[slot
] & ASL_MSG_OFFSET_MASK
), sizeof(char *));
984 page
->key
[slot
] = ASL_MSG_SLOT_FREE
;
985 page
->val
[slot
] = ASL_MSG_SLOT_FREE
;
992 asl_msg_lookup(asl_msg_t
*msg
, const char *key
, const char **valout
, uint32_t *opout
)
997 if (valout
!= NULL
) *valout
= NULL
;
998 if (opout
!= NULL
) *opout
= 0;
1003 i
= _asl_msg_index(msg
, key
, &slot
, &page
);
1004 if (i
== IndexNull
) return -1;
1006 if (valout
!= NULL
) *valout
= _asl_msg_slot_val(page
, slot
);
1007 if (opout
!= NULL
) *opout
= page
->op
[slot
];
1013 asl_msg_type(asl_msg_t
*msg
)
1015 if (msg
== NULL
) return 0;
1020 asl_msg_count(asl_msg_t
*msg
)
1026 for (; msg
!= NULL
; msg
= msg
->next
) total
+= msg
->count
;
1034 _asl_msg_equal(asl_msg_t
*a
, asl_msg_t
*b
)
1037 const char *key
, *va
, *vb
;
1039 if (asl_msg_count(a
) != asl_msg_count(b
)) return 0;
1045 for (x
= asl_msg_fetch(a
, 0, &key
, &va
, &oa
); x
!= IndexNull
; x
= asl_msg_fetch(a
, x
, &key
, &va
, &oa
))
1047 if (asl_msg_lookup(b
, key
, &vb
, &ob
) != 0) return 0;
1048 if (strcmp(va
, vb
)) return 0;
1049 if ((a
->type
== ASL_TYPE_QUERY
) && (oa
!= ob
)) return 0;
1056 _asl_isanumber(const char *s
)
1060 if (s
== NULL
) return 0;
1063 if ((s
[0] == '-') || (s
[0] == '+')) i
= 1;
1065 if (s
[i
] == '\0') return 0;
1067 for (; s
[i
] != '\0'; i
++)
1069 if (!isdigit(s
[i
])) return 0;
1076 _asl_msg_basic_test(uint32_t op
, const char *q
, const char *m
, uint32_t n
)
1084 t
= op
& ASL_QUERY_OP_TRUE
;
1086 /* NULL value from query or message string fails */
1087 if ((q
== NULL
) || (m
== NULL
)) return (t
& ASL_QUERY_OP_NOT_EQUAL
);
1089 if (op
& ASL_QUERY_OP_REGEX
)
1091 /* greater than or less than make no sense in substring search */
1092 if ((t
== ASL_QUERY_OP_GREATER
) || (t
== ASL_QUERY_OP_LESS
)) return 0;
1094 memset(&rex
, 0, sizeof(regex_t
));
1096 rflags
= REG_EXTENDED
| REG_NOSUB
;
1097 if (op
& ASL_QUERY_OP_CASEFOLD
) rflags
|= REG_ICASE
;
1099 /* A bad reqular expression matches nothing */
1100 if (regcomp(&rex
, q
, rflags
) != 0) return (t
& ASL_QUERY_OP_NOT_EQUAL
);
1102 cmp
= regexec(&rex
, m
, 0, NULL
, 0);
1105 if (t
== ASL_QUERY_OP_NOT_EQUAL
) return (cmp
!= 0);
1109 if (op
& ASL_QUERY_OP_NUMERIC
)
1111 if (_asl_isanumber(q
) == 0) return (t
== ASL_QUERY_OP_NOT_EQUAL
);
1112 if (_asl_isanumber(m
) == 0) return (t
== ASL_QUERY_OP_NOT_EQUAL
);
1119 case ASL_QUERY_OP_EQUAL
: return (nm
== nq
);
1120 case ASL_QUERY_OP_GREATER
: return (nm
> nq
);
1121 case ASL_QUERY_OP_GREATER_EQUAL
: return (nm
>= nq
);
1122 case ASL_QUERY_OP_LESS
: return (nm
< nq
);
1123 case ASL_QUERY_OP_LESS_EQUAL
: return (nm
<= nq
);
1124 case ASL_QUERY_OP_NOT_EQUAL
: return (nm
!= nq
);
1125 default: return (t
== ASL_QUERY_OP_NOT_EQUAL
);
1130 if (op
& ASL_QUERY_OP_CASEFOLD
)
1132 if (n
== 0) cmp
= strcasecmp(m
, q
);
1133 else cmp
= strncasecmp(m
, q
, n
);
1137 if (n
== 0) cmp
= strcmp(m
, q
);
1138 else cmp
= strncmp(m
, q
, n
);
1143 case ASL_QUERY_OP_EQUAL
: return (cmp
== 0);
1144 case ASL_QUERY_OP_GREATER
: return (cmp
> 0);
1145 case ASL_QUERY_OP_GREATER_EQUAL
: return (cmp
>= 0);
1146 case ASL_QUERY_OP_LESS
: return (cmp
< 0);
1147 case ASL_QUERY_OP_LESS_EQUAL
: return (cmp
<= 0);
1148 case ASL_QUERY_OP_NOT_EQUAL
: return (cmp
!= 0);
1151 return (t
== ASL_QUERY_OP_NOT_EQUAL
);
1155 _asl_msg_test_substring(uint32_t op
, const char *q
, const char *m
)
1157 uint32_t t
, i
, d
, lm
, lq
, match
, newop
;
1159 t
= op
& ASL_QUERY_OP_TRUE
;
1162 if (m
!= NULL
) lm
= strlen(m
);
1165 if (q
!= NULL
) lq
= strlen(q
);
1167 /* NULL is a substring of any string */
1168 if (lq
== 0) return (t
& ASL_QUERY_OP_EQUAL
);
1170 /* A long string is defined to be not equal to a short string */
1171 if (lq
> lm
) return (t
== ASL_QUERY_OP_NOT_EQUAL
);
1173 /* greater than or less than make no sense in substring search */
1174 if ((t
== ASL_QUERY_OP_GREATER
) || (t
== ASL_QUERY_OP_LESS
)) return 0;
1177 * We scan the string doing an equality test.
1178 * If the input test is equality, we stop as soon as we hit a match.
1179 * Otherwise we keep scanning the whole message string.
1182 newop
|= ASL_QUERY_OP_EQUAL
;
1186 for (i
= 0; i
<= d
; i
++)
1188 if (_asl_msg_basic_test(newop
, q
, m
+ i
, lq
) != 0)
1190 if (t
& ASL_QUERY_OP_EQUAL
) return 1;
1195 /* If the input test was for equality, no matches were found */
1196 if (t
& ASL_QUERY_OP_EQUAL
) return 0;
1198 /* The input test was for not equal. Return true if no matches were found */
1199 return (match
== 0);
1203 _asl_msg_test_prefix(uint32_t op
, const char *q
, const char *m
)
1207 t
= op
& ASL_QUERY_OP_TRUE
;
1210 if (m
!= NULL
) lm
= strlen(m
);
1213 if (q
!= NULL
) lq
= strlen(q
);
1215 /* NULL is a prefix of any string */
1216 if (lq
== 0) return (t
& ASL_QUERY_OP_EQUAL
);
1218 /* A long string is defined to be not equal to a short string */
1219 if (lq
> lm
) return (t
== ASL_QUERY_OP_NOT_EQUAL
);
1221 /* Compare two equal-length strings */
1222 return _asl_msg_basic_test(op
, q
, m
, lq
);
1226 _asl_msg_test_suffix(uint32_t op
, const char *q
, const char *m
)
1228 uint32_t lm
, lq
, d
, t
;
1230 t
= op
& ASL_QUERY_OP_TRUE
;
1233 if (m
!= NULL
) lm
= strlen(m
);
1236 if (q
!= NULL
) lq
= strlen(q
);
1238 /* NULL is a suffix of any string */
1239 if (lq
== 0) return (t
& ASL_QUERY_OP_EQUAL
);
1241 /* A long string is defined to be not equal to a short string */
1242 if (lq
> lm
) return (t
== ASL_QUERY_OP_NOT_EQUAL
);
1244 /* Compare two equal-length strings */
1246 return _asl_msg_basic_test(op
, q
, m
+ d
, lq
);
1250 * Splits out prefix, suffix, and substring tests.
1251 * Sends the rest to _asl_msg_basic_test().
1254 _asl_msg_test_expression(uint32_t op
, const char *q
, const char *m
)
1258 t
= op
& ASL_QUERY_OP_TRUE
;
1259 if (t
== ASL_QUERY_OP_TRUE
) return 1;
1261 if (op
& ASL_QUERY_OP_PREFIX
)
1263 if (op
& ASL_QUERY_OP_SUFFIX
) return _asl_msg_test_substring(op
, q
, m
);
1264 return _asl_msg_test_prefix(op
, q
, m
);
1266 if (op
& ASL_QUERY_OP_SUFFIX
) return _asl_msg_test_suffix(op
, q
, m
);
1268 return _asl_msg_basic_test(op
, q
, m
, 0);
1272 * Special case for comparing time values.
1273 * If both inputs are time strings, this compares the time
1274 * value in seconds. Otherwise it just does normal matching.
1277 _asl_msg_test_time_expression(uint32_t op
, const char *q
, const char *m
)
1282 if ((op
& ASL_QUERY_OP_PREFIX
) || (op
& ASL_QUERY_OP_SUFFIX
) || (op
& ASL_QUERY_OP_REGEX
)) return _asl_msg_test_expression(op
, q
, m
);
1283 if ((q
== NULL
) || (m
== NULL
)) return _asl_msg_test_expression(op
, q
, m
);
1285 tq
= asl_parse_time(q
);
1286 if (tq
< 0) return _asl_msg_test_expression(op
, q
, m
);
1288 tm
= asl_parse_time(m
);
1289 if (tm
< 0) return _asl_msg_test_expression(op
, q
, m
);
1291 t
= op
& ASL_QUERY_OP_TRUE
;
1295 case ASL_QUERY_OP_FALSE
:
1299 case ASL_QUERY_OP_EQUAL
:
1301 if (tm
== tq
) return 1;
1304 case ASL_QUERY_OP_GREATER
:
1306 if (tm
> tq
) return 1;
1309 case ASL_QUERY_OP_GREATER_EQUAL
:
1311 if (tm
>= tq
) return 1;
1314 case ASL_QUERY_OP_LESS
:
1316 if (tm
< tq
) return 1;
1319 case ASL_QUERY_OP_LESS_EQUAL
:
1321 if (tm
<= tq
) return 1;
1324 case ASL_QUERY_OP_NOT_EQUAL
:
1326 if (tm
!= tq
) return 1;
1329 case ASL_QUERY_OP_TRUE
:
1339 /* test a query against a message */
1341 _asl_msg_test(asl_msg_t
*q
, asl_msg_t
*m
)
1343 uint32_t i
, t
, x
, op
;
1345 const char *kq
, *vq
, *vm
;
1348 * Check each simple expression (key op val) separately.
1349 * The query suceeds (returns 1) if all simple expressions
1350 * succeed (i.e. AND the simple expressions).
1357 for (x
= asl_msg_fetch(q
, 0, &kq
, &vq
, &op
); x
!= IndexNull
; x
= asl_msg_fetch(q
, x
, &kq
, &vq
, &op
))
1359 /* Find query key in the message */
1361 i
= asl_msg_lookup(m
, kq
, &vm
, NULL
);
1363 /* ASL_QUERY_OP_TRUE tests if key is present in the message */
1364 t
= op
& ASL_QUERY_OP_TRUE
;
1365 if (t
== ASL_QUERY_OP_TRUE
)
1367 if (i
!= 0) return 0;
1371 /* ASL_QUERY_OP_FALSE tests if the key is NOT present in the message */
1372 if (t
== ASL_QUERY_OP_FALSE
)
1374 if (i
== 0) return 0;
1380 /* the message does NOT have query key - fail unless we are testing not equal */
1381 if (t
== ASL_QUERY_OP_NOT_EQUAL
) continue;
1386 if (streq(kq
, ASL_KEY_TIME
))
1388 cmp
= _asl_msg_test_time_expression(op
, vq
, vm
);
1392 cmp
= _asl_msg_test_expression(op
, vq
, vm
);
1395 if (cmp
== 0) return 0;
1402 asl_msg_cmp(asl_msg_t
*a
, asl_msg_t
*b
)
1405 if (a
== NULL
) return 0;
1406 if (b
== NULL
) return 0;
1408 if (a
->type
== b
->type
) return _asl_msg_equal(a
, b
);
1409 if (a
->type
== ASL_TYPE_QUERY
) return _asl_msg_test(a
, b
);
1410 return _asl_msg_test(b
, a
);
1415 _asl_time_string(const char *infmt
, const char *str
, const char *nano
)
1419 char *ltime
, *out
, *p
, *q
;
1420 char ltbuf
[32], nanobuf
[16], fmt
[32], zstr
[8];
1422 uint32_t subsec
= 0;
1426 memset(zstr
, 0, sizeof(zstr
));
1433 * The longest infmt string we understand is "-hh:mm.N" (8 chars), so
1434 * it is safe to ignore the input if it doesn't fit in the temp buffer.
1435 * The default is local time zone format.
1439 snprintf(fmt
, sizeof(fmt
), "local");
1441 else if (strlen(infmt
) >= sizeof (fmt
))
1443 snprintf(fmt
, sizeof(fmt
), "local");
1447 snprintf(fmt
, sizeof(fmt
), "%s", infmt
);
1450 q
= strchr(fmt
, '.');
1455 if (q
!= '\0') subsec
= atoi(q
);
1464 if (nano
!= NULL
) nsec
= atoi(nano
);
1465 snprintf(nanobuf
, sizeof(nanobuf
), ".%09u", nsec
);
1466 if (subsec
> 9) subsec
= 9;
1467 nanobuf
[subsec
+ 1] = '\0';
1471 if (str
!= NULL
) tick
= asl_parse_time(str
);
1473 if ((!strcasecmp(fmt
, "lcl")) || (!strcasecmp(fmt
, "local")))
1475 ltime
= ctime_r(&tick
, ltbuf
);
1476 if (ltime
== NULL
) return NULL
;
1478 asprintf(&out
, "%s%s", ltime
+ 4, nanobuf
);
1482 if ((!strcasecmp(fmt
, "jz")) || (!strcasecmp(fmt
, "iso8601")) || (!strcasecmp(fmt
, "iso8601e")))
1485 if (!strncasecmp(fmt
, "iso8601", 7)) sep
= 'T';
1487 if (NULL
== localtime_r(&tick
, &stm
)) return NULL
;
1489 off
= stm
.tm_gmtoff
;
1490 if ((neg
= (off
< 0))) off
*= -1;
1495 if (zm
== 0) snprintf(zstr
, sizeof(zstr
), "%c%02ld", neg
? '-' : '+', zh
);
1496 else snprintf(zstr
, sizeof(zstr
), "%c%02ld:%02ld", neg
? '-' : '+', zh
, zm
);
1498 asprintf(&out
, "%d-%02d-%02d%c%02d:%02d:%02d%s%s", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, sep
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
, zstr
);
1502 if (!strcasecmp(fmt
, "iso8601b"))
1504 if (NULL
== localtime_r(&tick
, &stm
)) return NULL
;
1506 off
= stm
.tm_gmtoff
;
1507 if ((neg
= (off
< 0))) off
*= -1;
1512 if (zm
== 0) snprintf(zstr
, sizeof(zstr
), "%c%02ld", neg
? '-' : '+', zh
);
1513 else snprintf(zstr
, sizeof(zstr
), "%c%02ld:%02ld", neg
? '-' : '+', zh
, zm
);
1515 asprintf(&out
, "%d%02d%02dT%02d%02d%02d%s%s", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
, zstr
);
1519 if ((!strcasecmp(fmt
, "sec")) || (!strcasecmp(fmt
, "raw")))
1521 asprintf(&out
, "%lu%s", tick
, nanobuf
);
1525 if (!strcasecmp(fmt
, "j"))
1527 if (NULL
== localtime_r(&tick
, &stm
)) return NULL
;
1528 asprintf(&out
, "%d-%02d-%02d %02d:%02d:%02d%s", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
);
1532 if ((!strcasecmp(fmt
, "utc")) || (!strcasecmp(fmt
, "zulu")) || (!strcasecmp(fmt
, "iso8601z")) || (!strcasecmp(fmt
, "iso8601ez")))
1535 if (!strncasecmp(fmt
, "iso8601", 7)) sep
= 'T';
1537 if (NULL
== gmtime_r(&tick
, &stm
)) return NULL
;
1538 asprintf(&out
, "%d-%02d-%02d%c%02d:%02d:%02d%sZ", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, sep
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
);
1542 if (!strcasecmp(fmt
, "iso8601bz"))
1544 if (NULL
== gmtime_r(&tick
, &stm
)) return NULL
;
1545 asprintf(&out
, "%d%02d%02dT%02d%02d%02d%sZ", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
);
1549 if ((fmt
[1] == '\0') && (((fmt
[0] >= 'a') && (fmt
[0] <= 'z')) || ((fmt
[0] >= 'A') && (fmt
[0] <= 'Z'))))
1552 if (z
>= 'a') z
-= 32;
1554 if (z
== 'Z') off
= 0;
1555 else if ((z
>= 'A') && (z
<= 'I')) off
= ((z
- 'A') + 1) * SEC_PER_HOUR
;
1556 else if ((z
>= 'K') && (z
<= 'M')) off
= (z
- 'A') * SEC_PER_HOUR
;
1557 else if ((z
>= 'N') && (z
<= 'Y')) off
= ('M' - z
) * SEC_PER_HOUR
;
1562 if (fmt
[0] == '-') neg
= true;
1563 if ((*p
== '-') || (*p
== '+')) p
++;
1564 if ((*p
) >= '0' && (*p
<= '9'))
1569 if (p
!= NULL
) zm
= atoi(p
+ 1);
1576 off
= (zh
* 3600) + (zm
* 60);
1579 if (zm
== 0) snprintf(zstr
, sizeof(zstr
), "%c%02ld", neg
? '-' : '+', zh
);
1580 else snprintf(zstr
, sizeof(zstr
), "%c%02ld:%02ld", neg
? '-' : '+', zh
, zm
);
1586 memset(&stm
, 0, sizeof (struct tm
));
1587 if (NULL
== gmtime_r(&tick
, &stm
)) return NULL
;
1589 if ((fmt
[0] >= 'A') && (fmt
[0] <= 'Z'))
1591 asprintf(&out
, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
, fmt
[0]);
1593 else if ((fmt
[0] >= 'a') && (fmt
[0] <= 'z'))
1595 asprintf(&out
, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
, fmt
[0] - 32);
1599 asprintf(&out
, "%d-%02d-%02d %02d:%02d:%02d%s%s", stm
.tm_year
+ 1900, stm
.tm_mon
+ 1, stm
.tm_mday
, stm
.tm_hour
, stm
.tm_min
, stm
.tm_sec
, nanobuf
, zstr
);
1605 /* called from asl_format_message and _asl_send_message */
1606 __private_extern__ asl_string_t
*
1607 asl_msg_to_string_raw(uint32_t encoding
, asl_msg_t
*msg
, const char *tfmt
)
1609 uint32_t i
, x
, count
;
1610 const char *key
, *val
, *nano
;
1613 if (msg
== NULL
) return NULL
;
1615 count
= asl_msg_count(msg
);
1616 if (count
== 0) return NULL
;
1618 str
= asl_string_new(encoding
);
1619 if (str
== NULL
) return NULL
;
1626 asl_msg_lookup(msg
, ASL_KEY_TIME_NSEC
, &nano
, NULL
);
1628 for (x
= asl_msg_fetch(msg
, 0, &key
, &val
, NULL
); x
!= IndexNull
; x
= asl_msg_fetch(msg
, x
, &key
, &val
, NULL
))
1630 if (key
== NULL
) continue;
1632 if (i
> 0) asl_string_append_char_no_encoding(str
, ' ');
1634 asl_string_append_char_no_encoding(str
, '[');
1635 asl_string_append_asl_key(str
, key
);
1637 if (!strcmp(key
, ASL_KEY_TIME
))
1640 asl_string_append_char_no_encoding(str
, ' ');
1642 if (val
!= NULL
) vtime
= _asl_time_string(tfmt
, val
, nano
);
1646 asl_string_append_no_encoding(str
, vtime
);
1651 asl_string_append_char_no_encoding(str
, '0');
1654 else if (val
!= NULL
)
1656 asl_string_append_char_no_encoding(str
, ' ');
1657 asl_string_append(str
, val
);
1660 asl_string_append_char_no_encoding(str
, ']');
1668 static asl_string_t
*
1669 _asl_string_append_asl_msg(asl_string_t
*str
, asl_msg_t
*msg
)
1671 const char *key
, *val
;
1674 if (msg
== NULL
) return str
;
1676 if (msg
->type
== ASL_TYPE_QUERY
) asl_string_append(str
, "Q ");
1686 i
= asl_msg_fetch(msg
, i
, &key
, &val
, &op
);
1689 if (n
!= 0) asl_string_append_char_no_encoding(str
, ' ');
1692 asl_string_append_char_no_encoding(str
, '[');
1694 if (msg
->type
== ASL_TYPE_QUERY
)
1696 asl_string_append_op(str
, op
);
1697 asl_string_append_char_no_encoding(str
, ' ');
1700 asl_string_append_asl_key(str
, key
);
1704 asl_string_append_char_no_encoding(str
, ' ');
1705 asl_string_append(str
, val
);
1708 asl_string_append_char_no_encoding(str
, ']');
1711 if (i
== IndexNull
) break;
1718 asl_msg_to_string(asl_msg_t
*msg
, uint32_t *len
)
1721 asl_string_t
*str
= asl_string_new(ASL_ENCODE_ASL
);
1722 if (str
== NULL
) return NULL
;
1724 str
= _asl_string_append_asl_msg(str
, msg
);
1725 *len
= asl_string_length(str
);
1726 out
= asl_string_free_return_bytes(str
);
1731 _asl_msg_op_from_string(char *o
)
1735 op
= ASL_QUERY_OP_NULL
;
1737 if (o
== NULL
) return op
;
1739 for (i
= 0; o
[i
] != '\0'; i
++)
1741 if (o
[i
] == '.') return ASL_QUERY_OP_NULL
;
1742 if (o
[i
] == 'C') op
|= ASL_QUERY_OP_CASEFOLD
;
1743 if (o
[i
] == 'R') op
|= ASL_QUERY_OP_REGEX
;
1744 if (o
[i
] == 'N') op
|= ASL_QUERY_OP_NUMERIC
;
1745 if (o
[i
] == 'S') op
|= ASL_QUERY_OP_SUBSTRING
;
1746 if (o
[i
] == 'A') op
|= ASL_QUERY_OP_PREFIX
;
1747 if (o
[i
] == 'Z') op
|= ASL_QUERY_OP_SUFFIX
;
1748 if (o
[i
] == '<') op
|= ASL_QUERY_OP_LESS
;
1749 if (o
[i
] == '>') op
|= ASL_QUERY_OP_GREATER
;
1750 if (o
[i
] == '=') op
|= ASL_QUERY_OP_EQUAL
;
1751 if (o
[i
] == '!') op
|= ASL_QUERY_OP_NOT_EQUAL
;
1752 if (o
[i
] == 'T') op
|= ASL_QUERY_OP_TRUE
;
1759 _asl_msg_get_next_word(char **p
, uint32_t *tt
, uint32_t spacedel
)
1761 char *str
, *out
, c
, oval
;
1762 uint32_t i
, len
, n
, outlen
;
1766 if (p
== NULL
) return NULL
;
1767 if (*p
== NULL
) return NULL
;
1768 if (**p
== '\0') return NULL
;
1770 /* skip one space if it's there (word separator) */
1771 if (**p
== ' ') (*p
)++;
1773 /* skip leading white space */
1776 while ((**p
== ' ') || (**p
== '\t')) (*p
)++;
1779 if (**p
== '\0') return NULL
;
1780 if (**p
== '\n') return NULL
;
1791 if (out
== NULL
) return NULL
;
1798 /* scan for token and calulate it's length (input and decoded output len) */
1806 /* stop scanning when we hit a delimiter */
1807 if (((spacedel
!= 0) && (c
== ' ')) || (c
== ']') || (c
== '\0')) break;
1813 if ((c
== 'a') || (c
== 'b') || (c
== 't') || (c
== 'n') || (c
== 'v') || (c
== 'f') || (c
== 'r') || (c
== 's') || (c
== '[') || (c
== '\\') || (c
== ']'))
1818 if (str
[++len
] == '\0') return NULL
;
1822 if (str
[++len
] == '\0') return NULL
;
1823 if (str
[++len
] == '\0') return NULL
;
1825 else if ((c
>= '0') && (c
<= '3'))
1827 if (str
[++len
] == '\0') return NULL
;
1828 if (str
[++len
] == '\0') return NULL
;
1842 if ((len
== 0) && (**p
== ']'))
1847 if (out
== NULL
) return NULL
;
1856 out
= malloc(outlen
+ 1);
1857 if (out
== NULL
) return NULL
;
1860 for (i
= 0; i
< len
; i
++)
1917 if (str
[i
] == '?') out
[n
++] = 127;
1918 else out
[n
++] = str
[i
] - 64;
1927 if (str
[i
] == '?') out
[n
++] = 255;
1928 else out
[n
++] = str
[i
] + 64;
1933 out
[n
++] = str
[i
] + 128;
1943 else if ((c
>= '0') && (c
<= '3'))
1945 oval
= (c
- '0') * 64;
1949 if ((c
< '0') || (c
> '7'))
1956 oval
+= ((c
- '0') * 8);
1960 if ((c
< '0') || (c
> '7'))
1981 if ((c
< '0') || (c
> '9')) *tt
= TOKEN_WORD
;
1992 asl_msg_from_string(const char *buf
)
1994 uint32_t tt
, type
, op
;
1995 char *k
, *v
, *o
, *p
;
1998 if (buf
== NULL
) return NULL
;
2000 type
= ASL_TYPE_MSG
;
2003 k
= _asl_msg_get_next_word(&p
, &tt
, 1);
2004 if (k
== NULL
) return NULL
;
2008 type
= ASL_TYPE_QUERY
;
2011 k
= _asl_msg_get_next_word(&p
, &tt
, 1);
2013 else if (tt
== TOKEN_INT
)
2015 /* Leading integer is a string length - skip it */
2017 k
= _asl_msg_get_next_word(&p
, &tt
, 1);
2018 if (k
== NULL
) return NULL
;
2021 out
= asl_msg_new(ASL_TYPE_MSG
);
2022 if (out
== NULL
) return NULL
;
2026 /* OPEN WORD [WORD [WORD]] CLOSE */
2029 op
= ASL_QUERY_OP_NULL
;
2031 if (tt
!= TOKEN_OPEN
)
2033 asl_msg_release(out
);
2039 /* get op for query type */
2040 if (type
== ASL_TYPE_QUERY
)
2042 o
= _asl_msg_get_next_word(&p
, &tt
, 1);
2043 if ((o
== NULL
) || (tt
!= TOKEN_WORD
))
2045 if (o
!= NULL
) free(o
);
2046 asl_msg_release(out
);
2050 op
= _asl_msg_op_from_string(o
);
2054 k
= _asl_msg_get_next_word(&p
, &tt
, 1);
2055 if (tt
== TOKEN_INT
) tt
= TOKEN_WORD
;
2056 if ((k
== NULL
) || (tt
!= TOKEN_WORD
))
2058 if (k
!= NULL
) free(k
);
2059 asl_msg_release(out
);
2063 v
= _asl_msg_get_next_word(&p
, &tt
, 0);
2064 if (tt
== TOKEN_INT
) tt
= TOKEN_WORD
;
2067 asl_msg_set_key_val_op(out
, k
, NULL
, op
);
2072 if (tt
== TOKEN_CLOSE
)
2074 asl_msg_set_key_val_op(out
, k
, NULL
, op
);
2076 else if (tt
== TOKEN_WORD
)
2078 asl_msg_set_key_val_op(out
, k
, v
, op
);
2082 if (k
!= NULL
) free(k
);
2083 if (v
!= NULL
) free(v
);
2084 asl_msg_release(out
);
2088 if (k
!= NULL
) free(k
);
2089 if (v
!= NULL
) free(v
);
2091 if (tt
!= TOKEN_CLOSE
)
2093 k
= _asl_msg_get_next_word(&p
, &tt
, 1);
2094 if (k
== NULL
) break;
2096 if (tt
!= TOKEN_CLOSE
)
2098 asl_msg_release(out
);
2105 k
= _asl_msg_get_next_word(&p
, &tt
, 1);
2106 if (k
== NULL
) break;
2113 asl_list_to_string(asl_search_result_t
*list
, uint32_t *len
)
2120 if (list
== NULL
) return NULL
;
2121 if (list
->count
== 0) return NULL
;
2122 if (list
->msg
== NULL
) return NULL
;
2124 str
= asl_string_new(ASL_ENCODE_ASL
);
2125 if (str
== NULL
) return NULL
;
2127 snprintf(tmp
, sizeof(tmp
), "%u", list
->count
);
2128 asl_string_append(str
, tmp
);
2129 asl_string_append_char_no_encoding(str
, '\n');
2131 for (i
= 0; i
< list
->count
; i
++)
2133 _asl_string_append_asl_msg(str
, list
->msg
[i
]);
2134 asl_string_append_char_no_encoding(str
, '\n');
2137 *len
= asl_string_length(str
);
2138 out
= asl_string_free_return_bytes(str
);
2142 asl_search_result_t
*
2143 asl_list_from_string(const char *buf
)
2147 asl_search_result_t
*out
;
2150 if (buf
== NULL
) return NULL
;
2154 if (n
== 0) return NULL
;
2156 out
= (asl_search_result_t
*)calloc(1, sizeof(asl_search_result_t
));
2157 if (out
== NULL
) return NULL
;
2159 out
->msg
= (asl_msg_t
**)calloc(n
, sizeof(asl_msg_t
*));
2160 if (out
->msg
== NULL
)
2166 for (i
= 0; i
< n
; i
++)
2168 p
= strchr(p
, '\n');
2171 aslresponse_free((aslresponse
)out
);
2177 m
= asl_msg_from_string(p
);
2180 aslresponse_free((aslresponse
)out
);
2184 out
->msg
[i
] = (asl_msg_t
*)m
;
2192 _asl_level_string(int level
)
2194 if (level
== ASL_LEVEL_EMERG
) return ASL_STRING_EMERG
;
2195 if (level
== ASL_LEVEL_ALERT
) return ASL_STRING_ALERT
;
2196 if (level
== ASL_LEVEL_CRIT
) return ASL_STRING_CRIT
;
2197 if (level
== ASL_LEVEL_ERR
) return ASL_STRING_ERR
;
2198 if (level
== ASL_LEVEL_WARNING
) return ASL_STRING_WARNING
;
2199 if (level
== ASL_LEVEL_NOTICE
) return ASL_STRING_NOTICE
;
2200 if (level
== ASL_LEVEL_INFO
) return ASL_STRING_INFO
;
2201 if (level
== ASL_LEVEL_DEBUG
) return ASL_STRING_DEBUG
;
2206 * Find the value for a key in a message and append a formatted value to str.
2207 * kf may be a simple (no embedded white space) key, or one of (key) or ((key)(format)).
2208 * WARNING: modifies kf!
2210 static asl_string_t
*
2211 _asl_string_append_value_for_key_format(asl_string_t
*str
, asl_msg_t
*msg
, char *kf
, const char *tfmt
)
2213 uint32_t i
, get_fmt
;
2216 const char *mval
, *nano
;
2218 if (str
== NULL
) return NULL
;
2219 if (msg
== NULL
) return str
;
2220 if (kf
== NULL
) return str
;
2226 for (i
= 0; kf
[i
] != '\0'; i
++)
2233 else if (kf
[i
] != '(')
2235 if (key
== NULL
) key
= kf
+ i
;
2236 else if ((get_fmt
== 1) && (fmt
== NULL
)) fmt
= kf
+ i
;
2240 if (key
== NULL
) return str
;
2243 asl_msg_lookup(msg
, ASL_KEY_TIME_NSEC
, &nano
, NULL
);
2245 status
= asl_msg_lookup(msg
, key
, &mval
, NULL
);
2246 if ((status
!= 0) || (mval
== NULL
)) return str
;
2248 if (!strcmp(key
, ASL_KEY_TIME
))
2252 /* format in $((Time)(fmt)) overrides tfmt */
2255 fval
= _asl_time_string(tfmt
, mval
, nano
);
2259 fval
= _asl_time_string(fmt
, mval
, nano
);
2264 asl_string_append_no_encoding(str
, fval
);
2269 asl_string_append_char_no_encoding(str
, '0');
2275 /* Level: num str */
2276 if (!strcmp(key
, ASL_KEY_LEVEL
))
2280 asl_string_append_no_encoding(str
, mval
);
2282 else if (!strcmp(fmt
, "str"))
2284 mval
= _asl_level_string(atoi(mval
));
2285 asl_string_append_no_encoding(str
, mval
);
2289 asl_string_append_no_encoding(str
, mval
);
2295 return asl_string_append(str
, mval
);
2299 * format a message for printing
2300 * out parameter len returns string length including trailing NUL
2303 asl_format_message(asl_msg_t
*msg
, const char *mfmt
, const char *tfmt
, uint32_t text_encoding
, uint32_t *len
)
2305 char *out
, *vtime
, *k
, c
, skey
[512], tfmt_ext
[16];
2306 const char *vhost
, *vpid
, *vsender
, *vmessage
, *vlevel
, *vrefproc
, *vrefpid
, *v
, *key
, *val
, *nano
;
2307 int i
, j
, l
, mf
, paren
, oval
, level
;
2315 if (msg
== NULL
) return NULL
;
2319 if (mfmt
== NULL
) mf
= MFMT_RAW
;
2320 else if (!strcmp(mfmt
, ASL_MSG_FMT_RAW
)) mf
= MFMT_RAW
;
2321 else if (!strcmp(mfmt
, ASL_MSG_FMT_STD
)) mf
= MFMT_STD
;
2322 else if (!strcmp(mfmt
, ASL_MSG_FMT_BSD
)) mf
= MFMT_BSD
;
2323 else if (!strcmp(mfmt
, ASL_MSG_FMT_XML
)) mf
= MFMT_XML
;
2324 else if (!strcmp(mfmt
, ASL_MSG_FMT_MSG
)) mf
= MFMT_MSG
;
2325 else if ((!strncmp(mfmt
, ASL_MSG_FMT_RAW
, 3)) && (mfmt
[3] == '.'))
2328 if ((tfmt
== NULL
) && (mfmt
[4] != '\0'))
2330 snprintf(tfmt_ext
, sizeof(tfmt_ext
), "sec.%s", mfmt
+ 4);
2331 tfmt
= (const char *)tfmt_ext
;
2334 else if ((!strncmp(mfmt
, ASL_MSG_FMT_STD
, 3)) && (mfmt
[3] == '.'))
2337 if ((tfmt
== NULL
) && (mfmt
[4] != '\0'))
2339 snprintf(tfmt_ext
, sizeof(tfmt_ext
), "lcl.%s", mfmt
+ 4);
2340 tfmt
= (const char *)tfmt_ext
;
2343 else if ((!strncmp(mfmt
, ASL_MSG_FMT_BSD
, 3)) && (mfmt
[3] == '.'))
2346 if ((tfmt
== NULL
) && (mfmt
[4] != '\0'))
2348 snprintf(tfmt_ext
, sizeof(tfmt_ext
), "lcl.%s", mfmt
+ 4);
2349 tfmt
= (const char *)tfmt_ext
;
2355 asl_msg_lookup(msg
, ASL_KEY_TIME_NSEC
, &nano
, NULL
);
2359 str
= asl_msg_to_string_raw(text_encoding
, msg
, tfmt
);
2360 asl_string_append_char_no_encoding(str
, '\n');
2362 *len
= asl_string_length(str
);
2363 out
= asl_string_free_return_bytes(str
);
2371 if (asl_msg_lookup(msg
, ASL_KEY_MSG
, &vmessage
, NULL
) != 0) return NULL
;
2373 str
= asl_string_new(text_encoding
);
2374 if (str
== NULL
) return NULL
;
2376 asl_string_append(str
, vmessage
);
2377 asl_string_append_char_no_encoding(str
, '\n');
2379 *len
= asl_string_length(str
);
2380 out
= asl_string_free_return_bytes(str
);
2384 if ((mf
== MFMT_STD
) || (mf
== MFMT_BSD
))
2386 /* COMMON: Mth dd hh:mm:ss host sender[pid] (refproc[refpid])*/
2387 /* BSD: <COMMON>: message */
2388 /* STD: <COMMON> <Level>: message */
2400 if (asl_msg_lookup(msg
, ASL_KEY_TIME
, &v
, NULL
) == 0)
2402 vtime
= _asl_time_string(tfmt
, v
, nano
);
2406 if (asl_msg_lookup(msg
, ASL_KEY_LEVEL
, &vlevel
, NULL
) == 0)
2408 if (vlevel
!= NULL
) level
= atoi(vlevel
);
2411 if (asl_msg_lookup(msg
, ASL_KEY_HOST
, &vhost
, NULL
) == 0)
2413 if (vhost
== NULL
) vhost
= "unknown";
2416 if (asl_msg_lookup(msg
, ASL_KEY_SENDER
, &vsender
, NULL
) == 0)
2418 if (vsender
== NULL
) vsender
= "unknown";
2421 asl_msg_lookup(msg
, ASL_KEY_PID
, &vpid
, NULL
);
2422 asl_msg_lookup(msg
, ASL_KEY_MSG
, &vmessage
, NULL
);
2423 asl_msg_lookup(msg
, ASL_KEY_REF_PROC
, &vrefproc
, NULL
);
2424 asl_msg_lookup(msg
, ASL_KEY_REF_PID
, &vrefpid
, NULL
);
2427 str
= asl_string_new(text_encoding
);
2428 if (str
== NULL
) return NULL
;
2432 asl_string_append(str
, vtime
);
2437 asl_string_append_char_no_encoding(str
, '0');
2440 asl_string_append_char_no_encoding(str
, ' ');
2441 asl_string_append(str
, vhost
);
2442 asl_string_append_char_no_encoding(str
, ' ');
2443 asl_string_append(str
, vsender
);
2445 if ((vpid
!= NULL
) && (strcmp(vpid
, "-1")))
2447 asl_string_append_char_no_encoding(str
, '[');
2448 asl_string_append(str
, vpid
);
2449 asl_string_append_char_no_encoding(str
, ']');
2452 if ((vrefproc
!= NULL
) || (vrefpid
!= NULL
)) asl_string_append_no_encoding(str
, " (");
2454 if (vrefproc
!= NULL
) asl_string_append(str
, vrefproc
);
2455 if (vrefpid
!= NULL
)
2457 asl_string_append_char_no_encoding(str
, '[');
2458 asl_string_append(str
, vrefpid
);
2459 asl_string_append_char_no_encoding(str
, ']');
2462 if ((vrefproc
!= NULL
) || (vrefpid
!= NULL
)) asl_string_append_char_no_encoding(str
, ')');
2466 asl_string_append_no_encoding(str
, " <");
2467 asl_string_append(str
, _asl_level_string(level
));
2468 asl_string_append_char_no_encoding(str
, '>');
2471 asl_string_append_no_encoding(str
, ": ");
2472 if (vmessage
!= NULL
) asl_string_append(str
, vmessage
);
2473 asl_string_append_char_no_encoding(str
, '\n');
2475 *len
= asl_string_length(str
);
2476 out
= asl_string_free_return_bytes(str
);
2482 str
= asl_string_new(text_encoding
);
2483 if (str
== NULL
) return NULL
;
2485 asl_string_append_char_no_encoding(str
, '\t');
2486 asl_string_append_no_encoding(str
, "<dict>");
2487 asl_string_append_char_no_encoding(str
, '\n');
2489 for (x
= asl_msg_fetch(msg
, 0, &key
, &val
, NULL
); x
!= IndexNull
; x
= asl_msg_fetch(msg
, x
, &key
, &val
, NULL
))
2491 if (asl_is_utf8(key
) == 1)
2493 asl_string_append_xml_tag(str
, "key", key
);
2494 if (!strcmp(key
, ASL_KEY_TIME
))
2496 vtime
= _asl_time_string(tfmt
, val
, nano
);
2499 asl_string_append_xml_tag(str
, "string", vtime
);
2504 asl_string_append_xml_tag(str
, "string", "0");
2509 if (asl_is_utf8(val
) == 1) asl_string_append_xml_tag(str
, "string", val
);
2512 b64
= asl_b64_encode((uint8_t *)val
, strlen(val
));
2513 asl_string_append_xml_tag(str
, "data", (char *)b64
);
2520 asl_string_append_char_no_encoding(str
, '\t');
2521 asl_string_append_no_encoding(str
, "</dict>");
2522 asl_string_append_char_no_encoding(str
, '\n');
2524 *len
= asl_string_length(str
);
2525 out
= asl_string_free_return_bytes(str
);
2531 * The format string may contain arbitrary characters.
2532 * Keys are identified by $Key or $(Key). The value for
2533 * that key is substituted. If there are alterate formats
2534 * for the value (for example a time may be formatted as
2535 * raw seconds, in UTC, or a local timezone), then the
2536 * key may be $((Key)(Format)). "\$" prints a plain "$".
2539 str
= asl_string_new(text_encoding
);
2540 if (str
== NULL
) return NULL
;
2543 * We need enough space to copy any keys found in mfmt.
2544 * The key obviously can't be longer than strlen(mfmt),
2545 * in fact, keys must be shorter, since there's at least a '$'
2546 * in front of the key, so we allocate a buffer with strlen(mfmt).
2547 * If strlen(mfmt) <= sizeof(skey), we use skey to avoid a malloc.
2551 if (x
<= sizeof(skey
))
2558 if (k
== NULL
) return NULL
;
2563 for (i
= 0; mfmt
[i
] != '\0'; i
++)
2569 /* scan key, (key) or ((key)(format)) */
2570 for (j
= i
+ 1; mfmt
[j
] != 0; j
++)
2576 else if (mfmt
[j
] == ')')
2578 if (paren
> 0) paren
--;
2585 else if (((mfmt
[j
] == ' ') || (mfmt
[j
] == '\t')) && (paren
== 0)) break;
2588 /* mfmt[i + 1] is the first char of the key or a '('. mfmt[j] is one char past the end. */
2590 memcpy(k
, mfmt
+i
+1, l
);
2592 _asl_string_append_value_for_key_format(str
, msg
, k
, tfmt
);
2598 if (mfmt
[i
] == '\\')
2601 if (mfmt
[i
] == '$') asl_string_append_char_no_encoding(str
, '$');
2602 else if (mfmt
[i
] == 'e') asl_string_append_char_no_encoding(str
, '\e');
2603 else if (mfmt
[i
] == 's') asl_string_append_char_no_encoding(str
, ' ');
2604 else if (mfmt
[i
] == 'a') asl_string_append_char_no_encoding(str
, '\a');
2605 else if (mfmt
[i
] == 'b') asl_string_append_char_no_encoding(str
, '\b');
2606 else if (mfmt
[i
] == 'f') asl_string_append_char_no_encoding(str
, '\f');
2607 else if (mfmt
[i
] == 'n') asl_string_append_char_no_encoding(str
, '\n');
2608 else if (mfmt
[i
] == 'r') asl_string_append_char_no_encoding(str
, '\r');
2609 else if (mfmt
[i
] == 't') asl_string_append_char_no_encoding(str
, '\t');
2610 else if (mfmt
[i
] == 'v') asl_string_append_char_no_encoding(str
, '\v');
2611 else if (mfmt
[i
] == '\'') asl_string_append_char_no_encoding(str
, '\'');
2612 else if (mfmt
[i
] == '\\') asl_string_append_char_no_encoding(str
, '\\');
2613 else if (isdigit(mfmt
[i
]))
2615 oval
= mfmt
[i
] - '0';
2616 if (isdigit(mfmt
[i
+1]))
2619 oval
= (oval
* 8) + (mfmt
[i
] - '0');
2620 if (isdigit(mfmt
[i
+1]))
2623 oval
= (oval
* 8) + (mfmt
[i
] - '0');
2627 asl_string_append_char_no_encoding(str
, c
);
2632 if (mfmt
[i
] == '\0') break;
2633 asl_string_append_char_no_encoding(str
, mfmt
[i
]);
2636 if (k
!= skey
) free(k
);
2638 asl_string_append_char_no_encoding(str
, '\n');
2640 *len
= asl_string_length(str
);
2641 out
= asl_string_free_return_bytes(str
);
2646 * OLD ASLMSG COMPATIBILITY
2649 asl_key(aslmsg msg
, uint32_t n
)
2655 for (page
= (asl_msg_t
*)msg
; page
!= NULL
; page
= page
->next
)
2657 for (slot
= 0; slot
< ASL_MSG_PAGE_SLOTS
; slot
++)
2659 if (page
->key
[slot
] != ASL_MSG_SLOT_FREE
)
2661 if (i
== n
) return _asl_msg_slot_key(page
, slot
);
2671 asl_new(uint32_t type
)
2673 return (aslmsg
)asl_msg_new(type
);
2677 asl_set(aslmsg msg
, const char *key
, const char *value
)
2679 return asl_msg_set_key_val_op((asl_msg_t
*)msg
, key
, value
, IndexNull
);
2683 asl_set_query(aslmsg msg
, const char *key
, const char *value
, uint32_t op
)
2685 return asl_msg_set_key_val_op((asl_msg_t
*)msg
, key
, value
, op
);
2689 asl_unset(aslmsg msg
, const char *key
)
2691 asl_msg_unset((asl_msg_t
*)msg
, key
);
2696 asl_get(aslmsg msg
, const char *key
)
2702 status
= asl_msg_lookup((asl_msg_t
*)msg
, key
, &val
, NULL
);
2703 if (status
!= 0) return NULL
;
2708 asl_free(aslmsg msg
)
2710 asl_msg_release((asl_msg_t
*)msg
);
2716 * aslresponse_next: Iterate over responses returned from asl_search()
2717 * a: a response returned from asl_search();
2718 * returns: The next log message (an aslmsg) or NULL on failure
2721 aslresponse_next(aslresponse r
)
2723 asl_search_result_t
*res
;
2726 res
= (asl_search_result_t
*)r
;
2727 if (res
== NULL
) return NULL
;
2729 if (res
->curr
>= res
->count
) return NULL
;
2730 m
= res
->msg
[res
->curr
];
2737 * aslresponse_free: Free a response returned from asl_search()
2738 * a: a response returned from asl_search()
2741 aslresponse_free(aslresponse r
)
2743 asl_search_result_t
*res
;
2746 res
= (asl_search_result_t
*)r
;
2747 if (res
== NULL
) return;
2749 for (i
= 0; i
< res
->count
; i
++) asl_msg_release(res
->msg
[i
]);