buf.c 10.5 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, 2010 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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
J
Jim Meyering 已提交
17
#include "c-ctype.h"
18

19 20
#define __VIR_BUFFER_C__

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

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

/* 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
42
virBufferSetError(const virBufferPtr buf)
43
{
44
    VIR_FREE(buf->content);
45 46 47 48 49
    buf->size = 0;
    buf->use = 0;
    buf->error = 1;
}

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

64 65 66 67 68
    if (buf->error)
        return -1;

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

    size = buf->use + len + 1000;

72
    if (VIR_REALLOC_N(buf->content, size) < 0) {
73
        virBufferSetError(buf);
74 75
        return -1;
    }
D
Daniel Veillard 已提交
76
    buf->size = size;
77
    return 0;
D
Daniel Veillard 已提交
78 79 80
}

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

95 96 97 98 99
    if ((str == NULL) || (buf == NULL) || (len == 0))
        return;

    if (buf->error)
        return;
D
Daniel Veillard 已提交
100 101 102 103 104

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

    needSize = buf->use + len + 2;
105 106 107
    if (needSize > buf->size &&
        virBufferGrow(buf, needSize - buf->use) < 0)
        return;
108 109

    memcpy (&buf->content[buf->use], str, len);
D
Daniel Veillard 已提交
110
    buf->use += len;
111
    buf->content[buf->use] = '\0';
D
Daniel Veillard 已提交
112 113
}

114 115 116 117 118 119 120 121
/**
 * virBufferAddChar:
 * @buf: the buffer to add to
 * @c: the character to add
 *
 * Add a single character 'c' to a buffer.
 *
 */
122
void
D
Daniel P. Berrange 已提交
123
virBufferAddChar (virBufferPtr buf, char c)
124 125 126 127
{
    unsigned int needSize;

    if (buf == NULL)
128 129 130 131
        return;

    if (buf->error)
        return;
132 133

    needSize = buf->use + 2;
134 135 136
    if (needSize > buf->size &&
        virBufferGrow (buf, needSize - buf->use) < 0)
        return;
137 138

    buf->content[buf->use++] = c;
139
    buf->content[buf->use] = '\0';
140 141
}

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

159 160
    if (buf->error) {
        memset(buf, 0, sizeof(*buf));
D
Daniel Veillard 已提交
161 162 163
        return NULL;
    }

164 165 166
    str = buf->content;
    memset(buf, 0, sizeof(*buf));
    return str;
D
Daniel Veillard 已提交
167 168
}

169 170 171 172 173 174 175 176 177 178 179 180 181
/**
 * 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);
}

182
/**
183 184
 * virBufferError:
 * @buf: the buffer
185
 *
186 187 188 189
 * Check to see if the buffer is in an error state due
 * to failed memory allocation
 *
 * Return true if in error, 0 if normal
190
 */
191
int
D
Daniel P. Berrange 已提交
192
virBufferError(const virBufferPtr buf)
D
Daniel Veillard 已提交
193
{
194 195 196 197
    if (buf == NULL)
        return 1;

    return buf->error;
D
Daniel Veillard 已提交
198 199 200
}

/**
201 202
 * virBufferUse:
 * @buf: the usage of the string in the buffer
D
Daniel Veillard 已提交
203
 *
204
 * Return the string usage in bytes
D
Daniel Veillard 已提交
205
 */
206 207
unsigned int
virBufferUse(const virBufferPtr buf)
D
Daniel Veillard 已提交
208
{
209
    if (buf == NULL)
210
        return 0;
D
Daniel Veillard 已提交
211

212
    return buf->use;
D
Daniel Veillard 已提交
213 214 215
}

/**
D
Daniel P. Berrange 已提交
216
 * virBufferVSprintf:
D
Daniel Veillard 已提交
217 218
 * @buf:  the buffer to dump
 * @format:  the format
219
 * @...:  the variable list of arguments
D
Daniel Veillard 已提交
220 221 222
 *
 * Do a formatted print to an XML buffer.
 */
