You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
4.3 KiB
214 lines
4.3 KiB
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include "parser.h"
|
|
#include "util.h"
|
|
#include "config.h"
|
|
|
|
#define VAR_MAX 256
|
|
|
|
void config_var(const char *, const char *);
|
|
|
|
struct config_file {
|
|
FILE *p;
|
|
char *name;
|
|
int line;
|
|
struct config_file *parent;
|
|
int comment;
|
|
};
|
|
|
|
static void parse_config_file(char *, struct config_file *);
|
|
static void parse_error(struct config_file *file, char *msg) {
|
|
fatal("parse error on line %d of %s: %s", file->line, file->name, msg);
|
|
}
|
|
|
|
static int parse_char(struct config_file *file) {
|
|
int c = getc(file->p);
|
|
if (c == '\n')
|
|
(file->line)++;
|
|
return c;
|
|
}
|
|
|
|
static void unparse_char(struct config_file *file, int c) {
|
|
if (c == EOF)
|
|
return;
|
|
ungetc(c, file->p);
|
|
if (c == '\n')
|
|
(file->line)--;
|
|
}
|
|
|
|
static void parse_name(struct config_file *file, char *name, size_t maxlen) {
|
|
int len = 0;
|
|
int c;
|
|
|
|
for (;;) {
|
|
c = parse_char(file);
|
|
|
|
if (c == EOF || c == '\n') {
|
|
unparse_char(file, c);
|
|
break;
|
|
}
|
|
|
|
if (!isalpha(c) && c != '_' && c != '-') {
|
|
unparse_char(file, c);
|
|
break;
|
|
}
|
|
|
|
if (len == maxlen - 1)
|
|
parse_error(file, "max name length exceeded");
|
|
|
|
name[len++] = c;
|
|
}
|
|
|
|
if (len == 0)
|
|
parse_error(file, "expected name");
|
|
|
|
name[len++] = '\0';
|
|
}
|
|
|
|
static void parse_value(struct config_file *file, char *value, size_t maxlen) {
|
|
int len = 0;
|
|
int quote = 0;
|
|
int comment = 0;
|
|
int space = 0;
|
|
int c;
|
|
|
|
for (;;) {
|
|
c = parse_char(file);
|
|
|
|
if (c == EOF || c == '\n')
|
|
break;
|
|
|
|
if (c == '#')
|
|
comment = 1;
|
|
|
|
if ((isspace(c) && !quote) || comment) {
|
|
space = 1;
|
|
continue;
|
|
}
|
|
|
|
if (c == '"') {
|
|
quote = ! quote;
|
|
continue;
|
|
}
|
|
|
|
if (len == maxlen - space - 1)
|
|
parse_error(file, "max value length exceeded");
|
|
|
|
if (space && len) {
|
|
value[len++] = ' ';
|
|
}
|
|
|
|
space = 0;
|
|
value[len++] = c;
|
|
}
|
|
|
|
if (quote)
|
|
parse_error(file, "unbalanced quotes");
|
|
|
|
value[len++] = '\0';
|
|
}
|
|
|
|
static void parse_include(struct config_file *file) {
|
|
char path[PATH_MAX];
|
|
struct config_file *parent = file->parent;
|
|
|
|
parse_value(file, path, sizeof(path));
|
|
|
|
while (parent != NULL) {
|
|
if (!strcmp(file->name, parent->name))
|
|
return;
|
|
parent = parent->parent;
|
|
}
|
|
|
|
parse_config_file(path, file);
|
|
}
|
|
|
|
static void parse_config(struct config_file *file) {
|
|
int c;
|
|
int comment = 0;
|
|
|
|
char var[VAR_MAX];
|
|
char value[VAR_MAX];
|
|
|
|
for (;;) {
|
|
c = parse_char(file);
|
|
|
|
if (c == '\n') {
|
|
comment = 0;
|
|
continue;
|
|
}
|
|
|
|
if (c == EOF)
|
|
return;
|
|
|
|
if (isspace(c) | comment)
|
|
continue;
|
|
|
|
if (c == '#') {
|
|
comment = 1;
|
|
continue;
|
|
}
|
|
|
|
unparse_char(file, c);
|
|
parse_name(file, var, sizeof(var));
|
|
|
|
if (!strcmp(var, "include")) {
|
|
parse_include(file);
|
|
continue;
|
|
}
|
|
|
|
for (;;) {
|
|
c = parse_char(file);
|
|
if (c == EOF || c == '\n')
|
|
parse_error(file, "expected '=' before line end");
|
|
if (c == '=')
|
|
break;
|
|
if (!isspace(c))
|
|
parse_error(file, "expected '='");
|
|
}
|
|
|
|
parse_value(file, value, sizeof(value));
|
|
config_var(var, value);
|
|
}
|
|
}
|
|
|
|
static void parse_config_file(char *name, struct config_file *parent) {
|
|
struct config_file file;
|
|
|
|
file.p = fopen(name, "r");
|
|
file.name = name;
|
|
file.line = 1;
|
|
file.parent = parent;
|
|
|
|
if (!file.p) {
|
|
if (parent)
|
|
parse_error(parent, strerror(errno));
|
|
else
|
|
fatal("failed to open configuration file '%s': %s", name, strerror(errno));
|
|
}
|
|
|
|
parse_config(&file);
|
|
|
|
fclose(file.p);
|
|
}
|
|
|
|
long config_long(char *var, char *val) {
|
|
char *endptr;
|
|
long longval;
|
|
|
|
longval = strtol(val, &endptr, 0);
|
|
|
|
if (*val == '\0' || *endptr != '\0')
|
|
fatal("expected integer value for %s", var);
|
|
|
|
return longval;
|
|
}
|
|
|
|
void config_parse(char *filename) {
|
|
parse_config_file(filename, NULL);
|
|
}
|
|
|