xref: /onnv-gate/usr/src/lib/gss_mechs/mech_krb5/profile/prof_parse.c (revision 7934:6aeeafc994de)
10Sstevel@tonic-gate /*
2*7934SMark.Phalan@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate 
7781Sgtb #include "prof_int.h"
8781Sgtb 
90Sstevel@tonic-gate #include <stdio.h>
100Sstevel@tonic-gate #include <string.h>
110Sstevel@tonic-gate #ifdef HAVE_STDLIB_H
120Sstevel@tonic-gate #include <stdlib.h>
130Sstevel@tonic-gate #endif
140Sstevel@tonic-gate #include <errno.h>
150Sstevel@tonic-gate #include <ctype.h>
160Sstevel@tonic-gate 
170Sstevel@tonic-gate #define SECTION_SEP_CHAR '/'
180Sstevel@tonic-gate 
190Sstevel@tonic-gate #define STATE_INIT_COMMENT	1
200Sstevel@tonic-gate #define STATE_STD_LINE		2
210Sstevel@tonic-gate #define STATE_GET_OBRACE	3
220Sstevel@tonic-gate 
230Sstevel@tonic-gate struct parse_state {
240Sstevel@tonic-gate 	int	state;
250Sstevel@tonic-gate 	int	group_level;
260Sstevel@tonic-gate 	struct profile_node *root_section;
270Sstevel@tonic-gate 	struct profile_node *current_section;
280Sstevel@tonic-gate };
290Sstevel@tonic-gate 
skip_over_blanks(char * cp)30781Sgtb static char *skip_over_blanks(char *cp)
310Sstevel@tonic-gate {
32781Sgtb 	while (*cp && isspace((int) (*cp)))
330Sstevel@tonic-gate 		cp++;
340Sstevel@tonic-gate 	return cp;
350Sstevel@tonic-gate }
360Sstevel@tonic-gate 
strip_line(char * line)37781Sgtb static void strip_line(char *line)
380Sstevel@tonic-gate {
39781Sgtb 	char *p = line + strlen(line);
40781Sgtb 	while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
41781Sgtb 	    *p-- = 0;
420Sstevel@tonic-gate }
430Sstevel@tonic-gate 
parse_quoted_string(char * str)440Sstevel@tonic-gate static void parse_quoted_string(char *str)
450Sstevel@tonic-gate {
460Sstevel@tonic-gate 	char *to, *from;
470Sstevel@tonic-gate 
480Sstevel@tonic-gate 	to = from = str;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate 	for (to = from = str; *from && *from != '"'; to++, from++) {
510Sstevel@tonic-gate 		if (*from == '\\') {
520Sstevel@tonic-gate 			from++;
530Sstevel@tonic-gate 			switch (*from) {
540Sstevel@tonic-gate 			case 'n':
550Sstevel@tonic-gate 				*to = '\n';
560Sstevel@tonic-gate 				break;
570Sstevel@tonic-gate 			case 't':
580Sstevel@tonic-gate 				*to = '\t';
590Sstevel@tonic-gate 				break;
600Sstevel@tonic-gate 			case 'b':
610Sstevel@tonic-gate 				*to = '\b';
620Sstevel@tonic-gate 				break;
630Sstevel@tonic-gate 			default:
640Sstevel@tonic-gate 				*to = *from;
650Sstevel@tonic-gate 			}
660Sstevel@tonic-gate 			continue;
670Sstevel@tonic-gate 		}
680Sstevel@tonic-gate 		*to = *from;
690Sstevel@tonic-gate 	}
700Sstevel@tonic-gate 	*to = '\0';
710Sstevel@tonic-gate }
720Sstevel@tonic-gate 
730Sstevel@tonic-gate 
parse_init_state(struct parse_state * state)74781Sgtb static errcode_t parse_init_state(struct parse_state *state)
750Sstevel@tonic-gate {
760Sstevel@tonic-gate 	state->state = STATE_INIT_COMMENT;
770Sstevel@tonic-gate 	state->group_level = 0;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 	return profile_create_node("(root)", 0, &state->root_section);
800Sstevel@tonic-gate }
810Sstevel@tonic-gate 
parse_std_line(char * line,struct parse_state * state)82781Sgtb static errcode_t parse_std_line(char *line, struct parse_state *state)
830Sstevel@tonic-gate {
840Sstevel@tonic-gate 	char	*cp, ch, *tag, *value;
850Sstevel@tonic-gate 	char	*p;
860Sstevel@tonic-gate 	errcode_t retval;
870Sstevel@tonic-gate 	struct profile_node	*node;
880Sstevel@tonic-gate 	int do_subsection = 0;
890Sstevel@tonic-gate 	void *iter = 0;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	if (*line == 0)
920Sstevel@tonic-gate 		return 0;
93*7934SMark.Phalan@Sun.COM 	cp = skip_over_blanks(line);
94*7934SMark.Phalan@Sun.COM 	if (cp[0] == ';' || cp[0] == '#')
950Sstevel@tonic-gate 		return 0;
96*7934SMark.Phalan@Sun.COM 	strip_line(cp);
970Sstevel@tonic-gate 	ch = *cp;
980Sstevel@tonic-gate 	if (ch == 0)
990Sstevel@tonic-gate 		return 0;
1000Sstevel@tonic-gate 	if (ch == '[') {
1010Sstevel@tonic-gate 		if (state->group_level > 0)
1020Sstevel@tonic-gate 			return PROF_SECTION_NOTOP;
1030Sstevel@tonic-gate 		cp++;
1040Sstevel@tonic-gate 		p = strchr(cp, ']');
1050Sstevel@tonic-gate 		if (p == NULL)
1060Sstevel@tonic-gate 			return PROF_SECTION_SYNTAX;
1070Sstevel@tonic-gate 		*p = '\0';
1080Sstevel@tonic-gate 		retval = profile_find_node_subsection(state->root_section,
1090Sstevel@tonic-gate 						 cp, &iter, 0,
1100Sstevel@tonic-gate 						 &state->current_section);
1110Sstevel@tonic-gate 		if (retval == PROF_NO_SECTION) {
1120Sstevel@tonic-gate 			retval = profile_add_node(state->root_section,
1130Sstevel@tonic-gate 						  cp, 0,
1140Sstevel@tonic-gate 						  &state->current_section);
1150Sstevel@tonic-gate 			if (retval)
1160Sstevel@tonic-gate 				return retval;
1170Sstevel@tonic-gate 		} else if (retval)
1180Sstevel@tonic-gate 			return retval;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 		/*
1210Sstevel@tonic-gate 		 * Finish off the rest of the line.
1220Sstevel@tonic-gate 		 */
1230Sstevel@tonic-gate 		cp = p+1;
1240Sstevel@tonic-gate 		if (*cp == '*') {
1250Sstevel@tonic-gate 			profile_make_node_final(state->current_section);
1260Sstevel@tonic-gate 			cp++;
1270Sstevel@tonic-gate 		}
1280Sstevel@tonic-gate 		/*
129781Sgtb 		 * A space after ']' should not be fatal
1300Sstevel@tonic-gate 		 */
1310Sstevel@tonic-gate 		cp = skip_over_blanks(cp);
1320Sstevel@tonic-gate 		if (*cp)
1330Sstevel@tonic-gate 			return PROF_SECTION_SYNTAX;
1340Sstevel@tonic-gate 		return 0;
1350Sstevel@tonic-gate 	}
1360Sstevel@tonic-gate 	if (ch == '}') {
1370Sstevel@tonic-gate 		if (state->group_level == 0)
1380Sstevel@tonic-gate 			return PROF_EXTRA_CBRACE;
1390Sstevel@tonic-gate 		if (*(cp+1) == '*')
1400Sstevel@tonic-gate 			profile_make_node_final(state->current_section);
1410Sstevel@tonic-gate 		retval = profile_get_node_parent(state->current_section,
1420Sstevel@tonic-gate 						 &state->current_section);
1430Sstevel@tonic-gate 		if (retval)
1440Sstevel@tonic-gate 			return retval;
1450Sstevel@tonic-gate 		state->group_level--;
1460Sstevel@tonic-gate 		return 0;
1470Sstevel@tonic-gate 	}
1480Sstevel@tonic-gate 	/*
1490Sstevel@tonic-gate 	 * Parse the relations
1500Sstevel@tonic-gate 	 */
1510Sstevel@tonic-gate 	tag = cp;
1520Sstevel@tonic-gate 	cp = strchr(cp, '=');
1530Sstevel@tonic-gate 	if (!cp)
1540Sstevel@tonic-gate 		return PROF_RELATION_SYNTAX;
155781Sgtb 	if (cp == tag)
156781Sgtb 	    return PROF_RELATION_SYNTAX;
1570Sstevel@tonic-gate 	*cp = '\0';
158781Sgtb 	p = tag;
159781Sgtb 	/* Look for whitespace on left-hand side.  */
160781Sgtb 	while (p < cp && !isspace((int)*p))
161781Sgtb 	    p++;
162781Sgtb 	if (p < cp) {
163781Sgtb 	    /* Found some sort of whitespace.  */
164781Sgtb 	    *p++ = 0;
165781Sgtb 	    /* If we have more non-whitespace, it's an error.  */
166781Sgtb 	    while (p < cp) {
167781Sgtb 		if (!isspace((int)*p))
168781Sgtb 		    return PROF_RELATION_SYNTAX;
169781Sgtb 		p++;
170781Sgtb 	    }
1710Sstevel@tonic-gate 	}
1720Sstevel@tonic-gate 	cp = skip_over_blanks(cp+1);
1730Sstevel@tonic-gate 	value = cp;
1740Sstevel@tonic-gate 	if (value[0] == '"') {
1750Sstevel@tonic-gate 		value++;
1760Sstevel@tonic-gate 		parse_quoted_string(value);
1770Sstevel@tonic-gate 	} else if (value[0] == 0) {
1780Sstevel@tonic-gate 		do_subsection++;
1790Sstevel@tonic-gate 		state->state = STATE_GET_OBRACE;
180781Sgtb 	} else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0)
1810Sstevel@tonic-gate 		do_subsection++;
1820Sstevel@tonic-gate 	else {
1830Sstevel@tonic-gate 		cp = value + strlen(value) - 1;
184781Sgtb 		while ((cp > value) && isspace((int) (*cp)))
1850Sstevel@tonic-gate 			*cp-- = 0;
1860Sstevel@tonic-gate 	}
1870Sstevel@tonic-gate 	if (do_subsection) {
1880Sstevel@tonic-gate 		p = strchr(tag, '*');
1890Sstevel@tonic-gate 		if (p)
1900Sstevel@tonic-gate 			*p = '\0';
1910Sstevel@tonic-gate 		retval = profile_add_node(state->current_section,
1920Sstevel@tonic-gate 					  tag, 0, &state->current_section);
1930Sstevel@tonic-gate 		if (retval)
1940Sstevel@tonic-gate 			return retval;
1950Sstevel@tonic-gate 		if (p)
1960Sstevel@tonic-gate 			profile_make_node_final(state->current_section);
1970Sstevel@tonic-gate 		state->group_level++;
1980Sstevel@tonic-gate 		return 0;
1990Sstevel@tonic-gate 	}
2000Sstevel@tonic-gate 	p = strchr(tag, '*');
2010Sstevel@tonic-gate 	if (p)
2020Sstevel@tonic-gate 		*p = '\0';
2030Sstevel@tonic-gate 	profile_add_node(state->current_section, tag, value, &node);
2040Sstevel@tonic-gate 	if (p)
2050Sstevel@tonic-gate 		profile_make_node_final(node);
2060Sstevel@tonic-gate 	return 0;
2070Sstevel@tonic-gate }
2080Sstevel@tonic-gate 
parse_line(char * line,struct parse_state * state)209781Sgtb static errcode_t parse_line(char *line, struct parse_state *state)
2100Sstevel@tonic-gate {
2110Sstevel@tonic-gate 	char	*cp;
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	switch (state->state) {
2140Sstevel@tonic-gate 	case STATE_INIT_COMMENT:
2150Sstevel@tonic-gate 		if (line[0] != '[')
2160Sstevel@tonic-gate 			return 0;
2170Sstevel@tonic-gate 		state->state = STATE_STD_LINE;
2180Sstevel@tonic-gate 		/*FALLTHRU*/
2190Sstevel@tonic-gate 	case STATE_STD_LINE:
2200Sstevel@tonic-gate 		return parse_std_line(line, state);
2210Sstevel@tonic-gate 	case STATE_GET_OBRACE:
2220Sstevel@tonic-gate 		cp = skip_over_blanks(line);
2230Sstevel@tonic-gate 		if (*cp != '{')
2240Sstevel@tonic-gate 			return PROF_MISSING_OBRACE;
2250Sstevel@tonic-gate 		state->state = STATE_STD_LINE;
2260Sstevel@tonic-gate 		/*FALLTHRU*/
2270Sstevel@tonic-gate 	}
2280Sstevel@tonic-gate 	return 0;
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate 
profile_parse_file(FILE * f,struct profile_node ** root)231781Sgtb errcode_t profile_parse_file(FILE *f, struct profile_node **root)
2320Sstevel@tonic-gate {
2330Sstevel@tonic-gate #define BUF_SIZE	2048
2340Sstevel@tonic-gate 	char *bptr;
2350Sstevel@tonic-gate 	errcode_t retval;
2360Sstevel@tonic-gate 	struct parse_state state;
2370Sstevel@tonic-gate 
238*7934SMark.Phalan@Sun.COM 	bptr = malloc (BUF_SIZE);
2390Sstevel@tonic-gate 	if (!bptr)
2400Sstevel@tonic-gate 		return ENOMEM;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	retval = parse_init_state(&state);
2430Sstevel@tonic-gate 	if (retval) {
2440Sstevel@tonic-gate 		free (bptr);
2450Sstevel@tonic-gate 		return retval;
2460Sstevel@tonic-gate 	}
2470Sstevel@tonic-gate 	while (!feof(f)) {
2480Sstevel@tonic-gate 		if (fgets(bptr, BUF_SIZE, f) == NULL)
2490Sstevel@tonic-gate 			break;
250781Sgtb #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
2510Sstevel@tonic-gate 		retval = parse_line(bptr, &state);
2520Sstevel@tonic-gate 		if (retval) {
253*7934SMark.Phalan@Sun.COM 			/* Solaris Kerberos: check if an unconfigured file */
2540Sstevel@tonic-gate 			if (strstr(bptr, "___"))
2550Sstevel@tonic-gate 				retval = PROF_NO_PROFILE;
2560Sstevel@tonic-gate 			free (bptr);
2570Sstevel@tonic-gate 			return retval;
2580Sstevel@tonic-gate 		}
259781Sgtb #else
260781Sgtb 		{
261781Sgtb 		    char *p, *end;
262781Sgtb 
263781Sgtb 		    if (strlen(bptr) >= BUF_SIZE - 1) {
264781Sgtb 			/* The string may have foreign newlines and
265781Sgtb 			   gotten chopped off on a non-newline
266781Sgtb 			   boundary.  Seek backwards to the last known
267781Sgtb 			   newline.  */
268781Sgtb 			long offset;
269781Sgtb 			char *c = bptr + strlen (bptr);
270781Sgtb 			for (offset = 0; offset > -BUF_SIZE; offset--) {
271781Sgtb 			    if (*c == '\r' || *c == '\n') {
272781Sgtb 				*c = '\0';
273781Sgtb 				fseek (f, offset, SEEK_CUR);
274781Sgtb 				break;
275781Sgtb 			    }
276781Sgtb 			    c--;
277781Sgtb 			}
278781Sgtb 		    }
279781Sgtb 
280781Sgtb 		    /* First change all newlines to \n */
281781Sgtb 		    for (p = bptr; *p != '\0'; p++) {
282781Sgtb 			if (*p == '\r')
283781Sgtb                             *p = '\n';
284781Sgtb 		    }
285781Sgtb 		    /* Then parse all lines */
286781Sgtb 		    p = bptr;
287781Sgtb 		    end = bptr + strlen (bptr);
288781Sgtb 		    while (p < end) {
289781Sgtb 			char* newline;
290781Sgtb 			char* newp;
291781Sgtb 
292781Sgtb 			newline = strchr (p, '\n');
293781Sgtb 			if (newline != NULL)
294781Sgtb 			    *newline = '\0';
295781Sgtb 
296781Sgtb 			/* parse_line modifies contents of p */
297781Sgtb 			newp = p + strlen (p) + 1;
298781Sgtb 			retval = parse_line (p, &state);
299781Sgtb 			if (retval) {
300781Sgtb 			    free (bptr);
301781Sgtb 			    return retval;
302781Sgtb 			}
303781Sgtb 
304781Sgtb 			p = newp;
305781Sgtb 		    }
306781Sgtb 		}
307781Sgtb #endif
3080Sstevel@tonic-gate 	}
3090Sstevel@tonic-gate 	*root = state.root_section;
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	free (bptr);
3120Sstevel@tonic-gate 	return 0;
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate /*
3160Sstevel@tonic-gate  * Return TRUE if the string begins or ends with whitespace
3170Sstevel@tonic-gate  */
need_double_quotes(char * str)318781Sgtb static int need_double_quotes(char *str)
3190Sstevel@tonic-gate {
320*7934SMark.Phalan@Sun.COM 	if (!str)
321*7934SMark.Phalan@Sun.COM                 return 0;
322*7934SMark.Phalan@Sun.COM 	if (str[0] == '\0')
323*7934SMark.Phalan@Sun.COM 		return 1;
324781Sgtb 	if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
3250Sstevel@tonic-gate 		return 1;
3260Sstevel@tonic-gate 	if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b'))
3270Sstevel@tonic-gate 		return 1;
3280Sstevel@tonic-gate 	return 0;
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate /*
3320Sstevel@tonic-gate  * Output a string with double quotes, doing appropriate backquoting
3330Sstevel@tonic-gate  * of characters as necessary.
3340Sstevel@tonic-gate  */
output_quoted_string(char * str,void (* cb)(const char *,void *),void * data)335781Sgtb static void output_quoted_string(char *str, void (*cb)(const char *,void *),
336781Sgtb 				 void *data)
3370Sstevel@tonic-gate {
3380Sstevel@tonic-gate 	char	ch;
339781Sgtb 	char buf[2];
340781Sgtb 
341781Sgtb 	cb("\"", data);
3420Sstevel@tonic-gate 	if (!str) {
343781Sgtb 		cb("\"", data);
3440Sstevel@tonic-gate 		return;
3450Sstevel@tonic-gate 	}
346781Sgtb 	buf[1] = 0;
3470Sstevel@tonic-gate 	while ((ch = *str++)) {
3480Sstevel@tonic-gate 		switch (ch) {
3490Sstevel@tonic-gate 		case '\\':
350781Sgtb 			cb("\\\\", data);
3510Sstevel@tonic-gate 			break;
3520Sstevel@tonic-gate 		case '\n':
353781Sgtb 			cb("\\n", data);
3540Sstevel@tonic-gate 			break;
3550Sstevel@tonic-gate 		case '\t':
356781Sgtb 			cb("\\t", data);
3570Sstevel@tonic-gate 			break;
3580Sstevel@tonic-gate 		case '\b':
359781Sgtb 			cb("\\b", data);
3600Sstevel@tonic-gate 			break;
3610Sstevel@tonic-gate 		default:
362781Sgtb 			/* This would be a lot faster if we scanned
363781Sgtb 			   forward for the next "interesting"
364781Sgtb 			   character.  */
365781Sgtb 			buf[0] = ch;
366781Sgtb 			cb(buf, data);
3670Sstevel@tonic-gate 			break;
3680Sstevel@tonic-gate 		}
3690Sstevel@tonic-gate 	}
370781Sgtb 	cb("\"", data);
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 
375781Sgtb #if defined(_WIN32)
3760Sstevel@tonic-gate #define EOL "\r\n"
3770Sstevel@tonic-gate #endif
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate #ifndef EOL
3800Sstevel@tonic-gate #define EOL "\n"
3810Sstevel@tonic-gate #endif
3820Sstevel@tonic-gate 
383781Sgtb /* Errors should be returned, not ignored!  */
dump_profile(struct profile_node * root,int level,void (* cb)(const char *,void *),void * data)384781Sgtb static void dump_profile(struct profile_node *root, int level,
385781Sgtb 			 void (*cb)(const char *, void *), void *data)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate 	int i;
3880Sstevel@tonic-gate 	struct profile_node *p;
3890Sstevel@tonic-gate 	void *iter;
3900Sstevel@tonic-gate 	long retval;
3910Sstevel@tonic-gate 	char *name, *value;
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	iter = 0;
3940Sstevel@tonic-gate 	do {
3950Sstevel@tonic-gate 		retval = profile_find_node_relation(root, 0, &iter,
3960Sstevel@tonic-gate 						    &name, &value);
3970Sstevel@tonic-gate 		if (retval)
3980Sstevel@tonic-gate 			break;
3990Sstevel@tonic-gate 		for (i=0; i < level; i++)
400781Sgtb 			cb("\t", data);
4010Sstevel@tonic-gate 		if (need_double_quotes(value)) {
402781Sgtb 			cb(name, data);
403781Sgtb 			cb(" = ", data);
404781Sgtb 			output_quoted_string(value, cb, data);
405781Sgtb 			cb(EOL, data);
406781Sgtb 		} else {
407781Sgtb 			cb(name, data);
408781Sgtb 			cb(" = ", data);
409781Sgtb 			cb(value, data);
410781Sgtb 			cb(EOL, data);
411781Sgtb 		}
4120Sstevel@tonic-gate 	} while (iter != 0);
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	iter = 0;
4150Sstevel@tonic-gate 	do {
4160Sstevel@tonic-gate 		retval = profile_find_node_subsection(root, 0, &iter,
4170Sstevel@tonic-gate 						      &name, &p);
4180Sstevel@tonic-gate 		if (retval)
4190Sstevel@tonic-gate 			break;
4200Sstevel@tonic-gate 		if (level == 0)	{ /* [xxx] */
421781Sgtb 			cb("[", data);
422781Sgtb 			cb(name, data);
423781Sgtb 			cb("]", data);
424781Sgtb 			cb(profile_is_node_final(p) ? "*" : "", data);
425781Sgtb 			cb(EOL, data);
426781Sgtb 			dump_profile(p, level+1, cb, data);
427781Sgtb 			cb(EOL, data);
4280Sstevel@tonic-gate 		} else { 	/* xxx = { ... } */
4290Sstevel@tonic-gate 			for (i=0; i < level; i++)
430781Sgtb 				cb("\t", data);
431781Sgtb 			cb(name, data);
432781Sgtb 			cb(" = {", data);
433781Sgtb 			cb(EOL, data);
434781Sgtb 			dump_profile(p, level+1, cb, data);
4350Sstevel@tonic-gate 			for (i=0; i < level; i++)
436781Sgtb 				cb("\t", data);
437781Sgtb 			cb("}", data);
438781Sgtb 			cb(profile_is_node_final(p) ? "*" : "", data);
439781Sgtb 			cb(EOL, data);
4400Sstevel@tonic-gate 		}
4410Sstevel@tonic-gate 	} while (iter != 0);
4420Sstevel@tonic-gate }
4430Sstevel@tonic-gate 
dump_profile_to_file_cb(const char * str,void * data)444781Sgtb static void dump_profile_to_file_cb(const char *str, void *data)
4450Sstevel@tonic-gate {
446781Sgtb 	fputs(str, data);
447781Sgtb }
448781Sgtb 
profile_write_tree_file(struct profile_node * root,FILE * dstfile)449781Sgtb errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
450781Sgtb {
451781Sgtb 	dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
4520Sstevel@tonic-gate 	return 0;
4530Sstevel@tonic-gate }
454781Sgtb 
455781Sgtb struct prof_buf {
456781Sgtb 	char *base;
457781Sgtb 	size_t cur, max;
458781Sgtb 	int err;
459781Sgtb };
460781Sgtb 
add_data_to_buffer(struct prof_buf * b,const void * d,size_t len)461781Sgtb static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
462781Sgtb {
463781Sgtb 	if (b->err)
464781Sgtb 		return;
465781Sgtb 	if (b->max - b->cur < len) {
466781Sgtb 		size_t newsize;
467781Sgtb 		char *newptr;
468781Sgtb 
469781Sgtb 		newsize = b->max + (b->max >> 1) + len + 1024;
470781Sgtb 		newptr = realloc(b->base, newsize);
471781Sgtb 		if (newptr == NULL) {
472781Sgtb 			b->err = 1;
473781Sgtb 			return;
474781Sgtb 		}
475781Sgtb 		b->base = newptr;
476781Sgtb 		b->max = newsize;
477781Sgtb 	}
478781Sgtb 	memcpy(b->base + b->cur, d, len);
479781Sgtb 	b->cur += len; 		/* ignore overflow */
480781Sgtb }
481781Sgtb 
dump_profile_to_buffer_cb(const char * str,void * data)482781Sgtb static void dump_profile_to_buffer_cb(const char *str, void *data)
483781Sgtb {
484781Sgtb 	add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
485781Sgtb }
486781Sgtb 
profile_write_tree_to_buffer(struct profile_node * root,char ** buf)487781Sgtb errcode_t profile_write_tree_to_buffer(struct profile_node *root,
488781Sgtb 				       char **buf)
489781Sgtb {
490781Sgtb 	struct prof_buf prof_buf = { 0, 0, 0, 0 };
491781Sgtb 
492781Sgtb 	dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
493781Sgtb 	if (prof_buf.err) {
494781Sgtb 		*buf = NULL;
495781Sgtb 		return ENOMEM;
496781Sgtb 	}
497781Sgtb 	add_data_to_buffer(&prof_buf, "", 1); /* append nul */
498781Sgtb 	if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
499781Sgtb 		char *newptr = realloc(prof_buf.base, prof_buf.cur);
500781Sgtb 		if (newptr)
501781Sgtb 			prof_buf.base = newptr;
502781Sgtb 	}
503781Sgtb 	*buf = prof_buf.base;
504781Sgtb 	return 0;
505781Sgtb }
506