[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 3/4] QemuOpts: parse config from file.
From: |
Anthony Liguori |
Subject: |
Re: [Qemu-devel] [PATCH 3/4] QemuOpts: parse config from file. |
Date: |
Wed, 14 Oct 2009 13:55:41 -0500 |
User-agent: |
Thunderbird 2.0.0.23 (X11/20090825) |
Gerd Hoffmann wrote:
Add functions to parse QemuOpts from a git-style config file.
Signed-off-by: Gerd Hoffmann <address@hidden>
FWIW, here's my parser.
Regards,
Anthony Liguori
commit 8b65f2a706e15efd40a308fd66bcf613f6569962
Author: Anthony Liguori <address@hidden>
Date: Mon Oct 12 09:25:21 2009 -0500
Config parser
diff --git a/Makefile b/Makefile
index 8d78dc1..0a8dadf 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/config-parser.c b/config-parser.c
new file mode 100644
index 0000000..aa8c08a
--- /dev/null
+++ b/config-parser.c
@@ -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;
+}
diff --git a/config-parser.h b/config-parser.h
new file mode 100644
index 0000000..cb113d4
--- /dev/null
+++ b/config-parser.h
@@ -0,0 +1,8 @@
+#ifndef CONFIG_PARSER_H
+#define CONFIG_PARSER_H
+
+#include "qlist.h"
+
+QList *parse_config(const char *input);
+
+#endif
diff --git a/qemu-config.c b/qemu-config.c
index bafaea2..6960874 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -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];
diff --git a/qemu-config.h b/qemu-config.h
index cdad5ac..bb3693a 100644
--- a/qemu-config.h
+++ b/qemu-config.h
@@ -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 */
diff --git a/qemu-options.hx b/qemu-options.hx
index 3dd76b3..074956b 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -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
address@hidden -config file
+Load configuration file.
+ETEXI
+
STEXI
@end table
ETEXI
diff --git a/qobject.h b/qobject.h
index 4cc9287..76f669f 100644
--- a/qobject.h
+++ b/qobject.h
@@ -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) \
diff --git a/qstring.c b/qstring.c
index 6d411da..8b6e9ba 100644
--- a/qstring.c
+++ b/qstring.c
@@ -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)
diff --git a/qstring.h b/qstring.h
index e012cb7..ca7d87d 100644
--- a/qstring.h
+++ b/qstring.h
@@ -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);
diff --git a/vl.c b/vl.c
index 374f85b..87afd75 100644
--- a/vl.c
+++ b/vl.c
@@ -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) {
- [Qemu-devel] [PATCH 0/4] QemuOpts: config file support., Gerd Hoffmann, 2009/10/14
- [Qemu-devel] [PATCH 1/4] QemuOpts: add find_list(), Gerd Hoffmann, 2009/10/14
- [Qemu-devel] [PATCH 4/4] QemuOpts: command line switches for the config file., Gerd Hoffmann, 2009/10/14
- [Qemu-devel] [PATCH 3/4] QemuOpts: parse config from file., Gerd Hoffmann, 2009/10/14
- Re: [Qemu-devel] [PATCH 3/4] QemuOpts: parse config from file.,
Anthony Liguori <=
- [Qemu-devel] [PATCH 2/4] QemuOpts: dump config., Gerd Hoffmann, 2009/10/14
- Re: [Qemu-devel] [PATCH 0/4] QemuOpts: config file support., Anthony Liguori, 2009/10/14
- Re: [Qemu-devel] [PATCH 0/4] QemuOpts: config file support., Anthony Liguori, 2009/10/14