commit 8b65f2a706e15efd40a308fd66bcf613f6569962
Author: Anthony Liguori <aliguori@us.ibm.com>
Date: Mon Oct 12 09:25:21 2009 -0500
Config parser
@@ -125,6 +125,7 @@ obj-y += qemu-char.o aio.o net-checksum.o savevm.o
obj-y += msmouse.o ps2.o
obj-y += qdev.o qdev-properties.o
obj-y += qint.o qstring.o qdict.o qlist.o qemu-config.o
+obj-y += config-parser.o
obj-$(CONFIG_BRLAPI) += baum.o
obj-$(CONFIG_WIN32) += tap-win32.o
@@ -212,6 +213,7 @@ check-qint: check-qint.o qint.o qemu-malloc.o
check-qstring: check-qstring.o qstring.o qemu-malloc.o
check-qdict: check-qdict.o qdict.o qint.o qstring.o qemu-malloc.o
check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o
+check-config-parser: check-config-parser.o config-parser.o qlist.o qstring.o qemu-malloc.o
clean:
# avoid old build problems by removing potentially incorrect old files
new file mode 100644
@@ -0,0 +1,384 @@
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "qlist.h"
+#include "qstring.h"
+
+#include "config-parser.h"
+
+typedef struct ConfigParser
+{
+} ConfigParser;
+
+static void parser_error(ConfigParser *parser, const char *ptr, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "parse error: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static int parse_is_whitespace(char ch)
+{
+ if (ch == ' ' || ch == '\t' || ch == '\r') {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int parse_is_alpha(char ch)
+{
+ if ((ch >= 'A' && ch <= 'Z') ||
+ (ch >= 'a' && ch <= 'z') ||
+ (ch == '-') ||
+ (ch == '_')) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int parse_is_digit(char ch)
+{
+ if (ch >= '0' && ch <= '9') {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int parse_is_hexdigit(char ch)
+{
+ if (parse_is_digit(ch) ||
+ (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F')) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static size_t parse_whitespace(ConfigParser *parser, const char *input)
+{
+ const char *ptr = input;
+
+ while (parse_is_whitespace(*ptr)) {
+ ptr++;
+ }
+
+ return (ptr - input);
+}
+
+static size_t parse_skip(ConfigParser *parser, const char *input)
+{
+ const char *ptr = input;
+
+ while (*ptr == '#' || parse_is_whitespace(*ptr)) {
+ ptr += parse_whitespace(parser, ptr);
+
+ if (*ptr == '#') {
+ ptr++;
+
+ while (*ptr != '\n' && *ptr) {
+ ptr++;
+ }
+ }
+ }
+
+ return (ptr - input);
+}
+
+static size_t parse_symbol(ConfigParser *parser, QString **str, const char *input)
+{
+ const char *ptr = input;
+
+ if (parse_is_alpha(*ptr)) {
+ ptr++;
+
+ while (parse_is_alpha(*ptr) ||
+ (*ptr >= '0' && *ptr <= '9')) {
+ ptr++;
+ }
+ }
+
+ if (ptr != input) {
+ *str = qstring_from_substr(input, (ptr - input));
+ }
+
+ return (ptr - input);
+}
+
+static size_t parse_escaped_string(ConfigParser *parser, QString **str, const char *input)
+{
+ const char *ptr = input;
+
+ if (*ptr != '\"') {
+ goto err;
+ }
+
+ ptr++;
+
+ while (*ptr && *ptr != '\"' && *ptr != '\n') {
+ if (*ptr == '\\') {
+ ptr++;
+
+ if (*ptr == 'x') { /* hex */
+ ptr++;
+
+ if (parse_is_hexdigit(*ptr)) {
+ ptr++;
+ }
+ if (parse_is_hexdigit(*ptr)) {
+ ptr++;
+ }
+ } else if (parse_is_digit(*ptr)) { /* decimal/octal */
+ ptr++;
+
+ if (parse_is_digit(*ptr)) {
+ ptr++;
+ }
+ if (parse_is_digit(*ptr)) {
+ ptr++;
+ }
+ } else if (*ptr && *ptr != '\n') {
+ ptr++;
+ }
+ } else {
+ ptr++;
+ }
+ }
+
+ if (*ptr != '\"') {
+ parser_error(parser, ptr, "Unterminated string literal");
+ goto err;
+ }
+ ptr++;
+
+ /* FIXME unquote */
+ *str = qstring_from_substr(input + 1, (ptr - input) - 2);
+ return (ptr - input);
+
+err:
+ return 0;
+}
+
+static size_t parse_string(ConfigParser *parser, QString **str, const char *input)
+{
+ const char *ptr = input;
+
+ while (*ptr && *ptr != '\n' && *ptr != '#') {
+ size_t ret;
+
+ if (parse_is_whitespace(*ptr)) {
+ ret = parse_whitespace(parser, ptr);
+
+ if (ptr[ret] == 0 || ptr[ret] == '\n' || ptr[ret] == '#') {
+ break;
+ }
+
+ ptr += ret;
+ } else {
+ ptr++;
+ }
+ }
+
+ if (ptr != input) {
+ *str = qstring_from_substr(input, (ptr - input));
+ }
+
+ return (ptr - input);
+}
+
+static size_t parse_empty_lines(ConfigParser *parser, const char *input)
+{
+ const char *ptr = input;
+
+ do {
+ if (*ptr == '\n') {
+ ptr++;
+ }
+
+ ptr += parse_skip(parser, ptr);
+ } while (*ptr == '\n');
+
+ return (ptr - input);
+}
+
+static size_t parse_item(ConfigParser *parser, QList **item, const char *input)
+{
+ QString *key = NULL;
+ QString *value = NULL;
+ const char *ptr = input;
+ size_t ret;
+
+ ptr += parse_empty_lines(parser, input);
+
+ ptr += parse_skip(parser, ptr);
+
+ ret = parse_symbol(parser, &key, ptr);
+ if (!ret) {
+ goto err;
+ }
+
+ ptr += ret;
+
+ ptr += parse_skip(parser, ptr);
+ if (*ptr != '=') {
+ parser_error(parser, ptr, "For key '%s', expected '=', got '%c'",
+ input, *ptr);
+ goto err;
+ }
+ ptr++;
+
+ ptr += parse_skip(parser, ptr);
+
+ ret = parse_escaped_string(parser, &value, ptr);
+ if (ret == 0) {
+ ret = parse_string(parser, &value, ptr);
+ }
+
+ if (!ret) {
+ parser_error(parser, ptr, "No value for key `%s'", qstring_get_str(key));
+ goto err;
+ }
+ ptr += ret;
+
+ ptr += parse_skip(parser, ptr);
+ if (*ptr != '\n' && *ptr != 0) {
+ parser_error(parser, ptr, "Unexpected input at end of line");
+ goto err;
+ }
+
+ if (*ptr == '\n') {
+ ptr++;
+ }
+
+ *item = qlist_new();
+
+ qlist_append(*item, key);
+ qlist_append(*item, value);
+
+ return (ptr - input);
+
+err:
+ QDECREF(key);
+ QDECREF(value);
+ return 0;
+}
+
+static size_t parse_section_name(ConfigParser *parser, QString **name, const char *input)
+{
+ const char *ptr = input;
+ size_t ret = 0;
+
+ ptr += parse_empty_lines(parser, input);
+
+ ptr += parse_skip(parser, ptr);
+ if (*ptr != '[') {
+ goto err;
+ }
+ ptr++;
+
+ ptr += parse_skip(parser, ptr);
+
+ ret = parse_symbol(parser, name, ptr);
+ if (!ret) {
+ goto err;
+ }
+ ptr += ret;
+
+ ptr += parse_skip(parser, ptr);
+ if (*ptr != ']') {
+ parser_error(parser, ptr, "Expected ']', got '%c'", *ptr);
+ goto err;
+ }
+ ptr++;
+
+ ptr += parse_skip(parser, ptr);
+ if (*ptr != '\n' && *ptr != 0) {
+ parser_error(parser, ptr, "Unexpected input at end of line");
+ goto err;
+ }
+
+ ptr++;
+
+ return (ptr - input);
+
+err:
+ QDECREF(*name);
+ *name = NULL;
+ return 0;
+}
+
+static size_t parse_items(ConfigParser *parser, QList **section, const char *input)
+{
+ const char *ptr = input;
+ size_t ret;
+
+ do {
+ QList *item = NULL;
+
+ ret = parse_item(parser, &item, ptr);
+ if (ret) {
+ qlist_append(*section, item);
+ ptr += ret;
+ }
+ } while (ret);
+
+ return (ptr - input);
+}
+
+QList *parse_config(const char *input)
+{
+ ConfigParser parser;
+ QList *sections = NULL;
+ QList *section = NULL;
+ const char *ptr = input;
+ size_t ret;
+
+ memset(&parser, 0, sizeof(parser));
+
+ sections = qlist_new();
+
+ section = qlist_new();
+ qlist_append(section, qstring_from_str("DEFAULT"));
+
+ ret = parse_items(&parser, §ion, ptr);
+ if (ret) {
+ qlist_append(sections, section);
+ ptr += ret;
+ } else {
+ QDECREF(section);
+ }
+
+ do {
+ QString *name = NULL;
+
+ ret = parse_section_name(&parser, &name, ptr);
+ if (ret) {
+ ptr += ret;
+
+ section = qlist_new();
+ qlist_append(section, name);
+
+ ptr += parse_items(&parser, §ion, ptr);
+
+ qlist_append(sections, section);
+ }
+ } while (ret);
+
+ ptr += parse_empty_lines(&parser, ptr);
+
+ ptr += parse_skip(&parser, ptr);
+
+ if (*ptr != 0) {
+ parser_error(&parser, ptr, "Garbage at end of input");
+ return NULL;
+ }
+
+ return sections;
+}
new file mode 100644
@@ -0,0 +1,8 @@
+#ifndef CONFIG_PARSER_H
+#define CONFIG_PARSER_H
+
+#include "qlist.h"
+
+QList *parse_config(const char *input);
+
+#endif
@@ -2,6 +2,8 @@
#include "qemu-option.h"
#include "qemu-config.h"
#include "sysemu.h"
+#include "qlist.h"
+#include "qstring.h"
QemuOptsList qemu_drive_opts = {
.name = "drive",
@@ -193,6 +195,51 @@ static QemuOptsList *lists[] = {
NULL,
};
+typedef struct QOptionSectionState
+{
+ int index;
+} QOptionSectionState;
+
+static void qemu_set_qoption_items(QObject *obj, void *opaque)
+{
+ QOptionSectionState *s = opaque;
+
+ if (s->index == 0) {
+ QString *name;
+ int i;
+
+ name = qobject_to_qstring(obj);
+
+ for (i = 0; lists[i] != NULL; i++) {
+ if (strcmp(lists[i]->name, qstring_get_str(name)) == 0) {
+ break;
+ }
+ }
+
+ if (lists[i] == NULL) {
+ qemu_error("there is no option group '%s'\n",
+ qstring_get_str(name));
+ return;
+ }
+
+ opts = qemu_opts_find(lists[i], id);
+ }
+}
+
+static void qemu_set_qoption_section(QObject *obj, void *opaque)
+{
+ QOptionSectionState s = {};
+
+ qlist_iter(qobject_to_qlist(obj), qemu_set_qoption_items, &s);
+}
+
+int qemu_set_qoption(QList *options)
+{
+ qlist_iter(options, qemu_set_qoption_section, NULL);
+
+ return 0;
+}
+
int qemu_set_option(const char *str)
{
char group[64], id[64], arg[64];
@@ -1,6 +1,8 @@
#ifndef QEMU_CONFIG_H
#define QEMU_CONFIG_H
+#include "qlist.h"
+
extern QemuOptsList qemu_drive_opts;
extern QemuOptsList qemu_chardev_opts;
extern QemuOptsList qemu_device_opts;
@@ -8,5 +10,6 @@ extern QemuOptsList qemu_net_opts;
extern QemuOptsList qemu_rtc_opts;
int qemu_set_option(const char *str);
+int qemu_set_qoption(QList *options);
#endif /* QEMU_CONFIG_H */
@@ -1665,6 +1665,13 @@ Immediately before starting guest execution, drop root privileges, switching
to the specified user.
ETEXI
+DEF("config", HAS_ARG, QEMU_OPTION_config, \
+ "-config file Load configuratino file.")
+STEXI
+@item -config file
+Load configuration file.
+ETEXI
+
STEXI
@end table
ETEXI
@@ -60,7 +60,7 @@ typedef struct QObject {
QObject base
/* Get the 'base' part of an object */
-#define QOBJECT(obj) (&obj->base)
+#define QOBJECT(obj) (&(obj)->base)
/* High-level interface for qobject_incref() */
#define QINCREF(obj) \
@@ -37,6 +37,23 @@ QString *qstring_from_str(const char *str)
}
/**
+ * qstring_from_str(): Create a new QString from a C string up to a certain length
+ *
+ * Return strong reference.
+ */
+QString *qstring_from_substr(const char *str, size_t len)
+{
+ QString *qstring;
+
+ qstring = qemu_malloc(sizeof(*qstring));
+ qstring->string = qemu_mallocz(len + 1);
+ strncpy(qstring->string, str, len);
+ QOBJECT_INIT(qstring, &qstring_type);
+
+ return qstring;
+}
+
+/**
* qobject_to_qstring(): Convert a QObject to a QString
*/
QString *qobject_to_qstring(const QObject *obj)
@@ -9,6 +9,7 @@ typedef struct QString {
} QString;
QString *qstring_from_str(const char *str);
+QString *qstring_from_substr(const char *str, size_t len);
const char *qstring_get_str(const QString *qstring);
QString *qobject_to_qstring(const QObject *obj);
@@ -1,7 +1,7 @@
/*
* QEMU System Emulator
*
- * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -171,6 +171,8 @@ int main(int argc, char **argv)
#include "slirp/libslirp.h"
#include "qemu-queue.h"
+#include "config-parser.h"
+#include "qlist.h"
//#define DEBUG_NET
//#define DEBUG_SLIRP
@@ -4770,6 +4772,7 @@ int main(int argc, char **argv, char **envp)
#endif
CPUState *env;
int show_vnc_port = 0;
+ const char *config_file = NULL;
init_clocks();
@@ -5513,10 +5516,51 @@ int main(int argc, char **argv, char **envp)
xen_mode = XEN_ATTACH;
break;
#endif
+ case QEMU_OPTION_config:
+ config_file = optarg;
+ break;
}
}
}
+ if (config_file) {
+ struct stat stbuf;
+ int fd;
+ char *buffer;
+ size_t offset = 0;
+ QList *config;
+
+ fd = open(config_file, O_RDONLY);
+ if (fd == -1)
+ abort();
+
+ if (fstat(fd, &stbuf) == -1)
+ abort();
+
+ buffer = qemu_malloc(stbuf.st_size + 1);
+ while (offset < stbuf.st_size) {
+ ssize_t len;
+
+ len = read(fd, buffer + offset, stbuf.st_size - offset);
+ if (len < 1)
+ abort();
+
+ offset += len;
+ }
+
+ close(fd);
+
+ buffer[offset] = 0;
+
+ config = parse_config(buffer);
+ if (config == NULL)
+ abort();
+
+ printf("successfully parsed config\n");
+
+ QDECREF(config);
+ }
+
/* If no data_dir is specified then try to find it relative to the
executable path. */
if (!data_dir) {