buf.c 9.1 KB
Newer Older
D
Daniel Veillard 已提交
1
/*
D
Daniel P. Berrange 已提交
2
 * buf.c: buffers for libvirt
D
Daniel Veillard 已提交
3
 *
4
 * Copyright (C) 2005-2008 Red Hat, Inc.
D
Daniel Veillard 已提交
5 6 7 8 9 10
 *
 * See COPYING.LIB for the License of this software
 *
 * Daniel Veillard <veillard@redhat.com>
 */

11
#include <config.h>
J
Jim Meyering 已提交
12

D
Daniel Veillard 已提交
13 14 15 16 17 18
#include "libvirt/libvirt.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
19

20 21
#define __VIR_BUFFER_C__

D
Daniel Veillard 已提交
22 23
#include "buf.h"

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

/* 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
virBufferNoMemory(const virBufferPtr buf)
{
    free(buf->content);
    buf->content = NULL;
    buf->size = 0;
    buf->use = 0;
    buf->error = 1;
}

D
Daniel Veillard 已提交
51
/**
D
Daniel P. Berrange 已提交
52
 * virBufferGrow:
D
Daniel Veillard 已提交
53
 * @buf:  the buffer
54
 * @len:  the minimum free size to allocate on top of existing used space
D
Daniel Veillard 已提交
55
 *
56
 * Grow the available space of a buffer to at least @len bytes.
D
Daniel Veillard 已提交
57
 *
58
 * Returns zero on success or -1 on error
D
Daniel Veillard 已提交
59 60
 */
static int
D
Daniel P. Berrange 已提交
61
virBufferGrow(virBufferPtr buf, unsigned int len)
D
Daniel Veillard 已提交
62 63 64 65
{
    int size;
    char *newbuf;

66 67 68 69 70
    if (buf->error)
        return -1;

    if ((len + buf->use) < buf->size)
        return 0;
D
Daniel Veillard 已提交
71 72 73

    size = buf->use + len + 1000;

74
    newbuf = realloc(buf->content, size);
75 76 77 78
    if (newbuf == NULL) {
        virBufferNoMemory(buf);
        return -1;
    }
D
Daniel Veillard 已提交
79 80
    buf->content = newbuf;
    buf->size = size;
81
    return 0;
D
Daniel Veillard 已提交
82 83 84
}

/**
D
Daniel P. Berrange 已提交
85
 * virBufferAdd:
86
 * @buf:  the buffer to add to
D
Daniel Veillard 已提交
87 88 89 90 91 92 93
 * @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.
 *
 */
94 95
void
__virBufferAdd(const virBufferPtr buf, const char *str, int len)
D
Daniel Veillard 已提交
96 97 98
{
    unsigned int needSize;

99 100 101 102 103
    if ((str == NULL) || (buf == NULL) || (len == 0))
        return;

    if (buf->error)
        return;
D
Daniel Veillard 已提交
104 105 106 107 108

    if (len < 0)
        len = strlen(str);

    needSize = buf->use + len + 2;
109 110 111
    if (needSize > buf->size &&
        virBufferGrow(buf, needSize - buf->use) < 0)
        return;
112 113

    memcpy (&buf->content[buf->use], str, len);
D
Daniel Veillard 已提交
114
    buf->use += len;
115
    buf->content[buf->use] = '\0';
D
Daniel Veillard 已提交
116 117
}

118 119 120 121 122 123 124 125
/**
 * virBufferAddChar:
 * @buf: the buffer to add to
 * @c: the character to add
 *
 * Add a single character 'c' to a buffer.
 *
 */
126
void
127
__virBufferAddChar (virBufferPtr buf, char c)
128 129 130 131
{
    unsigned int needSize;

    if (buf == NULL)
132 133 134 135
        return;

    if (buf->error)
        return;
136 137

    needSize = buf->use + 2;
138 139 140
    if (needSize > buf->size &&
        virBufferGrow (buf, needSize - buf->use) < 0)
        return;
141 142

    buf->content[buf->use++] = c;
143
    buf->content[buf->use] = '\0';
144 145
}

146
/**
147 148
 * virBufferContentAndReset:
 * @buf: Buffer
149
 *
150 151 152
 * 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.
153
 *
154
 * Returns the buffer content or NULL in case of error.
155
 */
156 157
char *
__virBufferContentAndReset(const virBufferPtr buf)
D
Daniel Veillard 已提交
158
{
159 160 161
    char *str;
    if (buf == NULL)
        return NULL;
D
Daniel Veillard 已提交
162

163 164
    if (buf->error) {
        memset(buf, 0, sizeof(*buf));
D
Daniel Veillard 已提交
165 166 167
        return NULL;
    }

168 169 170
    str = buf->content;
    memset(buf, 0, sizeof(*buf));
    return str;
D
Daniel Veillard 已提交
171 172
}

173
/**
174 175
 * virBufferError:
 * @buf: the buffer
176
 *
177 178 179 180
 * Check to see if the buffer is in an error state due
 * to failed memory allocation
 *
 * Return true if in error, 0 if normal
181
 */
