Blame src/util/profile/prof_parse.c

Packit fd8b60
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
Packit fd8b60
#include "prof_int.h"
Packit fd8b60
Packit fd8b60
#include <sys/types.h>
Packit fd8b60
#include <stdio.h>
Packit fd8b60
#include <string.h>
Packit fd8b60
#ifdef HAVE_STDLIB_H
Packit fd8b60
#include <stdlib.h>
Packit fd8b60
#endif
Packit fd8b60
#include <errno.h>
Packit fd8b60
#include <ctype.h>
Packit fd8b60
#ifndef _WIN32
Packit fd8b60
#include <dirent.h>
Packit fd8b60
#endif
Packit fd8b60
Packit fd8b60
#define SECTION_SEP_CHAR '/'
Packit fd8b60
Packit fd8b60
#define STATE_INIT_COMMENT      1
Packit fd8b60
#define STATE_STD_LINE          2
Packit fd8b60
#define STATE_GET_OBRACE        3
Packit fd8b60
Packit fd8b60
struct parse_state {
Packit fd8b60
    int     state;
Packit fd8b60
    int     group_level;
Packit fd8b60
    struct profile_node *root_section;
Packit fd8b60
    struct profile_node *current_section;
Packit fd8b60
};
Packit fd8b60
Packit fd8b60
static errcode_t parse_file(FILE *f, struct parse_state *state,
Packit fd8b60
                            char **ret_modspec);
