17c478bd9Sstevel@tonic-gate /*
2*159d09a2SMark Phalan  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate 
7505d05c7Sgtb #include "prof_int.h"
8505d05c7Sgtb 
97c478bd9Sstevel@tonic-gate #include <stdio.h>
107c478bd9Sstevel@tonic-gate #include <string.h>
117c478bd9Sstevel@tonic-gate #ifdef HAVE_STDLIB_H
127c478bd9Sstevel@tonic-gate #include <stdlib.h>
137c478bd9Sstevel@tonic-gate #endif
147c478bd9Sstevel@tonic-gate #include <errno.h>
157c478bd9Sstevel@tonic-gate #include <ctype.h>
167c478bd9Sstevel@tonic-gate 
177c478bd9Sstevel@tonic-gate #define SECTION_SEP_CHAR '/'
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate #define STATE_INIT_COMMENT	1
207c478bd9Sstevel@tonic-gate #define STATE_STD_LINE		2
217c478bd9Sstevel@tonic-gate #define STATE_GET_OBRACE	3
227c478bd9Sstevel@tonic-gate 
237c478bd9Sstevel@tonic-gate struct parse_state {
247c478bd9Sstevel@tonic-gate 	int	state;
257c478bd9Sstevel@tonic-gate 	int	group_level;
267c478bd9Sstevel@tonic-gate 	struct profile_node *root_section;
277c478bd9Sstevel@tonic-gate 	struct profile_node *current_section;
287c478bd9Sstevel@tonic-gate };
297c478bd9Sstevel@tonic-gate 
skip_over_blanks(char * cp)30505d05c7Sgtb static char *skip_over_blanks(char *cp)
317c478bd9Sstevel@tonic-gate {
32505d05c7Sgtb 	while (*cp && isspace((int) (*cp)))
337c478bd9Sstevel@tonic-gate 		cp++;
347c478bd9Sstevel@tonic-gate 	return cp;
357c478bd9Sstevel@tonic-gate }
367c478bd9Sstevel@tonic-gate 
strip_line(char * line)37505d05c7Sgtb static void strip_line(char *line)
387c478bd9Sstevel@tonic-gate {
39505d05c7Sgtb 	char *p = line + strlen(line);
40505d05c7Sgtb 	while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
41505d05c7Sgtb 	    *p-- = 0;
427c478bd9Sstevel@tonic-gate }
437c478bd9Sstevel@tonic-gate 
parse_quoted_string(char * str)447c478bd9Sstevel@tonic-gate static void parse_quoted_string(char *str)
457c478bd9Sstevel@tonic-gate {
467c478bd9Sstevel@tonic-gate 	char *to, *from;
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate 	to = from = str;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate 	for (to = from = str; *from && *from != '"'; to++, from++) {
517c478bd9Sstevel@tonic-gate 		if (*from == '\\') {
527c478bd9Sstevel@tonic-gate 			from++;
537c478bd9Sstevel@tonic-gate 			switch (*from) {
547c478bd9Sstevel@tonic-gate 			case 'n':
557c478bd9Sstevel@tonic-gate 				*to = '\n';
567c478bd9Sstevel@tonic-gate 				break;
577c478bd9Sstevel@tonic-gate 			case 't':
587c478bd9Sstevel@tonic-gate 				*to = '\t';
597c478bd9Sstevel@tonic-gate 				break;
607c478bd9Sstevel@tonic-gate 			case 'b':
617c478bd9Sstevel@tonic-gate 				*to = '\b';
627c478bd9Sstevel@tonic-gate 				break;
637c478bd9Sstevel@tonic-gate 			default:
647c478bd9Sstevel@tonic-gate 				*to = *from;
657c478bd9Sstevel@tonic-gate 			}
667c478bd9Sstevel@tonic-gate 			continue;
677c478bd9Sstevel@tonic-gate 		}
687c478bd9Sstevel@tonic-gate 		*to = *from;
697c478bd9Sstevel@tonic-gate 	}
707c478bd9Sstevel@tonic-gate 	*to = '\0';
717c478bd9Sstevel@tonic-gate }
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 
parse_init_state(struct parse_state * state)74505d05c7Sgtb static errcode_t parse_init_state(struct parse_state *state)
757c478bd9Sstevel@tonic-gate {
767c478bd9Sstevel@tonic-gate 	state->state = STATE_INIT_COMMENT;
777c478bd9Sstevel@tonic-gate 	state->group_level = 0;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 	return profile_create_node("(root)", 0, &state->root_section);
807c478bd9Sstevel@tonic-gate }
817c478bd9Sstevel@tonic-gate 
parse_std_line(char * line,struct parse_state * state)82505d05c7Sgtb static errcode_t parse_std_line(char *line, struct parse_state *state)
837c478bd9Sstevel@tonic-gate {
847c478bd9Sstevel@tonic-gate 	char	*cp, ch, *tag, *value;
857c478bd9Sstevel@tonic-gate 	char	*p;
867c478bd9Sstevel@tonic-gate 	errcode_t retval;
877c478bd9Sstevel@tonic-gate 	struct profile_node	*node;
887c478bd9Sstevel@tonic-gate 	int do_subsection = 0;
897c478bd9Sstevel@tonic-gate 	void *iter = 0;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate 	if (*line == 0)
927c478bd9Sstevel@tonic-gate 		return 0;
937c478bd9Sstevel@tonic-gate 	cp = skip_over_blanks(line);
94*159d09a2SMark Phalan 	if (cp[0] == ';' || cp[0] == '#')
95*159d09a2SMark Phalan 		return 0;
96*159d09a2SMark Phalan 	strip_line(cp);
977c478bd9Sstevel@tonic-gate 	ch = *cp;
987c478bd9Sstevel@tonic-gate 	if (ch == 0)
997c478bd9Sstevel@tonic-gate 		return 0;
1007c478bd9Sstevel@tonic-gate 	if (ch == '[') {
1017c478bd9Sstevel@tonic-gate 		if (state->group_level > 0)
1027c478bd9Sstevel@tonic-gate 			return PROF_SECTION_NOTOP;
1037c478bd9Sstevel@tonic-gate 		cp++;
1047c478bd9Sstevel@tonic-gate 		p = strchr(cp, ']');
1057c478bd9Sstevel@tonic-gate 		if (p == NULL)
1067c478bd9Sstevel@tonic-gate 			return PROF_SECTION_SYNTAX;
1077c478bd9Sstevel@tonic-gate 		*p = '\0';
1087c478bd9Sstevel@tonic-gate 		retval = profile_find_node_subsection(state->root_section,
1097c478bd9Sstevel@tonic-gate 						 cp, &iter, 0,
1107c478bd9Sstevel@tonic-gate 						 &state->current_section);
1117c478bd9Sstevel@tonic-gate 		if (retval == PROF_NO_SECTION) {
1127c478bd9Sstevel@tonic-gate 			retval = profile_add_node(state->root_section,
1137c478bd9Sstevel@tonic-gate 						  cp, 0,
1147c478bd9Sstevel@tonic-gate 						  &state->current_section);
1157c478bd9Sstevel@tonic-gate 			if (retval)
1167c478bd9Sstevel@tonic-gate 				return retval;
1177c478bd9Sstevel@tonic-gate 		} else if (retval)
1187c478bd9Sstevel@tonic-gate 			return retval;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 		/*
1217c478bd9Sstevel@tonic-gate 		 * Finish off the rest of the line.
1227c478bd9Sstevel@tonic-gate 		 */
1237c478bd9Sstevel@tonic-gate 		cp = p+1;
1247c478bd9Sstevel@tonic-gate 		if (*cp == '*') {
1257c478bd9Sstevel@tonic-gate 			profile_make_node_final(state->current_section);
1267c478bd9Sstevel@tonic-gate 			cp++;
1277c478bd9Sstevel@tonic-gate 		}
1287c478bd9Sstevel@tonic-gate 		/*
129505d05c7Sgtb 		 * A space after ']' should not be fatal
1307c478bd9Sstevel@tonic-gate 		 */
1317c478bd9Sstevel@tonic-gate 		cp = skip_over_blanks(cp);
1327c478bd9Sstevel@tonic-gate 		if (*cp)
1337c478bd9Sstevel@tonic-gate 			return PROF_SECTION_SYNTAX;
1347c478bd9Sstevel@tonic-gate 		return 0;
1357c478bd9Sstevel@tonic-gate 	}
1367c478bd9Sstevel@tonic-gate 	if (ch == '}') {
1377c478bd9Sstevel@tonic-gate 		if (state->group_level == 0)
1387c478bd9Sstevel@tonic-gate 			return PROF_EXTRA_CBRACE;
1397c478bd9Sstevel@tonic-gate 		if (*(cp+1) == '*')
1407c478bd9Sstevel@tonic-gate 			profile_make_node_final(state->current_section);
1417c478bd9Sstevel@tonic-gate 		retval = profile_get_node_parent(state->current_section,
1427c478bd9Sstevel@tonic-gate 						 &state->current_section);
1437c478bd9Sstevel@tonic-gate 		if (retval)
1447c478bd9Sstevel@tonic-gate 			return retval;
1457c478bd9Sstevel@tonic-gate 		state->group_level--;
1467c478bd9Sstevel@tonic-gate 		return 0;
1477c478bd9Sstevel@tonic-gate 	}
1487c478bd9Sstevel@tonic-gate 	/*
1497c478bd9Sstevel@tonic-gate 	 * Parse the relations
1507c478bd9Sstevel@tonic-gate 	 */
1517c478bd9Sstevel@tonic-gate 	tag = cp;
1527c478bd9Sstevel@tonic-gate 	cp = strchr(cp, '=');
1537c478bd9Sstevel@tonic-gate 	if (!cp)
1547c478bd9Sstevel@tonic-gate 		return PROF_RELATION_SYNTAX;
155505d05c7Sgtb 	if (cp == tag)
156505d05c7Sgtb 	    return PROF_RELATION_SYNTAX;
1577c478bd9Sstevel@tonic-gate 	*cp = '\0';
158505d05c7Sgtb 	p = tag;
159505d05c7Sgtb 	/* Look for whitespace on left-hand side.  */
160505d05c7Sgtb 	while (p < cp && !isspace((int)*p))
161505d05c7Sgtb 	    p++;
162505d05c7Sgtb 	if (p < cp) {
163505d05c7Sgtb 	    /* Found some sort of whitespace.  */
164505d05c7Sgtb 	    *p++ = 0;
165505d05c7Sgtb 	    /* If we have more non-whitespace, it's an error.  */
166505d05c7Sgtb 	    while (p < cp) {
167505d05c7Sgtb 		if (!isspace((int)*p))
168505d05c7Sgtb 		    return PROF_RELATION_SYNTAX;
169505d05c7Sgtb 		p++;
170505d05c7Sgtb 	    }
1717c478bd9Sstevel@tonic-gate 	}
1727c478bd9Sstevel@tonic-gate 	cp = skip_over_blanks(cp+1);
1737c478bd9Sstevel@tonic-gate 	value = cp;
1747c478bd9Sstevel@tonic-gate 	if (value[0] == '"') {
1757c478bd9Sstevel@tonic-gate 		value++;
1767c478bd9Sstevel@tonic-gate 		parse_quoted_string(value);
1777c478bd9Sstevel@tonic-gate 	} else if (value[0] == 0) {
1787c478bd9Sstevel@tonic-gate 		do_subsection++;
1797c478bd9Sstevel@tonic-gate 		state->state = STATE_GET_OBRACE;
180505d05c7Sgtb 	} else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0)
1817c478bd9Sstevel@tonic-gate 		do_subsection++;
1827c478bd9Sstevel@tonic-gate 	else {
1837c478bd9Sstevel@tonic-gate 		cp = value + strlen(value) - 1;
184505d05c7Sgtb 		while ((cp > value) && isspace((int) (*cp)))
1857c478bd9Sstevel@tonic-gate 			*cp-- = 0;
1867c478bd9Sstevel@tonic-gate 	}
1877c478bd9Sstevel@tonic-gate 	if (do_subsection) {
1887c478bd9Sstevel@tonic-gate 		p = strchr(tag, '*');
1897c478bd9Sstevel@tonic-gate 		if (p)
1907c478bd9Sstevel@tonic-gate 			*p = '\0';
1917c478bd9Sstevel@tonic-gate 		retval = profile_add_node(state->current_section,
1927c478bd9Sstevel@tonic-gate 					  tag, 0, &state->current_section);
1937c478bd9Sstevel@tonic-gate 		if (retval)
1947c478bd9Sstevel@tonic-gate 			return retval;
1957c478bd9Sstevel@tonic-gate 		if (p)
1967c478bd9Sstevel@tonic-gate 			profile_make_node_final(state->current_section);
1977c478bd9Sstevel@tonic-gate 		state->group_level++;
1987c478bd9Sstevel@tonic-gate 		return 0;
1997c478bd9Sstevel@tonic-gate 	}
2007c478bd9Sstevel@tonic-gate 	p = strchr(tag, '*');
2017c478bd9Sstevel@tonic-gate 	if (p)
2027c478bd9Sstevel@tonic-gate 		*p = '\0';
2037c478bd9Sstevel@tonic-gate 	profile_add_node(state->current_section, tag, value, &node);
2047c478bd9Sstevel@tonic-gate 	if (p)
2057c478bd9Sstevel@tonic-gate 		profile_make_node_final(node);
2067c478bd9Sstevel@tonic-gate 	return 0;
2077c478bd9Sstevel@tonic-gate }
2087c478bd9Sstevel@tonic-gate 
parse_line(char * line,struct parse_state * state)209505d05c7Sgtb static errcode_t parse_line(char *line, struct parse_state *state)
2107c478bd9Sstevel@tonic-gate {
2117c478bd9Sstevel@tonic-gate 	char	*cp;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	switch (state->state) {
2147c478bd9Sstevel@tonic-gate 	case STATE_INIT_COMMENT:
2157c478bd9Sstevel@tonic-gate 		if (line[0] != '[')
2167c478bd9Sstevel@tonic-gate 			return 0;
2177c478bd9Sstevel@tonic-gate 		state->state = STATE_STD_LINE;
2187c478bd9Sstevel@tonic-gate 		/*FALLTHRU*/
2197c478bd9Sstevel@tonic-gate 	case STATE_STD_LINE:
2207c478bd9Sstevel@tonic-gate 		return parse_std_line(line, state);
2217c478bd9Sstevel@tonic-gate 	case STATE_GET_OBRACE:
2227c478bd9Sstevel@tonic-gate 		cp = skip_over_blanks(line);
2237c478bd9Sstevel@tonic-gate 		if (*cp != '{')
2247c478bd9Sstevel@tonic-gate 			return PROF_MISSING_OBRACE;
2257c478bd9Sstevel@tonic-gate 		state->state = STATE_STD_LINE;
2267c478bd9Sstevel@tonic-gate 		/*FALLTHRU*/
2277c478bd9Sstevel@tonic-gate 	}
2287c478bd9Sstevel@tonic-gate 	return 0;
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate 
profile_parse_file(FILE * f,struct profile_node ** root)231505d05c7Sgtb errcode_t profile_parse_file(FILE *f, struct profile_node **root)
2327c478bd9Sstevel@tonic-gate {
2337c478bd9Sstevel@tonic-gate #define BUF_SIZE	2048
2347c478bd9Sstevel@tonic-gate 	char *bptr;
2357c478bd9Sstevel@tonic-gate 	errcode_t retval;
2367c478bd9Sstevel@tonic-gate 	struct parse_state state;
2377c478bd9Sstevel@tonic-gate 
238*159d09a2SMark Phalan 	bptr = malloc (BUF_SIZE);
2397c478bd9Sstevel@tonic-gate 	if (!bptr)
2407c478bd9Sstevel@tonic-gate 		return ENOMEM;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	retval = parse_init_state(&state);
2437c478bd9Sstevel@tonic-gate 	if (retval) {
2447c478bd9Sstevel@tonic-gate 		free (bptr);
2457c478bd9Sstevel@tonic-gate 		return retval;
2467c478bd9Sstevel@tonic-gate 	}
2477c478bd9Sstevel@tonic-gate 	while (!feof(f)) {
2487c478bd9Sstevel@tonic-gate 		if (fgets(bptr, BUF_SIZE, f) == NULL)
2497c478bd9Sstevel@tonic-gate 			break;
250505d05c7Sgtb #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
2517c478bd9Sstevel@tonic-gate 		retval = parse_line(bptr, &state);
2527c478bd9Sstevel@tonic-gate 		if (retval) {
253*159d09a2SMark Phalan 			/* Solaris Kerberos: check if an unconfigured file */
2547c478bd9Sstevel@tonic-gate 			if (strstr(bptr, "___"))
2557c478bd9Sstevel@tonic-gate 				retval = PROF_NO_PROFILE;
2567c478bd9Sstevel@tonic-gate 			free (bptr);
2577c478bd9Sstevel@tonic-gate 			return retval;
2587c478bd9Sstevel@tonic-gate 		}
259505d05c7Sgtb #else
260505d05c7Sgtb 		{
261505d05c7Sgtb 		    char *p, *end;
262505d05c7Sgtb 
263505d05c7Sgtb 		    if (strlen(bptr) >= BUF_SIZE - 1) {
264505d05c7Sgtb 			/* The string may have foreign newlines and
265505d05c7Sgtb 			   gotten chopped off on a non-newline
266505d05c7Sgtb 			   boundary.  Seek backwards to the last known
267505d05c7Sgtb 			   newline.  */
268505d05c7Sgtb 			long offset;
269505d05c7Sgtb 			char *c = bptr + strlen (bptr);
270505d05c7Sgtb 			for (offset = 0; offset > -BUF_SIZE; offset--) {
271505d05c7Sgtb 			    if (*c == '\r' || *c == '\n') {
272505d05c7Sgtb 				*c = '\0';
273505d05c7Sgtb 				fseek (f, offset, SEEK_CUR);
274505d05c7Sgtb 				break;
275505d05c7Sgtb 			    }
276505d05c7Sgtb 			    c--;
277505d05c7Sgtb 			}
278505d05c7Sgtb 		    }
279