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