Packit fd8b60
Packit fd8b60
static char *skip_over_blanks(char *cp)
Packit fd8b60
{
Packit fd8b60
    while (*cp && isspace((int) (*cp)))
Packit fd8b60
        cp++;
Packit fd8b60
    return cp;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static void strip_line(char *line)
Packit fd8b60
{
Packit fd8b60
    char *p = line + strlen(line);
Packit fd8b60
    while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
Packit fd8b60
        *--p = 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static void parse_quoted_string(char *str)
Packit fd8b60
{
Packit fd8b60
    char *to, *from;
Packit fd8b60
Packit fd8b60
    for (to = from = str; *from && *from != '"'; to++, from++) {
Packit fd8b60
        if (*from == '\\' && *(from + 1) != '\0') {
Packit fd8b60
            from++;
Packit fd8b60
            switch (*from) {
Packit fd8b60
            case 'n':
Packit fd8b60
                *to = '\n';
Packit fd8b60
                break;
Packit fd8b60
            case 't':
Packit fd8b60
                *to = '\t';
Packit fd8b60
                break;
Packit fd8b60
            case 'b':
Packit fd8b60
                *to = '\b';
Packit fd8b60
                break;
Packit fd8b60
            default:
Packit fd8b60
                *to = *from;
Packit fd8b60
            }
Packit fd8b60
            continue;
Packit fd8b60
        }
Packit fd8b60
        *to = *from;
Packit fd8b60
    }
Packit fd8b60
    *to = '\0';
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
Packit fd8b60
static errcode_t parse_std_line(char *line, struct parse_state *state)
Packit fd8b60
{
Packit fd8b60
    char    *cp, ch, *tag, *value;
Packit fd8b60
    char    *p;
Packit fd8b60
    errcode_t retval;
Packit fd8b60
    struct profile_node     *node;
Packit fd8b60
    int do_subsection = 0;
Packit fd8b60
    void *iter = 0;
Packit fd8b60
Packit fd8b60
    if (*line == 0)
Packit fd8b60
        return 0;
Packit fd8b60
    cp = skip_over_blanks(line);
Packit fd8b60
    if (cp[0] == ';' || cp[0] == '#')
Packit fd8b60
        return 0;
Packit fd8b60
    strip_line(cp);
Packit fd8b60
    ch = *cp;
Packit fd8b60
    if (ch == 0)
Packit fd8b60
        return 0;
Packit fd8b60
    if (ch == '[') {
Packit fd8b60
        if (state->group_level > 0)
Packit fd8b60
            return PROF_SECTION_NOTOP;
Packit fd8b60
        cp++;
Packit fd8b60
        p = strchr(cp, ']');
Packit fd8b60
        if (p == NULL)
Packit fd8b60
            return PROF_SECTION_SYNTAX;
Packit fd8b60
        *p = '\0';
Packit fd8b60
        retval = profile_find_node_subsection(state->root_section,
Packit fd8b60
                                              cp, &iter, 0,
Packit fd8b60
                                              &state->current_section);
Packit fd8b60
        if (retval == PROF_NO_SECTION) {
Packit fd8b60
            retval = profile_add_node(state->root_section,
Packit fd8b60
                                      cp, 0,
Packit fd8b60
                                      &state->current_section);
Packit fd8b60
            if (retval)
Packit fd8b60
                return retval;
Packit fd8b60
        } else if (retval)
Packit fd8b60
            return retval;
Packit fd8b60
Packit fd8b60
        /*
Packit fd8b60
         * Finish off the rest of the line.
Packit fd8b60
         */
Packit fd8b60
        cp = p+1;
Packit fd8b60
        if (*cp == '*') {
Packit fd8b60
            profile_make_node_final(state->current_section);
Packit fd8b60
            cp++;
Packit fd8b60
        }
Packit fd8b60
        /*
Packit fd8b60
         * A space after ']' should not be fatal
Packit fd8b60
         */
Packit fd8b60
        cp = skip_over_blanks(cp);
Packit fd8b60
        if (*cp)
Packit fd8b60
            return PROF_SECTION_SYNTAX;
Packit fd8b60
        return 0;
Packit fd8b60
    }
Packit fd8b60
    if (ch == '}') {
Packit fd8b60
        if (state->group_level == 0)
Packit fd8b60
            return PROF_EXTRA_CBRACE;
Packit fd8b60
        if (*(cp+1) == '*')
Packit fd8b60
            profile_make_node_final(state->current_section);
Packit fd8b60
        retval = profile_get_node_parent(state->current_section,
Packit fd8b60
                                         &state->current_section);
Packit fd8b60
        if (retval)
Packit fd8b60
            return retval;
Packit fd8b60
        state->group_level--;
Packit fd8b60
        return 0;
Packit fd8b60
    }
Packit fd8b60
    /*
Packit fd8b60
     * Parse the relations
Packit fd8b60
     */
Packit fd8b60
    tag = cp;
Packit fd8b60
    cp = strchr(cp, '=');
Packit fd8b60
    if (!cp)
Packit fd8b60
        return PROF_RELATION_SYNTAX;
Packit fd8b60
    if (cp == tag)
Packit fd8b60
        return PROF_RELATION_SYNTAX;
Packit fd8b60
    *cp = '\0';
Packit fd8b60
    p = tag;
Packit fd8b60
    /* Look for whitespace on left-hand side.  */
Packit fd8b60
    while (p < cp && !isspace((int)*p))
Packit fd8b60
        p++;
Packit fd8b60
    if (p < cp) {
Packit fd8b60
        /* Found some sort of whitespace.  */
Packit fd8b60
        *p++ = 0;
Packit fd8b60
        /* If we have more non-whitespace, it's an error.  */
Packit fd8b60
        while (p < cp) {
Packit fd8b60
            if (!isspace((int)*p))
Packit fd8b60
                return PROF_RELATION_SYNTAX;
Packit fd8b60
            p++;
Packit fd8b60
        }
Packit fd8b60
    }
Packit fd8b60
    cp = skip_over_blanks(cp+1);
Packit fd8b60
    value = cp;
Packit fd8b60
    if (value[0] == '"') {
Packit fd8b60
        value++;
Packit fd8b60
        parse_quoted_string(value);
Packit fd8b60
    } else if (value[0] == 0) {
Packit fd8b60
        do_subsection++;
Packit fd8b60
        state->state = STATE_GET_OBRACE;
Packit fd8b60
    } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0)
Packit fd8b60
        do_subsection++;
Packit fd8b60
    else {
Packit fd8b60
        cp = value + strlen(value) - 1;
Packit fd8b60
        while ((cp > value) && isspace((int) (*cp)))
Packit fd8b60
            *cp-- = 0;
Packit fd8b60
    }
Packit fd8b60
    if (do_subsection) {
Packit fd8b60
        p = strchr(tag, '*');
Packit fd8b60
        if (p)
Packit fd8b60
            *p = '\0';
Packit fd8b60
        retval = profile_add_node(state->current_section,
Packit fd8b60
                                  tag, 0, &state->current_section);
Packit fd8b60
        if (retval)
Packit fd8b60
            return retval;
Packit fd8b60
        if (p)
Packit fd8b60
            profile_make_node_final(state->current_section);
Packit fd8b60
        state->group_level++;
Packit fd8b60
        return 0;
Packit fd8b60
    }
Packit fd8b60
    p = strchr(tag, '*');
Packit fd8b60
    if (p)
Packit fd8b60
        *p = '\0';
Packit fd8b60
    profile_add_node(state->current_section, tag, value, &node);
Packit fd8b60
    if (p)
Packit fd8b60
        profile_make_node_final(node);
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Open and parse an included profile file. */
Packit fd8b60
static errcode_t parse_include_file(const char *filename,
Packit fd8b60
                                    struct profile_node *root_section)
Packit fd8b60
{
Packit fd8b60
    FILE    *fp;
Packit fd8b60
    errcode_t retval = 0;
Packit fd8b60
    struct parse_state state;
Packit fd8b60
Packit fd8b60
    /* Create a new state so that fragments are syntactically independent but
Packit fd8b60
     * share a root section. */
Packit fd8b60
    state.state = STATE_INIT_COMMENT;
Packit fd8b60
    state.group_level = 0;
Packit fd8b60
    state.root_section = root_section;
Packit fd8b60
    state.current_section = NULL;
Packit fd8b60
Packit fd8b60
    fp = fopen(filename, "r");
Packit fd8b60
    if (fp == NULL)
Packit fd8b60
        return PROF_FAIL_INCLUDE_FILE;
Packit fd8b60
    retval = parse_file(fp, &state, NULL);
Packit fd8b60
    fclose(fp);
Packit fd8b60
    return retval;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Return non-zero if filename contains only alphanumeric characters, dashes,
Packit fd8b60
 * and underscores, or if the filename ends in ".conf" and is not a dotfile. */
Packit fd8b60
static int valid_name(const char *filename)
Packit fd8b60
{
Packit fd8b60
    const char *p;
Packit fd8b60
    size_t len = strlen(filename);
Packit fd8b60
Packit fd8b60
    /* Ignore dotfiles, which might be editor or filesystem artifacts. */
Packit fd8b60
    if (*filename == '.')
Packit fd8b60
        return 0;
Packit fd8b60
Packit fd8b60
    if (len >= 5 && !strcmp(filename + len - 5, ".conf"))
Packit fd8b60
        return 1;
Packit fd8b60
Packit fd8b60
    for (p = filename; *p != '\0'; p++) {
Packit fd8b60
        if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_')
Packit fd8b60
            return 0;
Packit fd8b60
    }
Packit fd8b60
    return 1;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Include files within dirname.  Only files with names ending in ".conf", or
Packit fd8b60
 * consisting entirely of alphanumeric characters, dashes, and underscores are
Packit fd8b60
 * included.  This restriction avoids including editor backup files, .rpmsave
Packit fd8b60
 * files, and the like.  Files are processed in alphanumeric order.
Packit fd8b60
 */
Packit fd8b60
static errcode_t parse_include_dir(const char *dirname,
Packit fd8b60
                                   struct profile_node *root_section)
Packit fd8b60
{
Packit fd8b60
    errcode_t retval = 0;
Packit fd8b60
    char **fnames, *pathname;
Packit fd8b60
    int i;
Packit fd8b60
Packit fd8b60
    if (k5_dir_filenames(dirname, &fnames) != 0)
Packit fd8b60
        return PROF_FAIL_INCLUDE_DIR;
Packit fd8b60
Packit fd8b60
    for (i = 0; fnames != NULL && fnames[i] != NULL; i++) {
Packit fd8b60
        if (!valid_name(fnames[i]))
Packit fd8b60
            continue;
Packit fd8b60
        if (asprintf(&pathname, "%s/%s", dirname, fnames[i]) < 0) {
Packit fd8b60
            retval = ENOMEM;
Packit fd8b60
            break;
Packit fd8b60
        }
Packit fd8b60
        retval = parse_include_file(pathname, root_section);
Packit fd8b60
        free(pathname);
Packit fd8b60
        if (retval)
Packit fd8b60
            break;
Packit fd8b60
    }
Packit fd8b60
    k5_free_filenames(fnames);
Packit fd8b60
    return retval;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static errcode_t parse_line(char *line, struct parse_state *state,
Packit fd8b60
                            char **ret_modspec)
Packit fd8b60
{
Packit fd8b60
    char    *cp;
Packit fd8b60
Packit fd8b60
    if (strncmp(line, "include", 7) == 0 && isspace(line[7])) {
Packit fd8b60
        cp = skip_over_blanks(line + 7);
Packit fd8b60
        strip_line(cp);
Packit fd8b60
        return parse_include_file(cp, state->root_section);
Packit fd8b60
    }
Packit fd8b60
    if (strncmp(line, "includedir", 10) == 0 && isspace(line[10])) {
Packit fd8b60
        cp = skip_over_blanks(line + 10);
Packit fd8b60
        strip_line(cp);
Packit fd8b60
        return parse_include_dir(cp, state->root_section);
Packit fd8b60
    }
Packit fd8b60
    switch (state->state) {
Packit fd8b60
    case STATE_INIT_COMMENT:
Packit fd8b60
        if (strncmp(line, "module", 6) == 0 && isspace(line[6])) {
Packit fd8b60
            /*
Packit fd8b60
             * If we are expecting a module declaration, fill in *ret_modspec
Packit fd8b60
             * and return PROF_MODULE, which will cause parsing to abort and
Packit fd8b60
             * the module to be loaded instead.  If we aren't expecting a
Packit fd8b60
             * module declaration, return PROF_MODULE without filling in
Packit fd8b60
             * *ret_modspec, which will be treated as an ordinary error.
Packit fd8b60
             */
Packit fd8b60
            if (ret_modspec) {
Packit fd8b60
                cp = skip_over_blanks(line + 6);
Packit fd8b60
                strip_line(cp);
Packit fd8b60
                *ret_modspec = strdup(cp);
Packit fd8b60
                if (!*ret_modspec)
Packit fd8b60
                    return ENOMEM;
Packit fd8b60
            }
Packit fd8b60
            return PROF_MODULE;
Packit fd8b60
        }
Packit fd8b60
        if (line[0] != '[')
Packit fd8b60
            return 0;
Packit fd8b60
        state->state = STATE_STD_LINE;
Packit fd8b60
    case STATE_STD_LINE:
Packit fd8b60
        return parse_std_line(line, state);
Packit fd8b60
    case STATE_GET_OBRACE:
Packit fd8b60
        cp = skip_over_blanks(line);
Packit fd8b60
        if (*cp != '{')
Packit fd8b60
            return PROF_MISSING_OBRACE;
Packit fd8b60
        state->state = STATE_STD_LINE;
Packit fd8b60
    }
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static errcode_t parse_file(FILE *f, struct parse_state *state,
Packit fd8b60
                            char **ret_modspec)
Packit fd8b60
{
Packit fd8b60
#define BUF_SIZE        2048
Packit fd8b60
    char *bptr;
Packit fd8b60
    errcode_t retval;
Packit fd8b60
Packit fd8b60
    bptr = malloc (BUF_SIZE);
Packit fd8b60
    if (!bptr)
Packit fd8b60
        return ENOMEM;
Packit fd8b60
Packit fd8b60
    while (!feof(f)) {
Packit fd8b60
        if (fgets(bptr, BUF_SIZE, f) == NULL)
Packit fd8b60
            break;
Packit fd8b60
#ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
Packit fd8b60
        retval = parse_line(bptr, state, ret_modspec);
Packit fd8b60
        if (retval) {
Packit fd8b60
            free (bptr);
Packit fd8b60
            return retval;
Packit fd8b60
        }
Packit fd8b60
#else
Packit fd8b60
        {
Packit fd8b60
            char *p, *end;
Packit fd8b60
Packit fd8b60
            if (strlen(bptr) >= BUF_SIZE - 1) {
Packit fd8b60
                /* The string may have foreign newlines and
Packit fd8b60
                   gotten chopped off on a non-newline
Packit fd8b60
                   boundary.  Seek backwards to the last known
Packit fd8b60
                   newline.  */
Packit fd8b60
                long offset;
Packit fd8b60
                char *c = bptr + strlen (bptr);
Packit fd8b60
                for (offset = 0; offset > -BUF_SIZE; offset--) {
Packit fd8b60
                    if (*c == '\r' || *c == '\n') {
Packit fd8b60
                        *c = '\0';
Packit fd8b60
                        fseek (f, offset, SEEK_CUR);
Packit fd8b60
                        break;
Packit fd8b60
                    }
Packit fd8b60
                    c--;
Packit fd8b60
                }
Packit fd8b60
            }
Packit fd8b60
Packit fd8b60
            /* First change all newlines to \n */
Packit fd8b60
            for (p = bptr; *p != '\0'; p++) {
Packit fd8b60
                if (*p == '\r')
Packit fd8b60
                    *p = '\n';
Packit fd8b60
            }
Packit fd8b60
            /* Then parse all lines */
Packit fd8b60
            p = bptr;
Packit fd8b60
            end = bptr + strlen (bptr);
Packit fd8b60
            while (p < end) {
Packit fd8b60
                char* newline;
Packit fd8b60
                char* newp;
Packit fd8b60
Packit fd8b60
                newline = strchr (p, '\n');
Packit fd8b60
                if (newline != NULL)
Packit fd8b60
                    *newline = '\0';
Packit fd8b60
Packit fd8b60
                /* parse_line modifies contents of p */
Packit fd8b60
                newp = p + strlen (p) + 1;
Packit fd8b60
                retval = parse_line (p, state, ret_modspec);
Packit fd8b60
                if (retval) {
Packit fd8b60
                    free (bptr);
Packit fd8b60
                    return retval;
Packit fd8b60
                }
Packit fd8b60
Packit fd8b60
                p = newp;
Packit fd8b60
            }
Packit fd8b60
        }
Packit fd8b60
#endif
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    free (bptr);
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
errcode_t profile_parse_file(FILE *f, struct profile_node **root,
Packit fd8b60
                             char **ret_modspec)
Packit fd8b60
{
Packit fd8b60
    struct parse_state state;
Packit fd8b60
    errcode_t retval;
Packit fd8b60
Packit fd8b60
    *root = NULL;
Packit fd8b60
Packit fd8b60
    /* Initialize parsing state with a new root node. */
Packit fd8b60
    state.state = STATE_INIT_COMMENT;
Packit fd8b60
    state.group_level = 0;
Packit fd8b60
    state.current_section = NULL;
Packit fd8b60
    retval = profile_create_node("(root)", 0, &state.root_section);
Packit fd8b60
    if (retval)
Packit fd8b60
        return retval;
Packit fd8b60
Packit fd8b60
    retval = parse_file(f, &state, ret_modspec);
Packit fd8b60
    if (retval) {
Packit fd8b60
        profile_free_node(state.root_section);
Packit fd8b60
        return retval;
Packit fd8b60
    }
Packit fd8b60
    *root = state.root_section;
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
errcode_t profile_process_directory(const char *dirname,
Packit fd8b60
                                    struct profile_node **root)
Packit fd8b60
{
Packit fd8b60
    errcode_t retval;
Packit fd8b60
    struct profile_node *node;
Packit fd8b60
Packit fd8b60
    *root = NULL;
Packit fd8b60
    retval = profile_create_node("(root)", 0, &node);
Packit fd8b60
    if (retval)
Packit fd8b60
        return retval;
Packit fd8b60
    retval = parse_include_dir(dirname, node);
Packit fd8b60
    if (retval) {
Packit fd8b60
        profile_free_node(node);
Packit fd8b60
        return retval;
Packit fd8b60
    }
Packit fd8b60
    *root = node;
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Return TRUE if the string begins or ends with whitespace
Packit fd8b60
 */
Packit fd8b60
static int need_double_quotes(char *str)
Packit fd8b60
{
Packit fd8b60
    if (!str)
Packit fd8b60
        return 0;
Packit fd8b60
    if (str[0] == '\0')
Packit fd8b60
        return 1;
Packit fd8b60
    if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
Packit fd8b60
        return 1;
Packit fd8b60
    if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b'))
Packit fd8b60
        return 1;
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Output a string with double quotes, doing appropriate backquoting
Packit fd8b60
 * of characters as necessary.
Packit fd8b60
 */
Packit fd8b60
static void output_quoted_string(char *str, void (*cb)(const char *,void *),
Packit fd8b60
                                 void *data)
Packit fd8b60
{
Packit fd8b60
    char    ch;
Packit fd8b60
    char buf[2];
Packit fd8b60
Packit fd8b60
    cb("\"", data);
Packit fd8b60
    if (!str) {
Packit fd8b60
        cb("\"", data);
Packit fd8b60
        return;
Packit fd8b60
    }
Packit fd8b60
    buf[1] = 0;
Packit fd8b60
    while ((ch = *str++)) {
Packit fd8b60
        switch (ch) {
Packit fd8b60
        case '\\':
Packit fd8b60
            cb("\\\\", data);
Packit fd8b60
            break;
Packit fd8b60
        case '\n':
Packit fd8b60
            cb("\\n", data);
Packit fd8b60
            break;
Packit fd8b60
        case '\t':
Packit fd8b60
            cb("\\t", data);
Packit fd8b60
            break;
Packit fd8b60
        case '\b':
Packit fd8b60
            cb("\\b", data);
Packit fd8b60
            break;
Packit fd8b60
        default:
Packit fd8b60
            /* This would be a lot faster if we scanned
Packit fd8b60
               forward for the next "interesting"
Packit fd8b60
               character.  */
Packit fd8b60
            buf[0] = ch;
Packit fd8b60
            cb(buf, data);
Packit fd8b60
            break;
Packit fd8b60
        }
Packit fd8b60
    }
Packit fd8b60
    cb("\"", data);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
Packit fd8b60
Packit fd8b60
#if defined(_WIN32)
Packit fd8b60
#define EOL "\r\n"
Packit fd8b60
#endif
Packit fd8b60
Packit fd8b60
#ifndef EOL
Packit fd8b60
#define EOL "\n"
Packit fd8b60
#endif
Packit fd8b60
Packit fd8b60
/* Errors should be returned, not ignored!  */
Packit fd8b60
static void dump_profile(struct profile_node *root, int level,
Packit fd8b60
                         void (*cb)(const char *, void *), void *data)
Packit fd8b60
{
Packit fd8b60
    int i;
Packit fd8b60
    struct profile_node *p;
Packit fd8b60
    void *iter;
Packit fd8b60
    long retval;
Packit fd8b60
    char *name, *value;
Packit fd8b60
Packit fd8b60
    iter = 0;
Packit fd8b60
    do {
Packit fd8b60
        retval = profile_find_node_relation(root, 0, &iter,
Packit fd8b60
                                            &name, &value);
Packit fd8b60
        if (retval)
Packit fd8b60
            break;
Packit fd8b60
        for (i=0; i < level; i++)
Packit fd8b60
            cb("\t", data);
Packit fd8b60
        if (need_double_quotes(value)) {
Packit fd8b60
            cb(name, data);
Packit fd8b60
            cb(" = ", data);
Packit fd8b60
            output_quoted_string(value, cb, data);
Packit fd8b60
            cb(EOL, data);
Packit fd8b60
        } else {
Packit fd8b60
            cb(name, data);
Packit fd8b60
            cb(" = ", data);
Packit fd8b60
            cb(value, data);
Packit fd8b60
            cb(EOL, data);
Packit fd8b60
        }
Packit fd8b60
    } while (iter != 0);
Packit fd8b60
Packit fd8b60
    iter = 0;
Packit fd8b60
    do {
Packit fd8b60
        retval = profile_find_node_subsection(root, 0, &iter,
Packit fd8b60
                                              &name, &p);
Packit fd8b60
        if (retval)
Packit fd8b60
            break;
Packit fd8b60
        if (level == 0) { /* [xxx] */
Packit fd8b60
            cb("[", data);
Packit fd8b60
            cb(name, data);
Packit fd8b60
            cb("]", data);
Packit fd8b60
            cb(profile_is_node_final(p) ? "*" : "", data);
Packit fd8b60
            cb(EOL, data);
Packit fd8b60
            dump_profile(p, level+1, cb, data);
Packit fd8b60
            cb(EOL, data);
Packit fd8b60
        } else {        /* xxx = { ... } */
Packit fd8b60
            for (i=0; i < level; i++)
Packit fd8b60
                cb("\t", data);
Packit fd8b60
            cb(name, data);
Packit fd8b60
            cb(" = {", data);
Packit fd8b60
            cb(EOL, data);
Packit fd8b60
            dump_profile(p, level+1, cb, data);
Packit fd8b60
            for (i=0; i < level; i++)
Packit fd8b60
                cb("\t", data);
Packit fd8b60
            cb("}", data);
Packit fd8b60
            cb(profile_is_node_final(p) ? "*" : "", data);
Packit fd8b60
            cb(EOL, data);
Packit fd8b60
        }
Packit fd8b60
    } while (iter != 0);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static void dump_profile_to_file_cb(const char *str, void *data)
Packit fd8b60
{
Packit fd8b60
    fputs(str, data);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
Packit fd8b60
{
Packit fd8b60
    dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
struct prof_buf {
Packit fd8b60
    char *base;
Packit fd8b60
    size_t cur, max;
Packit fd8b60
    int err;
Packit fd8b60
};
Packit fd8b60
Packit fd8b60
static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
Packit fd8b60
{
Packit fd8b60
    if (b->err)
Packit fd8b60
        return;
Packit fd8b60
    if (b->max - b->cur < len) {
Packit fd8b60
        size_t newsize;
Packit fd8b60
        char *newptr;
Packit fd8b60
Packit fd8b60
        newsize = b->max + (b->max >> 1) + len + 1024;
Packit fd8b60
        newptr = realloc(b->base, newsize);
Packit fd8b60
        if (newptr == NULL) {
Packit fd8b60
            b->err = 1;
Packit fd8b60
            return;
Packit fd8b60
        }
Packit fd8b60
        b->base = newptr;
Packit fd8b60
        b->max = newsize;
Packit fd8b60
    }
Packit fd8b60
    memcpy(b->base + b->cur, d, len);
Packit fd8b60
    b->cur += len;          /* ignore overflow */
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static void dump_profile_to_buffer_cb(const char *str, void *data)
Packit fd8b60
{
Packit fd8b60
    add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
errcode_t profile_write_tree_to_buffer(struct profile_node *root,
Packit fd8b60
                                       char **buf)
Packit fd8b60
{
Packit fd8b60
    struct prof_buf prof_buf = { 0, 0, 0, 0 };
Packit fd8b60
Packit fd8b60
    dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
Packit fd8b60
    if (prof_buf.err) {
Packit fd8b60
        *buf = NULL;
Packit fd8b60
        return ENOMEM;
Packit fd8b60
    }
Packit fd8b60
    add_data_to_buffer(&prof_buf, "", 1); /* append nul */
Packit fd8b60
    if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
Packit fd8b60
        char *newptr = realloc(prof_buf.base, prof_buf.cur);
Packit fd8b60
        if (newptr)
Packit fd8b60
            prof_buf.base = newptr;
Packit fd8b60
    }
Packit fd8b60
    *buf = prof_buf.base;
Packit fd8b60
    return 0;
Packit fd8b60
}