buf.c 15.8 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-2012 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

/* If adding more fields, ensure to edit buf.h to match
   the number of fields */
struct _virBuffer {
    unsigned int size;
    unsigned int use;
E
Eric Blake 已提交
30
    unsigned int error; /* errno value, or -1 for usage error */
E
Eric Blake 已提交
31
    int indent;
32 33 34 35 36 37
    char *content;
};

/**
 * virBufferFail
 * @buf: the buffer
E
Eric Blake 已提交
38
 * @error: which error occurred (errno value, or -1 for usage)
39
 *
E
Eric Blake 已提交
40
 * Mark the buffer as failed, free the content and set the error flag.
41 42
 */
static void
E
Eric Blake 已提交
43
virBufferSetError(virBufferPtr buf, int error)
44
{
45
    VIR_FREE(buf->content);
46 47
    buf->size = 0;
    buf->use = 0;
E
Eric Blake 已提交
48
    buf->indent = 0;
E
Eric Blake 已提交
49
    buf->error = error;
50 51
}

E
Eric Blake 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
/**
 * virBufferAdjustIndent:
 * @buf: the buffer
 * @indent: adjustment to make
 *
 * Alter the auto-indent value by adding indent (positive to increase,
 * negative to decrease).  Automatic indentation is performed by all
 * additive functions when the existing buffer is empty or ends with a
 * newline (however, note that no indentation is added after newlines
 * embedded in an appended string).  If @indent would cause overflow,
 * the buffer error indicator is set.
 */
void
virBufferAdjustIndent(virBufferPtr buf, int indent)
{
    if (!buf || buf->error)
        return;
    if (indent > 0 ? INT_MAX - indent < buf->indent
        : buf->indent < -indent) {
        virBufferSetError(buf, -1);
        return;
    }
    buf->indent += indent;
}

/**
 * virBufferGetIndent:
 * @buf: the buffer
 * @dynamic: if false, return set value; if true, return 0 unless next
 * append would be affected by auto-indent
 *
 * Return the current auto-indent value, or -1 if there has been an error.
 */
int
virBufferGetIndent(const virBufferPtr buf, bool dynamic)
{
    if (!buf || buf->error)
        return -1;
    if (dynamic && buf->use && buf->content[buf->use - 1] != '\n')
        return 0;
    return buf->indent;
}

D
Daniel Veillard 已提交
95
/**
D
Daniel P. Berrange 已提交
96
 * virBufferGrow:
D
Daniel Veillard 已提交
97
 * @buf:  the buffer
98
 * @len:  the minimum free size to allocate on top of existing used space
D
Daniel Veillard 已提交
99
 *
100
 * Grow the available space of a buffer to at least @len bytes.
D
Daniel Veillard 已提交
101
 *
102
 * Returns zero on success or -1 on error
D
Daniel Veillard 已提交
103 104
 */
static int
D
Daniel P. Berrange 已提交
105
virBufferGrow(virBufferPtr buf, unsigned int len)
D
Daniel Veillard 已提交
106 107 108
{
    int size;

109 110 111 112 113
    if (buf->error)
        return -1;

    if ((len + buf->use) < buf->size)
        return 0;
D
Daniel Veillard 已提交
114 115 116

    size = buf->use + len + 1000;

117
    if (VIR_REALLOC_N(buf->content, size) < 0) {
E
Eric Blake 已提交
118
        virBufferSetError(buf, errno);
119 120
        return -1;
    }
D
Daniel Veillard 已提交
121
    buf->size = size;
122
    return 0;
D
Daniel Veillard 已提交
123 124 125
}

/**
D
Daniel P. Berrange 已提交
126
 * virBufferAdd:
E
Eric Blake 已提交
127 128 129
 * @buf: the buffer to append to
 * @str: the string
 * @len: the number of bytes to add, or -1
D
Daniel Veillard 已提交
130
 *
E
Eric Blake 已提交
131 132
 * Add a string range to an XML buffer. If @len == -1, the length of
 * str is recomputed to the full string.  Auto indentation may be applied.
D
Daniel Veillard 已提交
133 134
 *
 */
