All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anthony Liguori <anthony@codemonkey.ws>
To: Gerd Hoffmann <kraxel@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH 3/4] QemuOpts: parse config from file.
Date: Wed, 14 Oct 2009 13:55:41 -0500	[thread overview]
Message-ID: <4AD61EAD.4040402@codemonkey.ws> (raw)
In-Reply-To: <1255509568-10635-4-git-send-email-kraxel@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 195 bytes --]

Gerd Hoffmann wrote:
> Add functions to parse QemuOpts from a git-style config file.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>   

FWIW, here's my parser.

Regards,

Anthony Liguori

[-- Attachment #2: config-parser.patch --]
[-- Type: text/x-patch, Size: 15181 bytes --]

commit 8b65f2a706e15efd40a308fd66bcf613f6569962
Author: Anthony Liguori <aliguori@us.ibm.com>
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, &section, 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, &section, 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
+@item -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) {

  reply	other threads:[~2009-10-14 18:55 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-14  8:39 [Qemu-devel] [PATCH 0/4] QemuOpts: config file support Gerd Hoffmann
2009-10-14  8:39 ` [Qemu-devel] [PATCH 1/4] QemuOpts: add find_list() Gerd Hoffmann
2009-10-14  8:39 ` [Qemu-devel] [PATCH 2/4] QemuOpts: dump config Gerd Hoffmann
2009-10-14 18:56   ` Anthony Liguori
2009-10-14  8:39 ` [Qemu-devel] [PATCH 3/4] QemuOpts: parse config from file Gerd Hoffmann
2009-10-14 18:55   ` Anthony Liguori [this message]
2009-10-14  8:39 ` [Qemu-devel] [PATCH 4/4] QemuOpts: command line switches for the config file Gerd Hoffmann
2009-10-16 18:39   ` Nathan Baum
2009-10-16 19:29     ` Gerd Hoffmann
2009-10-16 18:48   ` Nathan Baum
2009-10-16 19:33     ` Gerd Hoffmann
2009-10-14 18:53 ` [Qemu-devel] [PATCH 0/4] QemuOpts: config file support Anthony Liguori
2009-10-14 19:01 ` Anthony Liguori
2009-10-16 10:16   ` Gerd Hoffmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4AD61EAD.4040402@codemonkey.ws \
    --to=anthony@codemonkey.ws \
    --cc=kraxel@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.