/* * buf.c: buffers for libvirt * * Copyright (C) 2005-2007 Red Hat, Inc. * * See COPYING.LIB for the License of this software * * Daniel Veillard */ #include "config.h" #include "libvirt/libvirt.h" #include #include #include #include #include #include "buf.h" /** * virBufferGrow: * @buf: the buffer * @len: the minimum free size to allocate on top of existing used space * * Grow the available space of a buffer to at least @len bytes. * * Returns the new available space or -1 in case of error */ static int virBufferGrow(virBufferPtr buf, unsigned int len) { int size; char *newbuf; if (buf == NULL) return (-1); if (len + buf->use < buf->size) return (0); size = buf->use + len + 1000; newbuf = realloc(buf->content, size); if (newbuf == NULL) return -1; buf->content = newbuf; buf->size = size; return (buf->size - buf->use); } /** * virBufferAdd: * @buf: the buffer to add to * @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. * * Returns 0 successful, -1 in case of internal or API error. */ int virBufferAdd(virBufferPtr buf, const char *str, int len) { unsigned int needSize; if ((str == NULL) || (buf == NULL)) { return -1; } if (len == 0) return 0; if (len < 0) len = strlen(str); needSize = buf->use + len + 2; if (needSize > buf->size) { if (!virBufferGrow(buf, needSize - buf->use)) { return (-1); } } memcpy (&buf->content[buf->use], str, len); buf->use += len; buf->content[buf->use] = 0; return (0); } /** * virBufferAddChar: * @buf: the buffer to add to * @c: the character to add * * Add a single character 'c' to a buffer. * * Returns 0 if successful, -1 in the case of error. */ int virBufferAddChar (virBufferPtr buf, char c) { unsigned int needSize; if (buf == NULL) return -1; needSize = buf->use + 2; if (needSize > buf->size) if (!virBufferGrow (buf, needSize - buf->use)) return -1; buf->content[buf->use++] = c; buf->content[buf->use] = 0; return 0; } /** * virBufferNew: * @size: creation size in bytes * * Creates a new buffer * * Returns a pointer to the buffer or NULL in case of error */ virBufferPtr virBufferNew(unsigned int size) { virBufferPtr buf; if (!(buf = malloc(sizeof(*buf)))) return NULL; if (size && (buf->content = malloc(size))==NULL) { free(buf); return NULL; } buf->size = size; buf->use = 0; return buf; } /** * virBufferFree: * @buf: the buffer to deallocate * * Free the set of resources used by a buffer. */ void virBufferFree(virBufferPtr buf) { if (buf) { if (buf->content) free(buf->content); free(buf); } } /** * virBufferContentAndFree: * @buf: Buffer * * Get the content from the buffer and free (only) the buffer structure. * * Returns the buffer content or NULL in case of error. */ char * virBufferContentAndFree (virBufferPtr buf) { char *content; if (buf == NULL) return(NULL); content = buf->content; if (content != NULL) content[buf->use] = 0; free (buf); return(content); } /** * virBufferVSprintf: * @buf: the buffer to dump * @format: the format * @...: the variable list of arguments * * Do a formatted print to an XML buffer. * * Returns 0 successful, -1 in case of internal or API error. */ int virBufferVSprintf(virBufferPtr buf, const char *format, ...) { int size, count, grow_size; va_list locarg, argptr; if ((format == NULL) || (buf == NULL)) { return (-1); } size = buf->size - buf->use - 1; va_start(argptr, format); va_copy(locarg, argptr); while (((count = vsnprintf(&buf->content[buf->use], size, format, locarg)) < 0) || (count >= size - 1)) { buf->content[buf->use] = 0; va_end(locarg); grow_size = (count > 1000) ? count : 1000; if (virBufferGrow(buf, grow_size) < 0) { return (-1); } size = buf->size - buf->use - 1; va_copy(locarg, argptr); } va_end(locarg); buf->use += count; buf->content[buf->use] = 0; return (0); } /** * 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. * * Returns 0 successful, -1 in case of internal or API error. */ int virBufferEscapeString(virBufferPtr buf, const char *format, const char *str) { int size, count, len, grow_size; char *escaped, *out; const char *cur; if ((format == NULL) || (buf == NULL) || (str == NULL)) { return (-1); } len = strlen(str); escaped = malloc(5 * len + 1); if (escaped == NULL) { return (-1); } 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 >= 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; size = buf->size - buf->use - 1; while (((count = snprintf(&buf->content[buf->use], size, format, (char *)escaped)) < 0) || (count >= size - 1)) { buf->content[buf->use] = 0; grow_size = (count > 1000) ? count : 1000; if (virBufferGrow(buf, grow_size) < 0) { free(escaped); return (-1); } size = buf->size - buf->use - 1; } buf->use += count; buf->content[buf->use] = 0; free(escaped); return (0); } /** * 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). * * Returns 0 successful, -1 in case of internal or API error. */ int virBufferURIEncodeString (virBufferPtr buf, const char *str) { int grow_size = 0; const char *p; unsigned char uc; const char *hex = "0123456789abcdef"; for (p = str; *p; ++p) { /* This may not work on EBCDIC. */ if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')) grow_size++; else grow_size += 3; /* %ab */ } if (virBufferGrow (buf, grow_size) == -1) return -1; for (p = str; *p; ++p) { /* This may not work on EBCDIC. */ if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')) 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'; return 0; } /** * virBufferStrcat: * @buf: the buffer to dump * @...: the variable list of strings, the last argument must be NULL * * Concatenate strings to an XML buffer. * * Returns 0 successful, -1 in case of internal or API error. */ int virBufferStrcat(virBufferPtr buf, ...) { va_list ap; char *str; 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)) return -1; } memcpy(&buf->content[buf->use], str, len); buf->use += len; buf->content[buf->use] = 0; } va_end(ap); return 0; } /* * vim: set tabstop=4: * vim: set shiftwidth=4: * vim: set expandtab: */ /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */