--- /dev/null
+/**
+ * conf.c: parser for a subset of the Python encoded Xen configuration files
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Daniel Veillard <veillard@redhat.com>
+ */
+
+#define _GNU_SOURCE /* want strndup ! */
+#include <string.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "internal.h"
+#include "xml.h"
+#include "conf.h"
+
+/************************************************************************
+ * *
+ * Structures and macros used by the mini parser *
+ * *
+ ************************************************************************/
+
+typedef struct _virConfParserCtxt virConfParserCtxt;
+typedef virConfParserCtxt *virConfParserCtxtPtr;
+
+struct _virConfParserCtxt {
+ const char* filename;
+ const char* base;
+ const char* cur;
+ const char *end;
+ int line;
+
+ virConfPtr conf;
+};
+
+#define CUR (*ctxt->cur)
+#define NEXT if (ctxt->cur < ctxt->end) ctxt->cur++;
+#define IS_EOL(c) (((c) == '\n') || ((c) == '\r'))
+#define IS_BLANK(c) (((c) == ' ') || ((c) == '\n') || ((c) == '\r') || \
+ ((c) == '\t'))
+#define SKIP_BLANKS {while ((ctxt->cur < ctxt->end) && (IS_BLANK(CUR))){\
+ if (CUR == '\n') ctxt->line++; \
+ ctxt->cur++;}}
+#define IS_SPACE(c) (((c) == ' ') || ((c) == '\t'))
+#define SKIP_SPACES {while ((ctxt->cur < ctxt->end) && (IS_SPACE(CUR))) \
+ ctxt->cur++;}
+#define IS_CHAR(c) ((((c) >= 'a') && ((c) <= 'z')) || \
+ (((c) >= 'A') && ((c) <= 'Z')))
+#define IS_DIGIT(c) (((c) >= '0') && ((c) <= '9'))
+
+/************************************************************************
+ * *
+ * Structures used by configuration data *
+ * *
+ ************************************************************************/
+
+typedef struct _virConfEntry virConfEntry;
+typedef virConfEntry *virConfEntryPtr;
+
+struct _virConfEntry {
+ virConfEntryPtr next;
+ char* name;
+ char* comment;
+ virConfValuePtr value;
+};
+
+struct _virConf {
+ const char* filename;
+ virConfEntryPtr entries;
+};
+
+/**
+ * virConfError:
+ * @conf: the configuration if available
+ * @error: the error number
+ * @info: extra information string
+ * @line: line for the error
+ *
+ * Handle an error at the xend daemon interface
+ */
+static void
+virConfError(virConfPtr conf ATTRIBUTE_UNUSED,
+ virErrorNumber error, const char *info, int line)
+{
+ const char *errmsg;
+
+ if (error == VIR_ERR_OK)
+ return;
+
+ errmsg = __virErrorMsg(error, info);
+ __virRaiseError(NULL, NULL, VIR_FROM_CONF, error, VIR_ERR_ERROR,
+ errmsg, info, NULL, line, 0, errmsg, info, line);
+}
+
+
+/************************************************************************
+ * *
+ * Structures allocations and deallocations *
+ * *
+ ************************************************************************/
+static void virConfFreeValue(virConfValuePtr val);
+
+/**
+ * virConfFreeList:
+ * @list: the list to free
+ *
+ * Free a list
+ */
+static void
+virConfFreeList(virConfValuePtr list)
+{
+ virConfValuePtr next;
+
+ while (list != NULL) {
+ next = list->next;
+ list->next = NULL;
+ virConfFreeValue(list);
+ list = next;
+ }
+}
+
+/**
+ * virConfFreeValue:
+ * @val: the value to free
+ *
+ * Free a value
+ */
+static void
+virConfFreeValue(virConfValuePtr val)
+{
+ if (val == NULL)
+ return;
+ if (val->str != NULL)
+ free(val->str);
+ if (val->list != NULL)
+ virConfFreeList(val->list);
+ free(val);
+}
+
+/**
+ * virConfCreate:
+ * @filename: the name to report errors
+ *
+ * Create a configuration internal structure
+ *
+ * Returns a pointer or NULL in case of error.
+ */
+static virConfPtr
+virConfCreate(const char *filename)
+{
+ virConfPtr ret;
+
+ ret = (virConfPtr) malloc(sizeof(virConf));
+ if (ret == NULL) {
+ virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", 0);
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(virConf));
+
+ ret->filename = filename;
+
+ return(ret);
+}
+
+/**
+ * virConfAddEntry:
+ * @conf: the conf structure
+ * @name: name of the entry or NULL for comment
+ * @value: the value if any
+ * @comm: extra comment for that entry if any
+ *
+ * add one entry to the conf, the parameters are included in the conf
+ * if successful and freed on virConfFree()
+ *
+ * Returns a pointer to the entry or NULL in case of failure
+ */
+static virConfEntryPtr
+virConfAddEntry(virConfPtr conf, char *name, virConfValuePtr value, char *comm)
+{
+ virConfEntryPtr ret, prev;
+
+ if (conf == NULL)
+ return(NULL);
+ if ((comm == NULL) && (name == NULL))
+ return(NULL);
+
+ ret = (virConfEntryPtr) malloc(sizeof(virConfEntry));
+ if (ret == NULL) {
+ virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", 0);
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(virConfEntry));
+ ret->name = name;
+ ret->value = value;
+ ret->comment = comm;
+
+ if (conf->entries == NULL) {
+ conf->entries = ret;
+ } else {
+ prev = conf->entries;
+ while (prev->next != NULL)
+ prev = prev->next;
+ prev->next = ret;
+ }
+ return(ret);
+}
+
+/************************************************************************
+ * *
+ * Serialization *
+ * *
+ ************************************************************************/
+
+/**
+ * virConfSaveValue:
+ * @buf: output buffer
+ * @val: a value
+ *
+ * Serialize the value to the buffer
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+static int
+virConfSaveValue(virBufferPtr buf, virConfValuePtr val)
+{
+ if (val == NULL)
+ return(-1);
+ switch (val->type) {
+ case VIR_CONF_NONE:
+ return(-1);
+ case VIR_CONF_LONG:
+ virBufferVSprintf(buf, "%ld", val->l);
+ break;
+ case VIR_CONF_STRING:
+ if (strchr(val->str, '\n') != NULL) {
+ virBufferVSprintf(buf, "\"\"\"%s\"\"\"", val->str);
+ } else if (strchr(val->str, '"') == NULL) {
+ virBufferVSprintf(buf, "\"%s\"", val->str);
+ } else if (strchr(val->str, '\'') == NULL) {
+ virBufferVSprintf(buf, "'%s'", val->str);
+ } else {
+ virBufferVSprintf(buf, "\"\"\"%s\"\"\"", val->str);
+ }
+ break;
+ case VIR_CONF_LIST: {
+ virConfValuePtr cur;
+
+ cur = val->list;
+ virBufferAdd(buf, "[ ", 2);
+ if (cur != NULL) {
+ virConfSaveValue(buf, cur);
+ cur = cur->next;
+ while (cur != NULL) {
+ virBufferAdd(buf, ", ", 2);
+ virConfSaveValue(buf, cur);
+ cur = cur->next;
+ }
+ }
+ virBufferAdd(buf, " ]", 2);
+ break;
+ }
+ default:
+ return(-1);
+ }
+ return(0);
+}
+
+/**
+ * virConfSaveEntry:
+ * @buf: output buffer
+ * @cur: a conf entry
+ *
+ * Serialize the entry to the buffer
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+static int
+virConfSaveEntry(virBufferPtr buf, virConfEntryPtr cur)
+{
+ if (cur->name != NULL) {
+ virBufferAdd(buf, cur->name, -1);
+ virBufferAdd(buf, " = ", 3);
+ virConfSaveValue(buf, cur->value);
+ if (cur->comment != NULL) {
+ virBufferAdd(buf, " #", 2);
+ virBufferAdd(buf, cur->comment, -1);
+ }
+ } else if (cur->comment != NULL) {
+ virBufferAdd(buf, "#", 1);
+ virBufferAdd(buf, cur->comment, -1);
+ }
+ virBufferAdd(buf, "\n", 1);
+ return(0);
+}
+
+/************************************************************************
+ * *
+ * The parser core *
+ * *
+ ************************************************************************/
+
+/**
+ * virConfParseLong:
+ * @ctxt: the parsing context
+ * @val: the result
+ *
+ * Parse one long int value
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+static int
+virConfParseLong(virConfParserCtxtPtr ctxt, long *val)
+{
+ long l = 0;
+ int neg = 0;
+
+ if (CUR == '-') {
+ neg = 1;
+ NEXT;
+ } else if (CUR == '+') {
+ NEXT;
+ }
+ if ((ctxt->cur >= ctxt->end) || (!IS_DIGIT(CUR))) {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated number",
+ ctxt->line);
+ return(-1);
+ }
+ while ((ctxt->cur < ctxt->end) && (IS_DIGIT(CUR))) {
+ l = l * 10 + (CUR - '0');
+ NEXT;
+ }
+ *val = l;
+ return(0);
+}
+
+/**
+ * virConfParseString:
+ * @ctxt: the parsing context
+ *
+ * Parse one string
+ *
+ * Returns a pointer to the string or NULL in case of error
+ */
+static char *
+virConfParseString(virConfParserCtxtPtr ctxt)
+{
+ const char *base;
+ char *ret = NULL;
+
+ if (CUR == '\'') {
+ NEXT;
+ base = ctxt->cur;
+ while ((ctxt->cur < ctxt->end) && (CUR != '\'') && (!IS_EOL(CUR)))
+ NEXT;
+ if (CUR != '\'') {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated string",
+ ctxt->line);
+ return(NULL);
+ }
+ ret = strndup(base, ctxt->cur - base);
+ NEXT;
+ } else if ((ctxt->cur + 6 < ctxt->end) && (ctxt->cur[0] == '"') &&
+ (ctxt->cur[1] == '"') && (ctxt->cur[2] == '"')) {
+ ctxt->cur += 3;
+ base = ctxt->cur;
+ while ((ctxt->cur + 2 < ctxt->end) && (ctxt->cur[0] == '"') &&
+ (ctxt->cur[1] == '"') && (ctxt->cur[2] == '"')) {
+ if (CUR == '\n') ctxt->line++;
+ NEXT;
+ }
+ if ((ctxt->cur[0] != '"') || (ctxt->cur[1] != '"') ||
+ (ctxt->cur[2] != '"')) {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated string",
+ ctxt->line);
+ return(NULL);
+ }
+ ret = strndup(base, ctxt->cur - base);
+ ctxt->cur += 3;
+ } else if (CUR == '"') {
+ NEXT;
+ base = ctxt->cur;
+ while ((ctxt->cur < ctxt->end) && (CUR != '"') && (!IS_EOL(CUR)))
+ NEXT;
+ if (CUR != '"') {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated string",
+ ctxt->line);
+ return(NULL);
+ }
+ ret = strndup(base, ctxt->cur - base);
+ NEXT;
+ }
+ return(ret);
+}
+
+/**
+ * virConfParseValue:
+ * @ctxt: the parsing context
+ *
+ * Parse one value
+ *
+ * Returns a pointer to the value or NULL in case of error
+ */
+static virConfValuePtr
+virConfParseValue(virConfParserCtxtPtr ctxt)
+{
+ virConfValuePtr ret, lst = NULL, tmp, prev;
+ virConfType type = VIR_CONF_NONE;
+ char *str = NULL;
+ long l = 0;
+
+ SKIP_SPACES;
+ if (ctxt->cur >= ctxt->end) {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a value",
+ ctxt->line);
+ return(NULL);
+ }
+ if ((CUR == '"') || (CUR == '\'')) {
+ type = VIR_CONF_STRING;
+ str = virConfParseString(ctxt);
+ if (str == NULL)
+ return(NULL);
+ } else if (CUR == '[') {
+ type = VIR_CONF_LIST;
+ NEXT;
+ SKIP_BLANKS;
+ if ((ctxt->cur < ctxt->end) && (CUR != ']')) {
+ lst = virConfParseValue(ctxt);
+ SKIP_BLANKS;
+ }
+ while ((ctxt->cur < ctxt->end) && (CUR != ']')) {
+ if (CUR != ',') {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX,
+ "expecting a separator in list", ctxt->line);
+ virConfFreeList(lst);
+ return(NULL);
+ }
+ NEXT;
+ SKIP_BLANKS;
+ tmp = virConfParseValue(ctxt);
+ if (tmp == NULL) {
+ virConfFreeList(lst);
+ return(NULL);
+ }
+ prev = lst;
+ while (prev->next != NULL) prev = prev->next;
+ prev->next = tmp;
+ SKIP_BLANKS;
+ }
+ if (CUR == ']') {
+ NEXT;
+ } else {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX,
+ "list is not closed with ] ", ctxt->line);
+ virConfFreeList(lst);
+ return(NULL);
+ }
+ } else if (IS_DIGIT(CUR) || (CUR == '-') || (CUR == '+')) {
+ if (virConfParseLong(ctxt, &l) < 0) {
+ return(NULL);
+ }
+ type = VIR_CONF_LONG;
+ } else {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a value",
+ ctxt->line);
+ return(NULL);
+ }
+ ret = (virConfValuePtr) malloc(sizeof(virConfValue));
+ if (ret == NULL) {
+ virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", 0);
+ if (str != NULL)
+ free(str);
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(virConfValue));
+ ret->type = type;
+ ret->l = l;
+ ret->str = str;
+ ret->list = lst;
+
+ return(ret);
+}
+
+/**
+ * virConfParseName:
+ * @ctxt: the parsing context
+ *
+ * Parse one name
+ *
+ * Returns a copy of the new string, NULL in case of error
+ */
+static char *
+virConfParseName(virConfParserCtxtPtr ctxt)
+{
+ const char *base;
+ char *ret;
+
+ SKIP_SPACES;
+ base = ctxt->cur;
+ /* TODO: probably need encoding support and UTF-8 parsing ! */
+ if (!IS_CHAR(CUR)) {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a name", ctxt->line);
+ return(NULL);
+ }
+ while ((ctxt->cur < ctxt->end) && ((IS_CHAR(CUR)) || (IS_DIGIT(CUR))))
+ NEXT;
+ ret = strndup(base, ctxt->cur - base);
+ if (ret == NULL) {
+ virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration",
+ ctxt->line);
+ return(NULL);
+ }
+ return(ret);
+}
+
+/**
+ * virConfParseComment:
+ * @ctxt: the parsing context
+ *
+ * Parse one standalone comment in the configuration file
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+static int
+virConfParseComment(virConfParserCtxtPtr ctxt)
+{
+ const char *base;
+ char *comm;
+
+ if (CUR != '#')
+ return(-1);
+ NEXT;
+ base = ctxt->cur;
+ while ((ctxt->cur < ctxt->end) && (!IS_EOL(CUR))) NEXT;
+ comm = strndup(base, ctxt->cur - base);
+ if (comm == NULL) {
+ virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration",
+ ctxt->line);
+ return(-1);
+ }
+ virConfAddEntry(ctxt->conf, NULL, NULL, comm);
+ return(0);
+}
+
+/**
+ * virConfParseSeparator:
+ * @ctxt: the parsing context
+ *
+ * Parse one separator between statement if not at the end.
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+static int
+virConfParseSeparator(virConfParserCtxtPtr ctxt)
+{
+ SKIP_SPACES;
+ if (ctxt->cur >= ctxt->end)
+ return(0);
+ if (IS_EOL(CUR)) {
+ SKIP_BLANKS
+ } else if (CUR == ';') {
+ NEXT;
+ SKIP_BLANKS;
+ } else {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a separator",
+ ctxt->line);
+ return(-1);
+ }
+ return(0);
+}
+
+/**
+ * virConfParseStatement:
+ * @ctxt: the parsing context
+ *
+ * Parse one statement in the conf file
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+static int
+virConfParseStatement(virConfParserCtxtPtr ctxt)
+{
+ const char *base;
+ char *name;
+ virConfValuePtr value;
+ char *comm = NULL;
+
+ SKIP_BLANKS;
+ if (CUR == '#') {
+ return(virConfParseComment(ctxt));
+ }
+ name = virConfParseName(ctxt);
+ if (name == NULL)
+ return(-1);
+ SKIP_SPACES;
+ if (CUR != '=') {
+ virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting an assignment",
+ ctxt->line);
+ return(-1);
+ }
+ NEXT;
+ SKIP_SPACES;
+ value = virConfParseValue(ctxt);
+ if (value == NULL) {
+ free(name);
+ return(-1);
+ }
+ SKIP_SPACES;
+ if (CUR == '#') {
+ NEXT;
+ base = ctxt->cur;
+ while ((ctxt->cur < ctxt->end) && (!IS_EOL(CUR))) NEXT;
+ comm = strndup(base, ctxt->cur - base);
+ if (comm == NULL) {
+ virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration",
+ ctxt->line);
+ free(name);
+ virConfFreeValue(value);
+ return(-1);
+ }
+ }
+ if (virConfAddEntry(ctxt->conf, name, value, comm) == NULL) {
+ free(name);
+ virConfFreeValue(value);
+ if (comm != NULL)
+ free(comm);
+ return(-1);
+ }
+ return(0);
+}
+
+/**
+ * virConfParse:
+ * @filename: the name to report errors
+ * @content: the configuration content in memory
+ * @len: the length in bytes
+ *
+ * Parse the subset of the Python language needed to handle simple
+ * Xen configuration files.
+ *
+ * Returns an handle to lookup settings or NULL if it failed to
+ * read or parse the file, use virConfFree() to free the data.
+ */
+static virConfPtr
+virConfParse(const char *filename, const char *content, int len) {
+ virConfParserCtxt ctxt;
+
+ ctxt.filename = filename;
+ ctxt.base = ctxt.cur = content;
+ ctxt.end = content + len - 1;
+ ctxt.line = 1;
+
+ ctxt.conf = virConfCreate(filename);
+ if (ctxt.conf == NULL)
+ return(NULL);
+
+ while (ctxt.cur < ctxt.end) {
+ if (virConfParseStatement(&ctxt) < 0)
+ goto error;
+ if (virConfParseSeparator(&ctxt) < 0)
+ goto error;
+ }
+
+ return(ctxt.conf);
+
+error:
+ virConfFree(ctxt.conf);
+ return(NULL);
+}
+
+/************************************************************************
+ * *
+ * The module entry points *
+ * *
+ ************************************************************************/
+
+/**
+ * virConfReadFile:
+ * @filename: the path to the configuration file.
+ *
+ * Reads a configuration file.
+ *
+ * Returns an handle to lookup settings or NULL if it failed to
+ * read or parse the file, use virConfFree() to free the data.
+ */
+virConfPtr
+virConfReadFile(const char *filename)
+{
+ char content[4096];
+ int fd;
+ int len;
+
+ if (filename == NULL) {
+ virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0);
+ return(NULL);
+ }
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ virConfError(NULL, VIR_ERR_OPEN_FAILED, filename, 0);
+ return(NULL);
+ }
+ len = read(fd, content, sizeof(content));
+ close(fd);
+ if (len <= 0) {
+ virConfError(NULL, VIR_ERR_READ_FAILED, filename, 0);
+ return(NULL);
+ }
+ return(virConfParse(filename, content, len));
+}
+
+/**
+ * virConfReadMem:
+ * @memory: pointer to the content of the configuration file
+ * @len: lenght in byte
+ *
+ * Reads a configuration file loaded in memory. The string can be
+ * zero terminated in which case @len can be 0
+ *
+ * Returns an handle to lookup settings or NULL if it failed to
+ * parse the content, use virConfFree() to free the data.
+ */
+virConfPtr
+virConfReadMem(const char *memory, int len)
+{
+ if ((memory == NULL) || (len < 0)) {
+ virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0);
+ return(NULL);
+ }
+ if (len == 0)
+ len = strlen(memory);
+
+ return(virConfParse("memory conf", memory, len));
+}
+
+/**
+ * virConfFree:
+ * @conf: a configuration file handle
+ *
+ * Frees all data associated to the handle
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+int
+virConfFree(virConfPtr conf)
+{
+ if (conf == NULL) {
+ virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0);
+ return(-1);
+ }
+ free(conf);
+ return(0);
+}
+
+/**
+ * virConfGetValue:
+ * @conf: a configuration file handle
+ * @entry: the name of the entry
+ *
+ * Lookup the value associated to this entry in the configuration file
+ *
+ * Returns a pointer to the value or NULL if the lookup failed, the data
+ * associated will be freed when virConfFree() is called
+ */
+virConfValuePtr
+virConfGetValue(virConfPtr conf, const char *setting)
+{
+}
+
+/**
+ * virConfWriteFile:
+ * @filename: the path to the configuration file.
+ * @conf: the conf
+ *
+ * Writes a configuration file back to a file.
+ *
+ * Returns the number of bytes written or -1 in case of error.
+ */
+int
+virConfWriteFile(const char *filename, virConfPtr conf)
+{
+ virBufferPtr buf;
+ virConfEntryPtr cur;
+ int ret;
+ int fd;
+
+ if (conf == NULL)
+ return(-1);
+
+ buf = virBufferNew(500);
+ if (buf == NULL)
+ return(-1);
+
+ cur = conf->entries;
+ while (cur != NULL) {
+ virConfSaveEntry(buf, cur);
+ cur = cur->next;
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR );
+ if (fd < 0) {
+ virConfError(NULL, VIR_ERR_WRITE_FAILED, "failed to open file", 0);
+ ret = -1;
+ goto error;
+ }
+
+ ret = write(fd, buf->content, buf->use);
+ close(fd);
+ if (ret != (int) buf->use) {
+ virConfError(NULL, VIR_ERR_WRITE_FAILED, "failed to save content", 0);
+ ret = -1;
+ goto error;
+ }
+error:
+ virBufferFree(buf);
+ return(ret);
+}
+
+/**
+ * virConfWriteMem:
+ * @memory: pointer to the memory to store the config file
+ * @len: pointer to the lenght in byte of the store, on output the size
+ * @conf: the conf
+ *
+ * Writes a configuration file back to a memory area. @len is an IN/OUT
+ * parameter, it indicates the size available in bytes, and on output the
+ * size required for the configuration file (even if the call fails due to
+ * insufficient space).
+ *
+ * Returns the number of bytes written or -1 in case of error.
+ */
+int
+virConfWriteMem(char *memory, int *len, virConfPtr conf)
+{
+ virBufferPtr buf;
+ virConfEntryPtr cur;
+ int ret;
+
+ if ((memory == NULL) || (len == NULL) || (*len <= 0) || (conf == NULL))
+ return(-1);
+
+ buf = virBufferNew(500);
+ if (buf == NULL)
+ return(-1);
+
+ cur = conf->entries;
+ while (cur != NULL) {
+ virConfSaveEntry(buf, cur);
+ cur = cur->next;
+ }
+
+ if ((int) buf->use >= *len) {
+ *len = buf->use;
+ ret = -1;
+ goto error;
+ }
+ memcpy(memory, buf->content, buf->use);
+ ret = buf->use;
+
+error:
+ virBufferFree(buf);
+ return(ret);
+}