1 /*
2  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * prof_tree.c --- these routines maintain the parse tree of the
10  * 	config file.
11  *
12  * All of the details of how the tree is stored is abstracted away in
13  * this file; all of the other profile routines build, access, and
14  * modify the tree via the accessor functions found in this file.
15  *
16  * Each node may represent either a relation or a section header.
17  *
18  * A section header must have its value field set to 0, and may a one
19  * or more child nodes, pointed to by first_child.
20  *
21  * A relation has as its value a pointer to allocated memory
22  * containing a string.  Its first_child pointer must be null.
23  *
24  */
25 
26 #include <stdio.h>
27 #include <string.h>
28 #ifdef HAVE_STDLIB_H
29 #include <stdlib.h>
30 #endif
31 #include <errno.h>
32 #include <ctype.h>
33 
34 #include "prof_int.h"
35 
36 struct profile_node {
37 	errcode_t	magic;
38 	char *name;
39 	char *value;
40 	int group_level;
41 	int final:1;		/* Indicate don't search next file */
42 	struct profile_node *first_child;
43 	struct profile_node *parent;
44 	struct profile_node *next, *prev;
45 };
46 
47 #define CHECK_MAGIC(node) \
48 	  if ((node)->magic != PROF_MAGIC_NODE) \
49 		  return PROF_MAGIC_NODE;
50 
51 /*
52  * Free a node, and any children
53  */
54 void profile_free_node(node)
55 	struct profile_node *node;
56 {
57 	struct profile_node *child, *next;
58 
59 	if (node->magic != PROF_MAGIC_NODE)
60 		return;
61 
62 	if (node->name)
63 		free(node->name);
64 	if (node->value)
65 		free(node->value);
66 
67 	for (child=node->first_child; child; child = next) {
68 		next = child->next;
69 		profile_free_node(child);
70 	}
71 	node->magic = 0;
72 
73 	free(node);
74 }
75 
76 /*
77  * Create a node
78  */
79 errcode_t profile_create_node(name, value, ret_node)
80 	const char *name, *value;
81 	struct profile_node **ret_node;
82 {
83 	struct profile_node *new;
84 
85 	new = (struct profile_node *)malloc(sizeof(struct profile_node));
86 	if (!new)
87 		return ENOMEM;
88 	memset(new, 0, sizeof(struct profile_node));
89 	new->name = (char *) malloc(strlen(name)+1);
90 	if (new->name == 0) {
91 		profile_free_node(new);
92 		return ENOMEM;
93 	}
94 	strcpy(new->name, name);
95 	if (value) {
96 		new->value = (char *) malloc(strlen(value)+1);
97 		if (new->value == 0) {
98 			profile_free_node(new);
99 			return ENOMEM;
100 		}
101 		strcpy(new->value, value);
102 	}
103 	new->magic = PROF_MAGIC_NODE;
104 
105 	*ret_node = new;
106 	return 0;
107 }
108 
109 /*
110  * This function verifies that all of the representation invarients of
111  * the profile are true.  If not, we have a programming bug somewhere,
112  * probably in this file.
113  */
114 errcode_t profile_verify_node(node)
115 	struct profile_node *node;
116 {
117 	struct profile_node *p, *last;
118 	errcode_t	retval;
119 
120 	CHECK_MAGIC(node);
121 
122 	if (node->value && node->first_child)
123 		return PROF_SECTION_WITH_VALUE;
124 
125 	last = 0;
126 	for (p = node->first_child; p; last = p, p = p->next) {
127 		if (p->prev != last)
128 			return PROF_BAD_LINK_LIST;
129 		if (last && (last->next != p))
130 			return PROF_BAD_LINK_LIST;
131 		if (node->group_level+1 != p->group_level)
132 			return PROF_BAD_GROUP_LVL;
133 		if (p->parent != node)
134 			return PROF_BAD_PARENT_PTR;
135 		retval = profile_verify_node(p);
136 		if (retval)
137 			return retval;
138 	}
139 	return 0;
140 }
141 
142 /*
143  * Add a node to a particular section
144  */
145 errcode_t profile_add_node(section, name, value, ret_node)
146 	struct profile_node *section;
147 	const char *name, *value;
148 	struct profile_node **ret_node;
149 {
150 	errcode_t retval;
151 	struct profile_node *p, *last, *new;
152 	int	cmp = -1;
153 
154 	CHECK_MAGIC(section);
155 
156 	if (section->value)
157 		return PROF_ADD_NOT_SECTION;
158 
159 	/*
160 	 * Find the place to insert the new node.  We look for the
161 	 * place *after* the last match of the node name, since
162 	 * order matters.
163 	 */
164 	for (p=section->first_child, last = 0; p; last = p, p = p->next) {
165 		cmp = strcmp(p->name, name);
166 		if (cmp > 0)
167 			break;
168 	}
169 	retval = profile_create_node(name, value, &new);
170 	if (retval)
171 		return retval;
172 	new->group_level = section->group_level+1;
173 	new->parent = section;
174 	new->prev = last;
175 	new->next = p;
176 	if (p)
177 		p->prev = new;
178 	if (last)
179 		last->next = new;
180 	else
181 		section->first_child = new;
182 	if (ret_node)
183 		*ret_node = new;
184 	return 0;
185 }
186 
187 /*
188  * Set the final flag on a particular node.
189  */
190 errcode_t profile_make_node_final(node)
191 	struct profile_node *node;
192 {
193 	CHECK_MAGIC(node);
194 
195 	node->final = 1;
196 	return 0;
197 }
198 
199 /*
200  * Check the final flag on a node
201  */
202 int profile_is_node_final(node)
203 	struct profile_node *node;
204 {
205 	return (node->final != 0);
206 }
207 
208 /*
209  * Return the name of a node.  (Note: this is for internal functions
210  * only; if the name needs to be returned from an exported function,
211  * strdup it first!)
212  */
213 const char *profile_get_node_name(node)
214 	struct profile_node *node;
215 {
216 	return node->name;
217 }
218 
219 /*
220  * Return the value of a node.  (Note: this is for internal functions
221  * only; if the name needs to be returned from an exported function,
222  * strdup it first!)
223  */
224 const char *profile_get_node_value(node)
225 	struct profile_node *node;
226 {
227 	return node->value;
228 }
229 
230 /*
231  * Iterate through the section, returning the nodes which match
232  * the given name.  If name is NULL, then interate through all the
233  * nodes in the section.  If section_flag is non-zero, only return the
234  * section which matches the name; don't return relations.  If value
235  * is non-NULL, then only return relations which match the requested
236  * value.  (The value argument is ignored if section_flag is non-zero.)
237  *
238  * The first time this routine is called, the state pointer must be
239  * null.  When this profile_find_node_relation() returns, if the state
240  * pointer is non-NULL, then this routine should be called again.
241  * (This won't happen if section_flag is non-zero, obviously.)
242  *
243  */
244 errcode_t profile_find_node(section, name, value, section_flag, state, node)
245 	struct profile_node *section;
246 	const char *name;
247 	const char *value;
248 	int section_flag;
249 	void **state;
250 	struct profile_node **node;
251 {
252 	struct profile_node *p;
253 
254 	CHECK_MAGIC(section);
255 	p = *state;
256 	if (p) {
257 		CHECK_MAGIC(p);
258 	} else
259 		p = section->first_child;
260 
261 	for (; p; p = p->next) {
262 		if (name && (strcmp(p->name, name)))
263 			continue;
264 		if (section_flag) {
265 			if (p->value)
266 				continue;
267 		} else {
268 			if (!p->value)
269 				continue;
270 			if (value && (strcmp(p->value, value)))
271 				continue;
272 		}
273 		/* A match! */
274 		if (node)
275 			*node = p;
276 		break;
277 	}
278 	if (p == 0) {
279 		*state = 0;
280 		return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION;
281 	}
282 	/*
283 	 * OK, we've found one match; now let's try to find another
284 	 * one.  This way, if we return a non-zero state pointer,
285 	 * there's guaranteed to be another match that's returned.
286 	 */
287 	for (p = p->next; p; p = p->next) {
288 		if (name && (strcmp(p->name, name)))
289 			continue;
290 		if (section_flag) {
291 			if (p->value)
292 				continue;
293 		} else {
294 			if (!p->value)
295 				continue;
296 			if (value && (strcmp(p->value, value)))
297 				continue;
298 		}
299 		/* A match! */
300 		break;
301 	}
302 	*state = p;
303 	return 0;
304 }
305 
306 
307 /*
308  * Iterate through the section, returning the relations which match
309  * the given name.  If name is NULL, then interate through all the
310  * relations in the section.  The first time this routine is called,
311  * the state pointer must be null.  When this profile_find_node_relation()
312  * returns, if the state pointer is non-NULL, then this routine should
313  * be called again.
314  *
315  * The returned character string in value points to the stored
316  * character string in the parse string.  Before this string value is
317  * returned to a calling application (profile_find_node_relation is not an
318  * exported interface), it should be strdup()'ed.
319  */
320 errcode_t profile_find_node_relation(section, name, state, ret_name, value)
321 	struct profile_node *section;
322 	const char *name;
323 	void **state;
324 	char **ret_name, **value;
325 {
326 	struct profile_node *p;
327 	errcode_t	retval;
328 
329 	retval = profile_find_node(section, name, 0, 0, state, &p);
330 	if (retval)
331 		return retval;
332 
333 	if (p) {
334 		if (value)
335 			*value = p->value;
336 		if (ret_name)
337 			*ret_name = p->name;
338 	}
339 	return 0;
340 }
341 
342 /*
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
348  * be called again.
349  *
350  * This is (plus accessor functions for the name and value given a
351  * profile node) makes this function mostly syntactic sugar for
352  * profile_find_node.
353  */
354 errcode_t profile_find_node_subsection(section, name, state, ret_name,
355 				       subsection)
356 	struct profile_node *section;
357 	const char *name;
358 	void **state;
359 	char **ret_name;
360 	struct profile_node **subsection;
361 {
362 	struct profile_node *p;
363 	errcode_t	retval;
364 
365 	if (section == (struct profile_node *)NULL)
366 		return (PROF_NO_PROFILE);
367 
368 	retval = profile_find_node(section, name, 0, 1, state, &p);
369 	if (retval)
370 		return retval;
371 
372 	if (p) {
373 		if (subsection)
374 			*subsection = p;
375 		if (ret_name)
376 			*ret_name = p->name;
377 	}
378 	return 0;
379 }
380 
381 /*
382  * This function returns the parent of a particular node.
383  */
384 errcode_t profile_get_node_parent(section, parent)
385 	struct profile_node *section, **parent;
386 {
387 	*parent = section->parent;
388 	return 0;
389 }
390 
391 /*
392  * This is a general-purpose iterator for returning all nodes that
393  * match the specified name array.
394  */
395 struct profile_iterator {
396 	prf_magic_t		magic;
397 	profile_t		profile;
398 	int			flags;
399 	const char 		**names;
400 	const char		*name;
401 	prf_file_t		file;
402 	int			file_serial;
403 	int			done_idx;
404 	struct profile_node 	*node;
405 	int			num;
406 };
407 
408 errcode_t profile_node_iterator_create(profile, names, flags, ret_iter)
409 	profile_t	profile;
410 	const char	**names;
411 	int		flags;
412 	void		**ret_iter;
413 {
414 	struct profile_iterator *iter;
415 	int	done_idx = 0;
416 
417 	if (profile == 0)
418 		return PROF_NO_PROFILE;
419 	if (profile->magic != PROF_MAGIC_PROFILE)
420 		return PROF_MAGIC_PROFILE;
421 	if (!names)
422 		return PROF_BAD_NAMESET;
423 	if (!(flags & PROFILE_ITER_LIST_SECTION)) {
424 		if (!names[0])
425 			return PROF_BAD_NAMESET;
426 		done_idx = 1;
427 	}
428 
429 	if ((iter = (struct profile_iterator *)
430 		malloc(sizeof(struct profile_iterator))) == NULL)
431 		return ENOMEM;
432 
433 	iter->magic = PROF_MAGIC_ITERATOR;
434 	iter->profile = profile;
435 	iter->names = names;
436 	iter->flags = flags;
437 	iter->file = profile->first_file;
438 	iter->done_idx = done_idx;
439 	iter->node = 0;
440 	iter->num = 0;
441 	*ret_iter = iter;
442 	return 0;
443 }
444 
445 void profile_node_iterator_free(iter_p)
446 	void	**iter_p;
447 {
448 	struct profile_iterator *iter;
449 
450 	if (!iter_p)
451 		return;
452 	iter = *iter_p;
453 	if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
454 		return;
455 	free(iter);
456 	*iter_p = 0;
457 }
458 
459 /*
460  * Note: the returned character strings in ret_name and ret_value
461  * points to the stored character string in the parse string.  Before
462  * this string value is returned to a calling application
463  * (profile_node_iterator is not an exported interface), it should be
464  * strdup()'ed.
465  */
466 errcode_t profile_node_iterator(iter_p, ret_node, ret_name, ret_value)
467 	void	**iter_p;
468 	struct profile_node	**ret_node;
469 	char **ret_name, **ret_value;
470 {
471 	struct profile_iterator 	*iter = *iter_p;
472 	struct profile_node 		*section, *p;
473 	const char			**cpp;
474 	errcode_t			retval;
475 	int				skip_num = 0;
476 
477 	if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
478 		return PROF_MAGIC_ITERATOR;
479 	/*
480 	 * If the file has changed, then the node pointer is invalid,
481 	 * so we'll have search the file again looking for it.
482 	 */
483 	if (iter->node && (iter->file->upd_serial != iter->file_serial)) {
484 		iter->flags &= ~PROFILE_ITER_FINAL_SEEN;
485 		skip_num = iter->num;
486 		iter->node = 0;
487 	}
488 get_new_file:
489 	if (iter->node == 0) {
490 		if (iter->file == 0 ||
491 		    (iter->flags & PROFILE_ITER_FINAL_SEEN)) {
492 			profile_node_iterator_free(iter_p);
493 			if (ret_node)
494 				*ret_node = 0;
495 			if (ret_name)
496 				*ret_name = 0;
497 			if (ret_value)
498 				*ret_value =0;
499 			return 0;
500 		}
501 		if ((retval = profile_update_file(iter->file))) {
502 			profile_node_iterator_free(iter_p);
503 			return retval;
504 		}
505 		iter->file_serial = iter->file->upd_serial;
506 		/*
507 		 * Find the section to list if we are a LIST_SECTION,
508 		 * or find the containing section if not.
509 		 */
510 		section = iter->file->root;
511 		for (cpp = iter->names; cpp[iter->done_idx]; cpp++) {
512 			for (p=section->first_child; p; p = p->next)
513 				if (!strcmp(p->name, *cpp) && !p->value)
514 					break;
515 			if (!p) {
516 				section = 0;
517 				break;
518 			}
519 			section = p;
520 			if (p->final)
521 				iter->flags |= PROFILE_ITER_FINAL_SEEN;
522 		}
523 		if (!section) {
524 			iter->file = iter->file->next;
525 			skip_num = 0;
526 			goto get_new_file;
527 		}
528 		iter->name = *cpp;
529 		iter->node = section->first_child;
530 	}
531 	/*
532 	 * OK, now we know iter->node is set up correctly.  Let's do
533 	 * the search.
534 	 */
535 	for (p = iter->node; p; p = p->next) {
536 		if (iter->name && strcmp(p->name, iter->name))
537 			continue;
538 		if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) &&
539 		    p->value)
540 			continue;
541 		if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) &&
542 		    !p->value)
543 			continue;
544 		if (skip_num > 0) {
545 			skip_num--;
546 			continue;
547 		}
548 		break;
549 	}
550 	iter->num++;
551 	if (!p) {
552 		iter->file = iter->file->next;
553 		iter->node = 0;
554 		skip_num = 0;
555 		goto get_new_file;
556 	}
557 	if ((iter->node = p->next) == NULL)
558 		iter->file = iter->file->next;
559 	if (ret_node)
560 		*ret_node = p;
561 	if (ret_name)
562 		*ret_name = p->name;
563 	if (ret_value)
564 		*ret_value = p->value;
565 	return 0;
566 }
567 
568 /*
569  * Remove a particular node.
570  *
571  * TYT, 2/25/99
572  */
573 errcode_t profile_remove_node(node)
574 	struct profile_node *node;
575 {
576 	CHECK_MAGIC(node);
577 
578 	if (node->parent == 0)
579 		return PROF_EINVAL; /* Can't remove the root! */
580 
581 	if (node->prev)
582 		node->prev->next = node->next;
583 	else
584 		node->parent->first_child = node->next;
585 
586 	if (node->next)
587 		node->next->prev = node->prev;
588 
589 	profile_free_node(node);
590 
591 	return 0;
592 }
593 
594 /*
595  * Set the value of a specific node containing a relation.
596  *
597  * TYT, 2/25/99
598  */
599 errcode_t profile_set_relation_value(node, new_value)
600 	struct profile_node *node;
601 	const char *new_value;
602 {
603 	char	*cp;
604 
605 	CHECK_MAGIC(node);
606 
607 	if (!node->value)
608 		return PROF_SET_SECTION_VALUE;
609 
610 	cp = (char *) malloc(strlen(new_value)+1);
611 	if (!cp)
612 		return ENOMEM;
613 
614 	strcpy(cp, new_value);
615 	free(node->value);
616 	node->value = cp;
617 
618 	return 0;
619 }
620 
621 /*
622  * Rename a specific node
623  *
624  * TYT 2/25/99
625  */
626 errcode_t profile_rename_node(node, new_name)
627 	struct profile_node	*node;
628 	const char		*new_name;
629 {
630 	char			*new_string;
631 	struct profile_node 	*p, *last;
632 
633 	CHECK_MAGIC(node);
634 
635 	if (strcmp(new_name, node->name) == 0)
636 		return 0;	/* It's the same name, return */
637 
638 	/*
639 	 * Make sure we can allocate memory for the new name, first!
640 	 */
641 	new_string = (char *) malloc(strlen(new_name)+1);
642 	if (!new_string)
643 		return ENOMEM;
644 	strcpy(new_string, new_name);
645 
646 	/*
647 	 * Find the place to where the new node should go.  We look
648 	 * for the place *after* the last match of the node name,
649 	 * since order matters.
650 	 */
651 	for (p=node->parent->first_child, last = 0; p; last = p, p = p->next) {
652 		if (strcmp(p->name, new_name) > 0)
653 			break;
654 	}
655 
656 	/*
657 	 * If we need to move the node, do it now.
658 	 */
659 	if ((p != node) && (last != node)) {
660 		/*
661 		 * OK, let's detach the node
662 		 */
663 		if (node->prev)
664 			node->prev->next = node->next;
665 		else
666 			node->parent->first_child = node->next;
667 		if (node->next)
668 			node->next->prev = node->prev;
669 
670 		/*
671 		 * Now let's reattach it in the right place.
672 		 */
673 		if (p)
674 			p->prev = node;
675 		if (last)
676 			last->next = node;
677 		else
678 			node->parent->first_child = node;
679 		node->next = p;
680 		node->prev = last;
681 	}
682 
683 	free(node->name);
684 	node->name = new_string;
685 	return 0;
686 }
687