libvirt/src/util/buf.c

579 lines
13 KiB
C
Raw Normal View History

2007-03-15 17:30:04 +00:00
/*
2007-06-26 22:21:22 +00:00
* buf.c: buffers for libvirt
2007-03-15 17:30:04 +00:00
*
* Copyright (C) 2005-2008, 2010-2011 Red Hat, Inc.
2007-03-15 17:30:04 +00:00
*
* See COPYING.LIB for the License of this software
*
* Daniel Veillard <veillard@redhat.com>
*/
#include <config.h>
2007-03-15 17:30:04 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "c-ctype.h"
#define __VIR_BUFFER_C__
2007-03-15 17:30:04 +00:00
#include "buf.h"
#include "memory.h"
2007-03-15 17:30:04 +00:00
/* If adding more fields, ensure to edit buf.h to match
the number of fields */
struct _virBuffer {
unsigned int size;
unsigned int use;
unsigned int error;
char *content;
};
/**
* virBufferFail
* @buf: the buffer
*
* Mark the buffer has having failed a memory allocation,
* freeing the content and setting the error flag.
*/
static void
virBufferSetError(const virBufferPtr buf)
{
VIR_FREE(buf->content);
buf->size = 0;
buf->use = 0;
buf->error = 1;
}
2007-03-15 17:30:04 +00:00
/**
2007-06-26 22:21:22 +00:00
* virBufferGrow:
2007-03-15 17:30:04 +00:00
* @buf: the buffer
* @len: the minimum free size to allocate on top of existing used space
2007-03-15 17:30:04 +00:00
*
* Grow the available space of a buffer to at least @len bytes.
2007-03-15 17:30:04 +00:00
*
* Returns zero on success or -1 on error
2007-03-15 17:30:04 +00:00
*/
static int
2007-06-26 22:21:22 +00:00
virBufferGrow(virBufferPtr buf, unsigned int len)
2007-03-15 17:30:04 +00:00
{
int size;
if (buf->error)
return -1;
if ((len + buf->use) < buf->size)
return 0;
2007-03-15 17:30:04 +00:00
size = buf->use + len + 1000;
if (VIR_REALLOC_N(buf->content, size) < 0) {
virBufferSetError(buf);
return -1;
}
2007-03-15 17:30:04 +00:00
buf->size = size;
return 0;
2007-03-15 17:30:04 +00:00
}
/**
2007-06-26 22:21:22 +00:00
* virBufferAdd:
* @buf: the buffer to add to
2007-03-15 17:30:04 +00:00
* @str: the string
* @len: the number of bytes to add
*
* Add a string range to an XML buffer. if len == -1, the length of
* str is recomputed to the full string.
*
*/
void
2008-11-17 11:03:25 +00:00
virBufferAdd(const virBufferPtr buf, const char *str, int len)
2007-03-15 17:30:04 +00:00
{
unsigned int needSize;
if ((str == NULL) || (buf == NULL) || (len == 0))
return;
if (buf->error)
return;
2007-03-15 17:30:04 +00:00
if (len < 0)
len = strlen(str);
needSize = buf->use + len + 2;
if (needSize > buf->size &&
virBufferGrow(buf, needSize - buf->use) < 0)
return;
memcpy (&buf->content[buf->use], str, len);
2007-03-15 17:30:04 +00:00
buf->use += len;
buf->content[buf->use] = '\0';
2007-03-15 17:30:04 +00:00
}
/**
* virBufferAddChar:
* @buf: the buffer to add to
* @c: the character to add
*
* Add a single character 'c' to a buffer.
*
*/
void
2008-11-17 11:03:25 +00:00
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';
}
/**
* virBufferContentAndReset:
* @buf: Buffer
*
* Get the content from the buffer and free (only) the buffer structure.
* The caller owns the returned string & should free it when no longer
* required. The buffer object is reset to its initial state.
*
* Returns the buffer content or NULL in case of error.
*/
char *
2008-11-17 11:03:25 +00:00
virBufferContentAndReset(const virBufferPtr buf)
2007-03-15 17:30:04 +00:00
{
char *str;
if (buf == NULL)
return NULL;
2007-03-15 17:30:04 +00:00
if (buf->error) {
memset(buf, 0, sizeof(*buf));
2007-03-15 17:30:04 +00:00
return NULL;
}
str = buf->content;
memset(buf, 0, sizeof(*buf));
return str;
2007-03-15 17:30:04 +00:00
}
/**
* virBufferFreeAndReset:
* @buf: the buffer to free and reset
*
* Frees the buffer content and resets the buffer structure.
*/
void virBufferFreeAndReset(const virBufferPtr buf)
{
char *str = virBufferContentAndReset(buf);
VIR_FREE(str);
}
/**
* virBufferError:
* @buf: the buffer
*
* Check to see if the buffer is in an error state due
* to failed memory allocation
*
* Return true if in error, 0 if normal
*/
int
2008-11-17 11:03:25 +00:00
virBufferError(const virBufferPtr buf)
2007-03-15 17:30:04 +00:00
{
if (buf == NULL)
return 1;
return buf->error;
2007-03-15 17:30:04 +00:00
}
/**
* virBufferUse:
* @buf: the usage of the string in the buffer
2007-03-15 17:30:04 +00:00
*
* Return the string usage in bytes
2007-03-15 17:30:04 +00:00
*/
unsigned int
virBufferUse(const virBufferPtr buf)
2007-03-15 17:30:04 +00:00
{
if (buf == NULL)
return 0;
2007-03-15 17:30:04 +00:00
return buf->use;
2007-03-15 17:30:04 +00:00
}
/**
* virBufferAsprintf:
2007-03-15 17:30:04 +00:00
* @buf: the buffer to dump
* @format: the format
* @...: the variable list of arguments
2007-03-15 17:30:04 +00:00
*
* Do a formatted print to an XML buffer.
*/
void
virBufferAsprintf(const virBufferPtr buf, const char *format, ...)
2007-03-15 17:30:04 +00:00
{
va_list argptr;
va_start(argptr, format);
virBufferVasprintf(buf, format, argptr);
va_end(argptr);
}
/**
* virBufferVasprintf:
* @buf: the buffer to dump
* @format: the format
* @argptr: the variable list of arguments
*
* Do a formatted print to an XML buffer.
*/
void
virBufferVasprintf(const virBufferPtr buf, const char *format, va_list argptr)
{
int size, count, grow_size;
va_list copy;
2007-03-15 17:30:04 +00:00
if ((format == NULL) || (buf == NULL))
return;
if (buf->error)
return;
if (buf->size == 0 &&
virBufferGrow(buf, 100) < 0)
return;
va_copy(copy, argptr);
size = buf->size - buf->use;
if ((count = vsnprintf(&buf->content[buf->use],
size, format, copy)) < 0) {
virBufferSetError(buf);
va_end(copy);
return;
}
va_end(copy);
/* Grow buffer if necessary and retry */
if (count >= size) {
2007-03-15 17:30:04 +00:00
buf->content[buf->use] = 0;
grow_size = (count + 1 > 1000) ? count + 1 : 1000;
if (virBufferGrow(buf, grow_size) < 0) {
return;
}
size = buf->size - buf->use;
if ((count = vsnprintf(&buf->content[buf->use],
size, format, argptr)) < 0) {
virBufferSetError(buf);
return;
}
2007-03-15 17:30:04 +00:00
}
buf->use += count;
}
/**
* virBufferEscapeString:
* @buf: the buffer to dump
* @format: a printf like format string but with only one %s parameter
* @str: the string argument which need 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.
*/
void
virBufferEscapeString(const virBufferPtr buf, const char *format, const char *str)
{
int len;
char *escaped, *out;
const char *cur;
if ((format == NULL) || (buf == NULL) || (str == NULL))
return;
if (buf->error)
return;
len = strlen(str);
if (strcspn(str, "<>&'\"") == len) {
virBufferAsprintf(buf, format, str);
return;
}
if (xalloc_oversized(6, len) ||
VIR_ALLOC_N(escaped, 6 * len + 1) < 0) {
virBufferSetError(buf);
return;
}
cur = str;
out = escaped;
while (*cur != 0) {
if (*cur == '<') {
*out++ = '&';
*out++ = 'l';
*out++ = 't';
*out++ = ';';
} else if (*cur == '>') {
*out++ = '&';
*out++ = 'g';
*out++ = 't';
*out++ = ';';
} else if (*cur == '&') {
*out++ = '&';
*out++ = 'a';
*out++ = 'm';
*out++ = 'p';
*out++ = ';';
} else if (*cur == '"') {
*out++ = '&';
*out++ = 'q';
*out++ = 'u';
*out++ = 'o';
*out++ = 't';
*out++ = ';';
} else if (*cur == '\'') {
*out++ = '&';
*out++ = 'a';
*out++ = 'p';
*out++ = 'o';
*out++ = 's';
*out++ = ';';
} else if (((unsigned char)*cur >= 0x20) || (*cur == '\n') || (*cur == '\t') ||
(*cur == '\r')) {
/*
* default case, just copy !
* Note that character over 0x80 are likely to give problem
* with UTF-8 XML, but since our string don't have an encoding
* it's hard to handle properly we have to assume it's UTF-8 too
*/
*out++ = *cur;
}
cur++;
}
*out = 0;
virBufferAsprintf(buf, format, escaped);
VIR_FREE(escaped);
}
/**
* virBufferEscapeSexpr:
* @buf: the buffer to dump
* @format: a printf like format string but with only one %s parameter
* @str: the string argument which need 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.
*/
void
virBufferEscapeSexpr(const virBufferPtr buf,
const char *format,
const char *str)
{
virBufferEscape(buf, "\\'", format, str);
}
/**
* virBufferEscape:
* @buf: the buffer to dump
* @toescape: NULL-terminated list of characters to escape
* @format: a printf like format string but with only one %s parameter
* @str: the string argument which need 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 \.
*/
void
virBufferEscape(const virBufferPtr buf,
const char *toescape,
const char *format,
const char *str)
{
int len;
char *escaped, *out;
const char *cur;
if ((format == NULL) || (buf == NULL) || (str == NULL))
return;
if (buf->error)
return;
len = strlen(str);
if (strcspn(str, toescape) == len) {
virBufferAsprintf(buf, format, str);
return;
}
if (xalloc_oversized(2, len) ||
VIR_ALLOC_N(escaped, 2 * len + 1) < 0) {
virBufferSetError(buf);
return;
}
cur = str;
out = escaped;
while (*cur != 0) {
if (strchr(toescape, *cur))
*out++ = '\\';
*out++ = *cur;
cur++;
}
*out = 0;
virBufferAsprintf(buf, format, escaped);
VIR_FREE(escaped);
}
/**
* virBufferURIEncodeString:
* @buf: the buffer to append to
* @str: the string argument which will be URI-encoded
*
* 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).
*/
void
virBufferURIEncodeString (virBufferPtr buf, const char *str)
{
int grow_size = 0;
const char *p;
unsigned char uc;
const char *hex = "0123456789abcdef";
if ((buf == NULL) || (str == NULL))
return;
if (buf->error)
return;
for (p = str; *p; ++p) {
if (c_isalnum(*p))
grow_size++;
else
grow_size += 3; /* %ab */
}
if (virBufferGrow (buf, grow_size) < 0)
return;
for (p = str; *p; ++p) {
if (c_isalnum(*p))
buf->content[buf->use++] = *p;
else {
uc = (unsigned char) *p;
buf->content[buf->use++] = '%';
buf->content[buf->use++] = hex[uc >> 4];
buf->content[buf->use++] = hex[uc & 0xf];
}
}
buf->content[buf->use] = '\0';
}
/**
* virBufferEscapeShell:
* @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.
*/
void
virBufferEscapeShell(virBufferPtr buf, const char *str)
{
int len;
char *escaped, *out;
const char *cur;
if ((buf == NULL) || (str == NULL))
return;
if (buf->error)
return;
/* Only quote if str includes shell metacharacters. */
if (*str && !strpbrk(str, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~")) {
virBufferAdd(buf, str, -1);
return;
}
if (*str) {
len = strlen(str);
if (xalloc_oversized(4, len) ||
VIR_ALLOC_N(escaped, 4 * len + 3) < 0) {
virBufferSetError(buf);
return;
}
} else {
virBufferAdd(buf, "''", 2);
return;
}
cur = str;
out = escaped;
*out++ = '\'';
while (*cur != 0) {
if (*cur == '\'') {
*out++ = '\'';
/* Replace literal ' with a close ', a \', and a open ' */
*out++ = '\\';
*out++ = '\'';
}
*out++ = *cur++;
}
*out++ = '\'';
*out = 0;
virBufferAdd(buf, escaped, -1);
VIR_FREE(escaped);
}
2007-03-15 17:30:04 +00:00
/**
2007-06-26 22:21:22 +00:00
* virBufferStrcat:
2007-03-15 17:30:04 +00:00
* @buf: the buffer to dump
* @...: the variable list of strings, the last argument must be NULL
2007-03-15 17:30:04 +00:00
*
* Concatenate strings to an XML buffer.
*/
void
2007-06-26 22:21:22 +00:00
virBufferStrcat(virBufferPtr buf, ...)
2007-03-15 17:30:04 +00:00
{
va_list ap;
char *str;
if (buf->error)
return;
2007-03-15 17:30:04 +00:00
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;
2007-03-15 17:30:04 +00:00
}
memcpy(&buf->content[buf->use], str, len);
buf->use += len;
buf->content[buf->use] = 0;
}
va_end(ap);
}