223
void
D
Daniel P. Berrange 已提交
224
virBufferVSprintf(const virBufferPtr buf, const char *format, ...)
D
Daniel Veillard 已提交
225
{
226
    int size, count, grow_size;
227
    va_list argptr;
D
Daniel Veillard 已提交
228

229 230 231 232 233
    if ((format == NULL) || (buf == NULL))
        return;

    if (buf->error)
        return;
234 235 236

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

D
Daniel Veillard 已提交
239
    va_start(argptr, format);
240 241 242 243

    size = buf->size - buf->use;
    if ((count = vsnprintf(&buf->content[buf->use],
                           size, format, argptr)) < 0) {
244
        virBufferSetError(buf);
245 246 247 248 249
        goto err;
    }

    /* Grow buffer if necessary and retry */
    if (count >= size) {
D
Daniel Veillard 已提交
250
        buf->content[buf->use] = 0;
251 252
        va_end(argptr);
        va_start(argptr, format);
253

254
        grow_size = (count + 1 > 1000) ? count + 1 : 1000;
255
        if (virBufferGrow(buf, grow_size) < 0) {
256
            goto err;
257
        }
258

259 260 261
        size = buf->size - buf->use;
        if ((count = vsnprintf(&buf->content[buf->use],
                               size, format, argptr)) < 0) {
262
            virBufferSetError(buf);
263 264
            goto err;
        }
D
Daniel Veillard 已提交
265 266
    }
    buf->use += count;
267 268 269 270

err:
    va_end(argptr);
    return;
D
Daniel Veillard 已提交
271 272
}

273 274 275 276 277 278 279 280 281
/**
 * 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.
 */
282 283
void
virBufferEscapeString(const virBufferPtr buf, const char *format, const char *str)
284
{
285
    int len;
286 287 288
    char *escaped, *out;
    const char *cur;

289 290 291 292 293
    if ((format == NULL) || (buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;
294 295

    len = strlen(str);
296 297 298 299 300
    if (strcspn(str, "<>&'\"") == len) {
        virBufferVSprintf(buf, format, str);
        return;
    }

301
    if (VIR_ALLOC_N(escaped, 6 * len + 1) < 0) {
302
        virBufferSetError(buf);
303
        return;
304
    }
305

306 307 308 309
    cur = str;
    out = escaped;
    while (*cur != 0) {
        if (*cur == '<') {
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
            *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++ = ';';
325 326 327 328 329 330 331 332 333 334 335 336 337 338
        } 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++ = ';';
339
        } else if (((unsigned char)*cur >= 0x20) || (*cur == '\n') || (*cur == '\t') ||
340 341 342 343 344 345 346 347 348 349
                   (*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++;
350 351 352
    }
    *out = 0;

353
    virBufferVSprintf(buf, format, escaped);
354
    VIR_FREE(escaped);
355 356
}

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
/**
 * 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)
{
    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) {
        virBufferVSprintf(buf, format, str);
        return;
    }

    if (VIR_ALLOC_N(escaped, 2 * len + 1) < 0) {
389
        virBufferSetError(buf);
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
        return;
    }

    cur = str;
    out = escaped;
    while (*cur != 0) {
        switch (*cur) {
        case '\\':
        case '\'':
            *out++ = '\\';
            /* fallthrough */
        default:
            *out++ = *cur;
        }
        cur++;
    }
    *out = 0;

    virBufferVSprintf(buf, format, escaped);
    VIR_FREE(escaped);
}

412 413 414 415 416 417 418 419 420
/**
 * 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).
 */
421
void
422 423 424 425 426 427 428
virBufferURIEncodeString (virBufferPtr buf, const char *str)
{
    int grow_size = 0;
    const char *p;
    unsigned char uc;
    const char *hex = "0123456789abcdef";

429 430 431 432 433 434
    if ((buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

435
    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
436
        if (c_isalnum(*p))
437 438 439 440 441
            grow_size++;
        else
            grow_size += 3; /* %ab */
    }

442 443
    if (virBufferGrow (buf, grow_size) < 0)
        return;
444 445

    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
446
        if (c_isalnum(*p))
447 448 449 450 451 452 453 454 455 456 457 458
            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 已提交
459
/**
D
Daniel P. Berrange 已提交
460
 * virBufferStrcat:
D
Daniel Veillard 已提交
461
 * @buf:  the buffer to dump
462
 * @...:  the variable list of strings, the last argument must be NULL
D
Daniel Veillard 已提交
463 464 465
 *
 * Concatenate strings to an XML buffer.
 */
466
void
D
Daniel P. Berrange 已提交
467
virBufferStrcat(virBufferPtr buf, ...)
D
Daniel Veillard 已提交
468 469 470 471
{
    va_list ap;
    char *str;

472 473 474
    if (buf->error)
        return;

D
Daniel Veillard 已提交
475 476 477 478 479 480 481
    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) {
482
            if (virBufferGrow(buf, needSize - buf->use) < 0)
483
                break;
D
Daniel Veillard 已提交
484 485 486 487 488 489 490
        }
        memcpy(&buf->content[buf->use], str, len);
        buf->use += len;
        buf->content[buf->use] = 0;
    }
    va_end(ap);
}