135
void
E
Eric Blake 已提交
136
virBufferAdd(virBufferPtr buf, const char *str, int len)
D
Daniel Veillard 已提交
137 138
{
    unsigned int needSize;
E
Eric Blake 已提交
139
    int indent;
D
Daniel Veillard 已提交
140

E
Eric Blake 已提交
141
    if (!str || !buf || (len == 0 && buf->indent == 0))
142 143 144 145
        return;

    if (buf->error)
        return;
D
Daniel Veillard 已提交
146

E
Eric Blake 已提交
147 148
    indent = virBufferGetIndent(buf, true);

D
Daniel Veillard 已提交
149 150 151
    if (len < 0)
        len = strlen(str);

E
Eric Blake 已提交
152
    needSize = buf->use + indent + len + 2;
153 154 155
    if (needSize > buf->size &&
        virBufferGrow(buf, needSize - buf->use) < 0)
        return;
156

E
Eric Blake 已提交
157 158 159
    memset(&buf->content[buf->use], ' ', indent);
    memcpy(&buf->content[buf->use + indent], str, len);
    buf->use += indent + len;
160
    buf->content[buf->use] = '\0';
D
Daniel Veillard 已提交
161 162
}

163 164
/**
 * virBufferAddChar:
E
Eric Blake 已提交
165
 * @buf: the buffer to append to
166 167
 * @c: the character to add
 *
E
Eric Blake 已提交
168
 * Add a single character 'c' to a buffer.  Auto indentation may be applied.
169 170
 *
 */
171
void
E
Eric Blake 已提交
172
virBufferAddChar(virBufferPtr buf, char c)
173
{
E
Eric Blake 已提交
174
    virBufferAdd(buf, &c, 1);
175 176
}

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
/**
 * virBufferCurrentContent:
 * @buf: Buffer
 *
 * Get the current content from the buffer.  The content is only valid
 * until the next operation on @buf, and an empty string is returned if
 * no content is present yet.
 *
 * Returns the buffer content or NULL in case of error.
 */
const char *
virBufferCurrentContent(virBufferPtr buf)
{
    if (!buf || buf->error)
        return NULL;
    return buf->use ? buf->content : "";
}

195
/**
196 197
 * virBufferContentAndReset:
 * @buf: Buffer
198
 *
199 200
 * Get the content from the buffer and free (only) the buffer structure.
 * The caller owns the returned string & should free it when no longer
201 202 203
 * required. The buffer object is reset to its initial state.  This
 * interface intentionally returns NULL instead of an empty string if
 * there is no content.
204
 *
205
 * Returns the buffer content or NULL in case of error.
206
 */
207
char *
E
Eric Blake 已提交
208
virBufferContentAndReset(virBufferPtr buf)
D
Daniel Veillard 已提交
209
{
210 211 212
    char *str;
    if (buf == NULL)
        return NULL;
D
Daniel Veillard 已提交
213

214 215
    if (buf->error) {
        memset(buf, 0, sizeof(*buf));
D
Daniel Veillard 已提交
216 217 218
        return NULL;
    }

219 220 221
    str = buf->content;
    memset(buf, 0, sizeof(*buf));
    return str;
D
Daniel Veillard 已提交
222 223
}

224 225 226 227 228 229
/**
 * virBufferFreeAndReset:
 * @buf: the buffer to free and reset
 *
 * Frees the buffer content and resets the buffer structure.
 */
E
Eric Blake 已提交
230
void virBufferFreeAndReset(virBufferPtr buf)
231 232 233 234 235 236
{
    char *str = virBufferContentAndReset(buf);

    VIR_FREE(str);
}

237
/**
238 239
 * virBufferError:
 * @buf: the buffer
240
 *
241
 * Check to see if the buffer is in an error state due
E
Eric Blake 已提交
242
 * to failed memory allocation or usage error
243
 *
E
Eric Blake 已提交
244
 * Return positive errno value or -1 on usage error, 0 if normal
245
 */
246
int
D
Daniel P. Berrange 已提交
247
virBufferError(const virBufferPtr buf)
D
Daniel Veillard 已提交
248
{
249
    if (buf == NULL)
E
Eric Blake 已提交
250
        return -1;
251 252

    return buf->error;
D
Daniel Veillard 已提交
253 254 255
}

