xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/profile/prof_parse.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * Copyright 2002-2003 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 #include <stdio.h>
9 #include <string.h>
10 #ifdef HAVE_STDLIB_H
11 #include <stdlib.h>
12 #endif
13 #include <errno.h>
14 #include <ctype.h>
15 
16 #include "prof_int.h"
17 
18 #define SECTION_SEP_CHAR '/'
19 
20 #define STATE_INIT_COMMENT	1
21 #define STATE_STD_LINE		2
22 #define STATE_GET_OBRACE	3
23 
24 struct parse_state {
25 	int	state;
26 	int	group_level;
27 	struct profile_node *root_section;
28 	struct profile_node *current_section;
29 };
30 
31 static char *skip_over_blanks(cp)
32 	char	*cp;
33 {
34 	while (*cp && isspace(*cp))
35 		cp++;
36 	return cp;
37 }
38 
39 static void strip_line(line)
40 	char	*line;
41 {
42 	char	*p;
43 
44 	while (*line) {
45 		p = line + strlen(line) - 1;
46 		if ((*p == '\n') || (*p == '\r'))
47 			*p = 0;
48 		else
49 			break;
50 	}
51 }
52 
53 static void parse_quoted_string(char *str)
54 {
55 	char *to, *from;
56 
57 	to = from = str;
58 
59 	for (to = from = str; *from && *from != '"'; to++, from++) {
60 		if (*from == '\\') {
61 			from++;
62 			switch (*from) {
63 			case 'n':
64 				*to = '\n';
65 				break;
66 			case 't':
67 				*to = '\t';
68 				break;
69 			case 'b':
70 				*to = '\b';
71 				break;
72 			default:
73 				*to = *from;
74 			}
75 			continue;
76 		}
77 		*to = *from;
78 	}
79 	*to = '\0';
80 }
81 
82 
83 static errcode_t parse_init_state(state)
84 	struct parse_state *state;
85 {
86 	state->state = STATE_INIT_COMMENT;
87 	state->group_level = 0;
88 
89 	return profile_create_node("(root)", 0, &state->root_section);
90 }
91 
92 static errcode_t parse_std_line(line, state)
93 	char	*line;
94 	struct parse_state *state;
95 {
96 	char	*cp, ch, *tag, *value;
97 	char	*p;
98 	errcode_t retval;
99 	struct profile_node	*node;
100 	int do_subsection = 0;
101 	void *iter = 0;
102 
103 	if (*line == 0)
104 		return 0;
105 	if (line[0] == ';' || line[0] == '#')
106 		return 0;
107 	strip_line(line);
108 	cp = skip_over_blanks(line);
109 	ch = *cp;
110 	if (ch == 0)
111 		return 0;
112 	if (ch == '[') {
113 		if (state->group_level > 0)
114 			return PROF_SECTION_NOTOP;
115 		cp++;
116 		p = strchr(cp, ']');
117 		if (p == NULL)
118 			return PROF_SECTION_SYNTAX;
119 		*p = '\0';
120 		retval = profile_find_node_subsection(state->root_section,
121 						 cp, &iter, 0,
122 						 &state->current_section);
123 		if (retval == PROF_NO_SECTION) {
124 			retval = profile_add_node(state->root_section,
125 						  cp, 0,
126 						  &state->current_section);
127 			if (retval)
128 				return retval;
129 		} else if (retval)
130 			return retval;
131 
132 		/*
133 		 * Finish off the rest of the line.
134 		 */
135 		cp = p+1;
136 
137 		if (*cp == '*') {
138 			profile_make_node_final(state->current_section);
139 			cp++;
140 		}
141 
142 		/*
143 		 * A space after ']' should not be fatal
144 		 */
145 		cp = skip_over_blanks(cp);
146 		if (*cp)
147 			return PROF_SECTION_SYNTAX;
148 		return 0;
149 	}
150 	if (ch == '}') {
151 		if (state->group_level == 0)
152 			return PROF_EXTRA_CBRACE;
153 		if (*(cp+1) == '*')
154 			profile_make_node_final(state->current_section);
155 		retval = profile_get_node_parent(state->current_section,
156 						 &state->current_section);
157 		if (retval)
158 			return retval;
159 		state->group_level--;
160 		return 0;
161 	}
162 	/*
163 	 * Parse the relations
164 	 */
165 	tag = cp;
166 	cp = strchr(cp, '=');
167 	if (!cp)
168 		return PROF_RELATION_SYNTAX;
169 	*cp = '\0';
170 	p = strchr(tag, ' ');
171 	if (p) {
172 		*p = '\0';
173 		p = skip_over_blanks(p+1);
174 		if (p != cp)
175 			return PROF_RELATION_SYNTAX;
176 	}
177 	cp = skip_over_blanks(cp+1);
178 	value = cp;
179 	if (value[0] == '"') {
180 		value++;
181 		parse_quoted_string(value);
182 	} else if (value[0] == 0) {
183 		do_subsection++;
184 		state->state = STATE_GET_OBRACE;
185 	} else if (value[0] == '{' && value[1] == 0)
186 		do_subsection++;
187 	else {
188 		/*
189 		 * Skip over trailing whitespace characters
190 		 */
191 		cp = value + strlen(value) - 1;
192 		while ((cp > value) && isspace(*cp))
193 			*cp-- = 0;
194 	}
195 
196 	if (do_subsection) {
197 		p = strchr(tag, '*');
198 		if (p)
199 			*p = '\0';
200 		retval = profile_add_node(state->current_section,
201 					  tag, 0, &state->current_section);
202 		if (retval)
203 			return retval;
204 		if (p)
205 			profile_make_node_final(state->current_section);
206 		state->group_level++;
207 		return 0;
208 	}
209 	p = strchr(tag, '*');
210 	if (p)
211 		*p = '\0';
212 	profile_add_node(state->current_section, tag, value, &node);
213 	if (p)
214 		profile_make_node_final(node);
215 	return 0;
216 }
217 
218 static errcode_t parse_line(line, state)
219 	char	*line;
220 	struct parse_state *state;
221 {
222 	char	*cp;
223 
224 	switch (state->state) {
225 	case STATE_INIT_COMMENT:
226 		if (line[0] != '[')
227 			return 0;
228 		state->state = STATE_STD_LINE;
229 		/*FALLTHRU*/
230 	case STATE_STD_LINE:
231 		return parse_std_line(line, state);
232 	case STATE_GET_OBRACE:
233 		cp = skip_over_blanks(line);
234 		if (*cp != '{')
235 			return PROF_MISSING_OBRACE;
236 		state->state = STATE_STD_LINE;
237 		/*FALLTHRU*/
238 	}
239 	return 0;
240 }
241 
242 errcode_t profile_parse_file(f, root)
243 	FILE	*f;
244 	struct profile_node **root;
245 {
246 #define BUF_SIZE	2048
247 	char *bptr;
248 	errcode_t retval;
249 	struct parse_state state;
250 
251 	bptr = (char *) malloc (BUF_SIZE);
252 	if (!bptr)
253 		return ENOMEM;
254 
255 	retval = parse_init_state(&state);
256 	if (retval) {
257 		free (bptr);
258 		return retval;
259 	}
260 	while (!feof(f)) {
261 		if (fgets(bptr, BUF_SIZE, f) == NULL)
262 			break;
263 		retval = parse_line(bptr, &state);
264 		if (retval) {
265 			/* check if an unconfigured file */
266 			if (strstr(bptr, "___"))
267 				retval = PROF_NO_PROFILE;
268 			free (bptr);
269 			return retval;
270 		}
271 	}
272 	*root = state.root_section;
273 
274 	free (bptr);
275 	return 0;
276 }
277 
278 /*
279  * Return TRUE if the string begins or ends with whitespace
280  */
281 static int need_double_quotes(str)
282 	char *str;
283 {
284 	if (!str || !*str)
285 		return 0;
286 	if (isspace(*str) ||isspace(*(str + strlen(str) - 1)))
287 		return 1;
288 	if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b'))
289 		return 1;
290 	return 0;
291 }
292 
293 /*
294  * Output a string with double quotes, doing appropriate backquoting
295  * of characters as necessary.
296  */
297 static void output_quoted_string(str, f)
298 	char	*str;
299 	FILE	*f;
300 {
301 	char	ch;
302 
303 	fputc('"', f);
304 	if (!str) {
305 		fputc('"', f);
306 		return;
307 	}
308 	while ((ch = *str++)) {
309 		switch (ch) {
310 		case '\\':
311 			fputs("\\\\", f);
312 			break;
313 		case '\n':
314 			fputs("\\n", f);
315 			break;
316 		case '\t':
317 			fputs("\\t", f);
318 			break;
319 		case '\b':
320 			fputs("\\b", f);
321 			break;
322 		default:
323 			fputc(ch, f);
324 			break;
325 		}
326 	}
327 	fputc('"', f);
328 }
329 
330 
331 
332 #if defined(_MSDOS) || defined(_WIN32)
333 #define EOL "\r\n"
334 #endif
335 
336 #ifdef macintosh
337 #define EOL "\r"
338 #endif
339 
340 #ifndef EOL
341 #define EOL "\n"
342 #endif
343 
344 static void dump_profile_to_file(root, level, dstfile)
345 	struct profile_node *root;
346 	int level;
347 	FILE *dstfile;
348 {
349 	int i;
350 	struct profile_node *p;
351 	void *iter;
352 	long retval;
353 	char *name, *value;
354 
355 	iter = 0;
356 	do {
357 		retval = profile_find_node_relation(root, 0, &iter,
358 						    &name, &value);
359 		if (retval)
360 			break;
361 		for (i=0; i < level; i++)
362 			fprintf(dstfile, "\t");
363 		if (need_double_quotes(value)) {
364 			fputs(name, dstfile);
365 			fputs(" = ", dstfile);
366 			output_quoted_string(value, dstfile);
367 			fputs(EOL, dstfile);
368 		} else
369 			fprintf(dstfile, "%s = %s%s", name, value, EOL);
370 	} while (iter != 0);
371 
372 	iter = 0;
373 	do {
374 		retval = profile_find_node_subsection(root, 0, &iter,
375 						      &name, &p);
376 		if (retval)
377 			break;
378 		if (level == 0)	{ /* [xxx] */
379 			for (i=0; i < level; i++)
380 				fprintf(dstfile, "\t");
381 			fprintf(dstfile, "[%s]%s%s", name,
382 				profile_is_node_final(p) ? "*" : "", EOL);
383 			dump_profile_to_file(p, level+1, dstfile);
384 			fprintf(dstfile, EOL);
385 		} else { 	/* xxx = { ... } */
386 			for (i=0; i < level; i++)
387 				fprintf(dstfile, "\t");
388 			fprintf(dstfile, "%s = {%s", name, EOL);
389 			dump_profile_to_file(p, level+1, dstfile);
390 			for (i=0; i < level; i++)
391 				fprintf(dstfile, "\t");
392 			fprintf(dstfile, "}%s%s",
393 				profile_is_node_final(p) ? "*" : "", EOL);
394 		}
395 	} while (iter != 0);
396 }
397 
398 errcode_t profile_write_tree_file(root, dstfile)
399 	struct profile_node *root;
400 	FILE		*dstfile;
401 {
402 	dump_profile_to_file(root, 0, dstfile);
403 	return 0;
404 }
405