buf.c 12.5 KB
Newer Older
D
Daniel Veillard 已提交
1
/*
D
Daniel P. Berrange 已提交
2
 * buf.c: buffers for libvirt
D
Daniel Veillard 已提交
3
 *
E
Eric Blake 已提交
4
 * Copyright (C) 2005-2008, 2010-2011 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
}

/**
216
 * virBufferAsprintf:
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
224
virBufferAsprintf(const virBufferPtr buf, const char *format, ...)
D
Daniel Veillard 已提交
225
{
226
    va_list argptr;
E
Eric Blake 已提交
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    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;
D
Daniel Veillard 已提交
245

246 247 248 249 250
    if ((format == NULL) || (buf == NULL))
        return;

    if (buf->error)
        return;
251 252 253

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

E
Eric Blake 已提交
256
    va_copy(copy, argptr);
257 258 259

    size = buf->size - buf->use;
    if ((count = vsnprintf(&buf->content[buf->use],
E
Eric Blake 已提交
260
                           size, format, copy)) < 0) {
261
        virBufferSetError(buf);
E
Eric Blake 已提交
262 263
        va_end(copy);
        return;
264
    }
E
Eric Blake 已提交
265
    va_end(copy);
266 267 268

    /* Grow buffer if necessary and retry */
    if (count >= size) {
D
Daniel Veillard 已提交
269
        buf->content[buf->use] = 0;
270

271
        grow_size = (count + 1 > 1000) ? count + 1 : 1000;
272
        if (virBufferGrow(buf, grow_size) < 0) {
E
Eric Blake 已提交
273
            return;
274
        }
275

276 277 278
        size = buf->size - buf->use;
        if ((count = vsnprintf(&buf->content[buf->use],
                               size, format, argptr)) < 0) {
279
            virBufferSetError(buf);
E
Eric Blake 已提交
280
            return;
281
        }
D
Daniel Veillard 已提交
282 283 284 285
    }
    buf->use += count;
}

286 287 288 289 290 291 292 293 294
/**
 * 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.
 */
295 296
void
virBufferEscapeString(const virBufferPtr buf, const char *format, const char *str)
297
{
298
    int len;
299 300 301
    char *escaped, *out;
    const char *cur;

302 303 304 305 306
    if ((format == NULL) || (buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;
307 308

    len = strlen(str);
309
    if (strcspn(str, "<>&'\"") == len) {
310
        virBufferAsprintf(buf, format, str);
311 312 313
        return;
    }

314 315
    if (xalloc_oversized(6, len) ||
        VIR_ALLOC_N(escaped, 6 * len + 1) < 0) {
316
        virBufferSetError(buf);
317
        return;
318
    }
319

320 321 322 323
    cur = str;
    out = escaped;
    while (*cur != 0) {
        if (*cur == '<') {
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
            *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++ = ';';
339 340 341 342 343 344 345 346 347 348 349 350 351 352
        } 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++ = ';';
353
        } else if (((unsigned char)*cur >= 0x20) || (*cur == '\n') || (*cur == '\t') ||
354 355 356 357 358 359 360 361 362 363
                   (*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++;
364 365 366
    }
    *out = 0;

367
    virBufferAsprintf(buf, format, escaped);
368
    VIR_FREE(escaped);
369 370
}

371 372 373 374 375 376 377 378 379 380 381 382 383 384
/**
 * 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)
S
Sage Weil 已提交
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
{
    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)
404 405 406 407 408 409 410 411 412 413 414 415
{
    int len;
    char *escaped, *out;
    const char *cur;

    if ((format == NULL) || (buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

    len = strlen(str);
S
Sage Weil 已提交
416
    if (strcspn(str, toescape) == len) {
417
        virBufferAsprintf(buf, format, str);
418 419 420
        return;
    }

421 422
    if (xalloc_oversized(2, len) ||
        VIR_ALLOC_N(escaped, 2 * len + 1) < 0) {
423
        virBufferSetError(buf);
424 425 426 427 428 429
        return;
    }

    cur = str;
    out = escaped;
    while (*cur != 0) {
S
Sage Weil 已提交
430
        if (strchr(toescape, *cur))
431
            *out++ = '\\';
S
Sage Weil 已提交
432
        *out++ = *cur;
433 434 435 436
        cur++;
    }
    *out = 0;

437
    virBufferAsprintf(buf, format, escaped);
438 439 440
    VIR_FREE(escaped);
}

441 442 443 444 445 446 447 448 449
/**
 * 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).
 */
450
void
451 452 453 454 455 456 457
virBufferURIEncodeString (virBufferPtr buf, const char *str)
{
    int grow_size = 0;
    const char *p;
    unsigned char uc;
    const char *hex = "0123456789abcdef";

458 459 460 461 462 463
    if ((buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

464
    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
465
        if (c_isalnum(*p))
466 467 468 469 470
            grow_size++;
        else
            grow_size += 3; /* %ab */
    }

471 472
    if (virBufferGrow (buf, grow_size) < 0)
        return;
473 474

    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
475
        if (c_isalnum(*p))
476 477 478 479 480 481 482 483 484 485 486 487
            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';
}

G
Guido Günther 已提交
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
/**
 * 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 (!strpbrk(str, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~")) {
        virBufferAdd(buf, str, -1);
        return;
    }

    len = strlen(str);
    if (xalloc_oversized(4, len) ||
        VIR_ALLOC_N(escaped, 4 * len + 3) < 0) {
        virBufferSetError(buf);
        return;
    }

    cur = str;
    out = escaped;

    *out++ = '\'';
    while (*cur != 0) {
        *out++ = *cur++;
        if (*cur == '\'') {
            /* Replace literal ' with a close ', a \', and a open ' */
            *out++ = '\\';
            *out++ = '\'';
            *out++ = '\'';
        }
    }
    *out++ = '\'';
    *out = 0;

    virBufferAdd(buf, escaped, -1);
    VIR_FREE(escaped);
}

D
Daniel Veillard 已提交
542
/**
D
Daniel P. Berrange 已提交
543
 * virBufferStrcat:
D
Daniel Veillard 已提交
544
 * @buf:  the buffer to dump
545
 * @...:  the variable list of strings, the last argument must be NULL
D
Daniel Veillard 已提交
546 547 548
 *
 * Concatenate strings to an XML buffer.
 */
549
void
D
Daniel P. Berrange 已提交
550
virBufferStrcat(virBufferPtr buf, ...)
D
Daniel Veillard 已提交
551 552 553 554
{
    va_list ap;
    char *str;

555 556 557
    if (buf->error)
        return;

D
Daniel Veillard 已提交
558 559 560 561 562 563 564
    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) {
565
            if (virBufferGrow(buf, needSize - buf->use) < 0)
566
                break;
D
Daniel Veillard 已提交
567 568 569 570 571 572 573
        }
        memcpy(&buf->content[buf->use], str, len);
        buf->use += len;
        buf->content[buf->use] = 0;
    }
    va_end(ap);
}