1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
14 #define SECTION_SEP_CHAR '/'
16 #define STATE_INIT_COMMENT 1
17 #define STATE_STD_LINE 2
18 #define STATE_GET_OBRACE 3
23 struct profile_node *root_section;
24 struct profile_node *current_section;
27 static errcode_t parse_file(FILE *f, struct parse_state *state);
29 static char *skip_over_blanks(char *cp)
31 while (*cp && isspace((int) (*cp)))
36 static void strip_line(char *line)
38 char *p = line + strlen(line);
39 while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
43 static void parse_quoted_string(char *str)
49 for (to = from = str; *from && *from != '"'; to++, from++) {
73 static errcode_t parse_std_line(char *line, struct parse_state *state)
75 char *cp, ch, *tag, *value;
78 struct profile_node *node;
79 int do_subsection = 0;
84 cp = skip_over_blanks(line);
85 if (cp[0] == ';' || cp[0] == '#')
92 if (state->group_level > 0)
93 return PROF_SECTION_NOTOP;
97 return PROF_SECTION_SYNTAX;
99 retval = profile_find_node_subsection(state->root_section,
101 &state->current_section);
102 if (retval == PROF_NO_SECTION) {
103 retval = profile_add_node(state->root_section,
105 &state->current_section);
112 * Finish off the rest of the line.
116 profile_make_node_final(state->current_section);
120 * A space after ']' should not be fatal
122 cp = skip_over_blanks(cp);
124 return PROF_SECTION_SYNTAX;
128 if (state->group_level == 0)
129 return PROF_EXTRA_CBRACE;
131 profile_make_node_final(state->current_section);
132 retval = profile_get_node_parent(state->current_section,
133 &state->current_section);
136 state->group_level--;
140 * Parse the relations
143 cp = strchr(cp, '=');
145 return PROF_RELATION_SYNTAX;
147 return PROF_RELATION_SYNTAX;
150 /* Look for whitespace on left-hand side. */
151 while (p < cp && !isspace((int)*p))
154 /* Found some sort of whitespace. */
156 /* If we have more non-whitespace, it's an error. */
158 if (!isspace((int)*p))
159 return PROF_RELATION_SYNTAX;
163 cp = skip_over_blanks(cp+1);
165 if (value[0] == '"') {
167 parse_quoted_string(value);
168 } else if (value[0] == 0) {
170 state->state = STATE_GET_OBRACE;
171 } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0)
174 cp = value + strlen(value) - 1;
175 while ((cp > value) && isspace((int) (*cp)))
179 p = strchr(tag, '*');
182 retval = profile_add_node(state->current_section,
183 tag, 0, &state->current_section);
187 profile_make_node_final(state->current_section);
188 state->group_level++;
191 p = strchr(tag, '*');
194 profile_add_node(state->current_section, tag, value, &node);
196 profile_make_node_final(node);
200 /* Open and parse an included profile file. */
201 static errcode_t parse_include_file(char *filename, struct parse_state *state)
204 errcode_t retval = 0;
205 struct parse_state incstate;
207 /* Create a new state so that fragments are syntactically independent,
208 * sharing the root section with the existing state. */
209 incstate.state = STATE_INIT_COMMENT;
210 incstate.group_level = 0;
211 incstate.root_section = state->root_section;
212 incstate.current_section = NULL;
214 fp = fopen(filename, "r");
216 return PROF_FAIL_INCLUDE_FILE;
217 retval = parse_file(fp, &incstate);
222 /* Return non-zero if filename contains only alphanumeric characters, dashes,
223 * and underscores. */
224 static int valid_name(const char *filename)
228 for (p = filename; *p != '\0'; p++) {
229 if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_')
236 * Include files within dirname. Only files with names consisting entirely of
237 * alphanumeric chracters, dashes, and underscores are included, in order to
238 * avoid including editor backup files, .rpmsave files, and the like.
240 static errcode_t parse_include_dir(char *dirname, struct parse_state *state)
244 errcode_t retval = 0;
247 dir = opendir(dirname);
249 return PROF_FAIL_INCLUDE_DIR;
250 while ((ent = readdir(dir)) != NULL) {
251 if (!valid_name(ent->d_name))
253 if (asprintf(&pathname, "%s/%s", dirname, ent->d_name) < 0) {
257 retval = parse_include_file(pathname, state);
266 static errcode_t parse_line(char *line, struct parse_state *state)
270 if (strncmp(line, "include", 7) == 0 && isspace(line[7])) {
271 cp = skip_over_blanks(line + 7);
273 return parse_include_file(cp, state);
275 if (strncmp(line, "includedir", 10) == 0 && isspace(line[10])) {
276 cp = skip_over_blanks(line + 10);
278 return parse_include_dir(cp, state);
280 switch (state->state) {
281 case STATE_INIT_COMMENT:
284 state->state = STATE_STD_LINE;
286 return parse_std_line(line, state);
287 case STATE_GET_OBRACE:
288 cp = skip_over_blanks(line);
290 return PROF_MISSING_OBRACE;
291 state->state = STATE_STD_LINE;
296 static errcode_t parse_file(FILE *f, struct parse_state *state)
298 #define BUF_SIZE 2048
302 bptr = malloc (BUF_SIZE);
307 if (fgets(bptr, BUF_SIZE, f) == NULL)
309 #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
310 retval = parse_line(bptr, state);
319 if (strlen(bptr) >= BUF_SIZE - 1) {
320 /* The string may have foreign newlines and
321 gotten chopped off on a non-newline
322 boundary. Seek backwards to the last known
325 char *c = bptr + strlen (bptr);
326 for (offset = 0; offset > -BUF_SIZE; offset--) {
327 if (*c == '\r' || *c == '\n') {
329 fseek (f, offset, SEEK_CUR);
336 /* First change all newlines to \n */
337 for (p = bptr; *p != '\0'; p++) {
341 /* Then parse all lines */
343 end = bptr + strlen (bptr);
348 newline = strchr (p, '\n');
352 /* parse_line modifies contents of p */
353 newp = p + strlen (p) + 1;
354 retval = parse_line (p, state);
370 errcode_t profile_parse_file(FILE *f, struct profile_node **root)
372 struct parse_state state;
377 /* Initialize parsing state with a new root node. */
378 state.state = STATE_INIT_COMMENT;
379 state.group_level = 0;
380 state.current_section = NULL;
381 retval = profile_create_node("(root)", 0, &state.root_section);
385 retval = parse_file(f, &state);
387 profile_free_node(state.root_section);
390 *root = state.root_section;
395 * Return TRUE if the string begins or ends with whitespace
397 static int need_double_quotes(char *str)
403 if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
405 if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b'))
411 * Output a string with double quotes, doing appropriate backquoting
412 * of characters as necessary.
414 static void output_quoted_string(char *str, void (*cb)(const char *,void *),
426 while ((ch = *str++)) {
441 /* This would be a lot faster if we scanned
442 forward for the next "interesting"
462 /* Errors should be returned, not ignored! */
463 static void dump_profile(struct profile_node *root, int level,
464 void (*cb)(const char *, void *), void *data)
467 struct profile_node *p;
474 retval = profile_find_node_relation(root, 0, &iter,
478 for (i=0; i < level; i++)
480 if (need_double_quotes(value)) {
483 output_quoted_string(value, cb, data);
495 retval = profile_find_node_subsection(root, 0, &iter,
499 if (level == 0) { /* [xxx] */
503 cb(profile_is_node_final(p) ? "*" : "", data);
505 dump_profile(p, level+1, cb, data);
507 } else { /* xxx = { ... } */
508 for (i=0; i < level; i++)
513 dump_profile(p, level+1, cb, data);
514 for (i=0; i < level; i++)
517 cb(profile_is_node_final(p) ? "*" : "", data);
523 static void dump_profile_to_file_cb(const char *str, void *data)
528 errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
530 dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
540 static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
544 if (b->max - b->cur < len) {
548 newsize = b->max + (b->max >> 1) + len + 1024;
549 newptr = realloc(b->base, newsize);
550 if (newptr == NULL) {
557 memcpy(b->base + b->cur, d, len);
558 b->cur += len; /* ignore overflow */
561 static void dump_profile_to_buffer_cb(const char *str, void *data)
563 add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
566 errcode_t profile_write_tree_to_buffer(struct profile_node *root,
569 struct prof_buf prof_buf = { 0, 0, 0, 0 };
571 dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
576 add_data_to_buffer(&prof_buf, "", 1); /* append nul */
577 if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
578 char *newptr = realloc(prof_buf.base, prof_buf.cur);
580 prof_buf.base = newptr;
582 *buf = prof_buf.base;