1/*
2 * Copyright (c) 2012-2013 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 <stdio.h>
26#include <stdbool.h>
27#include <time.h>
28#include <sys/time.h>
29#include <asl.h>
30#include <asl_core.h>
31#include <asl_msg.h>
32#include <asl_msg_list.h>
33
34asl_msg_list_t *
35asl_msg_list_new(void)
36{
37	asl_msg_list_t *out = (asl_msg_list_t *)calloc(1, sizeof(asl_msg_list_t));
38	if (out == NULL) return NULL;
39
40	out->asl_type = ASL_TYPE_LIST;
41	out->refcount = 1;
42
43	return out;
44}
45
46asl_msg_list_t *
47asl_msg_list_new_count(uint32_t n)
48{
49	asl_msg_list_t *out = (asl_msg_list_t *)calloc(1, sizeof(asl_msg_list_t));
50	if (out == NULL) return NULL;
51
52	out->asl_type = ASL_TYPE_LIST;
53	out->refcount = 1;
54	out->count = n;
55
56	out->msg = (asl_msg_t **)reallocf(out->msg, out->count * sizeof(asl_msg_t *));
57	if (out->msg == NULL)
58	{
59		free(out);
60		return NULL;
61	}
62
63	return out;
64}
65
66asl_msg_list_t *
67asl_msg_list_retain(asl_msg_list_t *list)
68{
69	if (list == NULL) return NULL;
70	asl_retain((asl_object_t)list);
71	return list;
72}
73
74void
75asl_msg_list_release(asl_msg_list_t *list)
76{
77	if (list == NULL) return;
78	asl_release((asl_object_t)list);
79}
80
81char *
82asl_msg_list_to_string(asl_msg_list_t *list, uint32_t *len)
83{
84	uint32_t i;
85	char tmp[16];
86	char *out;
87	asl_string_t *str;
88
89	if (list == NULL) return NULL;
90	if (list->count == 0) return NULL;
91	if (list->msg == NULL) return NULL;
92
93	str = asl_string_new(ASL_ENCODE_ASL);
94	if (str == NULL) return NULL;
95
96	snprintf(tmp, sizeof(tmp), "%u", list->count);
97	asl_string_append(str, tmp);
98	asl_string_append_char_no_encoding(str, '\n');
99
100	for (i = 0; i < list->count; i++)
101	{
102		asl_string_append_asl_msg(str, list->msg[i]);
103		asl_string_append_char_no_encoding(str, '\n');
104	}
105
106	*len = asl_string_length(str);
107	out = asl_string_release_return_bytes(str);
108	return out;
109}
110
111asl_string_t *
112asl_msg_list_to_asl_string(asl_msg_list_t *list, uint32_t encoding)
113{
114	uint32_t i;
115	char tmp[16];
116	asl_string_t *str;
117
118	if (list == NULL) return NULL;
119	if (list->count == 0) return NULL;
120	if (list->msg == NULL) return NULL;
121
122	str = asl_string_new(encoding);
123	if (str == NULL) return NULL;
124
125	snprintf(tmp, sizeof(tmp), "%u", list->count);
126	asl_string_append(str, tmp);
127	asl_string_append_char_no_encoding(str, '\n');
128
129	for (i = 0; i < list->count; i++)
130	{
131		asl_string_append_asl_msg(str, list->msg[i]);
132		asl_string_append_char_no_encoding(str, '\n');
133	}
134
135	return str;
136}
137
138asl_msg_list_t *
139asl_msg_list_from_string(const char *buf)
140{
141	uint32_t i, n;
142	const char *p;
143	asl_msg_list_t *out;
144	asl_msg_t *m;
145
146	if (buf == NULL) return NULL;
147	p = buf;
148
149	n = atoi(buf);
150	if (n == 0) return NULL;
151
152	out = asl_msg_list_new();
153	if (out == NULL) return NULL;
154
155	for (i = 0; i < n; i++)
156	{
157		p = strchr(p, '\n');
158		if (p == NULL)
159		{
160			asl_msg_list_release(out);
161			return NULL;
162		}
163
164		p++;
165
166		m = asl_msg_from_string(p);
167		if (m == NULL)
168		{
169			asl_msg_list_release(out);
170			return NULL;
171		}
172
173		asl_msg_list_append(out, m);
174		asl_msg_release(m);
175	}
176
177	return out;
178}
179
180void
181asl_msg_list_insert(asl_msg_list_t *list, uint32_t x, void *obj)
182{
183	uint32_t i, j;
184	asl_object_private_t *oo = (asl_object_private_t *)obj;
185
186	if (list == NULL) return;
187	if (obj == NULL) return;
188	if (list->count == UINT32_MAX) return;
189
190	if (x >= list->count) x = list->count;
191
192	uint32_t type = asl_get_type((asl_object_t)oo);
193	uint32_t count = 0;
194
195	if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY)) count = 1;
196	else count = asl_object_count(oo);
197
198	if (count == 0) return;
199
200	uint64_t check = list->count;
201	check += count;
202	if (check > UINT32_MAX) return;
203
204	list->msg = (asl_msg_t **)reallocf(list->msg, (list->count + count) * sizeof(asl_msg_t *));
205	if (list->msg == NULL)
206	{
207		list->count = 0;
208		list->curr = 0;
209		return;
210	}
211
212	for (i = list->count, j = i - 1; i > x; i--, j--) list->msg[i] = list->msg[j];
213
214	asl_object_set_iteration_index(oo, 0);
215
216	if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY))
217	{
218		list->msg[x] = (asl_msg_t *)asl_retain((asl_object_t)oo);
219	}
220	else
221	{
222		for (i = x, j = 0; j < count; i++, j++) list->msg[i] = (asl_msg_t *)asl_object_next(oo);
223	}
224
225	asl_object_set_iteration_index(oo, 0);
226
227	list->count += count;
228}
229
230void
231asl_msg_list_append(asl_msg_list_t *list, void *obj)
232{
233	asl_msg_list_insert(list, UINT32_MAX, obj);
234}
235
236void
237asl_msg_list_prepend(asl_msg_list_t *list, void *obj)
238{
239	asl_msg_list_insert(list, 0, obj);
240}
241
242size_t
243asl_msg_list_count(asl_msg_list_t *list)
244{
245	if (list == NULL) return 0;
246	return list->count;
247}
248
249asl_msg_t *
250asl_msg_list_get_index(asl_msg_list_t *list, size_t index)
251{
252	asl_msg_t *out;
253
254	if (list == NULL) return NULL;
255	if (index >= list->count) return NULL;
256	if (list->msg == NULL)
257	{
258		list->curr = 0;
259		list->count = 0;
260		return NULL;
261	}
262
263	out = list->msg[index];
264	return out;
265}
266
267void
268asl_msg_list_remove_index(asl_msg_list_t *list, size_t index)
269{
270	uint32_t i, j;
271
272	if (list == NULL) return;
273	if (index >= list->count) return;
274	if (list->msg == NULL)
275	{
276		list->curr = 0;
277		list->count = 0;
278		return;
279	}
280
281	asl_msg_release(list->msg[index]);
282
283	for (i = index + 1, j = index; i < list->count; i++) list->msg[j] = list->msg[i];
284	list->count--;
285
286	list->msg = (asl_msg_t **)reallocf(list->msg, list->count * sizeof(asl_msg_t *));
287	if (list->msg == NULL)
288	{
289		list->count = 0;
290		list->curr = 0;
291	}
292}
293
294asl_msg_t *
295asl_msg_list_next(asl_msg_list_t *list)
296{
297	asl_msg_t *out;
298
299	if (list == NULL) return NULL;
300	if (list->curr >= list->count) return NULL;
301	if (list->msg == NULL)
302	{
303		list->curr = 0;
304		list->count = 0;
305		return NULL;
306	}
307
308	out = list->msg[list->curr];
309	list->curr++;
310	return out;
311}
312
313asl_msg_t *
314asl_msg_list_prev(asl_msg_list_t *list)
315{
316	asl_msg_t *out;
317
318	if (list == NULL) return NULL;
319	if (list->curr == 0) return NULL;
320	if (list->msg == NULL)
321	{
322		list->curr = 0;
323		list->count = 0;
324		return NULL;
325	}
326
327	if (list->curr > list->count) list->curr = list->count;
328
329	list->curr--;
330	out = list->msg[list->curr];
331	return out;
332}
333
334void
335asl_msg_list_reset_iteration(asl_msg_list_t *list, size_t position)
336{
337	if (list == NULL) return;
338
339	if (position > list->count) position = SIZE_MAX;
340	list->curr = position;
341}
342
343asl_msg_list_t *
344asl_msg_list_search(asl_msg_list_t *list, asl_msg_t *query)
345{
346	uint32_t i;
347	asl_msg_list_t *out = NULL;
348
349	if (list == NULL) return NULL;
350
351	if (list->msg == NULL)
352	{
353		list->curr = 0;
354		list->count = 0;
355		return NULL;
356	}
357
358	for (i = 0; i < list->count; i++)
359	{
360		int match = 0;
361		if (query == NULL) match = 1;
362		else match = asl_msg_cmp(query, list->msg[i]);
363
364		if (match != 0)
365		{
366			if (out == NULL) out = asl_msg_list_new();
367			if (out == NULL) return NULL;
368			asl_msg_list_append(out, list->msg[i]);
369		}
370	}
371
372	return out;
373}
374
375asl_msg_list_t *
376asl_msg_list_match(asl_msg_list_t *list, asl_msg_list_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int32_t direction)
377{
378	uint32_t i, end, n = 0;
379	struct timeval now, finish;
380	asl_msg_list_t *out = NULL;
381
382	if (list == NULL) return NULL;
383	if (list->msg == NULL)
384	{
385		list->curr = 0;
386		list->count = 0;
387		return NULL;
388	}
389
390	/* start the timer if a timeout was specified */
391	memset(&finish, 0, sizeof(struct timeval));
392	if (duration != 0)
393	{
394		if (gettimeofday(&finish, NULL) == 0)
395		{
396			finish.tv_sec += (duration / USEC_PER_SEC);
397			finish.tv_usec += (duration % USEC_PER_SEC);
398			if (finish.tv_usec > USEC_PER_SEC)
399			{
400				finish.tv_usec -= USEC_PER_SEC;
401				finish.tv_sec += 1;
402			}
403		}
404		else
405		{
406			/* shouldn't happen, but if gettimeofday failed we just run without a timeout */
407			memset(&finish, 0, sizeof(struct timeval));
408		}
409	}
410
411	end = list->count - 1;
412	if (direction >= 0)
413	{
414		if (start >= list->count)
415		{
416			if (last != NULL) *last = list->count;
417			return 0;
418		}
419
420		direction = 1;
421	}
422	else
423	{
424		if (start >= list->count) start = list->count - 1;
425		end = 0;
426		direction = -1;
427	}
428
429	i = start;
430
431	do
432	{
433		int match = 0;
434		if (qlist == NULL) match = 1;
435		else match = asl_msg_cmp_list(list->msg[i], qlist);
436
437		if (last != NULL) *last = i;
438
439		if (match != 0)
440		{
441			if (out == NULL) out = asl_msg_list_new();
442			if (out == NULL) return NULL;
443
444			asl_msg_list_append(out, list->msg[i]);
445			n++;
446		}
447
448		if (n >= count) return n;
449
450		/* check the timer */
451		if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0))
452		{
453			if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) return n;
454		}
455
456		i += direction;
457	} while (i != end);
458
459	return out;
460}
461
462#pragma mark -
463#pragma mark asl_object support
464
465static asl_object_private_t *
466_jump_alloc(uint32_t type)
467{
468	return (asl_object_private_t *)asl_msg_list_new();
469}
470
471static void
472_jump_dealloc(asl_object_private_t *obj)
473{
474	asl_msg_list_t *list = (asl_msg_list_t *)obj;
475
476	if (list == NULL) return;
477	if (list->msg != NULL)
478	{
479		uint32_t i;
480		for (i = 0; i < list->count; i++) asl_msg_release(list->msg[i]);
481		free(list->msg);
482	}
483
484	free(list);
485}
486
487static size_t
488_jump_count(asl_object_private_t *obj)
489{
490	return asl_msg_list_count((asl_msg_list_t *)obj);
491}
492
493static asl_object_private_t *
494_jump_next(asl_object_private_t *obj)
495{
496	return (asl_object_private_t *)asl_msg_list_next((asl_msg_list_t *)obj);
497}
498
499static asl_object_private_t *
500_jump_prev(asl_object_private_t *obj)
501{
502	return (asl_object_private_t *)asl_msg_list_prev((asl_msg_list_t *)obj);
503}
504
505static asl_object_private_t *
506_jump_get_object_at_index(asl_object_private_t *obj, size_t n)
507{
508	return (asl_object_private_t *)asl_msg_list_get_index((asl_msg_list_t *)obj, n);
509}
510
511static void
512_jump_set_iteration_index(asl_object_private_t *obj, size_t n)
513{
514	asl_msg_list_reset_iteration((asl_msg_list_t *)obj, n);
515}
516
517static void
518_jump_remove_object_at_index(asl_object_private_t *obj, size_t n)
519{
520	asl_msg_list_remove_index((asl_msg_list_t *)obj, n);
521}
522
523static void
524_jump_append(asl_object_private_t *obj, asl_object_private_t *newobj)
525{
526	int type = asl_get_type((asl_object_t)newobj);
527	if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
528
529	asl_msg_list_append((asl_msg_list_t *)obj, newobj);
530}
531
532static void
533_jump_prepend(asl_object_private_t *obj, asl_object_private_t *newobj)
534{
535	int type = asl_get_type((asl_object_t)newobj);
536	if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
537
538	asl_msg_list_prepend((asl_msg_list_t *)obj, newobj);
539}
540
541static asl_object_private_t *
542_jump_search(asl_object_private_t *obj, asl_object_private_t *query)
543{
544	int type = asl_get_type((asl_object_t)query);
545
546	if ((query != NULL) && (type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return NULL;
547
548	asl_msg_list_t *out = asl_msg_list_search((asl_msg_list_t *)obj, (asl_msg_t *)query);
549	if (out == NULL) return NULL;
550	return (asl_object_private_t *)out;
551}
552
553static asl_object_private_t *
554_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)
555{
556	int type = asl_get_type((asl_object_t)qlist);
557
558	if ((qlist != NULL) && (type != ASL_TYPE_LIST)) return NULL;
559
560	return (asl_object_private_t *)asl_msg_list_match((asl_msg_list_t *)obj, (asl_msg_list_t *)qlist, last, start, count, duration, dir);
561}
562
563__private_extern__ const asl_jump_table_t *
564asl_msg_list_jump_table()
565{
566	static const asl_jump_table_t jump =
567	{
568		.alloc = &_jump_alloc,
569		.dealloc = &_jump_dealloc,
570		.set_key_val_op = NULL,
571		.unset_key = NULL,
572		.get_val_op_for_key = NULL,
573		.get_key_val_op_at_index = NULL,
574		.count = &_jump_count,
575		.next = &_jump_next,
576		.prev = &_jump_prev,
577		.get_object_at_index = &_jump_get_object_at_index,
578		.set_iteration_index = &_jump_set_iteration_index,
579		.remove_object_at_index = &_jump_remove_object_at_index,
580		.append = &_jump_append,
581		.prepend = &_jump_prepend,
582		.search = &_jump_search,
583		.match = &_jump_match
584	};
585
586	return &jump;
587}
588