182 183
int
__virBufferError(const virBufferPtr buf)
D
Daniel Veillard 已提交
184
{
185 186 187 188
    if (buf == NULL)
        return 1;

    return buf->error;
D
Daniel Veillard 已提交
189 190 191
}

/**
192 193
 * virBufferUse:
 * @buf: the usage of the string in the buffer
D
Daniel Veillard 已提交
194
 *
195
 * Return the string usage in bytes
D
Daniel Veillard 已提交
196
 */
197 198
unsigned int
virBufferUse(const virBufferPtr buf)
D
Daniel Veillard 已提交
199
{
200
    if (buf == NULL)
201
        return 0;
D
Daniel Veillard 已提交
202

203
    return buf->use;
D
Daniel Veillard 已提交
204 205 206
}

/**
D
Daniel P. Berrange 已提交
207
 * virBufferVSprintf:
D
Daniel Veillard 已提交
208 209
 * @buf:  the buffer to dump
 * @format:  the format
210
 * @...:  the variable list of arguments
D
Daniel Veillard 已提交
211 212 213
 *
 * Do a formatted print to an XML buffer.
 */
214 215
void
__virBufferVSprintf(const virBufferPtr buf, const char *format, ...)
D
Daniel Veillard 已提交
216
{
217
    int size, count, grow_size;
D
Daniel Veillard 已提交
218 219
    va_list locarg, argptr;

220 221 222 223 224
    if ((format == NULL) || (buf == NULL))
        return;

    if (buf->error)
        return;
225 226 227

    if (buf->size == 0 &&
        virBufferGrow(buf, 100) < 0)
228
        return;
229

D
Daniel Veillard 已提交
230 231 232 233 234 235 236
    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);
237

238
        grow_size = (count > 1000) ? count : 1000;
239 240 241
        if (virBufferGrow(buf, grow_size) < 0)
            return;

D
Daniel Veillard 已提交
242 243 244 245 246
        size = buf->size - buf->use - 1;
        va_copy(locarg, argptr);
    }
    va_end(locarg);
    buf->use += count;
247
    buf->content[buf->use] = '\0';
D
Daniel Veillard 已提交
248 249
}

250 251 252 253 254 255 256 257 258
/**
 * 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.
 */
259 260
void
virBufferEscapeString(const virBufferPtr buf, const char *format, const char *str)
261
{
262
    int size, count, len, grow_size;
263 264 265
    char *escaped, *out;
    const char *cur;

266 267 268 269 270
    if ((format == NULL) || (buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;
271 272 273 274

    len = strlen(str);
    escaped = malloc(5 * len + 1);
    if (escaped == NULL) {
275 276
        virBufferNoMemory(buf);
        return;
277
    }
278

279 280 281 282
    cur = str;
    out = escaped;
    while (*cur != 0) {
        if (*cur == '<') {
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
            *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++;
309 310 311 312 313 314 315
    }
    *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;
316 317
        grow_size = (count > 1000) ? count : 1000;
        if (virBufferGrow(buf, grow_size) < 0) {
318
            free(escaped);
319
            return;
320 321 322 323
        }
        size = buf->size - buf->use - 1;
    }
    buf->use += count;
324
    buf->content[buf->use] = '\0';
325 326 327
    free(escaped);
}

328 329 330 331 332 333 334 335 336
/**
 * 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).
 */
337
void
338 339 340 341 342 343 344
virBufferURIEncodeString (virBufferPtr buf, const char *str)
{
    int grow_size = 0;
    const char *p;
    unsigned char uc;
    const char *hex = "0123456789abcdef";

345 346 347 348 349 350
    if ((buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

351 352 353 354 355 356 357 358 359 360
    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 */
    }

361 362
    if (virBufferGrow (buf, grow_size) < 0)
        return;
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

    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';
}

D
Daniel Veillard 已提交
381
/**
D
Daniel P. Berrange 已提交
382
 * virBufferStrcat:
D
Daniel Veillard 已提交
383
 * @buf:  the buffer to dump
384
 * @...:  the variable list of strings, the last argument must be NULL
D
Daniel Veillard 已提交
385 386 387
 *
 * Concatenate strings to an XML buffer.
 */
388
void
D
Daniel P. Berrange 已提交
389
virBufferStrcat(virBufferPtr buf, ...)
D
Daniel Veillard 已提交
390 391 392 393
{
    va_list ap;
    char *str;

394 395 396
    if (buf->error)
        return;

D
Daniel Veillard 已提交
397 398 399 400 401 402 403
    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) {
404 405
            if (virBufferGrow(buf, needSize - buf->use) < 0)
                return;
D
Daniel Veillard 已提交
406 407 408 409 410 411 412
        }
        memcpy(&buf->content[buf->use], str, len);
        buf->use += len;
        buf->content[buf->use] = 0;
    }
    va_end(ap);
}