|
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 |
}
|