/**
256 257
 * virBufferUse:
 * @buf: the usage of the string in the buffer
D
Daniel Veillard 已提交
258
 *
259
 * Return the string usage in bytes
D
Daniel Veillard 已提交
260
 */
261 262
unsigned int
virBufferUse(const virBufferPtr buf)
D
Daniel Veillard 已提交
263
{
264
    if (buf == NULL)
265
        return 0;
D
Daniel Veillard 已提交
266

267
    return buf->use;
D
Daniel Veillard 已提交
268 269 270
}

/**
271
 * virBufferAsprintf:
E
Eric Blake 已提交
272
 * @buf: the buffer to append to
D
Daniel Veillard 已提交
273
 * @format:  the format
274
 * @...:  the variable list of arguments
D
Daniel Veillard 已提交
275
 *
E
Eric Blake 已提交
276
 * Do a formatted print to an XML buffer.  Auto indentation may be applied.
D
Daniel Veillard 已提交
277
 */
278
void
E
Eric Blake 已提交
279
virBufferAsprintf(virBufferPtr buf, const char *format, ...)
D
Daniel Veillard 已提交
280
{
281
    va_list argptr;
E
Eric Blake 已提交
282 283 284 285 286 287 288
    va_start(argptr, format);
    virBufferVasprintf(buf, format, argptr);
    va_end(argptr);
}

/**
 * virBufferVasprintf:
E
Eric Blake 已提交
289
 * @buf: the buffer to append to
E
Eric Blake 已提交
290 291 292
 * @format:  the format
 * @argptr:  the variable list of arguments
 *
E
Eric Blake 已提交
293
 * Do a formatted print to an XML buffer.  Auto indentation may be applied.
E
Eric Blake 已提交
294 295
 */
void
E
Eric Blake 已提交
296
virBufferVasprintf(virBufferPtr buf, const char *format, va_list argptr)
E
Eric Blake 已提交
297 298 299
{
    int size, count, grow_size;
    va_list copy;
D
Daniel Veillard 已提交
300

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

    if (buf->error)
        return;
306

E
Eric Blake 已提交
307 308
    virBufferAddLit(buf, ""); /* auto-indent */

309 310
    if (buf->size == 0 &&
        virBufferGrow(buf, 100) < 0)
311
        return;
312

E
Eric Blake 已提交
313
    va_copy(copy, argptr);
314 315 316

    size = buf->size - buf->use;
    if ((count = vsnprintf(&buf->content[buf->use],
E
Eric Blake 已提交
317
                           size, format, copy)) < 0) {
E
Eric Blake 已提交
318
        virBufferSetError(buf, errno);
E
Eric Blake 已提交
319 320
        va_end(copy);
        return;
321
    }
E
Eric Blake 已提交
322
    va_end(copy);
323 324 325

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

328
        grow_size = (count + 1 > 1000) ? count + 1 : 1000;
329
        if (virBufferGrow(buf, grow_size) < 0) {
E
Eric Blake 已提交
330
            return;
331
        }
332

333 334 335
        size = buf->size - buf->use;
        if ((count = vsnprintf(&buf->content[buf->use],
                               size, format, argptr)) < 0) {
E
Eric Blake 已提交
336
            virBufferSetError(buf, errno);
E
Eric Blake 已提交
337
            return;
338
        }
D
Daniel Veillard 已提交
339 340 341 342
    }
    buf->use += count;
}

343 344
/**
 * virBufferEscapeString:
E
Eric Blake 已提交
345
 * @buf: the buffer to append to
346
 * @format: a printf like format string but with only one %s parameter
E
Eric Blake 已提交
347
 * @str: the string argument which needs to be escaped
348
 *
E
Eric Blake 已提交
349 350 351 352
 * Do a formatted print with a single string to an XML buffer. The
 * string is escaped for use in XML.  If @str is NULL, nothing is
 * added (not even the rest of @format).  Auto indentation may be
 * applied.
353
 */
