2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
8 * prof_tree.c --- these routines maintain the parse tree of the
11 * All of the details of how the tree is stored is abstracted away in
12 * this file; all of the other profile routines build, access, and
13 * modify the tree via the accessor functions found in this file.
15 * Each node may represent either a relation or a section header.
17 * A section header must have its value field set to 0, and may a one
18 * or more child nodes, pointed to by first_child.
20 * A relation has as its value a pointer to allocated memory
21 * containing a string. Its first_child pointer must be null.
41 int final
:1; /* Indicate don't search next file */
43 struct profile_node
*first_child
;
44 struct profile_node
*parent
;
45 struct profile_node
*next
, *prev
;
48 #define CHECK_MAGIC(node) \
49 if ((node)->magic != PROF_MAGIC_NODE) \
50 return PROF_MAGIC_NODE;
53 * Free a node, and any children
55 void profile_free_node(struct profile_node
*node
)
57 struct profile_node
*child
, *next
;
59 if (node
->magic
!= PROF_MAGIC_NODE
)
67 for (child
=node
->first_child
; child
; child
= next
) {
69 profile_free_node(child
);
78 #define strdup MYstrdup
79 static char *MYstrdup (const char *s
)
81 size_t sz
= strlen(s
) + 1;
92 errcode_t
profile_create_node(const char *name
, const char *value
,
93 struct profile_node
**ret_node
)
95 struct profile_node
*new;
97 new = malloc(sizeof(struct profile_node
));
100 memset(new, 0, sizeof(struct profile_node
));
101 new->name
= strdup(name
);
102 if (new->name
== 0) {
103 profile_free_node(new);
107 new->value
= strdup(value
);
108 if (new->value
== 0) {
109 profile_free_node(new);
113 new->magic
= PROF_MAGIC_NODE
;
120 * This function verifies that all of the representation invarients of
121 * the profile are true. If not, we have a programming bug somewhere,
122 * probably in this file.
124 errcode_t
profile_verify_node(struct profile_node
*node
)
126 struct profile_node
*p
, *last
;
131 if (node
->value
&& node
->first_child
)
132 return PROF_SECTION_WITH_VALUE
;
135 for (p
= node
->first_child
; p
; last
= p
, p
= p
->next
) {
137 return PROF_BAD_LINK_LIST
;
138 if (last
&& (last
->next
!= p
))
139 return PROF_BAD_LINK_LIST
;
140 if (node
->group_level
+1 != p
->group_level
)
141 return PROF_BAD_GROUP_LVL
;
142 if (p
->parent
!= node
)
143 return PROF_BAD_PARENT_PTR
;
144 retval
= profile_verify_node(p
);
152 * Add a node to a particular section
154 errcode_t
profile_add_node(struct profile_node
*section
, const char *name
,
155 const char *value
, struct profile_node
**ret_node
)
158 struct profile_node
*p
, *last
, *new;
160 CHECK_MAGIC(section
);
163 return PROF_ADD_NOT_SECTION
;
166 * Find the place to insert the new node. We look for the
167 * place *after* the last match of the node name, since
170 for (p
=section
->first_child
, last
= 0; p
; last
= p
, p
= p
->next
) {
172 cmp
= strcmp(p
->name
, name
);
176 retval
= profile_create_node(name
, value
, &new);
179 new->group_level
= section
->group_level
+1;
181 new->parent
= section
;
189 section
->first_child
= new;
196 * Set the final flag on a particular node.
198 errcode_t
profile_make_node_final(struct profile_node
*node
)
207 * Check the final flag on a node
209 int profile_is_node_final(struct profile_node
*node
)
211 return (node
->final
!= 0);
215 * Return the name of a node. (Note: this is for internal functions
216 * only; if the name needs to be returned from an exported function,
219 const char *profile_get_node_name(struct profile_node
*node
)
225 * Return the value of a node. (Note: this is for internal functions
226 * only; if the name needs to be returned from an exported function,
229 const char *profile_get_node_value(struct profile_node
*node
)
235 * Iterate through the section, returning the nodes which match
236 * the given name. If name is NULL, then interate through all the
237 * nodes in the section. If section_flag is non-zero, only return the
238 * section which matches the name; don't return relations. If value
239 * is non-NULL, then only return relations which match the requested
240 * value. (The value argument is ignored if section_flag is non-zero.)
242 * The first time this routine is called, the state pointer must be
243 * null. When this profile_find_node_relation() returns, if the state
244 * pointer is non-NULL, then this routine should be called again.
245 * (This won't happen if section_flag is non-zero, obviously.)
248 errcode_t
profile_find_node(struct profile_node
*section
, const char *name
,
249 const char *value
, int section_flag
, void **state
,
250 struct profile_node
**node
)
252 struct profile_node
*p
;
254 CHECK_MAGIC(section
);
259 p
= section
->first_child
;
261 for (; p
; p
= p
->next
) {
262 if (name
&& (strcmp(p
->name
, name
)))
270 if (value
&& (strcmp(p
->value
, value
)))
282 return section_flag
? PROF_NO_SECTION
: PROF_NO_RELATION
;
285 * OK, we've found one match; now let's try to find another
286 * one. This way, if we return a non-zero state pointer,
287 * there's guaranteed to be another match that's returned.
289 for (p
= p
->next
; p
; p
= p
->next
) {
290 if (name
&& (strcmp(p
->name
, name
)))
298 if (value
&& (strcmp(p
->value
, value
)))
310 * Iterate through the section, returning the relations which match
311 * the given name. If name is NULL, then interate through all the
312 * relations in the section. The first time this routine is called,
313 * the state pointer must be null. When this profile_find_node_relation()
314 * returns, if the state pointer is non-NULL, then this routine should
317 * The returned character string in value points to the stored
318 * character string in the parse string. Before this string value is
319 * returned to a calling application (profile_find_node_relation is not an
320 * exported interface), it should be strdup()'ed.
322 errcode_t
profile_find_node_relation(struct profile_node
*section
,
323 const char *name
, void **state
,
324 char **ret_name
, char **value
)
326 struct profile_node
*p
;
329 retval
= profile_find_node(section
, name
, 0, 0, state
, &p
);
343 * Iterate through the section, returning the subsections which match
344 * the given name. If name is NULL, then interate through all the
345 * subsections in the section. The first time this routine is called,
346 * the state pointer must be null. When this profile_find_node_subsection()
347 * returns, if the state pointer is non-NULL, then this routine should
350 * This is (plus accessor functions for the name and value given a
351 * profile node) makes this function mostly syntactic sugar for
354 errcode_t
profile_find_node_subsection(struct profile_node
*section
,
355 const char *name
, void **state
,
357 struct profile_node
**subsection
)
359 struct profile_node
*p
;
362 /* Solaris Kerberos */
363 if (section
== (struct profile_node
*)NULL
)
364 return (PROF_NO_PROFILE
);
366 retval
= profile_find_node(section
, name
, 0, 1, state
, &p
);
380 * This function returns the parent of a particular node.
382 errcode_t
profile_get_node_parent(struct profile_node
*section
,
383 struct profile_node
**parent
)
385 *parent
= section
->parent
;
390 * This is a general-purpose iterator for returning all nodes that
391 * match the specified name array.
393 struct profile_iterator
{
397 const char *const *names
;
402 struct profile_node
*node
;
406 errcode_t
profile_node_iterator_create(profile_t profile
,
407 const char *const *names
, int flags
,
410 struct profile_iterator
*iter
;
414 return PROF_NO_PROFILE
;
415 if (profile
->magic
!= PROF_MAGIC_PROFILE
)
416 return PROF_MAGIC_PROFILE
;
418 return PROF_BAD_NAMESET
;
419 if (!(flags
& PROFILE_ITER_LIST_SECTION
)) {
421 return PROF_BAD_NAMESET
;
425 if ((iter
= malloc(sizeof(struct profile_iterator
))) == NULL
)
428 iter
->magic
= PROF_MAGIC_ITERATOR
;
429 iter
->profile
= profile
;
432 iter
->file
= profile
->first_file
;
433 iter
->done_idx
= done_idx
;
440 void profile_node_iterator_free(void **iter_p
)
442 struct profile_iterator
*iter
;
447 if (!iter
|| iter
->magic
!= PROF_MAGIC_ITERATOR
)
454 * Note: the returned character strings in ret_name and ret_value
455 * points to the stored character string in the parse string. Before
456 * this string value is returned to a calling application
457 * (profile_node_iterator is not an exported interface), it should be
460 errcode_t
profile_node_iterator(void **iter_p
, struct profile_node
**ret_node
,
461 char **ret_name
, char **ret_value
)
463 struct profile_iterator
*iter
= *iter_p
;
464 struct profile_node
*section
, *p
;
465 const char *const *cpp
;
469 if (!iter
|| iter
->magic
!= PROF_MAGIC_ITERATOR
)
470 return PROF_MAGIC_ITERATOR
;
471 if (iter
->file
&& iter
->file
->magic
!= PROF_MAGIC_FILE
)
472 return PROF_MAGIC_FILE
;
473 if (iter
->file
&& iter
->file
->data
->magic
!= PROF_MAGIC_FILE_DATA
)
474 return PROF_MAGIC_FILE_DATA
;
476 * If the file has changed, then the node pointer is invalid,
477 * so we'll have search the file again looking for it.
480 retval
= k5_mutex_lock(&iter
->file
->data
->lock
);
484 if (iter
->node
&& (iter
->file
->data
->upd_serial
!= iter
->file_serial
)) {
485 iter
->flags
&= ~PROFILE_ITER_FINAL_SEEN
;
486 skip_num
= iter
->num
;
489 if (iter
->node
&& iter
->node
->magic
!= PROF_MAGIC_NODE
) {
491 k5_mutex_unlock(&iter
->file
->data
->lock
);
492 return PROF_MAGIC_NODE
;
495 if (iter
->node
== 0) {
496 if (iter
->file
== 0 ||
497 (iter
->flags
& PROFILE_ITER_FINAL_SEEN
)) {
499 k5_mutex_unlock(&iter
->file
->data
->lock
);
500 profile_node_iterator_free(iter_p
);
509 k5_mutex_unlock(&iter
->file
->data
->lock
);
510 if ((retval
= profile_update_file(iter
->file
))) {
511 if (retval
== ENOENT
|| retval
== EACCES
) {
512 /* XXX memory leak? */
513 iter
->file
= iter
->file
->next
;
515 retval
= k5_mutex_lock(&iter
->file
->data
->lock
);
517 profile_node_iterator_free(iter_p
);
525 profile_node_iterator_free(iter_p
);
529 retval
= k5_mutex_lock(&iter
->file
->data
->lock
);
531 profile_node_iterator_free(iter_p
);
534 iter
->file_serial
= iter
->file
->data
->upd_serial
;
536 * Find the section to list if we are a LIST_SECTION,
537 * or find the containing section if not.
539 section
= iter
->file
->data
->root
;
540 assert(section
!= NULL
);
541 for (cpp
= iter
->names
; cpp
[iter
->done_idx
]; cpp
++) {
542 for (p
=section
->first_child
; p
; p
= p
->next
) {
543 if (!strcmp(p
->name
, *cpp
) && !p
->value
)
552 iter
->flags
|= PROFILE_ITER_FINAL_SEEN
;
555 k5_mutex_unlock(&iter
->file
->data
->lock
);
556 iter
->file
= iter
->file
->next
;
558 retval
= k5_mutex_lock(&iter
->file
->data
->lock
);
560 profile_node_iterator_free(iter_p
);
568 iter
->node
= section
->first_child
;
571 * OK, now we know iter->node is set up correctly. Let's do
574 for (p
= iter
->node
; p
; p
= p
->next
) {
575 if (iter
->name
&& strcmp(p
->name
, iter
->name
))
577 if ((iter
->flags
& PROFILE_ITER_SECTIONS_ONLY
) &&
580 if ((iter
->flags
& PROFILE_ITER_RELATIONS_ONLY
) &&
593 k5_mutex_unlock(&iter
->file
->data
->lock
);
594 iter
->file
= iter
->file
->next
;
596 retval
= k5_mutex_lock(&iter
->file
->data
->lock
);
598 profile_node_iterator_free(iter_p
);
606 k5_mutex_unlock(&iter
->file
->data
->lock
);
607 if ((iter
->node
= p
->next
) == NULL
)
608 iter
->file
= iter
->file
->next
;
614 *ret_value
= p
->value
;
619 * Remove a particular node.
623 errcode_t
profile_remove_node(struct profile_node
*node
)
627 if (node
->parent
== 0)
628 return PROF_EINVAL
; /* Can't remove the root! */
636 * Set the value of a specific node containing a relation.
640 errcode_t
profile_set_relation_value(struct profile_node
*node
,
641 const char *new_value
)
648 return PROF_SET_SECTION_VALUE
;
650 cp
= malloc(strlen(new_value
)+1);
654 strcpy(cp
, new_value
);
662 * Rename a specific node
666 errcode_t
profile_rename_node(struct profile_node
*node
, const char *new_name
)
669 struct profile_node
*p
, *last
;
673 if (strcmp(new_name
, node
->name
) == 0)
674 return 0; /* It's the same name, return */
677 * Make sure we can allocate memory for the new name, first!
679 new_string
= malloc(strlen(new_name
)+1);
682 strcpy(new_string
, new_name
);
685 * Find the place to where the new node should go. We look
686 * for the place *after* the last match of the node name,
687 * since order matters.
689 for (p
=node
->parent
->first_child
, last
= 0; p
; last
= p
, p
= p
->next
) {
690 if (strcmp(p
->name
, new_name
) > 0)
695 * If we need to move the node, do it now.
697 if ((p
!= node
) && (last
!= node
)) {
699 * OK, let's detach the node
702 node
->prev
->next
= node
->next
;
704 node
->parent
->first_child
= node
->next
;
706 node
->next
->prev
= node
->prev
;
709 * Now let's reattach it in the right place.
716 node
->parent
->first_child
= node
;
722 node
->name
= new_string
;