Line data Source code
1 : /*
2 : COLLECTION LIBRARY
3 :
4 : Implementation of the collection library interface.
5 :
6 : Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
7 :
8 : Collection Library is free software: you can redistribute it and/or modify
9 : it under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation, either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : Collection Library is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with Collection Library. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "config.h"
23 : #include <string.h>
24 : #include <stdlib.h>
25 : #include <errno.h>
26 : #include <ctype.h>
27 : #include <time.h>
28 : #include "trace.h"
29 :
30 : /* The collection should use the real structures */
31 : #include "collection_priv.h"
32 : #include "collection.h"
33 :
34 :
35 : /* Internal constants defined to denote actions that can be performed by find handler */
36 : #define COLLECTION_ACTION_FIND 1
37 : #define COLLECTION_ACTION_DEL 2
38 : #define COLLECTION_ACTION_UPDATE 3
39 : #define COLLECTION_ACTION_GET 4
40 :
41 :
42 : /* Special internal error code to indicate that collection search was interrupted */
43 : #define EINTR_INTERNAL 10000
44 :
45 :
46 : /* Potential subject for management with libtools */
47 : #define DATE_FORMAT "%c"
48 :
49 : #define TIME_ARRAY_SIZE 100
50 :
51 : /* Magic numbers for hashing */
52 : #if SIZEOF_LONG == 8
53 : #define FNV1a_prime 1099511628211ul
54 : #define FNV1a_base 14695981039346656037ul
55 : #elif SIZEOF_LONG_LONG == 8
56 : #define FNV1a_prime 1099511628211ull
57 : #define FNV1a_base 14695981039346656037ull
58 : #else
59 : #error "Platform cannot support 64-bit constant integers"
60 : #endif
61 :
62 : /* Struct used for passing parameter for update operation */
63 : struct update_property {
64 : int type;
65 : void *data;
66 : int length;
67 : int found;
68 : };
69 :
70 : /* This struct is used to construct path
71 : * to an item in the collection (tree)
72 : */
73 : struct path_data {
74 : char *name;
75 : int length;
76 : struct path_data *previous_path;
77 : };
78 :
79 : /* Structure to keep data needed to
80 : * copy collection
81 : * while traversing it
82 : */
83 : struct col_copy {
84 : int mode;
85 : struct path_data *current_path;
86 : char *given_name;
87 : int given_len;
88 : col_copy_cb copy_cb;
89 : void *ext_data;
90 : };
91 :
92 : /******************** FUNCTION DECLARATIONS ****************************/
93 :
94 : /* Have to declare those due to function cross referencing */
95 : static int col_find_item_and_do(struct collection_item *ci,
96 : const char *property_to_find,
97 : int type,
98 : int mode_flags,
99 : col_item_fn item_handler,
100 : void *custom_data,
101 : int action);
102 :
103 : /* Traverse callback for find & delete function */
104 : static int col_act_traverse_handler(struct collection_item *head,
105 : struct collection_item *previous,
106 : struct collection_item *current,
107 : void *passed_traverse_data,
108 : col_item_fn user_item_handler,
109 : void *custom_data,
110 : int *stop);
111 :
112 : /* Traverse handler to find parent of the item */
113 : static int col_parent_traverse_handler(struct collection_item *head,
114 : struct collection_item *previous,
115 : struct collection_item *current,
116 : void *traverse_data,
117 : col_item_fn user_item_handler,
118 : void *custom_data,
119 : int *stop);
120 :
121 : /* Traverse callback signature */
122 : typedef int (*internal_item_fn)(struct collection_item *head,
123 : struct collection_item *previous,
124 : struct collection_item *current,
125 : void *traverse_data,
126 : col_item_fn user_item_handler,
127 : void *custom_data,
128 : int *stop);
129 : /* Function to walk_items */
130 : static int col_walk_items(struct collection_item *ci,
131 : int mode_flags,
132 : internal_item_fn traverse_handler,
133 : void *traverse_data,
134 : col_item_fn user_item_handler,
135 : void *custom_data,
136 : unsigned *depth);
137 :
138 : /* Function to get sub collection */
139 : static int col_get_subcollection(const char *property,
140 : int property_len,
141 : int type,
142 : void *data,
143 : int length,
144 : void *found,
145 : int *dummy);
146 :
147 : /* Function to destroy collection */
148 : void col_destroy_collection(struct collection_item *ci);
149 :
150 : /******************** SUPPLEMENTARY FUNCTIONS ****************************/
151 : /* BASIC OPERATIONS */
152 :
153 : /* Function that checks if property can be added */
154 637698 : static int col_validate_property(const char *property)
155 : {
156 : TRACE_FLOW_STRING("col_validate_property", "Entry point.");
157 : /* Only alpha numeric characters are allowed in names of the properties */
158 637698 : int invalid = 0;
159 : const char *check;
160 :
161 637698 : check = property;
162 7981938 : while (*check != '\0') {
163 : /* It turned out that limiting collection charcters is bad */
164 6706543 : if ((*check < ' ') || (*check == '!')) {
165 1 : invalid = 1;
166 1 : break;
167 : }
168 6706542 : check++;
169 : }
170 : TRACE_FLOW_NUMBER("col_validate_property. Returning ", invalid);
171 637698 : return invalid;
172 : }
173 :
174 :
175 : /* Function that cleans the item with callback */
176 637683 : static void col_delete_item_with_cb(struct collection_item *item,
177 : col_item_cleanup_fn cb,
178 : void *custom_data)
179 : {
180 : struct collection_item *other_collection;
181 :
182 : TRACE_FLOW_STRING("col_delete_item","Entry point.");
183 :
184 637683 : if (item == NULL) {
185 : TRACE_FLOW_STRING("col_delete_item","Nothing to delete!");
186 637683 : return;
187 : }
188 :
189 : /* Handle external or embedded collection */
190 637683 : if(item->type == COL_TYPE_COLLECTIONREF) {
191 : /* Our data is a pointer to a whole external collection so dereference
192 : * it or delete */
193 2660 : other_collection = *((struct collection_item **)(item->data));
194 2660 : col_destroy_collection_with_cb(other_collection, cb, custom_data);
195 : }
196 :
197 : /* Call the callback */
198 637683 : if (cb) cb(item->property,
199 : item->property_len,
200 : item->type,
201 : item->data,
202 : item->length,
203 : custom_data);
204 :
205 : TRACE_INFO_STRING("Deleting property:", item->property);
206 : TRACE_INFO_NUMBER("Type:", item->type);
207 :
208 637683 : if (item->property != NULL) free(item->property);
209 637683 : if (item->data != NULL) free(item->data);
210 :
211 637683 : free(item);
212 :
213 : TRACE_FLOW_STRING("col_delete_item","Exit.");
214 : }
215 :
216 : /* Function that cleans the item */
217 496996 : void col_delete_item(struct collection_item *item)
218 : {
219 : TRACE_FLOW_STRING("col_delete_item","Entry point.");
220 :
221 496996 : col_delete_item_with_cb(item, NULL, NULL);
222 :
223 : TRACE_FLOW_STRING("col_delete_item","Exit.");
224 496996 : }
225 :
226 :
227 :
228 : /* A generic function to allocate a property item */
229 637684 : int col_allocate_item(struct collection_item **ci, const char *property,
230 : const void *item_data, int length, int type)
231 : {
232 637684 : struct collection_item *item = NULL;
233 :
234 : TRACE_FLOW_STRING("col_allocate_item", "Entry point.");
235 : TRACE_INFO_NUMBER("Will be using type:", type);
236 :
237 : /* Check the length */
238 637684 : if (length >= COL_MAX_DATA) {
239 : TRACE_ERROR_STRING("col_allocate_item", "Data to long.");
240 0 : return EMSGSIZE;
241 : }
242 :
243 637684 : if (col_validate_property(property)) {
244 : TRACE_ERROR_STRING("Invalid chracters in the property name", property);
245 1 : return EINVAL;
246 : }
247 :
248 : /* Allocate memory for the structure */
249 637683 : item = (struct collection_item *)malloc(sizeof(struct collection_item));
250 637683 : if (item == NULL) {
251 : TRACE_ERROR_STRING("col_allocate_item", "Malloc failed.");
252 0 : return ENOMEM;
253 : }
254 :
255 : /* After we initialize members we can use delete_item() in case of error */
256 637683 : item->next = NULL;
257 637683 : item->property = NULL;
258 637683 : item->data = NULL;
259 : TRACE_INFO_NUMBER("About to set type to:", type);
260 637683 : item->type = type;
261 :
262 : /* Copy property */
263 637683 : item->property = strdup(property);
264 637683 : if (item->property == NULL) {
265 : TRACE_ERROR_STRING("col_allocate_item", "Failed to dup property.");
266 0 : col_delete_item(item);
267 0 : return ENOMEM;
268 : }
269 :
270 637683 : item->phash = col_make_hash(property, 0, &(item->property_len));
271 : TRACE_INFO_NUMBER("Item hash", item->phash);
272 : TRACE_INFO_NUMBER("Item property length", item->property_len);
273 : TRACE_INFO_NUMBER("Item property strlen", strlen(item->property));
274 :
275 : /* Deal with data */
276 637683 : item->data = malloc(length);
277 637683 : if (item->data == NULL) {
278 : TRACE_ERROR_STRING("col_allocate_item", "Failed to dup data.");
279 0 : col_delete_item(item);
280 0 : return ENOMEM;
281 : }
282 :
283 637683 : memcpy(item->data, item_data, length);
284 637683 : item->length = length;
285 :
286 : /* Make sure that data is NULL terminated in case of string */
287 637683 : if (type == COL_TYPE_STRING) ((char *)(item->data))[length-1] = '\0';
288 :
289 637683 : *ci = item;
290 :
291 : TRACE_INFO_STRING("Item property", item->property);
292 : TRACE_INFO_NUMBER("Item property type", item->type);
293 : TRACE_INFO_NUMBER("Item data length", item->length);
294 : TRACE_FLOW_STRING("col_allocate_item", "Success exit.");
295 637683 : return EOK;
296 : }
297 :
298 : /* Structure used to find things in collection */
299 : struct property_search {
300 : const char *property;
301 : uint64_t hash;
302 : struct collection_item *parent;
303 : int index;
304 : int count;
305 : int found;
306 : int use_type;
307 : int type;
308 : };
309 :
310 : /* Find the parent of the item with given name */
311 582 : static int col_find_property(struct collection_item *collection,
312 : const char *refprop,
313 : int idx,
314 : int use_type,
315 : int type,
316 : struct collection_item **parent)
317 : {
318 : struct property_search ps;
319 582 : int i = 0;
320 582 : unsigned depth = 0;
321 :
322 : TRACE_FLOW_STRING("col_find_property", "Entry.");
323 :
324 582 : *parent = NULL;
325 :
326 582 : ps.property = refprop;
327 582 : ps.hash = FNV1a_base;
328 582 : ps.parent = NULL;
329 582 : ps.index = idx;
330 582 : ps.count = 0;
331 582 : ps.found = 0;
332 582 : ps.use_type = use_type;
333 582 : ps.type = type;
334 :
335 : /* Create hash of the string to search */
336 4035 : while(refprop[i] != 0) {
337 2871 : ps.hash = ps.hash ^ toupper(refprop[i]);
338 2871 : ps.hash *= FNV1a_prime;
339 2871 : i++;
340 : }
341 :
342 : /* We do not care about error here */
343 582 : (void)col_walk_items(collection, COL_TRAVERSE_ONELEVEL,
344 : col_parent_traverse_handler,
345 : (void *)parent, NULL, (void *)&ps,
346 : &depth);
347 :
348 582 : if (*parent) {
349 : /* Item is found in the collection */
350 : TRACE_FLOW_STRING("col_find_property", "Exit - item found");
351 198 : return 1;
352 : }
353 :
354 : /* Item is not found */
355 : TRACE_FLOW_STRING("col_find_property", "Exit - item NOT found");
356 384 : return EOK;
357 : }
358 :
359 :
360 :
361 : /* Insert item into the current collection */
362 637638 : int col_insert_item_into_current(struct collection_item *collection,
363 : struct collection_item *item,
364 : int disposition,
365 : const char *refprop,
366 : int idx,
367 : unsigned flags)
368 : {
369 637638 : struct collection_header *header = NULL;
370 637638 : struct collection_item *parent = NULL;
371 637638 : struct collection_item *current = NULL;
372 637638 : int refindex = 0;
373 :
374 : TRACE_FLOW_STRING("col_insert_item_into_current", "Entry point");
375 :
376 : /* Do best effort on the item */
377 637638 : if ((!item) || (item->next)) {
378 : TRACE_ERROR_STRING("Passed in item is invalid", "");
379 0 : return EINVAL;
380 : }
381 :
382 637638 : if (collection == NULL) {
383 : TRACE_INFO_STRING("col_insert_item_into_current",
384 : "Collection accepting is NULL");
385 3927 : if (item->type == COL_TYPE_COLLECTION) {
386 : /* This is a special case of self creation */
387 : TRACE_INFO_STRING("col_insert_item_into_current",
388 : "Adding header item to new collection.");
389 3927 : collection = item;
390 : }
391 : else {
392 : TRACE_ERROR_STRING("Passed in item is invalid", "");
393 0 : return EINVAL;
394 : }
395 : }
396 : else {
397 : /* We can add items only to collections */
398 633711 : if (collection->type != COL_TYPE_COLLECTION) {
399 : TRACE_ERROR_STRING("Attempt to add item to non collection.","");
400 : TRACE_ERROR_STRING("Collection name:", collection->property);
401 : TRACE_ERROR_NUMBER("Collection type:", collection->type);
402 0 : return EINVAL;
403 : }
404 : }
405 :
406 : /* After processing flags we can process disposition */
407 :
408 637638 : header = (struct collection_header *)collection->data;
409 :
410 : /* Check flags first */
411 637638 : switch(flags) {
412 : case COL_INSERT_NOCHECK: /* No check - good just fall through */
413 : TRACE_INFO_STRING("Insert without check", "");
414 637072 : break;
415 : case COL_INSERT_DUPOVER: /* Find item and overwrite - ignore disposition */
416 281 : if (col_find_property(collection, item->property, 0, 0, 0, &parent)) {
417 20 : current = parent->next;
418 20 : item->next = current->next;
419 20 : parent->next = item;
420 20 : if (header->last == current) header->last = item;
421 20 : col_delete_item(current);
422 : /* Deleted one added another - count stays the same! */
423 : TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit");
424 20 : return EOK;
425 : }
426 : /* Not found so we fall thorough and add as requested */
427 261 : break;
428 :
429 : case COL_INSERT_DUPOVERT: /* Find item by name and type and overwrite - ignore disposition */
430 1 : if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) {
431 1 : current = parent->next;
432 1 : item->next = current->next;
433 1 : parent->next = item;
434 1 : if (header->last == current) header->last = item;
435 1 : col_delete_item(current);
436 : /* Deleted one added another - count stays the same! */
437 : TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit");
438 1 : return EOK;
439 : }
440 : /* Not found so we fall thorough and add as requested */
441 0 : break;
442 :
443 282 : case COL_INSERT_DUPERROR: if (col_find_property(collection, item->property, 0, 0, 0, &parent)) {
444 : /* Return error */
445 : TRACE_ERROR_NUMBER("Duplicate property", EEXIST);
446 159 : return EEXIST;
447 : }
448 123 : break;
449 :
450 0 : case COL_INSERT_DUPERRORT: if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) {
451 : /* Return error */
452 : TRACE_ERROR_NUMBER("Duplicate property of the same type", EEXIST);
453 0 : return EEXIST;
454 : }
455 0 : break;
456 :
457 : case COL_INSERT_DUPMOVE: /* Find item and delete */
458 1 : if (col_find_property(collection, item->property, 0, 0, 0, &parent)) {
459 1 : current = parent->next;
460 1 : parent->next = current->next;
461 1 : if (header->last == current) header->last = parent;
462 1 : col_delete_item(current);
463 1 : header->count--;
464 : }
465 : /* Now add item according to the disposition */
466 1 : break;
467 :
468 : case COL_INSERT_DUPMOVET: /* Find item and delete */
469 : TRACE_INFO_STRING("Property:", item->property);
470 : TRACE_INFO_NUMBER("Type:", item->type);
471 1 : if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) {
472 : TRACE_INFO_LNUMBER("Current:", parent->next);
473 1 : current = parent->next;
474 1 : parent->next = current->next;
475 1 : if (header->last == current) header->last = parent;
476 1 : col_delete_item(current);
477 1 : header->count--;
478 : }
479 : /* Now add item according to the disposition */
480 1 : break;
481 :
482 : default: /* The new ones should be added here */
483 : TRACE_ERROR_NUMBER("Flag is not implemented", ENOSYS);
484 0 : return ENOSYS;
485 : }
486 :
487 :
488 637458 : switch (disposition) {
489 : case COL_DSP_END: /* Link new item to the last item in the list if there any */
490 637439 : if (header->count != 0) header->last->next = item;
491 : /* Make sure we save a new last element */
492 637439 : header->last = item;
493 637439 : header->count++;
494 637439 : break;
495 :
496 : case COL_DSP_FRONT: /* Same as above if there is header only */
497 4 : if (header->count == 1) {
498 1 : header->last->next = item;
499 1 : header->last = item;
500 : }
501 : else {
502 3 : item->next = collection->next;
503 3 : collection->next = item;
504 : }
505 4 : header->count++;
506 4 : break;
507 :
508 : case COL_DSP_BEFORE: /* Check argument */
509 3 : if (!refprop) {
510 : TRACE_ERROR_STRING("In this case property is required", "");
511 0 : return EINVAL;
512 : }
513 :
514 : /* We need to find property */
515 3 : if (col_find_property(collection, refprop, 0, 0, 0, &parent)) {
516 3 : item->next = parent->next;
517 3 : parent->next = item;
518 3 : header->count++;
519 : }
520 : else {
521 : TRACE_ERROR_STRING("Property not found", refprop);
522 0 : return ENOENT;
523 : }
524 3 : break;
525 :
526 : case COL_DSP_AFTER: /* Check argument */
527 2 : if (!refprop) {
528 : TRACE_ERROR_STRING("In this case property is required", "");
529 0 : return EINVAL;
530 : }
531 :
532 : /* We need to find property */
533 2 : if (col_find_property(collection, refprop, 0, 0, 0, &parent)) {
534 2 : parent = parent->next;
535 2 : if (parent->next) {
536 : /* It is not the last item */
537 2 : item->next = parent->next;
538 2 : parent->next = item;
539 : }
540 : else {
541 : /* It is the last item */
542 0 : header->last->next = item;
543 0 : header->last = item;
544 : }
545 2 : header->count++;
546 : }
547 : else {
548 : TRACE_ERROR_STRING("Property not found", refprop);
549 0 : return ENOENT;
550 : }
551 2 : break;
552 :
553 5 : case COL_DSP_INDEX: if(idx == 0) {
554 : /* Same is first */
555 1 : if (header->count == 1) {
556 0 : header->last->next = item;
557 0 : header->last = item;
558 : }
559 : else {
560 1 : item->next = collection->next;
561 1 : collection->next = item;
562 : }
563 : }
564 4 : else if(idx >= header->count - 1) {
565 : /* In this case add to the end */
566 3 : header->last->next = item;
567 : /* Make sure we save a new last element */
568 3 : header->last = item;
569 : }
570 : else {
571 : /* In the middle */
572 1 : parent = collection;
573 : /* Move to the right position counting */
574 3 : while (idx > 0) {
575 1 : idx--;
576 1 : if (parent->next) parent = parent->next;
577 : }
578 1 : item->next = parent->next;
579 1 : parent->next = item;
580 : }
581 5 : header->count++;
582 5 : break;
583 :
584 : case COL_DSP_FIRSTDUP:
585 : case COL_DSP_LASTDUP:
586 : case COL_DSP_NDUP:
587 :
588 5 : if (disposition == COL_DSP_FIRSTDUP) refindex = 0;
589 4 : else if (disposition == COL_DSP_LASTDUP) refindex = -1;
590 2 : else refindex = idx;
591 :
592 : /* We need to find property based on index */
593 5 : if (col_find_property(collection, item->property, refindex, 0, 0, &parent)) {
594 5 : item->next = parent->next;
595 5 : parent->next = item;
596 5 : header->count++;
597 5 : if(header->last == parent) header->last = item;
598 : }
599 : else {
600 : TRACE_ERROR_STRING("Property not found", refprop);
601 0 : return ENOENT;
602 : }
603 5 : break;
604 :
605 : default:
606 : TRACE_ERROR_STRING("Disposition is not implemented", "");
607 0 : return ENOSYS;
608 :
609 : }
610 :
611 :
612 : TRACE_INFO_STRING("Collection:", collection->property);
613 : TRACE_INFO_STRING("Just added item is:", item->property);
614 : TRACE_INFO_NUMBER("Item type.", item->type);
615 : TRACE_INFO_NUMBER("Number of items in collection now is.", header->count);
616 :
617 : TRACE_FLOW_STRING("col_insert_item_into_current", "Exit");
618 637458 : return EOK;
619 : }
620 :
621 : /* Extract item from the current collection */
622 496829 : int col_extract_item_from_current(struct collection_item *collection,
623 : int disposition,
624 : const char *refprop,
625 : int idx,
626 : int type,
627 : struct collection_item **ret_ref)
628 : {
629 496829 : struct collection_header *header = NULL;
630 496829 : struct collection_item *parent = NULL;
631 496829 : struct collection_item *current = NULL;
632 496829 : struct collection_item *found = NULL;
633 496829 : int refindex = 0;
634 496829 : int use_type = 0;
635 :
636 : TRACE_FLOW_STRING("col_extract_item_from_current", "Entry point");
637 :
638 : /* Check that collection is not empty */
639 496829 : if ((collection == NULL) || (collection->type != COL_TYPE_COLLECTION)) {
640 : TRACE_ERROR_STRING("Collection can't be NULL", "");
641 0 : return EINVAL;
642 : }
643 :
644 496829 : header = (struct collection_header *)collection->data;
645 :
646 : /* Before moving forward we need to check if there is anything to extract */
647 496829 : if (header->count <= 1) {
648 : TRACE_ERROR_STRING("Collection is empty.", "Nothing to extract.");
649 63 : return ENOENT;
650 : }
651 :
652 496766 : if (type != 0) use_type = 1;
653 :
654 496766 : switch (disposition) {
655 : case COL_DSP_END: /* Extract last item in the list. */
656 4 : parent = collection;
657 4 : current = collection->next;
658 36 : while (current->next != NULL) {
659 28 : parent = current;
660 28 : current = current->next;
661 : }
662 4 : *ret_ref = parent->next;
663 4 : parent->next = NULL;
664 : /* Special case - one data element */
665 4 : if (header->count == 2) header->last = collection;
666 4 : else header->last = parent;
667 4 : break;
668 :
669 : case COL_DSP_FRONT: /* Extract first item in the list */
670 496755 : *ret_ref = collection->next;
671 496755 : collection->next = (*ret_ref)->next;
672 : /* Special case - one data element */
673 496755 : if (header->count == 2) header->last = collection;
674 496755 : break;
675 :
676 : case COL_DSP_BEFORE: /* Check argument */
677 1 : if (!refprop) {
678 : TRACE_ERROR_STRING("In this case property is required", "");
679 0 : return EINVAL;
680 : }
681 :
682 : /* We have to do it in two steps */
683 : /* First find the property that is mentioned */
684 1 : if (col_find_property(collection, refprop, 0, use_type, type, &found)) {
685 : /* We found the requested property */
686 1 : if (found->next == collection->next) {
687 : /* The referenced property is the first in the list */
688 : TRACE_ERROR_STRING("Nothing to extract. Lists starts with property", refprop);
689 0 : return ENOENT;
690 : }
691 : /* Get to the parent of the item that is before the one that is found */
692 1 : parent = collection;
693 1 : current = collection->next;
694 3 : while (current != found) {
695 1 : parent = current;
696 1 : current = current->next;
697 : }
698 1 : *ret_ref = current;
699 1 : parent->next = current->next;
700 :
701 : }
702 : else {
703 : TRACE_ERROR_STRING("Property not found", refprop);
704 0 : return ENOENT;
705 : }
706 1 : break;
707 :
708 : case COL_DSP_AFTER: /* Check argument */
709 2 : if (!refprop) {
710 : TRACE_ERROR_STRING("In this case property is required", "");
711 0 : return EINVAL;
712 : }
713 :
714 : /* We need to find property */
715 2 : if (col_find_property(collection, refprop, 0, use_type, type, &parent)) {
716 2 : current = parent->next;
717 2 : if (current->next) {
718 2 : *ret_ref = current->next;
719 2 : current->next = (*ret_ref)->next;
720 : /* If we removed the last element adjust header */
721 2 : if(current->next == NULL) header->last = current;
722 : }
723 : else {
724 : TRACE_ERROR_STRING("Property is last in the list", refprop);
725 0 : return ENOENT;
726 : }
727 : }
728 : else {
729 : TRACE_ERROR_STRING("Property not found", refprop);
730 0 : return ENOENT;
731 : }
732 2 : break;
733 :
734 1 : case COL_DSP_INDEX: if (idx == 0) {
735 0 : *ret_ref = collection->next;
736 0 : collection->next = (*ret_ref)->next;
737 : /* Special case - one data element */
738 0 : if (header->count == 2) header->last = collection;
739 : }
740 : /* Index 0 stands for the first data element.
741 : * Count includes header element.
742 : */
743 1 : else if (idx >= (header->count - 1)) {
744 : TRACE_ERROR_STRING("Index is out of boundaries", refprop);
745 0 : return ENOENT;
746 : }
747 : else {
748 : /* Loop till the element with right index */
749 1 : refindex = 0;
750 1 : parent = collection;
751 1 : current = collection->next;
752 3 : while (refindex < idx) {
753 1 : parent = current;
754 1 : current = current->next;
755 1 : refindex++;
756 : }
757 1 : *ret_ref = parent->next;
758 1 : parent->next = (*ret_ref)->next;
759 : /* If we removed the last element adjust header */
760 1 : if (parent->next == NULL) header->last = parent;
761 : }
762 1 : break;
763 :
764 : case COL_DSP_FIRSTDUP:
765 : case COL_DSP_LASTDUP:
766 : case COL_DSP_NDUP:
767 :
768 3 : if (disposition == COL_DSP_FIRSTDUP) refindex = 0;
769 2 : else if (disposition == COL_DSP_LASTDUP) refindex = -2;
770 1 : else refindex = idx;
771 :
772 : /* We need to find property based on index */
773 3 : if (col_find_property(collection, refprop, refindex, use_type, type, &parent)) {
774 3 : *ret_ref = parent->next;
775 3 : parent->next = (*ret_ref)->next;
776 : /* If we removed the last element adjust header */
777 3 : if(parent->next == NULL) header->last = parent;
778 : }
779 : else {
780 : TRACE_ERROR_STRING("Property not found", refprop);
781 0 : return ENOENT;
782 : }
783 3 : break;
784 :
785 : default:
786 : TRACE_ERROR_STRING("Disposition is not implemented", "");
787 0 : return ENOSYS;
788 :
789 : }
790 :
791 :
792 : /* Clear item and reduce count */
793 496766 : (*ret_ref)->next = NULL;
794 496766 : header->count--;
795 :
796 : TRACE_INFO_STRING("Collection:", (*ret_ref)->property);
797 : TRACE_INFO_NUMBER("Item type.", (*ret_ref)->type);
798 : TRACE_INFO_NUMBER("Number of items in collection now is.", header->count);
799 :
800 : TRACE_FLOW_STRING("col_extract_item_from_current", "Exit");
801 496766 : return EOK;
802 : }
803 :
804 : /* Extract item from the collection */
805 11 : int col_extract_item(struct collection_item *collection,
806 : const char *subcollection,
807 : int disposition,
808 : const char *refprop,
809 : int idx,
810 : int type,
811 : struct collection_item **ret_ref)
812 : {
813 11 : struct collection_item *col = NULL;
814 11 : int error = EOK;
815 :
816 : TRACE_FLOW_STRING("col_extract_item", "Entry point");
817 :
818 : /* Check that collection is not empty */
819 11 : if ((collection == NULL) || (collection->type != COL_TYPE_COLLECTION)) {
820 : TRACE_ERROR_STRING("Collection can't be NULL", "");
821 0 : return EINVAL;
822 : }
823 :
824 : /* Get subcollection if needed */
825 11 : if (subcollection == NULL) {
826 11 : col = collection;
827 : }
828 : else {
829 : TRACE_INFO_STRING("Subcollection id not null, searching", subcollection);
830 0 : error = col_find_item_and_do(collection, subcollection,
831 : COL_TYPE_COLLECTIONREF,
832 : COL_TRAVERSE_DEFAULT,
833 : col_get_subcollection, (void *)(&col),
834 : COLLECTION_ACTION_FIND);
835 0 : if (error) {
836 : TRACE_ERROR_NUMBER("Search for subcollection returned error:", error);
837 0 : return error;
838 : }
839 :
840 0 : if (col == NULL) {
841 : TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
842 0 : return ENOENT;
843 : }
844 :
845 : }
846 :
847 : /* Extract from the current collection */
848 11 : error = col_extract_item_from_current(col,
849 : disposition,
850 : refprop,
851 : idx,
852 : type,
853 : ret_ref);
854 11 : if (error) {
855 : TRACE_ERROR_NUMBER("Failed to extract item from the current collection", error);
856 0 : return error;
857 : }
858 :
859 : TRACE_FLOW_STRING("col_extract_item", "Exit");
860 11 : return EOK;
861 : }
862 :
863 :
864 : /* Remove item (property) from collection.*/
865 2 : int col_remove_item(struct collection_item *ci,
866 : const char *subcollection,
867 : int disposition,
868 : const char *refprop,
869 : int idx,
870 : int type)
871 : {
872 2 : int error = EOK;
873 2 : struct collection_item *ret_ref = NULL;
874 :
875 : TRACE_FLOW_STRING("col_remove_item", "Exit");
876 :
877 : /* Extract from the current collection */
878 2 : error = col_extract_item(ci,
879 : subcollection,
880 : disposition,
881 : refprop,
882 : idx,
883 : type,
884 : &ret_ref);
885 2 : if (error) {
886 : TRACE_ERROR_NUMBER("Failed to extract item from the collection", error);
887 0 : return error;
888 : }
889 :
890 2 : col_delete_item(ret_ref);
891 :
892 : TRACE_FLOW_STRING("col_remove_item", "Exit");
893 2 : return EOK;
894 : }
895 :
896 : /* Remove item (property) from current collection.
897 : * Just a simple wrapper.
898 : */
899 1 : int col_remove_item_from_current(struct collection_item *ci,
900 : int disposition,
901 : const char *refprop,
902 : int idx,
903 : int type)
904 : {
905 1 : int error = EOK;
906 :
907 : TRACE_FLOW_STRING("col_remove_item_from_current", "Exit");
908 :
909 : /* Remove item from current collection */
910 1 : error = col_remove_item(ci,
911 : NULL,
912 : disposition,
913 : refprop,
914 : idx,
915 : type);
916 :
917 : TRACE_FLOW_NUMBER("col_remove_item_from_current. Exit. Returning", error);
918 1 : return error;
919 : }
920 :
921 :
922 : /* Insert the item into the collection or subcollection */
923 637628 : int col_insert_item(struct collection_item *collection,
924 : const char *subcollection,
925 : struct collection_item *item,
926 : int disposition,
927 : const char *refprop,
928 : int idx,
929 : unsigned flags)
930 : {
931 : int error;
932 637628 : struct collection_item *acceptor = NULL;
933 :
934 : TRACE_FLOW_STRING("col_insert_item", "Entry point.");
935 :
936 : /* Do best effort on the item */
937 637628 : if ((!item) || (item->next)) {
938 : TRACE_ERROR_STRING("Passed in item is invalid", "");
939 0 : return EINVAL;
940 : }
941 :
942 : /* Check that collection is not empty */
943 637628 : if ((collection == NULL) && (item->type != COL_TYPE_COLLECTION)) {
944 : TRACE_ERROR_STRING("Collection can't be NULL", "");
945 0 : return EINVAL;
946 : }
947 :
948 : /* Add item to collection */
949 637628 : if (subcollection == NULL) {
950 637611 : acceptor = collection;
951 : }
952 : else {
953 : TRACE_INFO_STRING("Subcollection id not null, searching", subcollection);
954 17 : error = col_find_item_and_do(collection, subcollection,
955 : COL_TYPE_COLLECTIONREF,
956 : COL_TRAVERSE_DEFAULT,
957 : col_get_subcollection, (void *)(&acceptor),
958 : COLLECTION_ACTION_FIND);
959 17 : if (error) {
960 : TRACE_ERROR_NUMBER("Search for subcollection returned error:", error);
961 0 : return error;
962 : }
963 :
964 17 : if (acceptor == NULL) {
965 : TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
966 0 : return ENOENT;
967 : }
968 :
969 : }
970 :
971 : /* Instert item to the current collection */
972 637628 : error = col_insert_item_into_current(acceptor,
973 : item,
974 : disposition,
975 : refprop,
976 : idx,
977 : flags);
978 :
979 637628 : if (error) {
980 : TRACE_ERROR_NUMBER("Failed to insert item into current collection", error);
981 159 : return error;
982 : }
983 :
984 : TRACE_FLOW_STRING("insert_item", "Exit");
985 637469 : return EOK;
986 : }
987 :
988 :
989 : /* Insert property with reference.
990 : * This is internal function so we do not check parameters.
991 : * See external wrapper below.
992 : */
993 571341 : static int col_insert_property_with_ref_int(struct collection_item *collection,
994 : const char *subcollection,
995 : int disposition,
996 : const char *refprop,
997 : int idx,
998 : unsigned flags,
999 : const char *property,
1000 : int type,
1001 : const void *data,
1002 : int length,
1003 : struct collection_item **ret_ref)
1004 : {
1005 571341 : struct collection_item *item = NULL;
1006 : int error;
1007 :
1008 : TRACE_FLOW_STRING("col_insert_property_with_ref_int", "Entry point.");
1009 :
1010 : /* Create a new property out of the given parameters */
1011 571341 : error = col_allocate_item(&item, property, data, length, type);
1012 571341 : if (error) {
1013 : TRACE_ERROR_NUMBER("Failed to allocate item", error);
1014 1 : return error;
1015 : }
1016 :
1017 : /* Send the property to the insert_item function */
1018 571340 : error = col_insert_item(collection,
1019 : subcollection,
1020 : item,
1021 : disposition,
1022 : refprop,
1023 : idx,
1024 : flags);
1025 571340 : if (error) {
1026 : TRACE_ERROR_NUMBER("Failed to insert item", error);
1027 159 : col_delete_item(item);
1028 159 : return error;
1029 : }
1030 :
1031 571181 : if (ret_ref) *ret_ref = item;
1032 :
1033 : TRACE_FLOW_STRING("col_insert_property_with_ref_int", "Exit");
1034 571181 : return EOK;
1035 : }
1036 :
1037 : /* Special function used to copy item from one
1038 : * collection to another using caller's callback.
1039 : */
1040 66261 : static int col_copy_item_with_cb(struct collection_item *collection,
1041 : const char *property,
1042 : int type,
1043 : const void *data,
1044 : int length,
1045 : col_copy_cb copy_cb,
1046 : void *ext_data)
1047 : {
1048 66261 : struct collection_item *item = NULL;
1049 66261 : int skip = 0;
1050 66261 : int error = EOK;
1051 :
1052 : TRACE_FLOW_STRING("col_copy_item_with_cb", "Entry point.");
1053 :
1054 : /* Create a new property out of the given parameters */
1055 66261 : error = col_allocate_item(&item, property, data, length, type);
1056 66261 : if (error) {
1057 : TRACE_ERROR_NUMBER("Failed to allocate item", error);
1058 0 : return error;
1059 : }
1060 :
1061 : /* Call callback if any */
1062 66261 : if (copy_cb) {
1063 : TRACE_INFO_STRING("Calling callback for item:", item->property);
1064 66246 : error = copy_cb(item, ext_data, &skip);
1065 66246 : if (error) {
1066 : TRACE_ERROR_NUMBER("Callback failed", error);
1067 0 : col_delete_item(item);
1068 0 : return error;
1069 : }
1070 : }
1071 :
1072 : /* Are we told to skip this item? */
1073 66261 : if (skip) col_delete_item(item);
1074 : else {
1075 : /* Insted property into the collection */
1076 66260 : error = col_insert_item(collection,
1077 : NULL,
1078 : item,
1079 : COL_DSP_END,
1080 : NULL,
1081 : 0,
1082 : 0);
1083 66260 : if (error) {
1084 : TRACE_ERROR_NUMBER("Failed to insert item", error);
1085 0 : col_delete_item(item);
1086 0 : return error;
1087 : }
1088 : }
1089 :
1090 : TRACE_FLOW_STRING("col_copy_item_with_cb", "Exit");
1091 66261 : return EOK;
1092 : }
1093 :
1094 :
1095 : /* This is public function so we need to check the validity
1096 : * of the arguments.
1097 : */
1098 564754 : int col_insert_property_with_ref(struct collection_item *collection,
1099 : const char *subcollection,
1100 : int disposition,
1101 : const char *refprop,
1102 : int idx,
1103 : unsigned flags,
1104 : const char *property,
1105 : int type,
1106 : const void *data,
1107 : int length,
1108 : struct collection_item **ret_ref)
1109 : {
1110 : int error;
1111 :
1112 : TRACE_FLOW_STRING("col_insert_property_with_ref", "Entry point.");
1113 :
1114 : /* Check that collection is not empty */
1115 564754 : if (collection == NULL) {
1116 : TRACE_ERROR_STRING("Collection cant be NULL", "");
1117 0 : return EINVAL;
1118 : }
1119 :
1120 564754 : error = col_insert_property_with_ref_int(collection,
1121 : subcollection,
1122 : disposition,
1123 : refprop,
1124 : idx,
1125 : flags,
1126 : property,
1127 : type,
1128 : data,
1129 : length,
1130 : ret_ref);
1131 :
1132 : TRACE_FLOW_NUMBER("col_insert_property_with_ref_int Returning:", error);
1133 564754 : return error;
1134 : }
1135 : /* TRAVERSE HANDLERS */
1136 :
1137 : /* Special handler to just set a flag if the item is found */
1138 8 : static int col_is_in_item_handler(const char *property,
1139 : int property_len,
1140 : int type,
1141 : void *data,
1142 : int length,
1143 : void *found,
1144 : int *dummy)
1145 : {
1146 : TRACE_FLOW_STRING("col_is_in_item_handler", "Entry.");
1147 : TRACE_INFO_STRING("Property:", property);
1148 : TRACE_INFO_NUMBER("Property length:", property_len);
1149 : TRACE_INFO_NUMBER("Type:", type);
1150 : TRACE_INFO_NUMBER("Length:", length);
1151 :
1152 8 : *((int *)(found)) = COL_MATCH;
1153 :
1154 : TRACE_FLOW_STRING("col_is_in_item_handler", "Success Exit.");
1155 :
1156 8 : return EOK;
1157 : }
1158 :
1159 : /* Special handler to retrieve the sub collection */
1160 94 : static int col_get_subcollection(const char *property,
1161 : int property_len,
1162 : int type,
1163 : void *data,
1164 : int length,
1165 : void *found,
1166 : int *dummy)
1167 : {
1168 : TRACE_FLOW_STRING("col_get_subcollection", "Entry.");
1169 : TRACE_INFO_STRING("Property:", property);
1170 : TRACE_INFO_NUMBER("Property length:", property_len);
1171 : TRACE_INFO_NUMBER("Type:", type);
1172 : TRACE_INFO_NUMBER("Length:", length);
1173 :
1174 94 : *((struct collection_item **)(found)) = *((struct collection_item **)(data));
1175 :
1176 : TRACE_FLOW_STRING("col_get_subcollection","Success Exit.");
1177 :
1178 94 : return EOK;
1179 :
1180 : }
1181 :
1182 :
1183 :
1184 : /* CLEANUP */
1185 :
1186 : /* Cleans the collection tree including current item. */
1187 : /* The passed in variable should not be used after the call
1188 : * as memory is freed!!! */
1189 144614 : static void col_delete_collection(struct collection_item *ci,
1190 : col_item_cleanup_fn cb,
1191 : void *custom_data)
1192 : {
1193 : TRACE_FLOW_STRING("col_delete_collection", "Entry.");
1194 :
1195 144614 : if (ci == NULL) {
1196 : TRACE_FLOW_STRING("col_delete_collection", "Nothing to do Exit.");
1197 148541 : return;
1198 : }
1199 :
1200 : TRACE_INFO_STRING("Real work to do", "");
1201 : TRACE_INFO_STRING("Property", ci->property);
1202 : TRACE_INFO_NUMBER("Next item", ci->next);
1203 :
1204 140687 : col_delete_collection(ci->next, cb, custom_data);
1205 :
1206 : /* Delete this item */
1207 140687 : col_delete_item_with_cb(ci, cb, custom_data);
1208 : TRACE_FLOW_STRING("col_delete_collection", "Exit.");
1209 : }
1210 :
1211 :
1212 : /* NAME MANAGEMENT - used by search */
1213 :
1214 : /* Internal data structures used for search */
1215 :
1216 :
1217 : struct find_name {
1218 : const char *name_to_find;
1219 : int name_len_to_find;
1220 : uint64_t hash;
1221 : int type_to_match;
1222 : char *given_name;
1223 : int given_len;
1224 : struct path_data *current_path;
1225 : int action;
1226 : };
1227 :
1228 : /* Create a new name */
1229 74166 : static int col_create_path_data(struct path_data **name_path,
1230 : const char *name, int length,
1231 : const char *property, int property_len,
1232 : char sep)
1233 : {
1234 74166 : int error = EOK;
1235 : struct path_data *new_name_path;
1236 :
1237 : TRACE_FLOW_STRING("col_create_path_data", "Entry.");
1238 :
1239 : TRACE_INFO_STRING("Constructing path from name:", name);
1240 : TRACE_INFO_STRING("Constructing path from property:", property);
1241 :
1242 : /* Allocate structure */
1243 74166 : new_name_path = (struct path_data *)malloc(sizeof(struct path_data));
1244 74166 : if (new_name_path == NULL) {
1245 : TRACE_ERROR_NUMBER("Failed to allocate memory for new path struct.", ENOMEM);
1246 0 : return ENOMEM;
1247 : }
1248 74166 : new_name_path->name = malloc(length + property_len + 2);
1249 74166 : if (new_name_path->name == NULL) {
1250 : TRACE_ERROR_NUMBER("Failed to allocate memory for new path name.", ENOMEM);
1251 0 : free(new_name_path);
1252 0 : return ENOMEM;
1253 : }
1254 :
1255 : /* Construct the new name */
1256 74166 : new_name_path->length = 0;
1257 :
1258 74166 : if(length > 0) {
1259 5564 : memcpy(new_name_path->name, name, length);
1260 5564 : new_name_path->length = length;
1261 5564 : new_name_path->name[new_name_path->length] = sep;
1262 5564 : new_name_path->length++;
1263 5564 : new_name_path->name[new_name_path->length] = '\0';
1264 : TRACE_INFO_STRING("Name so far:", new_name_path->name);
1265 : TRACE_INFO_NUMBER("Len so far:", new_name_path->length);
1266 : }
1267 74166 : memcpy(&new_name_path->name[new_name_path->length], property, property_len);
1268 74166 : new_name_path->length += property_len;
1269 74166 : new_name_path->name[new_name_path->length] = '\0';
1270 :
1271 : /* Link to the chain */
1272 74166 : new_name_path->previous_path = *name_path;
1273 74166 : *name_path = new_name_path;
1274 :
1275 : TRACE_INFO_STRING("Constructed path", new_name_path->name);
1276 :
1277 :
1278 : TRACE_FLOW_NUMBER("col_create_path_data. Returning:", error);
1279 74166 : return error;
1280 : }
1281 :
1282 : /* Matching item name and type */
1283 124997608 : static int col_match_item(struct collection_item *current,
1284 : struct find_name *traverse_data)
1285 : {
1286 :
1287 : const char *find_str;
1288 : const char *start;
1289 : const char *data_str;
1290 :
1291 : TRACE_FLOW_STRING("col_match_item", "Entry");
1292 :
1293 124997608 : if (traverse_data->type_to_match & current->type) {
1294 :
1295 : /* Check if there is any value to match */
1296 249990697 : if ((traverse_data->name_to_find == NULL) ||
1297 124995348 : (*(traverse_data->name_to_find) == '\0')) {
1298 : TRACE_INFO_STRING("col_match_item",
1299 : "Returning MATCH because there is no search criteria!");
1300 2 : return COL_MATCH;
1301 : }
1302 :
1303 : /* Check the hashes - if they do not match return */
1304 124995347 : if (traverse_data->hash != current->phash) {
1305 : TRACE_INFO_STRING("col_match_item","Returning NO match!");
1306 124994692 : return COL_NOMATCH;
1307 : }
1308 :
1309 : /* We will do the actual string comparison only if the hashes matched */
1310 :
1311 : /* Start comparing the two strings from the end */
1312 655 : find_str = traverse_data->name_to_find + traverse_data->name_len_to_find;
1313 655 : start = current->property;
1314 655 : data_str = start + current->property_len;
1315 :
1316 : TRACE_INFO_STRING("Searching for:", traverse_data->name_to_find);
1317 : TRACE_INFO_STRING("Item name:", current->property);
1318 : TRACE_INFO_STRING("Current path:", traverse_data->current_path->name);
1319 : TRACE_INFO_NUMBER("Searching:", toupper(*find_str));
1320 : TRACE_INFO_NUMBER("Have:", toupper(*data_str));
1321 :
1322 : /* We start pointing to 0 so the loop will be executed at least once */
1323 5477 : while (toupper(*data_str) == toupper(*find_str)) {
1324 :
1325 : TRACE_INFO_STRING("Loop iteration:","");
1326 :
1327 4819 : if (data_str == start) {
1328 658 : if (find_str > traverse_data->name_to_find) {
1329 16 : if (*(find_str-1) == '!') {
1330 : /* We matched the property but the search string is
1331 : * longer so we need to continue matching */
1332 : TRACE_INFO_STRING("col_match_item",
1333 : "Need to continue matching");
1334 16 : start = traverse_data->current_path->name;
1335 16 : data_str = &start[traverse_data->current_path->length - 1];
1336 16 : find_str -= 2;
1337 16 : continue;
1338 : }
1339 : else {
1340 : TRACE_INFO_STRING("col_match_item","Returning NO match!");
1341 0 : return COL_NOMATCH;
1342 : }
1343 : }
1344 : else {
1345 : TRACE_INFO_STRING("col_match_item","Returning MATCH!");
1346 642 : return COL_MATCH;
1347 : }
1348 : }
1349 4171 : else if ((find_str == traverse_data->name_to_find) &&
1350 20 : (*(data_str-1) == '!')) return COL_MATCH;
1351 :
1352 4151 : data_str--;
1353 4151 : find_str--;
1354 : TRACE_INFO_NUMBER("Searching:", toupper(*find_str));
1355 : TRACE_INFO_NUMBER("Have:", toupper(*data_str));
1356 :
1357 : }
1358 : }
1359 :
1360 : TRACE_FLOW_STRING("col_match_item","Returning NO match!");
1361 2262 : return COL_NOMATCH;
1362 :
1363 : }
1364 :
1365 : /* Function to delete the data that contains search path */
1366 74166 : static void col_delete_path_data(struct path_data *path)
1367 : {
1368 : TRACE_FLOW_STRING("col_delete_path_data","Entry.");
1369 :
1370 74166 : if (path != NULL) {
1371 : TRACE_INFO_STRING("col_delete_path_data", "Item to delete exits.");
1372 74166 : if (path->previous_path != NULL) {
1373 : TRACE_INFO_STRING("col_delete_path_data",
1374 : "But previous item to delete exits to. Nesting.");
1375 34 : col_delete_path_data(path->previous_path);
1376 : }
1377 74166 : if (path->name != NULL) {
1378 : TRACE_INFO_STRING("col_delete_path_data Deleting path:", path->name);
1379 74166 : free(path->name);
1380 : }
1381 : TRACE_INFO_STRING("col_delete_path_data", "Deleting path element");
1382 74166 : free(path);
1383 : }
1384 : TRACE_FLOW_STRING("col_delete_path_data", "Exit");
1385 74166 : }
1386 :
1387 :
1388 : /* MAIN TRAVERSAL FUNCTION */
1389 :
1390 : /* Internal function to walk collection */
1391 : /* For each item walked it will call traverse handler.
1392 : Traverse handler accepts: current item,
1393 : user provided item handler and user provided custom data. */
1394 : /* See below different traverse handlers for different cases */
1395 78903 : static int col_walk_items(struct collection_item *ci,
1396 : int mode_flags,
1397 : internal_item_fn traverse_handler,
1398 : void *traverse_data,
1399 : col_item_fn user_item_handler,
1400 : void *custom_data,
1401 : unsigned *depth)
1402 : {
1403 : struct collection_item *current;
1404 78903 : struct collection_item *parent = NULL;
1405 : struct collection_item *sub;
1406 78903 : int stop = 0;
1407 78903 : int error = EOK;
1408 :
1409 : TRACE_FLOW_STRING("col_walk_items", "Entry.");
1410 : TRACE_INFO_NUMBER("Mode flags:", mode_flags);
1411 :
1412 : /* Increase depth */
1413 : /* NOTE: The depth is increased at the entry to the function.
1414 : * and decreased right before the exit so it is safe to decrease it.
1415 : */
1416 78903 : (*depth)++;
1417 :
1418 78903 : current = ci;
1419 :
1420 125437065 : while (current) {
1421 :
1422 : TRACE_INFO_STRING("Processing item:", current->property);
1423 : TRACE_INFO_NUMBER("Item type:", current->type);
1424 :
1425 125280156 : if (current->type == COL_TYPE_COLLECTIONREF) {
1426 :
1427 : TRACE_INFO_STRING("Subcollection:", current->property);
1428 :
1429 9461 : if ((mode_flags & COL_TRAVERSE_IGNORE) == 0) {
1430 :
1431 : TRACE_INFO_STRING("Subcollection is not ignored.", "");
1432 : /* We are not ignoring sub collections */
1433 :
1434 9461 : if ((mode_flags & COL_TRAVERSE_FLAT) == 0) {
1435 :
1436 : TRACE_INFO_STRING("Subcollection is not flattened.", "");
1437 : /* We are not flattening sub collections.
1438 : * The flattening means that we are not going
1439 : * to return reference and headers for sub collections.
1440 : * We will also not do special end collection
1441 : * invocation for sub collections.
1442 : */
1443 9461 : error = traverse_handler(ci, parent, current, traverse_data,
1444 : user_item_handler, custom_data, &stop);
1445 9461 : if (stop != 0) {
1446 : TRACE_INFO_STRING("Traverse handler returned STOP.", "");
1447 206 : error = EINTR_INTERNAL;
1448 : }
1449 : /* Check what error we got */
1450 9461 : if (error == EINTR_INTERNAL) {
1451 : TRACE_FLOW_NUMBER("Internal error - means we are stopping.", error);
1452 206 : (*depth)--;
1453 206 : return error;
1454 : }
1455 9255 : else if (error) {
1456 : TRACE_ERROR_NUMBER("Traverse handler returned error.", error);
1457 0 : (*depth)--;
1458 0 : return error;
1459 : }
1460 : }
1461 :
1462 9255 : if ((mode_flags & COL_TRAVERSE_ONELEVEL) == 0) {
1463 : TRACE_INFO_STRING("Before diving into sub collection","");
1464 7916 : sub = *((struct collection_item **)(current->data));
1465 : TRACE_INFO_STRING("Sub collection name", sub->property);
1466 : TRACE_INFO_NUMBER("Header type", sub->type);
1467 : /* We need to go into sub collections */
1468 7916 : error = col_walk_items(sub, mode_flags,
1469 : traverse_handler, traverse_data,
1470 : user_item_handler, custom_data,
1471 : depth);
1472 : TRACE_INFO_STRING("Returned from sub collection processing", "");
1473 : TRACE_INFO_STRING("Done processing item:", current->property);
1474 : TRACE_INFO_NUMBER("Done processing item type:", current->type);
1475 :
1476 : }
1477 : }
1478 : }
1479 : else {
1480 : /* Check if it is a header and we are not on the root level.
1481 : * If we are flattening collection we need to skip headers
1482 : * for sub collections.
1483 : */
1484 :
1485 : /* Call handler if:
1486 : * a) It is not collection header
1487 : * OR
1488 : * b) It is header we are flattening but we are on top level
1489 : * OR
1490 : * c) It is header and we are not flattening.
1491 : */
1492 125349598 : if ((current->type != COL_TYPE_COLLECTION) ||
1493 157806 : (((mode_flags & COL_TRAVERSE_FLAT) != 0) && (*depth == 1)) ||
1494 78902 : ((mode_flags & COL_TRAVERSE_FLAT) == 0)) {
1495 : /* Call handler then move on */
1496 125270695 : error = traverse_handler(ci, parent, current,
1497 : traverse_data, user_item_handler,
1498 : custom_data, &stop);
1499 :
1500 : }
1501 : }
1502 : /* If we are stopped - return EINTR_INTERNAL */
1503 125279950 : if (stop != 0) {
1504 : TRACE_INFO_STRING("Traverse handler returned STOP.", "");
1505 657 : error = EINTR_INTERNAL;
1506 : }
1507 : /* Check what error we got */
1508 125279950 : if (error == EINTR_INTERNAL) {
1509 : TRACE_FLOW_NUMBER("Internal error - means we are stopping.", error);
1510 691 : (*depth)--;
1511 691 : return error;
1512 : }
1513 125279259 : else if (error) {
1514 : TRACE_ERROR_NUMBER("Traverse handler returned error.", error);
1515 0 : (*depth)--;
1516 0 : return error;
1517 : }
1518 :
1519 : TRACE_INFO_NUMBER("Next element", current->next);
1520 :
1521 125279259 : parent = current;
1522 125279259 : current = current->next;
1523 : }
1524 :
1525 : TRACE_INFO_STRING("Out of loop", "");
1526 :
1527 : /* Check if we need to have a special
1528 : * call at the end of the collection.
1529 : */
1530 78006 : if ((mode_flags & COL_TRAVERSE_END) != 0) {
1531 :
1532 : /* Do this dummy invocation only:
1533 : * a) If we are flattening and on the root level
1534 : * b) We are not flattening
1535 : */
1536 146956 : if ((((mode_flags & COL_TRAVERSE_FLAT) != 0) && (*depth == 1)) ||
1537 73478 : ((mode_flags & COL_TRAVERSE_FLAT) == 0)) {
1538 :
1539 : TRACE_INFO_STRING("About to do the special end collection invocation of handler", "");
1540 73478 : error = traverse_handler(ci, parent, current,
1541 : traverse_data, user_item_handler,
1542 : custom_data, &stop);
1543 : }
1544 : }
1545 :
1546 : TRACE_FLOW_NUMBER("col_walk_items. Returns: ", error);
1547 78006 : (*depth)--;
1548 78006 : return error;
1549 : }
1550 :
1551 :
1552 : /* ACTION */
1553 :
1554 : /* Find an item by property name and perform an action on it. */
1555 : /* No pattern matching supported in the first implementation. */
1556 : /* To refer to child properties use notatation like this: */
1557 : /* parent!child!subchild!subsubchild etc. */
1558 68602 : static int col_find_item_and_do(struct collection_item *ci,
1559 : const char *property_to_find,
1560 : int type,
1561 : int mode_flags,
1562 : col_item_fn item_handler,
1563 : void *custom_data,
1564 : int action)
1565 : {
1566 :
1567 68602 : int error = EOK;
1568 68602 : struct find_name *traverse_data = NULL;
1569 68602 : unsigned depth = 0;
1570 68602 : int count = 0;
1571 : const char *last_part;
1572 : char *sep;
1573 :
1574 : TRACE_FLOW_STRING("col_find_item_and_do", "Entry.");
1575 :
1576 : /* Item handler is always required */
1577 68602 : if ((item_handler == NULL) &&
1578 : (action == COLLECTION_ACTION_FIND)) {
1579 : TRACE_ERROR_NUMBER("No item handler - returning error!", EINVAL);
1580 0 : return EINVAL;
1581 : }
1582 :
1583 : /* Collection is requered */
1584 68602 : if (ci == NULL) {
1585 : TRACE_ERROR_NUMBER("No collection to search!", EINVAL);
1586 0 : return EINVAL;
1587 : }
1588 :
1589 : /* Make sure that there is anything to search */
1590 68602 : type &= COL_TYPE_ANY;
1591 68602 : if ((type == 0) &&
1592 1 : ((property_to_find == NULL) ||
1593 1 : ((property_to_find != NULL) && (*property_to_find == '\0')))) {
1594 : TRACE_ERROR_NUMBER("No item search criteria specified - returning error!", ENOENT);
1595 2 : return ENOENT;
1596 : }
1597 :
1598 : /* Prepare data for traversal */
1599 68600 : traverse_data = (struct find_name *)malloc(sizeof(struct find_name));
1600 68600 : if (traverse_data == NULL) {
1601 : TRACE_ERROR_NUMBER("Failed to allocate traverse data memory - returning error!", ENOMEM);
1602 0 : return ENOMEM;
1603 : }
1604 :
1605 : TRACE_INFO_STRING("col_find_item_and_do", "Filling in traverse data.");
1606 :
1607 68600 : traverse_data->name_to_find = property_to_find;
1608 :
1609 68600 : if (property_to_find != NULL) {
1610 :
1611 68599 : traverse_data->name_len_to_find = strlen(property_to_find);
1612 :
1613 : /* Check if the search string ends with "!" - this is illegal */
1614 68599 : if (traverse_data->name_to_find[traverse_data->name_len_to_find - 1] == '!') {
1615 : TRACE_ERROR_NUMBER("Search string is invalid.", EINVAL);
1616 1 : free(traverse_data);
1617 1 : return EINVAL;
1618 : }
1619 :
1620 : /* Find last ! if any */
1621 68598 : sep = strrchr(traverse_data->name_to_find, '!');
1622 68598 : if (sep != NULL) {
1623 13 : sep++;
1624 13 : last_part = sep;
1625 : }
1626 68585 : else last_part = traverse_data->name_to_find;
1627 :
1628 : TRACE_INFO_STRING("Last item", last_part);
1629 :
1630 : /* Create hash of the last part */
1631 68598 : traverse_data->hash = FNV1a_base;
1632 :
1633 : /* Create hash of the string to search */
1634 1985707 : while(last_part[count] != 0) {
1635 1848511 : traverse_data->hash = traverse_data->hash ^ toupper(last_part[count]);
1636 1848511 : traverse_data->hash *= FNV1a_prime;
1637 1848511 : count++;
1638 : }
1639 : }
1640 : else {
1641 : /* We a looking for a first element of a given type */
1642 : TRACE_INFO_STRING("No search string", "");
1643 1 : traverse_data->name_len_to_find = 0;
1644 : }
1645 :
1646 :
1647 68599 : traverse_data->type_to_match = type;
1648 68599 : traverse_data->given_name = NULL;
1649 68599 : traverse_data->given_len = 0;
1650 68599 : traverse_data->current_path = NULL;
1651 68599 : traverse_data->action = action;
1652 :
1653 68599 : mode_flags |= COL_TRAVERSE_END;
1654 :
1655 : TRACE_INFO_STRING("col_find_item_and_do", "About to walk the tree.");
1656 : TRACE_INFO_NUMBER("Traverse flags", mode_flags);
1657 :
1658 68599 : error = col_walk_items(ci, mode_flags, col_act_traverse_handler,
1659 : (void *)traverse_data, item_handler, custom_data,
1660 : &depth);
1661 :
1662 68599 : if (traverse_data->current_path != NULL) {
1663 : TRACE_INFO_STRING("find_item_and_do",
1664 : "Path was not cleared - deleting");
1665 654 : col_delete_path_data(traverse_data->current_path);
1666 : }
1667 :
1668 68599 : free(traverse_data);
1669 :
1670 68599 : if (error && (error != EINTR_INTERNAL)) {
1671 : TRACE_ERROR_NUMBER("Walk items returned error. Returning: ", error);
1672 0 : return error;
1673 : }
1674 : else {
1675 : TRACE_FLOW_STRING("Walk items returned SUCCESS.", "");
1676 68599 : return EOK;
1677 : }
1678 : }
1679 :
1680 : /* Function to replace data in the item */
1681 1 : static int col_update_current_item(struct collection_item *current,
1682 : struct update_property *update_data)
1683 : {
1684 : TRACE_FLOW_STRING("col_update_current_item", "Entry");
1685 :
1686 : /* If type is different or same but it is string or binary we need to
1687 : * replace the storage */
1688 2 : if ((current->type != update_data->type) ||
1689 2 : ((current->type == update_data->type) &&
1690 2 : ((current->type == COL_TYPE_STRING) ||
1691 1 : (current->type == COL_TYPE_BINARY)))) {
1692 : TRACE_INFO_STRING("Replacing item data buffer", "");
1693 0 : free(current->data);
1694 0 : current->data = malloc(update_data->length);
1695 0 : if (current->data == NULL) {
1696 : TRACE_ERROR_STRING("Failed to allocate memory", "");
1697 0 : current->length = 0;
1698 0 : return ENOMEM;
1699 : }
1700 0 : current->length = update_data->length;
1701 : }
1702 :
1703 : TRACE_INFO_STRING("Overwriting item data", "");
1704 1 : memcpy(current->data, update_data->data, current->length);
1705 1 : current->type = update_data->type;
1706 :
1707 1 : if (current->type == COL_TYPE_STRING)
1708 0 : ((char *)(current->data))[current->length-1] = '\0';
1709 :
1710 : TRACE_FLOW_STRING("update_current_item", "Exit");
1711 1 : return EOK;
1712 : }
1713 :
1714 : /* TRAVERSE CALLBACKS */
1715 :
1716 : /* Traverse handler for simple traverse function */
1717 : /* Handler must be able to deal with NULL current item */
1718 137420 : static int col_simple_traverse_handler(struct collection_item *head,
1719 : struct collection_item *previous,
1720 : struct collection_item *current,
1721 : void *traverse_data,
1722 : col_item_fn user_item_handler,
1723 : void *custom_data,
1724 : int *stop)
1725 : {
1726 137420 : int error = EOK;
1727 : struct collection_item end_item;
1728 137420 : char zero = '\0';
1729 :
1730 : TRACE_FLOW_STRING("col_simple_traverse_handler", "Entry.");
1731 :
1732 137420 : if (current == NULL) {
1733 0 : memset((void *)&end_item, 0, sizeof(struct collection_item));
1734 0 : end_item.type = COL_TYPE_END;
1735 0 : end_item.property = &zero;
1736 0 : current = &end_item;
1737 : }
1738 :
1739 137420 : error = user_item_handler(current->property,
1740 : current->property_len,
1741 : current->type,
1742 : current->data,
1743 : current->length,
1744 : custom_data,
1745 : stop);
1746 :
1747 : TRACE_FLOW_NUMBER("col_simple_traverse_handler. Returning:", error);
1748 137420 : return error;
1749 : }
1750 :
1751 : /* Traverse handler for to find parent */
1752 2111 : static int col_parent_traverse_handler(struct collection_item *head,
1753 : struct collection_item *previous,
1754 : struct collection_item *current,
1755 : void *traverse_data,
1756 : col_item_fn user_item_handler,
1757 : void *custom_data,
1758 : int *stop)
1759 : {
1760 : struct property_search *to_find;
1761 2111 : int done = 0;
1762 2111 : int match = 0;
1763 :
1764 : TRACE_FLOW_STRING("col_parent_traverse_handler", "Entry.");
1765 :
1766 2111 : to_find = (struct property_search *)custom_data;
1767 :
1768 : TRACE_INFO_NUMBER("Looking for HASH:", (unsigned)(to_find->hash));
1769 : TRACE_INFO_NUMBER("Current HASH:", (unsigned)(current->phash));
1770 :
1771 : /* Check hashes first */
1772 2111 : if(to_find->hash == current->phash) {
1773 :
1774 : /* Check type if we are asked to use type */
1775 203 : if ((to_find->use_type) && (!(to_find->type & current->type))) {
1776 : TRACE_FLOW_STRING("parent_traverse_handler. Returning:","Exit. Hash is Ok, type is not");
1777 0 : return EOK;
1778 : }
1779 :
1780 : /* Validate property. Make sure we include terminating 0 in the comparison */
1781 203 : if (strncasecmp(current->property, to_find->property, current->property_len + 1) == 0) {
1782 :
1783 203 : match = 1;
1784 203 : to_find->found = 1;
1785 :
1786 : /* Do the right thing based on index */
1787 : /* If index is 0 we are looking for the first value in the list of duplicate properties */
1788 203 : if (to_find->index == 0) done = 1;
1789 : /* If index is non zero we are looking for N-th instance of the dup property */
1790 11 : else if (to_find->index > 0) {
1791 5 : if (to_find->count == to_find->index) done = 1;
1792 : else {
1793 : /* Record found instance and move on */
1794 3 : to_find->parent = previous;
1795 3 : (to_find->count)++;
1796 : }
1797 : }
1798 : /* If we are looking for last instance just record it */
1799 6 : else to_find->parent = previous;
1800 : }
1801 : }
1802 :
1803 2111 : if (done) {
1804 194 : *stop = 1;
1805 194 : *((struct collection_item **)traverse_data) = previous;
1806 : }
1807 : else {
1808 : /* As soon as we found first non matching one but there was a match
1809 : * return the parent of the last found item.
1810 : */
1811 1917 : if (((!match) || (current->next == NULL)) && (to_find->index != 0) && (to_find->found)) {
1812 4 : *stop = 1;
1813 4 : if (to_find->index == -2)
1814 1 : *((struct collection_item **)traverse_data) = to_find->parent;
1815 : else
1816 3 : *((struct collection_item **)traverse_data) = to_find->parent->next;
1817 : }
1818 : }
1819 :
1820 :
1821 : TRACE_FLOW_STRING("col_parent_traverse_handler. Returning:","Exit");
1822 2111 : return EOK;
1823 : }
1824 :
1825 :
1826 : /* Traverse callback for find & delete function */
1827 125145246 : static int col_act_traverse_handler(struct collection_item *head,
1828 : struct collection_item *previous,
1829 : struct collection_item *current,
1830 : void *passed_traverse_data,
1831 : col_item_fn user_item_handler,
1832 : void *custom_data,
1833 : int *stop)
1834 : {
1835 125145246 : int error = EOK;
1836 125145246 : struct find_name *traverse_data = NULL;
1837 : char *name;
1838 : int length;
1839 : struct path_data *temp;
1840 : struct collection_header *header;
1841 : char *property;
1842 : int property_len;
1843 : struct update_property *update_data;
1844 :
1845 : TRACE_FLOW_STRING("col_act_traverse_handler", "Entry.");
1846 :
1847 125145246 : traverse_data = (struct find_name *)passed_traverse_data;
1848 :
1849 : /* We can be called when current points to NULL */
1850 125145246 : if (current == NULL) {
1851 : TRACE_INFO_STRING("col_act_traverse_handler",
1852 : "Special call at the end of the collection.");
1853 73475 : temp = traverse_data->current_path;
1854 73475 : traverse_data->current_path = temp->previous_path;
1855 73475 : temp->previous_path = NULL;
1856 73475 : col_delete_path_data(temp);
1857 73475 : traverse_data->given_name = NULL;
1858 73475 : traverse_data->given_len = 0;
1859 : TRACE_FLOW_NUMBER("Handling end of collection - removed path. Returning:", error);
1860 73475 : return error;
1861 : }
1862 :
1863 : /* Create new path at the beginning of a new sub collection */
1864 125071771 : if (current->type == COL_TYPE_COLLECTION) {
1865 :
1866 : TRACE_INFO_STRING("col_act_traverse_handler",
1867 : "Processing collection handle.");
1868 :
1869 : /* Create new path */
1870 74163 : if (traverse_data->current_path != NULL) {
1871 : TRACE_INFO_STRING("Already have part of the path", "");
1872 5564 : name = traverse_data->current_path->name;
1873 5564 : length = traverse_data->current_path->length;
1874 : TRACE_INFO_STRING("Path:", name);
1875 : TRACE_INFO_NUMBER("Path len:", length);
1876 : }
1877 : else {
1878 68599 : name = NULL;
1879 68599 : length = 0;
1880 : }
1881 :
1882 74163 : if (traverse_data->given_name != NULL) {
1883 5564 : property = traverse_data->given_name;
1884 5564 : property_len = traverse_data->given_len;
1885 : }
1886 : else {
1887 68599 : property = current->property;
1888 68599 : property_len = current->property_len;
1889 : }
1890 :
1891 : TRACE_INFO_STRING("col_act_traverse_handler", "About to create path data.");
1892 :
1893 74163 : error = col_create_path_data(&(traverse_data->current_path),
1894 : name, length,
1895 : property, property_len, '!');
1896 :
1897 : TRACE_INFO_NUMBER("col_create_path_data returned:", error);
1898 74163 : return error;
1899 : }
1900 :
1901 : /* Handle the collection pointers */
1902 124997608 : if (current->type == COL_TYPE_COLLECTIONREF) {
1903 5853 : traverse_data->given_name = current->property;
1904 5853 : traverse_data->given_len = current->property_len;
1905 : TRACE_INFO_STRING("Saved given name:", traverse_data->given_name);
1906 : }
1907 :
1908 : TRACE_INFO_STRING("Processing item with property:", current->property);
1909 :
1910 : /* Do here what we do with items */
1911 124997608 : if (col_match_item(current, traverse_data)) {
1912 : TRACE_INFO_STRING("Matched item:", current->property);
1913 654 : switch (traverse_data->action) {
1914 : case COLLECTION_ACTION_FIND:
1915 : TRACE_INFO_STRING("It is a find action - calling handler.", "");
1916 145 : if (user_item_handler != NULL) {
1917 : /* Call user handler */
1918 145 : error = user_item_handler(current->property,
1919 : current->property_len,
1920 : current->type,
1921 : current->data,
1922 : current->length,
1923 : custom_data,
1924 : stop);
1925 :
1926 : TRACE_INFO_NUMBER("Handler returned:", error);
1927 : TRACE_INFO_NUMBER("Handler set STOP to:", *stop);
1928 :
1929 : }
1930 145 : break;
1931 :
1932 : case COLLECTION_ACTION_GET:
1933 : TRACE_INFO_STRING("It is a get action.", "");
1934 505 : if (custom_data != NULL)
1935 505 : *((struct collection_item **)(custom_data)) = current;
1936 505 : break;
1937 :
1938 : case COLLECTION_ACTION_DEL:
1939 : TRACE_INFO_STRING("It is a delete action.", "");
1940 : /* Make sure we tell the caller we found a match */
1941 3 : if (custom_data != NULL)
1942 3 : *(int *)custom_data = COL_MATCH;
1943 :
1944 : /* Adjust header of the collection */
1945 3 : header = (struct collection_header *)head->data;
1946 3 : header->count--;
1947 3 : if (current->next == NULL)
1948 3 : header->last = previous;
1949 :
1950 : /* Unlink and delete iteam */
1951 : /* Previous can't be NULL here becuase we never delete
1952 : * header elements */
1953 3 : previous->next = current->next;
1954 3 : col_delete_item(current);
1955 : TRACE_INFO_STRING("Did the delete of the item.", "");
1956 3 : break;
1957 :
1958 : case COLLECTION_ACTION_UPDATE:
1959 : TRACE_INFO_STRING("It is an update action.", "");
1960 2 : if((current->type == COL_TYPE_COLLECTION) ||
1961 1 : (current->type == COL_TYPE_COLLECTIONREF)) {
1962 : TRACE_ERROR_STRING("Can't update collections it is an error for now", "");
1963 0 : return EINVAL;
1964 : }
1965 :
1966 : /* Make sure we tell the caller we found a match */
1967 1 : if (custom_data != NULL) {
1968 1 : update_data = (struct update_property *)custom_data;
1969 1 : update_data->found = COL_MATCH;
1970 1 : error = col_update_current_item(current, update_data);
1971 : }
1972 : else {
1973 : TRACE_ERROR_STRING("Error - update data is required", "");
1974 0 : return EINVAL;
1975 : }
1976 :
1977 : TRACE_INFO_STRING("Did the delete of the item.", "");
1978 1 : break;
1979 : default:
1980 0 : break;
1981 : }
1982 : /* Force interrupt if we found */
1983 654 : *stop = 1;
1984 : }
1985 :
1986 : TRACE_FLOW_NUMBER("col_act_traverse_handler returning", error);
1987 124997608 : return error;
1988 : }
1989 :
1990 :
1991 : /* Traverse handler for copy function */
1992 68857 : static int col_copy_traverse_handler(struct collection_item *head,
1993 : struct collection_item *previous,
1994 : struct collection_item *current,
1995 : void *passed_traverse_data,
1996 : col_item_fn user_item_handler,
1997 : void *custom_data,
1998 : int *stop)
1999 : {
2000 68857 : int error = EOK;
2001 : struct collection_item *parent;
2002 68857 : struct collection_item *other = NULL;
2003 : struct col_copy *traverse_data;
2004 : struct path_data *temp;
2005 : char *name;
2006 : int length;
2007 68857 : char *property = NULL;
2008 : int property_len;
2009 : struct collection_header *header;
2010 : char *offset;
2011 :
2012 : TRACE_FLOW_STRING("col_copy_traverse_handler", "Entry.");
2013 :
2014 68857 : parent = (struct collection_item *)custom_data;
2015 68857 : traverse_data = (struct col_copy *)passed_traverse_data;
2016 :
2017 : /* We can be called when current points to NULL */
2018 : /* This will happen only in the FLATDOT case */
2019 68857 : if (current == NULL) {
2020 : TRACE_INFO_STRING("col_copy_traverse_handler",
2021 : "Special call at the end of the collection.");
2022 3 : temp = traverse_data->current_path;
2023 3 : traverse_data->current_path = temp->previous_path;
2024 3 : temp->previous_path = NULL;
2025 3 : col_delete_path_data(temp);
2026 3 : traverse_data->given_name = NULL;
2027 3 : traverse_data->given_len = 0;
2028 : TRACE_FLOW_NUMBER("Handling end of collection - removed path. Returning:", error);
2029 3 : return error;
2030 : }
2031 :
2032 : /* Create new path at the beginning of a new sub collection */
2033 68854 : if (current->type == COL_TYPE_COLLECTION) {
2034 :
2035 : TRACE_INFO_STRING("col_copy_traverse_handler",
2036 : "Processing collection handle.");
2037 1398 : if (traverse_data->mode == COL_COPY_FLATDOT) {
2038 : /* Create new path */
2039 3 : if (traverse_data->current_path != NULL) {
2040 : TRACE_INFO_STRING("Already have part of the path", "");
2041 0 : name = traverse_data->current_path->name;
2042 0 : length = traverse_data->current_path->length;
2043 : TRACE_INFO_STRING("Path:", name);
2044 : TRACE_INFO_NUMBER("Path len:", length);
2045 0 : if (traverse_data->given_name != NULL) {
2046 0 : property = traverse_data->given_name;
2047 0 : property_len = traverse_data->given_len;
2048 : }
2049 : else {
2050 0 : property = current->property;
2051 0 : property_len = current->property_len;
2052 : }
2053 : }
2054 : else {
2055 : /* Do not create prefix for top collection
2056 : * if there is no given name.
2057 : */
2058 3 : name = NULL;
2059 3 : length = 0;
2060 3 : if (traverse_data->given_name != NULL) {
2061 1 : property = traverse_data->given_name;
2062 1 : property_len = traverse_data->given_len;
2063 : }
2064 : else {
2065 2 : property = NULL;
2066 2 : property_len = 0;
2067 : }
2068 : }
2069 :
2070 : TRACE_INFO_STRING("col_copy_traverse_handler", "About to create path data.");
2071 :
2072 3 : error = col_create_path_data(&(traverse_data->current_path),
2073 : name, length,
2074 : property, property_len, '.');
2075 :
2076 : TRACE_FLOW_NUMBER("col_copy_traverse_handler processed header:", error);
2077 3 : return error;
2078 : }
2079 : else {
2080 : TRACE_FLOW_NUMBER("col_copy_traverse_handler skipping the header:", error);
2081 1395 : return error;
2082 : }
2083 : }
2084 :
2085 :
2086 : /* Check if this is a special case of sub collection */
2087 67456 : if (current->type == COL_TYPE_COLLECTIONREF) {
2088 :
2089 : TRACE_INFO_STRING("Found a subcollection we need to copy. Name:",
2090 : current->property);
2091 :
2092 : /* Based on the mode we need to do different things */
2093 1195 : switch (traverse_data->mode) {
2094 : case COL_COPY_NORMAL:
2095 :
2096 2390 : error = col_copy_collection_with_cb(&other,
2097 1195 : *((struct collection_item **)(current->data)),
2098 1195 : current->property,
2099 : COL_COPY_NORMAL,
2100 : traverse_data->copy_cb,
2101 : traverse_data->ext_data);
2102 1195 : if (error) {
2103 : TRACE_ERROR_NUMBER("Copy subcollection returned error:", error);
2104 0 : return error;
2105 : }
2106 :
2107 : /* Add new item to a collection
2108 : * all references are now sub collections */
2109 1195 : error = col_insert_property_with_ref_int(parent,
2110 : NULL,
2111 : COL_DSP_END,
2112 : NULL,
2113 : 0,
2114 : 0,
2115 1195 : current->property,
2116 : COL_TYPE_COLLECTIONREF,
2117 : (void *)(&other),
2118 : sizeof(struct collection_item **),
2119 : NULL);
2120 :
2121 : TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in NORMAL mode:", error);
2122 1195 : return error;
2123 :
2124 : case COL_COPY_KEEPREF:
2125 :
2126 : /* Just increase reference count of the referenced collection */
2127 0 : other = *((struct collection_item **)(current->data));
2128 0 : header = (struct collection_header *)(other->data);
2129 0 : header->reference_count++;
2130 :
2131 : /* Add new item to a collection
2132 : * all references are now sub collections */
2133 0 : error = col_insert_property_with_ref_int(parent,
2134 : NULL,
2135 : COL_DSP_END,
2136 : NULL,
2137 : 0,
2138 : 0,
2139 0 : current->property,
2140 : COL_TYPE_COLLECTIONREF,
2141 : (void *)(&other),
2142 : sizeof(struct collection_item **),
2143 : NULL);
2144 : TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in KEEPREF mode:", error);
2145 0 : return error;
2146 :
2147 : case COL_COPY_TOP:
2148 : /* Told to ignore sub collections */
2149 : TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in TOP mode:", error);
2150 0 : return error;
2151 :
2152 : case COL_COPY_FLATDOT:
2153 :
2154 0 : traverse_data->given_name = current->property;
2155 0 : traverse_data->given_len = current->property_len;
2156 : TRACE_INFO_STRING("Saved given name:", traverse_data->given_name);
2157 : TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in FLATDOT mode:", error);
2158 0 : return error;
2159 :
2160 : /* NOTE: The mode COL_COPY_FLAT is not in the list of cases becuase
2161 : * in this flat mode we traverse collection using COL_TRAVERSE_FLAT flag
2162 : * thus we should not be called on referenced collections at all
2163 : * by the col_walk_items() function.
2164 : */
2165 :
2166 : default:
2167 : TRACE_ERROR_NUMBER("col_copy_traverse_handler bad mode error:", EINVAL);
2168 0 : return EINVAL;
2169 : }
2170 : }
2171 : else {
2172 :
2173 66261 : if (traverse_data->mode == COL_COPY_FLATDOT) {
2174 : /* Since this code can't use asprintf have to do it hard way */
2175 27 : property = malloc(traverse_data->current_path->length +
2176 18 : current->property_len + 2);
2177 9 : if (property == NULL) {
2178 : TRACE_ERROR_NUMBER("Failed to allocate memory for a new name:", error);
2179 0 : return error;
2180 : }
2181 : /* Add first part and dot only if we have prefix */
2182 9 : offset = property;
2183 9 : if (traverse_data->current_path->length) {
2184 3 : memcpy(offset, traverse_data->current_path->name,
2185 3 : traverse_data->current_path->length);
2186 3 : offset[traverse_data->current_path->length] = '.';
2187 3 : offset += traverse_data->current_path->length + 1;
2188 : }
2189 9 : memcpy(offset, current->property, current->property_len);
2190 9 : offset[current->property_len] = '\0';
2191 : }
2192 66252 : else property = current->property;
2193 :
2194 : TRACE_INFO_STRING("Using property:", property);
2195 :
2196 132522 : error = col_copy_item_with_cb(parent,
2197 : property,
2198 : current->type,
2199 66261 : current->data,
2200 : current->length,
2201 : traverse_data->copy_cb,
2202 : traverse_data->ext_data);
2203 :
2204 : /* Free property if we allocated it */
2205 66261 : if (traverse_data->mode == COL_COPY_FLATDOT) free(property);
2206 :
2207 66261 : if (error) {
2208 : TRACE_ERROR_NUMBER("Failed to copy property:", error);
2209 0 : return error;
2210 : }
2211 : }
2212 :
2213 : TRACE_FLOW_NUMBER("col_copy_traverse_handler returning", error);
2214 66261 : return error;
2215 : }
2216 :
2217 :
2218 :
2219 :
2220 : /********************* MAIN INTERFACE FUNCTIONS *****************************/
2221 :
2222 :
2223 : /* CREATE */
2224 :
2225 : /* Function that creates an named collection of a given class*/
2226 3927 : int col_create_collection(struct collection_item **ci, const char *name,
2227 : unsigned cclass)
2228 : {
2229 3927 : struct collection_item *handle = NULL;
2230 : struct collection_header header;
2231 3927 : int error = EOK;
2232 :
2233 : TRACE_FLOW_STRING("col_create_collection", "Entry.");
2234 :
2235 : /* Prepare header */
2236 3927 : header.last = NULL;
2237 3927 : header.reference_count = 1;
2238 3927 : header.count = 0;
2239 3927 : header.cclass = cclass;
2240 :
2241 : /* Create a collection type property */
2242 3927 : error = col_insert_property_with_ref_int(NULL,
2243 : NULL,
2244 : COL_DSP_END,
2245 : NULL,
2246 : 0,
2247 : 0,
2248 : name,
2249 : COL_TYPE_COLLECTION,
2250 : &header,
2251 : sizeof(header),
2252 : &handle);
2253 :
2254 :
2255 3927 : if (error) return error;
2256 :
2257 3927 : *ci = handle;
2258 :
2259 : TRACE_FLOW_STRING("col_create_collection", "Success Exit.");
2260 3927 : return EOK;
2261 : }
2262 :
2263 :
2264 : /* DESTROY */
2265 :
2266 : /* Function that destroys a collection */
2267 4616 : void col_destroy_collection_with_cb(struct collection_item *ci,
2268 : col_item_cleanup_fn cb,
2269 : void *custom_data)
2270 : {
2271 : struct collection_header *header;
2272 :
2273 : TRACE_FLOW_STRING("col_destroy_collection_with_cb", "Entry.");
2274 :
2275 : /* Do not try to delete NULL */
2276 4616 : if (ci == NULL) return;
2277 :
2278 : /* You can delete only whole collection not a part of it */
2279 4155 : if (ci->type != COL_TYPE_COLLECTION) {
2280 : TRACE_ERROR_STRING("Attempt to delete a non collection - BAD!", "");
2281 : TRACE_ERROR_NUMBER("Actual type is:", ci->type);
2282 0 : return;
2283 : }
2284 :
2285 : TRACE_INFO_STRING("Name:", ci->property);
2286 :
2287 : /* Collection can be referenced by other collection */
2288 4155 : header = (struct collection_header *)(ci->data);
2289 : TRACE_INFO_NUMBER("Reference count:", header->reference_count);
2290 4155 : if (header->reference_count > 1) {
2291 : TRACE_INFO_STRING("Dereferencing a referenced collection.", "");
2292 228 : header->reference_count--;
2293 : TRACE_INFO_NUMBER("Number after dereferencing.",
2294 : header->reference_count);
2295 : }
2296 : else {
2297 3927 : col_delete_collection(ci, cb, custom_data);
2298 : }
2299 :
2300 : TRACE_FLOW_STRING("col_destroy_collection_with_cb", "Exit.");
2301 : }
2302 :
2303 :
2304 : /* Function that destroys a collection */
2305 764 : void col_destroy_collection(struct collection_item *ci)
2306 : {
2307 : TRACE_FLOW_STRING("col_destroy_collection", "Entry.");
2308 :
2309 764 : col_destroy_collection_with_cb(ci, NULL, NULL);
2310 :
2311 : TRACE_FLOW_STRING("col_destroy_collection", "Exit.");
2312 764 : }
2313 :
2314 : /* COPY */
2315 :
2316 : /* Wrapper around a more advanced function */
2317 2 : int col_copy_collection(struct collection_item **collection_copy,
2318 : struct collection_item *collection_to_copy,
2319 : const char *name_to_use,
2320 : int copy_mode)
2321 : {
2322 2 : int error = EOK;
2323 : TRACE_FLOW_STRING("col_copy_collection", "Entry.");
2324 :
2325 2 : error = col_copy_collection_with_cb(collection_copy,
2326 : collection_to_copy,
2327 : name_to_use,
2328 : copy_mode,
2329 : NULL,
2330 : NULL);
2331 :
2332 : TRACE_FLOW_NUMBER("col_copy_collection. Exit. Returning", error);
2333 2 : return error;
2334 : }
2335 :
2336 : /* Create a deep copy of the current collection. */
2337 : /* Referenced collections of the donor are copied as sub collections. */
2338 1395 : int col_copy_collection_with_cb(struct collection_item **collection_copy,
2339 : struct collection_item *collection_to_copy,
2340 : const char *name_to_use,
2341 : int copy_mode,
2342 : col_copy_cb copy_cb,
2343 : void *ext_data)
2344 : {
2345 1395 : int error = EOK;
2346 1395 : struct collection_item *new_collection = NULL;
2347 : const char *name;
2348 : struct collection_header *header;
2349 1395 : unsigned depth = 0;
2350 : struct col_copy traverse_data;
2351 : int flags;
2352 :
2353 : TRACE_FLOW_STRING("col_copy_collection_with_cb", "Entry.");
2354 :
2355 : /* Collection is required */
2356 1395 : if (collection_to_copy == NULL) {
2357 : TRACE_ERROR_NUMBER("No collection to search!", EINVAL);
2358 0 : return EINVAL;
2359 : }
2360 :
2361 : /* Storage is required too */
2362 1395 : if (collection_copy == NULL) {
2363 : TRACE_ERROR_NUMBER("No memory provided to receive collection copy!", EINVAL);
2364 0 : return EINVAL;
2365 : }
2366 :
2367 : /* NOTE: Refine this check if adding a new copy mode */
2368 1395 : if ((copy_mode < 0) || (copy_mode > COL_COPY_TOP)) {
2369 : TRACE_ERROR_NUMBER("Invalid copy mode:", copy_mode);
2370 0 : return EINVAL;
2371 : }
2372 :
2373 : /* Determine what name to use */
2374 1395 : if (name_to_use != NULL)
2375 1377 : name = name_to_use;
2376 : else
2377 18 : name = collection_to_copy->property;
2378 :
2379 1395 : header = (struct collection_header *)collection_to_copy->data;
2380 :
2381 : /* Create a new collection */
2382 1395 : error = col_create_collection(&new_collection, name, header->cclass);
2383 1395 : if (error) {
2384 : TRACE_ERROR_NUMBER("col_create_collection failed returning", error);
2385 0 : return error;
2386 : }
2387 :
2388 1395 : traverse_data.mode = copy_mode;
2389 1395 : traverse_data.current_path = NULL;
2390 1395 : traverse_data.given_name = NULL;
2391 1395 : traverse_data.given_len = 0;
2392 1395 : traverse_data.copy_cb = copy_cb;
2393 1395 : traverse_data.ext_data = ext_data;
2394 :
2395 1395 : if (copy_mode == COL_COPY_FLATDOT) flags = COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END;
2396 1394 : else if (copy_mode == COL_COPY_FLAT) flags = COL_TRAVERSE_FLAT;
2397 1394 : else flags = COL_TRAVERSE_ONELEVEL;
2398 :
2399 1395 : error = col_walk_items(collection_to_copy, flags,
2400 : col_copy_traverse_handler, (void *)(&traverse_data),
2401 : NULL, new_collection, &depth);
2402 :
2403 1395 : if (!error) *collection_copy = new_collection;
2404 0 : else col_destroy_collection(new_collection);
2405 :
2406 : TRACE_FLOW_NUMBER("col_copy_collection_with_cb returning", error);
2407 1395 : return error;
2408 :
2409 : }
2410 :
2411 :
2412 : /* EXTRACTION */
2413 :
2414 : /* Extract collection */
2415 128 : int col_get_collection_reference(struct collection_item *ci,
2416 : struct collection_item **acceptor,
2417 : const char *collection_to_find)
2418 : {
2419 : struct collection_header *header;
2420 128 : struct collection_item *subcollection = NULL;
2421 128 : int error = EOK;
2422 :
2423 : TRACE_FLOW_STRING("col_get_collection_reference", "Entry.");
2424 :
2425 256 : if ((ci == NULL) ||
2426 256 : (ci->type != COL_TYPE_COLLECTION) ||
2427 : (acceptor == NULL)) {
2428 : TRACE_ERROR_NUMBER("Invalid parameter - returning error",EINVAL);
2429 0 : return EINVAL;
2430 : }
2431 :
2432 128 : if (collection_to_find) {
2433 : /* Find a sub collection */
2434 : TRACE_INFO_STRING("We are given subcollection name - search it:",
2435 : collection_to_find);
2436 127 : error = col_find_item_and_do(ci, collection_to_find,
2437 : COL_TYPE_COLLECTIONREF,
2438 : COL_TRAVERSE_DEFAULT,
2439 : col_get_subcollection,
2440 : (void *)(&subcollection),
2441 : COLLECTION_ACTION_FIND);
2442 127 : if (error) {
2443 : TRACE_ERROR_NUMBER("Search failed returning error", error);
2444 0 : return error;
2445 : }
2446 :
2447 127 : if (subcollection == NULL) {
2448 : TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
2449 55 : return ENOENT;
2450 : }
2451 : }
2452 : else {
2453 : /* Create reference to the same collection */
2454 : TRACE_INFO_STRING("Creating reference to the top level collection.", "");
2455 1 : subcollection = ci;
2456 : }
2457 :
2458 73 : header = (struct collection_header *)subcollection->data;
2459 : TRACE_INFO_NUMBER("Count:", header->count);
2460 : TRACE_INFO_NUMBER("Ref count:", header->reference_count);
2461 73 : header->reference_count++;
2462 : TRACE_INFO_NUMBER("Ref count after increment:", header->reference_count);
2463 73 : *acceptor = subcollection;
2464 :
2465 : TRACE_FLOW_STRING("col_get_collection_reference", "Success Exit.");
2466 73 : return EOK;
2467 : }
2468 :
2469 : /* Get collection - if current item is a reference get a real collection from it. */
2470 0 : int col_get_reference_from_item(struct collection_item *ci,
2471 : struct collection_item **acceptor)
2472 : {
2473 : struct collection_header *header;
2474 0 : struct collection_item *subcollection = NULL;
2475 :
2476 : TRACE_FLOW_STRING("get_reference_from_item", "Entry.");
2477 :
2478 0 : if ((ci == NULL) ||
2479 0 : (ci->type != COL_TYPE_COLLECTIONREF) ||
2480 : (acceptor == NULL)) {
2481 : TRACE_ERROR_NUMBER("Invalid parameter - returning error",EINVAL);
2482 0 : return EINVAL;
2483 : }
2484 :
2485 0 : subcollection = *((struct collection_item **)ci->data);
2486 :
2487 0 : header = (struct collection_header *)subcollection->data;
2488 : TRACE_INFO_NUMBER("Count:", header->count);
2489 : TRACE_INFO_NUMBER("Ref count:", header->reference_count);
2490 0 : header->reference_count++;
2491 : TRACE_INFO_NUMBER("Ref count after increment:", header->reference_count);
2492 0 : *acceptor = subcollection;
2493 :
2494 : TRACE_FLOW_STRING("col_get_reference_from_item", "Success Exit.");
2495 0 : return EOK;
2496 : }
2497 :
2498 : /* ADDITION */
2499 :
2500 : /* Add collection to collection */
2501 1468 : int col_add_collection_to_collection(struct collection_item *ci,
2502 : const char *sub_collection_name,
2503 : const char *as_property,
2504 : struct collection_item *collection_to_add,
2505 : int mode)
2506 : {
2507 1468 : struct collection_item *acceptor = NULL;
2508 : const char *name_to_use;
2509 : struct collection_header *header;
2510 : struct collection_item *collection_copy;
2511 1468 : int error = EOK;
2512 : struct col_copy traverse_data;
2513 1468 : unsigned depth = 0;
2514 :
2515 :
2516 : TRACE_FLOW_STRING("col_add_collection_to_collection", "Entry.");
2517 :
2518 2936 : if ((ci == NULL) ||
2519 2936 : (ci->type != COL_TYPE_COLLECTION) ||
2520 2936 : (collection_to_add == NULL) ||
2521 1468 : (collection_to_add->type != COL_TYPE_COLLECTION)) {
2522 : /* Need to debug here */
2523 : TRACE_ERROR_NUMBER("Missing parameter - returning error", EINVAL);
2524 0 : return EINVAL;
2525 : }
2526 :
2527 1468 : if (sub_collection_name != NULL) {
2528 : /* Find a sub collection */
2529 : TRACE_INFO_STRING("We are given subcollection name - search it:",
2530 : sub_collection_name);
2531 5 : error = col_find_item_and_do(ci, sub_collection_name,
2532 : COL_TYPE_COLLECTIONREF,
2533 : COL_TRAVERSE_DEFAULT,
2534 : col_get_subcollection,
2535 : (void *)(&acceptor),
2536 : COLLECTION_ACTION_FIND);
2537 5 : if (error) {
2538 : TRACE_ERROR_NUMBER("Search failed returning error", error);
2539 0 : return error;
2540 : }
2541 :
2542 5 : if (acceptor == NULL) {
2543 : TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
2544 0 : return ENOENT;
2545 : }
2546 :
2547 : }
2548 : else {
2549 1463 : acceptor = ci;
2550 : }
2551 :
2552 1468 : if (as_property != NULL)
2553 8 : name_to_use = as_property;
2554 : else
2555 1460 : name_to_use = collection_to_add->property;
2556 :
2557 :
2558 : TRACE_INFO_STRING("Going to use name:", name_to_use);
2559 :
2560 :
2561 1468 : switch (mode) {
2562 : case COL_ADD_MODE_REFERENCE:
2563 : TRACE_INFO_STRING("We are adding a reference.", "");
2564 : TRACE_INFO_NUMBER("Type of the header element:",
2565 : collection_to_add->type);
2566 : TRACE_INFO_STRING("Header name we are adding.",
2567 : collection_to_add->property);
2568 : /* Create a pointer to external collection */
2569 : /* For future thread safety: Transaction start -> */
2570 73 : error = col_insert_property_with_ref_int(acceptor,
2571 : NULL,
2572 : COL_DSP_END,
2573 : NULL,
2574 : 0,
2575 : 0,
2576 : name_to_use,
2577 : COL_TYPE_COLLECTIONREF,
2578 : (void *)(&collection_to_add),
2579 : sizeof(struct collection_item **),
2580 : NULL);
2581 :
2582 : TRACE_INFO_NUMBER("Type of the header element after adding property:",
2583 : collection_to_add->type);
2584 : TRACE_INFO_STRING("Header name we just added.",
2585 : collection_to_add->property);
2586 73 : if (error) {
2587 : TRACE_ERROR_NUMBER("Adding property failed with error:", error);
2588 0 : return error;
2589 : }
2590 73 : header = (struct collection_header *)collection_to_add->data;
2591 : TRACE_INFO_NUMBER("Count:", header->count);
2592 : TRACE_INFO_NUMBER("Ref count:", header->reference_count);
2593 73 : header->reference_count++;
2594 : TRACE_INFO_NUMBER("Ref count after increment:",
2595 : header->reference_count);
2596 : /* -> Transaction end */
2597 73 : break;
2598 :
2599 : case COL_ADD_MODE_EMBED:
2600 : TRACE_INFO_STRING("We are embedding the collection.", "");
2601 : /* First check if the passed in collection is referenced more than once */
2602 : TRACE_INFO_NUMBER("Type of the header element we are adding:",
2603 : collection_to_add->type);
2604 : TRACE_INFO_STRING("Header name we are adding.",
2605 : collection_to_add->property);
2606 : TRACE_INFO_NUMBER("Type of the header element we are adding to:",
2607 : acceptor->type);
2608 : TRACE_INFO_STRING("Header name we are adding to.",
2609 : acceptor->property);
2610 :
2611 1391 : error = col_insert_property_with_ref_int(acceptor,
2612 : NULL,
2613 : COL_DSP_END,
2614 : NULL,
2615 : 0,
2616 : 0,
2617 : name_to_use,
2618 : COL_TYPE_COLLECTIONREF,
2619 : (void *)(&collection_to_add),
2620 : sizeof(struct collection_item **),
2621 : NULL);
2622 :
2623 :
2624 : TRACE_INFO_NUMBER("Adding property returned:", error);
2625 1391 : break;
2626 :
2627 : case COL_ADD_MODE_CLONE:
2628 : TRACE_INFO_STRING("We are cloning the collection.", "");
2629 : TRACE_INFO_STRING("Name we will use.", name_to_use);
2630 :
2631 : /* For future thread safety: Transaction start -> */
2632 1 : error = col_copy_collection(&collection_copy,
2633 : collection_to_add, name_to_use,
2634 : COL_COPY_NORMAL);
2635 1 : if (error) return error;
2636 :
2637 : TRACE_INFO_STRING("We have a collection copy.", collection_copy->property);
2638 : TRACE_INFO_NUMBER("Collection type.", collection_copy->type);
2639 : TRACE_INFO_STRING("Acceptor collection.", acceptor->property);
2640 : TRACE_INFO_NUMBER("Acceptor collection type.", acceptor->type);
2641 :
2642 1 : error = col_insert_property_with_ref_int(acceptor,
2643 : NULL,
2644 : COL_DSP_END,
2645 : NULL,
2646 : 0,
2647 : 0,
2648 : name_to_use,
2649 : COL_TYPE_COLLECTIONREF,
2650 : (void *)(&collection_copy),
2651 : sizeof(struct collection_item **),
2652 : NULL);
2653 :
2654 : /* -> Transaction end */
2655 : TRACE_INFO_NUMBER("Adding property returned:", error);
2656 1 : break;
2657 :
2658 : case COL_ADD_MODE_FLAT:
2659 : TRACE_INFO_STRING("We are flattening the collection.", "");
2660 :
2661 1 : traverse_data.mode = COL_COPY_FLAT;
2662 1 : traverse_data.current_path = NULL;
2663 1 : traverse_data.copy_cb = NULL;
2664 1 : traverse_data.ext_data = NULL;
2665 :
2666 1 : if ((as_property) && (*as_property)) {
2667 : /* The normal assignement generates a warning
2668 : * becuase I am assigning const to a non const.
2669 : * I can't make the structure member to be const
2670 : * since it changes but it changes
2671 : * to point to different stings at different time
2672 : * This is just an initial sting it will use.
2673 : * The logic does not change the content of the string.
2674 : * To overcome the issue I use memcpy();
2675 : */
2676 0 : memcpy(&(traverse_data.given_name),
2677 : &(as_property), sizeof(char *));
2678 0 : traverse_data.given_len = strlen(as_property);
2679 : }
2680 : else {
2681 1 : traverse_data.given_name = NULL;
2682 1 : traverse_data.given_len = 0;
2683 : }
2684 :
2685 1 : error = col_walk_items(collection_to_add, COL_TRAVERSE_FLAT,
2686 : col_copy_traverse_handler, (void *)(&traverse_data),
2687 : NULL, acceptor, &depth);
2688 :
2689 : TRACE_INFO_NUMBER("Copy collection flat returned:", error);
2690 1 : break;
2691 :
2692 : case COL_ADD_MODE_FLATDOT:
2693 : TRACE_INFO_STRING("We are flattening the collection with dots.", "");
2694 :
2695 2 : traverse_data.mode = COL_COPY_FLATDOT;
2696 2 : traverse_data.current_path = NULL;
2697 2 : traverse_data.copy_cb = NULL;
2698 2 : traverse_data.ext_data = NULL;
2699 :
2700 2 : if ((as_property) && (*as_property)) {
2701 : /* The normal assignement generates a warning
2702 : * becuase I am assigning const to a non const.
2703 : * I can't make the structure member to be const
2704 : * since it changes but it changes
2705 : * to point to different stings at different time
2706 : * This is just an initial sting it will use.
2707 : * The logic does not change the content of the string.
2708 : * To overcome the issue I use memcpy();
2709 : */
2710 1 : memcpy(&(traverse_data.given_name),
2711 : &(as_property), sizeof(char *));
2712 1 : traverse_data.given_len = strlen(as_property);
2713 : }
2714 : else {
2715 1 : traverse_data.given_name = NULL;
2716 1 : traverse_data.given_len = 0;
2717 : }
2718 :
2719 2 : error = col_walk_items(collection_to_add, COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END,
2720 : col_copy_traverse_handler, (void *)(&traverse_data),
2721 : NULL, acceptor, &depth);
2722 :
2723 : TRACE_INFO_NUMBER("Copy collection flatdot returned:", error);
2724 2 : break;
2725 :
2726 : default:
2727 0 : error = EINVAL;
2728 : }
2729 :
2730 : TRACE_FLOW_NUMBER("col_add_collection_to_collection returning:", error);
2731 1468 : return error;
2732 : }
2733 :
2734 : /* TRAVERSING */
2735 :
2736 : /* Function to traverse the entire collection including optionally
2737 : * sub collections */
2738 408 : int col_traverse_collection(struct collection_item *ci,
2739 : int mode_flags,
2740 : col_item_fn item_handler,
2741 : void *custom_data)
2742 : {
2743 :
2744 408 : int error = EOK;
2745 408 : unsigned depth = 0;
2746 :
2747 : TRACE_FLOW_STRING("col_traverse_collection", "Entry.");
2748 :
2749 408 : if (ci == NULL) {
2750 : TRACE_ERROR_NUMBER("No collection to traverse!", EINVAL);
2751 0 : return EINVAL;
2752 : }
2753 :
2754 408 : error = col_walk_items(ci, mode_flags, col_simple_traverse_handler,
2755 : NULL, item_handler, custom_data, &depth);
2756 :
2757 408 : if ((error != 0) && (error != EINTR_INTERNAL)) {
2758 : TRACE_ERROR_NUMBER("Error walking tree", error);
2759 0 : return error;
2760 : }
2761 :
2762 : TRACE_FLOW_STRING("col_traverse_collection", "Success exit.");
2763 408 : return EOK;
2764 : }
2765 :
2766 : /* CHECK */
2767 :
2768 : /* Convenience function to check if specific property is in the collection */
2769 11 : int col_is_item_in_collection(struct collection_item *ci,
2770 : const char *property_to_find,
2771 : int type,
2772 : int mode_flags,
2773 : int *found)
2774 : {
2775 : int error;
2776 :
2777 : TRACE_FLOW_STRING("col_is_item_in_collection","Entry.");
2778 :
2779 11 : *found = COL_NOMATCH;
2780 11 : error = col_find_item_and_do(ci, property_to_find,
2781 : type, mode_flags,
2782 : col_is_in_item_handler,
2783 : (void *)found,
2784 : COLLECTION_ACTION_FIND);
2785 :
2786 : TRACE_FLOW_NUMBER("col_is_item_in_collection returning", error);
2787 11 : return error;
2788 : }
2789 :
2790 : /* SEARCH */
2791 : /* Search function. Looks up an item in the collection based on the property.
2792 : Essentually it is a traverse function with spacial traversing logic.
2793 : */
2794 61 : int col_get_item_and_do(struct collection_item *ci,
2795 : const char *property_to_find,
2796 : int type,
2797 : int mode_flags,
2798 : col_item_fn item_handler,
2799 : void *custom_data)
2800 : {
2801 61 : int error = EOK;
2802 :
2803 : TRACE_FLOW_STRING("col_get_item_and_do","Entry.");
2804 :
2805 61 : error = col_find_item_and_do(ci, property_to_find,
2806 : type, mode_flags,
2807 : item_handler,
2808 : custom_data,
2809 : COLLECTION_ACTION_FIND);
2810 :
2811 : TRACE_FLOW_NUMBER("col_get_item_and_do returning", error);
2812 61 : return error;
2813 : }
2814 :
2815 :
2816 : /* Get raw item */
2817 68376 : int col_get_item(struct collection_item *ci,
2818 : const char *property_to_find,
2819 : int type,
2820 : int mode_flags,
2821 : struct collection_item **item)
2822 : {
2823 :
2824 68376 : int error = EOK;
2825 :
2826 : TRACE_FLOW_STRING("col_get_item", "Entry.");
2827 :
2828 68376 : error = col_find_item_and_do(ci, property_to_find,
2829 : type, mode_flags,
2830 : NULL, (void *)item,
2831 : COLLECTION_ACTION_GET);
2832 :
2833 : TRACE_FLOW_NUMBER("col_get_item returning", error);
2834 68376 : return error;
2835 : }
2836 :
2837 : /* DELETE */
2838 : /* Delete property from the collection */
2839 4 : int col_delete_property(struct collection_item *ci,
2840 : const char *property_to_find,
2841 : int type,
2842 : int mode_flags)
2843 : {
2844 4 : int error = EOK;
2845 : int found;
2846 :
2847 : TRACE_FLOW_STRING("col_delete_property", "Entry.");
2848 4 : found = COL_NOMATCH;
2849 :
2850 4 : error = col_find_item_and_do(ci, property_to_find,
2851 : type, mode_flags,
2852 : NULL, (void *)(&found),
2853 : COLLECTION_ACTION_DEL);
2854 :
2855 4 : if ((error == EOK) && (found == COL_NOMATCH))
2856 1 : error = ENOENT;
2857 :
2858 : TRACE_FLOW_NUMBER("col_delete_property returning", error);
2859 4 : return error;
2860 : }
2861 :
2862 : /* UPDATE */
2863 : /* Update property in the collection */
2864 1 : int col_update_property(struct collection_item *ci,
2865 : const char *property_to_find,
2866 : int type,
2867 : void *new_data,
2868 : int length,
2869 : int mode_flags)
2870 : {
2871 1 : int error = EOK;
2872 : struct update_property update_data;
2873 :
2874 : TRACE_FLOW_STRING("col_update_property", "Entry.");
2875 1 : update_data.type = type;
2876 1 : update_data.data = new_data;
2877 1 : update_data.length = length;
2878 1 : update_data.found = COL_NOMATCH;
2879 :
2880 1 : error = col_find_item_and_do(ci, property_to_find,
2881 : type, mode_flags,
2882 : NULL, (void *)(&update_data),
2883 : COLLECTION_ACTION_UPDATE);
2884 :
2885 1 : if ((error == EOK) && (update_data.found == COL_NOMATCH))
2886 0 : error = ENOENT;
2887 :
2888 : TRACE_FLOW_NUMBER("col_update_property returning", error);
2889 1 : return error;
2890 : }
2891 :
2892 :
2893 : /* Function to modify the item */
2894 66634 : int col_modify_item(struct collection_item *item,
2895 : const char *property,
2896 : int type,
2897 : const void *data,
2898 : int length)
2899 : {
2900 : TRACE_FLOW_STRING("col_modify_item", "Entry");
2901 :
2902 133268 : if ((item == NULL) ||
2903 133268 : (item->type == COL_TYPE_COLLECTION) ||
2904 66634 : (item->type == COL_TYPE_COLLECTIONREF)) {
2905 : TRACE_ERROR_NUMBER("Invalid argument or invalid argument type", EINVAL);
2906 0 : return EINVAL;
2907 : }
2908 :
2909 66634 : if (property != NULL) {
2910 14 : if (col_validate_property(property)) {
2911 : TRACE_ERROR_STRING("Invalid chracters in the property name", property);
2912 0 : return EINVAL;
2913 : }
2914 14 : free(item->property);
2915 14 : item->property = strdup(property);
2916 14 : if (item->property == NULL) {
2917 : TRACE_ERROR_STRING("Failed to allocate memory", "");
2918 0 : return ENOMEM;
2919 : }
2920 :
2921 : /* Update property length and hash if we rename the property */
2922 14 : item->phash = col_make_hash(property, 0, &(item->property_len));
2923 : TRACE_INFO_NUMBER("Item hash", item->phash);
2924 : TRACE_INFO_NUMBER("Item property length", item->property_len);
2925 : TRACE_INFO_NUMBER("Item property strlen", strlen(item->property));
2926 :
2927 : }
2928 :
2929 : /* We need to change data ? */
2930 66634 : if(length) {
2931 :
2932 : /* If type is different or same but it is string or binary we need to
2933 : * replace the storage */
2934 133248 : if ((item->type != type) ||
2935 133232 : ((item->type == type) &&
2936 133230 : ((item->type == COL_TYPE_STRING) || (item->type == COL_TYPE_BINARY)))) {
2937 : TRACE_INFO_STRING("Replacing item data buffer", "");
2938 66632 : free(item->data);
2939 66632 : item->data = malloc(length);
2940 66632 : if (item->data == NULL) {
2941 : TRACE_ERROR_STRING("Failed to allocate memory", "");
2942 0 : item->length = 0;
2943 0 : return ENOMEM;
2944 : }
2945 66632 : item->length = length;
2946 : }
2947 :
2948 : TRACE_INFO_STRING("Overwriting item data", "");
2949 66632 : memcpy(item->data, data, item->length);
2950 66632 : item->type = type;
2951 :
2952 66632 : if (item->type == COL_TYPE_STRING)
2953 4 : ((char *)(item->data))[item->length - 1] = '\0';
2954 : }
2955 :
2956 : TRACE_FLOW_STRING("col_modify_item", "Exit");
2957 66634 : return EOK;
2958 : }
2959 :
2960 :
2961 : /* Set collection class */
2962 1 : int col_set_collection_class(struct collection_item *item,
2963 : unsigned cclass)
2964 : {
2965 : struct collection_header *header;
2966 :
2967 : TRACE_FLOW_STRING("col_set_collection_class", "Entry");
2968 :
2969 1 : if (item->type != COL_TYPE_COLLECTION) {
2970 : TRACE_INFO_NUMBER("Not a collectin object. Type is", item->type);
2971 0 : return EINVAL;
2972 : }
2973 :
2974 1 : header = (struct collection_header *)item->data;
2975 1 : header->cclass = cclass;
2976 : TRACE_FLOW_STRING("col_set_collection_class", "Exit");
2977 1 : return EOK;
2978 : }
2979 :
2980 : /* Get collection class */
2981 993039 : int col_get_collection_class(struct collection_item *item,
2982 : unsigned *cclass)
2983 : {
2984 : struct collection_header *header;
2985 :
2986 : TRACE_FLOW_STRING("col_get_collection_class", "Entry");
2987 :
2988 993039 : if (item->type != COL_TYPE_COLLECTION) {
2989 : TRACE_ERROR_NUMBER("Not a collection object. Type is", item->type);
2990 0 : return EINVAL;
2991 : }
2992 :
2993 993039 : header = (struct collection_header *)item->data;
2994 993039 : *cclass = header->cclass;
2995 : TRACE_FLOW_STRING("col_get_collection_class", "Exit");
2996 993039 : return EOK;
2997 : }
2998 :
2999 : /* Get collection count */
3000 282 : int col_get_collection_count(struct collection_item *item,
3001 : unsigned *count)
3002 : {
3003 : struct collection_header *header;
3004 :
3005 : TRACE_FLOW_STRING("col_get_collection_count", "Entry");
3006 :
3007 282 : if (item->type != COL_TYPE_COLLECTION) {
3008 : TRACE_ERROR_NUMBER("Not a collectin object. Type is", item->type);
3009 0 : return EINVAL;
3010 : }
3011 :
3012 282 : header = (struct collection_header *)item->data;
3013 282 : *count = header->count;
3014 : TRACE_FLOW_STRING("col_get_collection_count", "Exit");
3015 282 : return EOK;
3016 :
3017 : }
3018 :
3019 : /* Convinience function to check if the collection is of the specific class */
3020 : /* In case of internal error assumes that collection is not of the right class */
3021 993038 : int col_is_of_class(struct collection_item *item, unsigned cclass)
3022 : {
3023 993038 : int error = EOK;
3024 993038 : unsigned ret_class = 0;
3025 :
3026 : TRACE_FLOW_STRING("col_is_of_class invoked", "");
3027 :
3028 993038 : error = col_get_collection_class(item, &ret_class);
3029 993038 : if (error || (ret_class != cclass))
3030 8 : return 0;
3031 : else
3032 993030 : return 1;
3033 : }
3034 :
3035 : /* Get propery */
3036 2126 : const char *col_get_item_property(struct collection_item *ci,
3037 : int *property_len)
3038 : {
3039 2126 : if (property_len != NULL) *property_len = ci->property_len;
3040 2126 : return ci->property;
3041 : }
3042 :
3043 : /* Get type */
3044 66624 : int col_get_item_type(struct collection_item *ci)
3045 : {
3046 66624 : return ci->type;
3047 : }
3048 :
3049 : /* Get length */
3050 17 : int col_get_item_length(struct collection_item *ci)
3051 : {
3052 17 : return ci->length;
3053 : }
3054 :
3055 : /* Get data */
3056 563574 : void *col_get_item_data(struct collection_item *ci)
3057 : {
3058 563574 : return ci->data;
3059 : }
3060 :
3061 : /* Get hash */
3062 376 : uint64_t col_get_item_hash(struct collection_item *ci)
3063 : {
3064 376 : return ci->phash;
3065 : }
3066 :
3067 : /* Calculates hash of the string using internal hashing
3068 : * algorithm. Populates "length" with length
3069 : * of the string not counting 0.
3070 : * Length argument can be NULL.
3071 : */
3072 637761 : uint64_t col_make_hash(const char *string, int sub_len, int *length)
3073 : {
3074 637761 : uint64_t hash = 0;
3075 637761 : int str_len = 0;
3076 :
3077 : TRACE_FLOW_STRING("col_make_hash called for string:", string);
3078 :
3079 637761 : if (string) {
3080 637761 : hash = FNV1a_base;
3081 7982591 : while (string[str_len] != 0) {
3082 :
3083 : /* Check if we need to stop */
3084 6707071 : if ((sub_len > 0) && (str_len == sub_len)) break;
3085 :
3086 6707069 : hash = hash ^ toupper(string[str_len]);
3087 6707069 : hash *= FNV1a_prime;
3088 6707069 : str_len++;
3089 : }
3090 : }
3091 :
3092 637761 : if (length) *length = str_len;
3093 :
3094 : TRACE_FLOW_NUMBER("col_make_hash returning hash:", hash);
3095 :
3096 637761 : return hash;
3097 : }
|