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,
32 static char *skip_over_blanks(char *cp)
34 while (*cp && isspace((int) (*cp)))
39 static void strip_line(char *line)
41 char *p = line + strlen(line);
42 while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
46 static void parse_quoted_string(char *str)
50 for (to = from = str; *from && *from != '"'; to++, from++) {
74 static errcode_t parse_std_line(char *line, struct parse_state *state)
76 char *cp, ch, *tag, *value;
79 struct profile_node *node;
80 int do_subsection = 0;
85 cp = skip_over_blanks(line);
86 if (cp[0] == ';' || cp[0] == '#')
93 if (state->group_level > 0)
94 return PROF_SECTION_NOTOP;
98 return PROF_SECTION_SYNTAX;
100 retval = profile_find_node_subsection(state->root_section,
102 &state->current_section);
103 if (retval == PROF_NO_SECTION) {
104 retval = profile_add_node(state->root_section,
106 &state->current_section);
113 * Finish off the rest of the line.
117 profile_make_node_final(state->current_section);
121 * A space after ']' should not be fatal
123 cp = skip_over_blanks(cp);
125 return PROF_SECTION_SYNTAX;
129 if (state->group_level == 0)
130 return PROF_EXTRA_CBRACE;
132 profile_make_node_final(state->current_section);
133 retval = profile_get_node_parent(state->current_section,
134 &state->current_section);
137 state->group_level--;
141 * Parse the relations
144 cp = strchr(cp, '=');
146 return PROF_RELATION_SYNTAX;
148 return PROF_RELATION_SYNTAX;
151 /* Look for whitespace on left-hand side. */
152 while (p < cp && !isspace((int)*p))
155 /* Found some sort of whitespace. */
157 /* If we have more non-whitespace, it's an error. */
159 if (!isspace((int)*p))
160 return PROF_RELATION_SYNTAX;
164 cp = skip_over_blanks(cp+1);
166 if (value[0] == '"') {
168 parse_quoted_string(value);
169 } else if (value[0] == 0) {
171 state->state = STATE_GET_OBRACE;
172 } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0)
175 cp = value + strlen(value) - 1;
176 while ((cp > value) && isspace((int) (*cp)))
180 p = strchr(tag, '*');
183 retval = profile_add_node(state->current_section,
184 tag, 0, &state->current_section);
188 profile_make_node_final(state->current_section);
189 state->group_level++;
192 p = strchr(tag, '*');
195 profile_add_node(state->current_section, tag, value, &node);
197 profile_make_node_final(node);
201 /* Open and parse an included profile file. */
202 static errcode_t parse_include_file(char *filename, struct parse_state *state)
205 errcode_t retval = 0;
206 struct parse_state incstate;
208 /* Create a new state so that fragments are syntactically independent,
209 * sharing the root section with the existing state. */
210 incstate.state = STATE_INIT_COMMENT;
211 incstate.group_level = 0;
212 incstate.root_section = state->root_section;
213 incstate.current_section = NULL;
215 fp = fopen(filename, "r");
217 return PROF_FAIL_INCLUDE_FILE;
218 retval = parse_file(fp, &incstate, NULL);
223 /* Return non-zero if filename contains only alphanumeric characters, dashes,
224 * and underscores. */
225 static int valid_name(const char *filename)
229 for (p = filename; *p != '\0'; p++) {
230 if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_')
237 * Include files within dirname. Only files with names consisting entirely of
238 * alphanumeric chracters, dashes, and underscores are included, in order to
239 * avoid including editor backup files, .rpmsave files, and the like.
241 static errcode_t parse_include_dir(char *dirname, struct parse_state *state)
244 char *wildcard = NULL, *pathname;
247 errcode_t retval = 0;
249 if (asprintf(&wildcard, "%s\\*", dirname) < 0)
252 handle = FindFirstFile(wildcard, &ffd);
253 if (handle == INVALID_HANDLE_VALUE) {
254 retval = PROF_FAIL_INCLUDE_DIR;
259 if (!valid_name(ffd.cFileName))
261 if (asprintf(&pathname, "%s\\%s", dirname, ffd.cFileName) < 0) {
265 retval = parse_include_file(pathname, state);
269 } while (FindNextFile(handle, &ffd) != 0);
277 #else /* not _WIN32 */
281 errcode_t retval = 0;
284 dir = opendir(dirname);
286 return PROF_FAIL_INCLUDE_DIR;
287 while ((ent = readdir(dir)) != NULL) {
288 if (!valid_name(ent->d_name))
290 if (asprintf(&pathname, "%s/%s", dirname, ent->d_name) < 0) {
294 retval = parse_include_file(pathname, state);
301 #endif /* not _WIN32 */
304 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:
321 if (strncmp(line, "module", 6) == 0 && isspace(line[6])) {
323 * If we are expecting a module declaration, fill in *ret_modspec
324 * and return PROF_MODULE, which will cause parsing to abort and
325 * the module to be loaded instead. If we aren't expecting a
326 * module declaration, return PROF_MODULE without filling in
327 * *ret_modspec, which will be treated as an ordinary error.
330 cp = skip_over_blanks(line + 6);
332 *ret_modspec = strdup(cp);
340 state->state = STATE_STD_LINE;
342 return parse_std_line(line, state);
343 case STATE_GET_OBRACE:
344 cp = skip_over_blanks(line);
346 return PROF_MISSING_OBRACE;
347 state->state = STATE_STD_LINE;
352 static errcode_t parse_file(FILE *f, struct parse_state *state,
355 #define BUF_SIZE 2048
359 bptr = malloc (BUF_SIZE);
364 if (fgets(bptr, BUF_SIZE, f) == NULL)
366 #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
367 retval = parse_line(bptr, state, ret_modspec);
376 if (strlen(bptr) >= BUF_SIZE - 1) {
377 /* The string may have foreign newlines and
378 gotten chopped off on a non-newline
379 boundary. Seek backwards to the last known
382 char *c = bptr + strlen (bptr);
383 for (offset = 0; offset > -BUF_SIZE; offset--) {
384 if (*c == '\r' || *c == '\n') {
386 fseek (f, offset, SEEK_CUR);
393 /* First change all newlines to \n */
394 for (p = bptr; *p != '\0'; p++) {
398 /* Then parse all lines */
400 end = bptr + strlen (bptr);
405 newline = strchr (p, '\n');
409 /* parse_line modifies contents of p */
410 newp = p + strlen (p) + 1;
411 retval = parse_line (p, state, ret_modspec);
427 errcode_t profile_parse_file(FILE *f, struct profile_node **root,
430 struct parse_state state;
435 /* Initialize parsing state with a new root node. */
436 state.state = STATE_INIT_COMMENT;
437 state.group_level = 0;
438 state.current_section = NULL;
439 retval = profile_create_node("(root)", 0, &state.root_section);
443 retval = parse_file(f, &state, ret_modspec);
445 profile_free_node(state.root_section);
448 *root = state.root_section;
453 * Return TRUE if the string begins or ends with whitespace
455 static int need_double_quotes(char *str)
461 if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
463 if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b'))
469 * Output a string with double quotes, doing appropriate backquoting
470 * of characters as necessary.
472 static void output_quoted_string(char *str, void (*cb)(const char *,void *),
484 while ((ch = *str++)) {
499 /* This would be a lot faster if we scanned
500 forward for the next "interesting"
520 /* Errors should be returned, not ignored! */
521 static void dump_profile(struct profile_node *root, int level,
522 void (*cb)(const char *, void *), void *data)
525 struct profile_node *p;
532 retval = profile_find_node_relation(root, 0, &iter,
536 for (i=0; i < level; i++)
538 if (need_double_quotes(value)) {
541 output_quoted_string(value, cb, data);
553 retval = profile_find_node_subsection(root, 0, &iter,
557 if (level == 0) { /* [xxx] */
561 cb(profile_is_node_final(p) ? "*" : "", data);
563 dump_profile(p, level+1, cb, data);
565 } else { /* xxx = { ... } */
566 for (i=0; i < level; i++)
571 dump_profile(p, level+1, cb, data);
572 for (i=0; i < level; i++)
575 cb(profile_is_node_final(p) ? "*" : "", data);
581 static void dump_profile_to_file_cb(const char *str, void *data)
586 errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
588 dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
598 static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
602 if (b->max - b->cur < len) {
606 newsize = b->max + (b->max >> 1) + len + 1024;
607 newptr = realloc(b->base, newsize);
608 if (newptr == NULL) {
615 memcpy(b->base + b->cur, d, len);
616 b->cur += len; /* ignore overflow */
619 static void dump_profile_to_buffer_cb(const char *str, void *data)
621 add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
624 errcode_t profile_write_tree_to_buffer(struct profile_node *root,
627 struct prof_buf prof_buf = { 0, 0, 0, 0 };
629 dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
634 add_data_to_buffer(&prof_buf, "", 1); /* append nul */
635 if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
636 char *newptr = realloc(prof_buf.base, prof_buf.cur);
638 prof_buf.base = newptr;
640 *buf = prof_buf.base;