unsigned int size;
unsigned int use;
unsigned int error; /* errno value, or -1 for usage error */
+ int indent;
char *content;
};
VIR_FREE(buf->content);
buf->size = 0;
buf->use = 0;
+ buf->indent = 0;
buf->error = error;
}
+/**
+ * virBufferAdjustIndent:
+ * @buf: the buffer
+ * @indent: adjustment to make
+ *
+ * Alter the auto-indent value by adding indent (positive to increase,
+ * negative to decrease). Automatic indentation is performed by all
+ * additive functions when the existing buffer is empty or ends with a
+ * newline (however, note that no indentation is added after newlines
+ * embedded in an appended string). If @indent would cause overflow,
+ * the buffer error indicator is set.
+ */
+void
+virBufferAdjustIndent(virBufferPtr buf, int indent)
+{
+ if (!buf || buf->error)
+ return;
+ if (indent > 0 ? INT_MAX - indent < buf->indent
+ : buf->indent < -indent) {
+ virBufferSetError(buf, -1);
+ return;
+ }
+ buf->indent += indent;
+}
+
+/**
+ * virBufferGetIndent:
+ * @buf: the buffer
+ * @dynamic: if false, return set value; if true, return 0 unless next
+ * append would be affected by auto-indent
+ *
+ * Return the current auto-indent value, or -1 if there has been an error.
+ */
+int
+virBufferGetIndent(const virBufferPtr buf, bool dynamic)
+{
+ if (!buf || buf->error)
+ return -1;
+ if (dynamic && buf->use && buf->content[buf->use - 1] != '\n')
+ return 0;
+ return buf->indent;
+}
+
/**
* virBufferGrow:
* @buf: the buffer
/**
* virBufferAdd:
- * @buf: the buffer to add to
- * @str: the string
- * @len: the number of bytes to add
+ * @buf: the buffer to append to
+ * @str: the string
+ * @len: the number of bytes to add, or -1
*
- * Add a string range to an XML buffer. if len == -1, the length of
- * str is recomputed to the full string.
+ * Add a string range to an XML buffer. If @len == -1, the length of
+ * str is recomputed to the full string. Auto indentation may be applied.
*
*/
void
virBufferAdd(virBufferPtr buf, const char *str, int len)
{
unsigned int needSize;
+ int indent;
- if ((str == NULL) || (buf == NULL) || (len == 0))
+ if (!str || !buf || (len == 0 && buf->indent == 0))
return;
if (buf->error)
return;
+ indent = virBufferGetIndent(buf, true);
+
if (len < 0)
len = strlen(str);
- needSize = buf->use + len + 2;
+ needSize = buf->use + indent + len + 2;
if (needSize > buf->size &&
virBufferGrow(buf, needSize - buf->use) < 0)
return;
- memcpy (&buf->content[buf->use], str, len);
- buf->use += len;
+ memset(&buf->content[buf->use], ' ', indent);
+ memcpy(&buf->content[buf->use + indent], str, len);
+ buf->use += indent + len;
buf->content[buf->use] = '\0';
}
* @buf: the buffer to append to
* @c: the character to add
*
- * Add a single character 'c' to a buffer.
+ * Add a single character 'c' to a buffer. Auto indentation may be applied.
*
*/
void
-virBufferAddChar (virBufferPtr buf, char c)
+virBufferAddChar(virBufferPtr buf, char c)
{
- unsigned int needSize;
-
- if (buf == NULL)
- return;
-
- if (buf->error)
- return;
-
- needSize = buf->use + 2;
- if (needSize > buf->size &&
- virBufferGrow (buf, needSize - buf->use) < 0)
- return;
-
- buf->content[buf->use++] = c;
- buf->content[buf->use] = '\0';
+ virBufferAdd(buf, &c, 1);
}
/**
* @format: the format
* @...: the variable list of arguments
*
- * Do a formatted print to an XML buffer.
+ * Do a formatted print to an XML buffer. Auto indentation may be applied.
*/
void
virBufferAsprintf(virBufferPtr buf, const char *format, ...)
/**
* virBufferVasprintf:
- * @buf: the buffer to dump
+ * @buf: the buffer to append to
* @format: the format
* @argptr: the variable list of arguments
*
- * Do a formatted print to an XML buffer.
+ * Do a formatted print to an XML buffer. Auto indentation may be applied.
*/
void
virBufferVasprintf(virBufferPtr buf, const char *format, va_list argptr)
if (buf->error)
return;
+ virBufferAddLit(buf, ""); /* auto-indent */
+
if (buf->size == 0 &&
virBufferGrow(buf, 100) < 0)
return;
* virBufferEscapeString:
* @buf: the buffer to append to
* @format: a printf like format string but with only one %s parameter
- * @str: the string argument which need to be escaped
+ * @str: the string argument which needs to be escaped
*
- * Do a formatted print with a single string to an XML buffer. The string
- * is escaped to avoid generating a not well-formed XML instance.
+ * Do a formatted print with a single string to an XML buffer. The
+ * string is escaped for use in XML. If @str is NULL, nothing is
+ * added (not even the rest of @format). Auto indentation may be
+ * applied.
*/
void
virBufferEscapeString(virBufferPtr buf, const char *format, const char *str)
* virBufferEscapeSexpr:
* @buf: the buffer to append to
* @format: a printf like format string but with only one %s parameter
- * @str: the string argument which need to be escaped
+ * @str: the string argument which needs to be escaped
*
- * Do a formatted print with a single string to an sexpr buffer. The string
- * is escaped to avoid generating a sexpr that xen will choke on. This
- * doesn't fully escape the sexpr, just enough for our code to work.
+ * Do a formatted print with a single string to an sexpr buffer. The
+ * string is escaped to avoid generating a sexpr that xen will choke
+ * on. This doesn't fully escape the sexpr, just enough for our code
+ * to work. Auto indentation may be applied.
*/
void
virBufferEscapeSexpr(virBufferPtr buf,
* @str: the string argument which needs to be escaped
*
* Do a formatted print with a single string to a buffer. Any characters
- * in the provided list are escaped with a preceeding \.
+ * in the provided list are escaped with a preceeding \. Auto indentation
+ * may be applied.
*/
void
virBufferEscape(virBufferPtr buf, const char *toescape,
*
* Append the string to the buffer. The string will be URI-encoded
* during the append (ie any non alpha-numeric characters are replaced
- * with '%xx' hex sequences).
+ * with '%xx' hex sequences). Auto indentation may be applied.
*/
void
virBufferURIEncodeString(virBufferPtr buf, const char *str)
if (buf->error)
return;
+ virBufferAddLit(buf, ""); /* auto-indent */
+
for (p = str; *p; ++p) {
if (c_isalnum(*p))
grow_size++;
grow_size += 3; /* %ab */
}
- if (virBufferGrow (buf, grow_size) < 0)
+ if (virBufferGrow(buf, grow_size) < 0)
return;
for (p = str; *p; ++p) {
/**
* virBufferEscapeShell:
- * @buf: the buffer to append to
- * @str: an unquoted string
+ * @buf: the buffer to append to
+ * @str: an unquoted string
*
* Quotes a string so that the shell (/bin/sh) will interpret the
- * quoted string to mean str.
+ * quoted string to mean str. Auto indentation may be applied.
*/
void
virBufferEscapeShell(virBufferPtr buf, const char *str)
* @buf: the buffer to append to
* @...: the variable list of strings, the last argument must be NULL
*
- * Concatenate strings to an XML buffer.
+ * Concatenate strings to an XML buffer. Auto indentation may be applied
+ * after each string argument.
*/
void
virBufferStrcat(virBufferPtr buf, ...)
return;
va_start(ap, buf);
-
- while ((str = va_arg(ap, char *)) != NULL) {
- unsigned int len = strlen(str);
- unsigned int needSize = buf->use + len + 2;
-
- if (needSize > buf->size) {
- if (virBufferGrow(buf, needSize - buf->use) < 0)
- break;
- }
- memcpy(&buf->content[buf->use], str, len);
- buf->use += len;
- buf->content[buf->use] = 0;
- }
+ while ((str = va_arg(ap, char *)) != NULL)
+ virBufferAdd(buf, str, -1);
va_end(ap);
}
int doEscape;
};
-static int testBufInfiniteLoop(const void *data ATTRIBUTE_UNUSED)
+static int testBufInfiniteLoop(const void *data)
{
virBuffer bufinit = VIR_BUFFER_INITIALIZER;
virBufferPtr buf = &bufinit;
int ret = -1;
const struct testInfo *info = data;
- /* This relies of virBuffer internals, so may break if things change
- * in the future */
virBufferAddChar(buf, 'a');
- if (buf->a != 1002 || buf->b != 1) {
- TEST_ERROR("Buf was not expected size, size=%d use=%d\n",
- buf->a, buf->b);
- goto out;
- }
/*
- * Infinite loop triggers if:
+ * Infinite loop used to trigger if:
* (strlen + 1 > 1000) && (strlen == buf-size - buf-use - 1)
+ * which was the case after the above addchar at the time of the bug.
+ * This test is a bit fragile, since it relies on virBuffer internals.
*/
if (virAsprintf(&addstr, "%*s", buf->a - buf->b - 1, "a") < 0) {
goto out;
return ret;
}
+static int testBufAutoIndent(const void *data ATTRIBUTE_UNUSED)
+{
+ virBuffer bufinit = VIR_BUFFER_INITIALIZER;
+ virBufferPtr buf = &bufinit;
+ const char expected[] =
+ " 1\n 2\n 3\n 4\n 5\n 6\n 7\n &\n 8\n 9\n 10\n ' 11'\n";
+ char *result = NULL;
+ int ret = 0;
+
+ if (virBufferGetIndent(buf, false) != 0 ||
+ virBufferGetIndent(buf, true) != 0) {
+ TEST_ERROR("Wrong indentation");
+ ret = -1;
+ }
+ virBufferAdjustIndent(buf, 3);
+ if (virBufferGetIndent(buf, false) != 3 ||
+ virBufferGetIndent(buf, true) != 3 ||
+ virBufferError(buf)) {
+ TEST_ERROR("Wrong indentation");
+ ret = -1;
+ }
+ virBufferAdjustIndent(buf, -2);
+ if (virBufferGetIndent(buf, false) != 1 ||
+ virBufferGetIndent(buf, true) != 1 ||
+ virBufferError(buf)) {
+ TEST_ERROR("Wrong indentation");
+ ret = -1;
+ }
+ virBufferAdjustIndent(buf, -3);
+ if (virBufferGetIndent(buf, false) != -1 ||
+ virBufferGetIndent(buf, true) != -1 ||
+ virBufferError(buf) != -1) {
+ TEST_ERROR("Usage error not flagged");
+ ret = -1;
+ }
+ virBufferFreeAndReset(buf);
+ if (virBufferGetIndent(buf, false) != 0 ||
+ virBufferGetIndent(buf, true) != 0 ||
+ virBufferError(buf)) {
+ TEST_ERROR("Reset didn't clear indentation");
+ ret = -1;
+ }
+ virBufferAdjustIndent(buf, 2);
+ virBufferAddLit(buf, "1");
+ if (virBufferGetIndent(buf, false) != 2 ||
+ virBufferGetIndent(buf, true) != 0) {
+ TEST_ERROR("Wrong indentation");
+ ret = -1;
+ }
+ virBufferAddLit(buf, "\n");
+ virBufferAdd(buf, "" "2\n", -1); /* Extra "" appeases syntax-check */
+ virBufferAddChar(buf, '3');
+ virBufferAddChar(buf, '\n');
+ virBufferAsprintf(buf, "%d", 4);
+ virBufferAsprintf(buf, "%c", '\n');
+ virBufferStrcat(buf, "5", "\n", "6\n", NULL);
+ virBufferEscapeString(buf, "%s\n", "7");
+ virBufferEscapeString(buf, "%s\n", "&");
+ virBufferEscapeSexpr(buf, "%s", "8\n");
+ virBufferURIEncodeString(buf, "9");
+ virBufferAddChar(buf, '\n');
+ virBufferEscapeShell(buf, "10");
+ virBufferAddChar(buf, '\n');
+ virBufferEscapeShell(buf, " 11");
+ virBufferAddChar(buf, '\n');
+
+ result = virBufferContentAndReset(buf);
+ if (!result || STRNEQ(result, expected)) {
+ virtTestDifference(stderr, expected, result);
+ ret = -1;
+ }
+ VIR_FREE(result);
+ return ret;
+}
+
static int
mymain(void)
{
DO_TEST("EscapeString infinite loop", testBufInfiniteLoop, 1);
DO_TEST("VSprintf infinite loop", testBufInfiniteLoop, 0);
+ DO_TEST("Auto-indentation", testBufAutoIndent, 0);
return(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
}