354
void
E
Eric Blake 已提交
355
virBufferEscapeString(virBufferPtr buf, const char *format, const char *str)
356
{
357
    int len;
358 359 360
    char *escaped, *out;
    const char *cur;

361 362 363 364 365
    if ((format == NULL) || (buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;
366 367

    len = strlen(str);
368
    if (strcspn(str, "<>&'\"") == len) {
369
        virBufferAsprintf(buf, format, str);
370 371 372
        return;
    }

373 374
    if (xalloc_oversized(6, len) ||
        VIR_ALLOC_N(escaped, 6 * len + 1) < 0) {
E
Eric Blake 已提交
375
        virBufferSetError(buf, errno);
376
        return;
377
    }
378

379 380 381 382
    cur = str;
    out = escaped;
    while (*cur != 0) {
        if (*cur == '<') {
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
            *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++ = ';';
398 399 400 401 402 403 404 405 406 407 408 409 410 411
        } 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++ = ';';
412
        } else if (((unsigned char)*cur >= 0x20) || (*cur == '\n') || (*cur == '\t') ||
413 414 415 416 417 418 419 420 421 422
                   (*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++;
423 424 425
    }
    *out = 0;

426
    virBufferAsprintf(buf, format, escaped);
427
    VIR_FREE(escaped);
428 429
}

430 431
/**
 * virBufferEscapeSexpr:
E
Eric Blake 已提交
432
 * @buf: the buffer to append to
433
 * @format: a printf like format string but with only one %s parameter
E
Eric Blake 已提交
434
 * @str: the string argument which needs to be escaped
435
 *
E
Eric Blake 已提交
436 437 438 439
 * 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.  Auto indentation may be applied.
440 441
 */
void
E
Eric Blake 已提交
442
virBufferEscapeSexpr(virBufferPtr buf,
443 444
                     const char *format,
                     const char *str)
S
Sage Weil 已提交
445
{
446
    virBufferEscape(buf, '\\', "\\'", format, str);
S
Sage Weil 已提交
447 448 449 450
}

/**
 * virBufferEscape:
E
Eric Blake 已提交
451
 * @buf: the buffer to append to
452
 * @escape: the escape character to inject
E
Eric Blake 已提交
453
 * @toescape: NUL-terminated list of characters to escape
S
Sage Weil 已提交
454
 * @format: a printf like format string but with only one %s parameter
E
Eric Blake 已提交
455
 * @str: the string argument which needs to be escaped
S
Sage Weil 已提交
456 457
 *
 * Do a formatted print with a single string to a buffer.  Any characters
458
 * in the provided list are escaped with the given escape.  Auto indentation
E
Eric Blake 已提交
459
 * may be applied.
S
Sage Weil 已提交
460 461
 */
void
462
virBufferEscape(virBufferPtr buf, char escape, const char *toescape,
E
Eric Blake 已提交
463
                const char *format, const char *str)
464 465 466 467 468 469 470 471 472 473 474 475
{
    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 已提交
476
    if (strcspn(str, toescape) == len) {
477
        virBufferAsprintf(buf, format, str);
478 479 480
        return;
    }

481 482
    if (xalloc_oversized(2, len) ||
        VIR_ALLOC_N(escaped, 2 * len + 1) < 0) {
E
Eric Blake 已提交
483
        virBufferSetError(buf, errno);
484 485 486 487 488 489
        return;
    }

    cur = str;
    out = escaped;
    while (*cur != 0) {
490 491 492 493 494
        /* strchr work-around for gcc 4.3 & 4.4 bug with -Wlogical-op
         * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36513
         */
        char needle[2] = { *cur, 0 };
        if (strstr(toescape, needle))
495
            *out++ = escape;
S
Sage Weil 已提交
496
        *out++ = *cur;
497 498 499 500
        cur++;
    }
    *out = 0;

501
    virBufferAsprintf(buf, format, escaped);
502 503 504
    VIR_FREE(escaped);
}

505 506
/**
 * virBufferURIEncodeString:
E
Eric Blake 已提交
507
 * @buf: the buffer to append to
508 509 510 511
 * @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
E
Eric Blake 已提交
512
 * with '%xx' hex sequences).  Auto indentation may be applied.
513
 */
514
void
E
Eric Blake 已提交
515
virBufferURIEncodeString(virBufferPtr buf, const char *str)
516 517 518 519 520 521
{
    int grow_size = 0;
    const char *p;
    unsigned char uc;
    const char *hex = "0123456789abcdef";

522 523 524 525 526 527
    if ((buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

E
Eric Blake 已提交
528 529
    virBufferAddLit(buf, ""); /* auto-indent */

530
    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
531
        if (c_isalnum(*p))
532 533 534 535 536
            grow_size++;
        else
            grow_size += 3; /* %ab */
    }

E
Eric Blake 已提交
537
    if (virBufferGrow(buf, grow_size) < 0)
538
        return;
539 540

    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
541
        if (c_isalnum(*p))
542 543 544 545 546 547 548 549 550 551 552 553
            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 已提交
554 555
/**
 * virBufferEscapeShell:
E
Eric Blake 已提交
556 557
 * @buf: the buffer to append to
 * @str: an unquoted string
G
Guido Günther 已提交
558 559
 *
 * Quotes a string so that the shell (/bin/sh) will interpret the
E
Eric Blake 已提交
560
 * quoted string to mean str.  Auto indentation may be applied.
G
Guido Günther 已提交
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
 */
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. */
576
    if (*str && !strpbrk(str, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~")) {
G
Guido Günther 已提交
577 578 579 580
        virBufferAdd(buf, str, -1);
        return;
    }

581 582 583 584
    if (*str) {
        len = strlen(str);
        if (xalloc_oversized(4, len) ||
            VIR_ALLOC_N(escaped, 4 * len + 3) < 0) {
E
Eric Blake 已提交
585
            virBufferSetError(buf, errno);
586 587 588
            return;
        }
    } else {
589
        virBufferAddLit(buf, "''");
G
Guido Günther 已提交
590 591 592 593 594 595 596 597 598
        return;
    }

    cur = str;
    out = escaped;

    *out++ = '\'';
    while (*cur != 0) {
        if (*cur == '\'') {
599
            *out++ = '\'';
G
Guido Günther 已提交
600 601 602 603
            /* Replace literal ' with a close ', a \', and a open ' */
            *out++ = '\\';
            *out++ = '\'';
        }
604
        *out++ = *cur++;
G
Guido Günther 已提交
605 606 607 608 609 610 611 612
    }
    *out++ = '\'';
    *out = 0;

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

D
Daniel Veillard 已提交
613
/**
D
Daniel P. Berrange 已提交
614
 * virBufferStrcat:
E
Eric Blake 已提交
615
 * @buf: the buffer to append to
616
 * @...:  the variable list of strings, the last argument must be NULL
D
Daniel Veillard 已提交
617
 *
E
Eric Blake 已提交
618 619
 * Concatenate strings to an XML buffer.  Auto indentation may be applied
 * after each string argument.
D
Daniel Veillard 已提交
620
 */
621
void
D
Daniel P. Berrange 已提交
622
virBufferStrcat(virBufferPtr buf, ...)
D
Daniel Veillard 已提交
623 624 625 626
{
    va_list ap;
    char *str;

627 628 629
    if (buf->error)
        return;

D
Daniel Veillard 已提交
630
    va_start(ap, buf);
E
Eric Blake 已提交
631 632
    while ((str = va_arg(ap, char *)) != NULL)
        virBufferAdd(buf, str, -1);
D
Daniel Veillard 已提交
633 634
    va_end(ap);
}
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670

/**
 * virBufferTrim:
 * @buf: the buffer to trim
 * @str: the optional string, to force an exact trim
 * @len: the number of bytes to trim, or -1 to use @str
 *
 * Trim the tail of a buffer.  If @str is provided, the trim only occurs
 * if the current tail of the buffer matches @str; a non-negative @len
 * further limits how much of the tail is trimmed.  If @str is NULL, then
 * @len must be non-negative.
 *
 * Returns -1 if @buf has previously encountered an error or if @len is
 * invalid, 0 if there was nothing to trim (@buf was too short or @str
 * didn't match), and 1 if the trim was successful.
 */
int
virBufferTrim(virBufferPtr buf, const char *str, int len)
{
    size_t len2 = 0;

    if (!buf || buf->error || (!str && len < 0))
        return -1;

    if (len > 0 && len > buf->use)
        return 0;
    if (str) {
        len2 = strlen(str);
        if (len2 > buf->use ||
            memcmp(&buf->content[buf->use - len2], str, len2) != 0)
            return 0;
    }
    buf->use -= len < 0 ? len2 : len;
    buf->content[buf->use] = '\0';
    return 1;
}