1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
16 #define SECTION_SEP_CHAR '/'
18 #define STATE_INIT_COMMENT 1
19 #define STATE_STD_LINE 2
20 #define STATE_GET_OBRACE 3
25 struct profile_node *root_section;
26 struct profile_node *current_section;
29 static errcode_t parse_file(FILE *f, struct parse_state *state);
31 static char *skip_over_blanks(char *cp)
33 while (*cp && isspace((int) (*cp)))
38 static void strip_line(char *line)
40 char *p = line + strlen(line);
41 while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
45 static void parse_quoted_string(char *str)
51 for (to = from = str; *from && *from != '"'; to++, from++) {
75 static errcode_t parse_std_line(char *line, struct parse_state *state)
77 char *cp, ch, *tag, *value;
80 struct profile_node *node;
81 int do_subsection = 0;
86 cp = skip_over_blanks(line);
87 if (cp[0] == ';' || cp[0] == '#')
94 if (state->group_level > 0)
95 return PROF_SECTION_NOTOP;
99 return PROF_SECTION_SYNTAX;
101 retval = profile_find_node_subsection(state->root_section,
103 &state->current_section);
104 if (retval == PROF_NO_SECTION) {
105 retval = profile_add_node(state->root_section,
107 &state->current_section);
114 * Finish off the rest of the line.
118 profile_make_node_final(state->current_section);
122 * A space after ']' should not be fatal
124 cp = skip_over_blanks(cp);
126 return PROF_SECTION_SYNTAX;
130 if (state->group_level == 0)
131 return PROF_EXTRA_CBRACE;
133 profile_make_node_final(state->current_section);
134 retval = profile_get_node_parent(state->current_section,
135 &state->current_section);
138 state->group_level--;
142 * Parse the relations
145 cp = strchr(cp, '=');
147 return PROF_RELATION_SYNTAX;
149 return PROF_RELATION_SYNTAX;
152 /* Look for whitespace on left-hand side. */
153 while (p < cp && !isspace((int)*p))
156 /* Found some sort of whitespace. */
158 /* If we have more non-whitespace, it's an error. */
160 if (!isspace((int)*p))
161 return PROF_RELATION_SYNTAX;
165 cp = skip_over_blanks(cp+1);
167 if (value[0] == '"') {
169 parse_quoted_string(value);
170 } else if (value[0] == 0) {
172 state->state = STATE_GET_OBRACE;
173 } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0)
176 cp = value + strlen(value) - 1;
177 while ((cp > value) && isspace((int) (*cp)))
181 p = strchr(tag, '*');
184 retval = profile_add_node(state->current_section,
185 tag, 0, &state->current_section);
189 profile_make_node_final(state->current_section);
190 state->group_level++;
193 p = strchr(tag, '*');
196 profile_add_node(state->current_section, tag, value, &node);
198 profile_make_node_final(node);
202 /* Open and parse an included profile file. */
203 static errcode_t parse_include_file(char *filename, struct parse_state *state)
206 errcode_t retval = 0;
207 struct parse_state incstate;
209 /* Create a new state so that fragments are syntactically independent,
210 * sharing the root section with the existing state. */
211 incstate.state = STATE_INIT_COMMENT;
212 incstate.group_level = 0;
213 incstate.root_section = state->root_section;
214 incstate.current_section = NULL;
216 fp = fopen(filename, "r");
218 return PROF_FAIL_INCLUDE_FILE;
219 retval = parse_file(fp, &incstate);
224 /* Return non-zero if filename contains only alphanumeric characters, dashes,
225 * and underscores. */
226 static int valid_name(const char *filename)
230 for (p = filename; *p != '\0'; p++) {
231 if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_')
238 * Include files within dirname. Only files with names consisting entirely of
239 * alphanumeric chracters, dashes, and underscores are included, in order to
240 * avoid including editor backup files, .rpmsave files, and the like.
242 static errcode_t parse_include_dir(char *dirname, struct parse_state *state)
245 char *wildcard = NULL, *pathname;
248 errcode_t retval = 0;
250 if (asprintf(&wildcard, "%s\\*", dirname) < 0)
253 handle = FindFirstFile(wildcard, &ffd);
254 if (handle == INVALID_HANDLE_VALUE) {
255 retval = PROF_FAIL_INCLUDE_DIR;
260 if (!valid_name(ffd.cFileName))
262 if (asprintf(&pathname, "%s\\%s", dirname, ffd.cFileName) < 0) {
266 retval = parse_include_file(pathname, state);
270 } while (FindNextFile(handle, &ffd) != 0);
278 #else /* not _WIN32 */
282 errcode_t retval = 0;
285 dir = opendir(dirname);
287 return PROF_FAIL_INCLUDE_DIR;
288 while ((ent = readdir(dir)) != NULL) {
289 if (!valid_name(ent->d_name))
291 if (asprintf(&pathname, "%s/%s", dirname, ent->d_name) < 0) {
295 retval = parse_include_file(pathname, state);
302 #endif /* not _WIN32 */
305 static errcode_t parse_line(char *line, struct parse_state *state)
309 if (strncmp(line, "include", 7) == 0 && isspace(line[7])) {
310 cp = skip_over_blanks(line + 7);
312 return parse_include_file(cp, state);
314 if (strncmp(line, "includedir", 10) == 0 && isspace(line[10])) {
315 cp = skip_over_blanks(line + 10);
317 return parse_include_dir(cp, state);
319 switch (state->state) {
320 case STATE_INIT_COMMENT:
323 state->state = STATE_STD_LINE;
325 return parse_std_line(line, state);
326 case STATE_GET_OBRACE:
327 cp = skip_over_blanks(line);
329 return PROF_MISSING_OBRACE;
330 state->state = STATE_STD_LINE;
335 static errcode_t parse_file(FILE *f, struct parse_state *state)
337 #define BUF_SIZE 2048
341 bptr = malloc (BUF_SIZE);
346 if (fgets(bptr, BUF_SIZE, f) == NULL)
348 #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
349 retval = parse_line(bptr, state);
358 if (strlen(bptr) >= BUF_SIZE - 1) {
359 /* The string may have foreign newlines and
360 gotten chopped off on a non-newline
361 boundary. Seek backwards to the last known
364 char *c = bptr + strlen (bptr);
365 for (offset = 0; offset > -BUF_SIZE; offset--) {
366 if (*c == '\r' || *c == '\n') {
368 fseek (f, offset, SEEK_CUR);
375 /* First change all newlines to \n */
376 for (p = bptr; *p != '\0'; p++) {
380 /* Then parse all lines */
382 end = bptr + strlen (bptr);
387 newline = strchr (p, '\n');
391 /* parse_line modifies contents of p */
392 newp = p + strlen (p) + 1;
393 retval = parse_line (p, state);
409 errcode_t profile_parse_file(FILE *f, struct profile_node **root)
411 struct parse_state state;
416 /* Initialize parsing state with a new root node. */
417 state.state = STATE_INIT_COMMENT;
418 state.group_level = 0;
419 state.current_section = NULL;
420 retval = profile_create_node("(root)", 0, &state.root_section);
424 retval = parse_file(f, &state);
426 profile_free_node(state.root_section);
429 *root = state.root_section;
434 * Return TRUE if the string begins or ends with whitespace
436 static int need_double_quotes(char *str)
442 if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
444 if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b'))
450 * Output a string with double quotes, doing appropriate backquoting
451 * of characters as necessary.
453 static void output_quoted_string(char *str, void (*cb)(const char *,void *),
465 while ((ch = *str++)) {
480 /* This would be a lot faster if we scanned
481 forward for the next "interesting"
501 /* Errors should be returned, not ignored! */
502 static void dump_profile(struct profile_node *root, int level,
503 void (*cb)(const char *, void *), void *data)
506 struct profile_node *p;
513 retval = profile_find_node_relation(root, 0, &iter,
517 for (i=0; i < level; i++)
519 if (need_double_quotes(value)) {
522 output_quoted_string(value, cb, data);
534 retval = profile_find_node_subsection(root, 0, &iter,
538 if (level == 0) { /* [xxx] */
542 cb(profile_is_node_final(p) ? "*" : "", data);
544 dump_profile(p, level+1, cb, data);
546 } else { /* xxx = { ... } */
547 for (i=0; i < level; i++)
552 dump_profile(p, level+1, cb, data);
553 for (i=0; i < level; i++)
556 cb(profile_is_node_final(p) ? "*" : "", data);
562 static void dump_profile_to_file_cb(const char *str, void *data)
567 errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
569 dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
579 static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
583 if (b->max - b->cur < len) {
587 newsize = b->max + (b->max >> 1) + len + 1024;
588 newptr = realloc(b->base, newsize);
589 if (newptr == NULL) {
596 memcpy(b->base + b->cur, d, len);
597 b->cur += len; /* ignore overflow */
600 static void dump_profile_to_buffer_cb(const char *str, void *data)
602 add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
605 errcode_t profile_write_tree_to_buffer(struct profile_node *root,
608 struct prof_buf prof_buf = { 0, 0, 0, 0 };
610 dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
615 add_data_to_buffer(&prof_buf, "", 1); /* append nul */
616 if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
617 char *newptr = realloc(prof_buf.base, prof_buf.cur);
619 prof_buf.base = newptr;
621 *buf = prof_buf.base;