buf.c 15.3 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
 * virBufferContentAndReset:
 * @buf: Buffer
180
 *
181 182 183
 * 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.
184
 *
185
 * Returns the buffer content or NULL in case of error.
186
 */
187
char *
E
Eric Blake 已提交
188
virBufferContentAndReset(virBufferPtr buf)
D
Daniel Veillard 已提交
189
{
190 191 192
    char *str;
    if (buf == NULL)
        return NULL;
D
Daniel Veillard 已提交
193

194 195
    if (buf->error) {
        memset(buf, 0, sizeof(*buf));
D
Daniel Veillard 已提交
196 197 198
        return NULL;
    }

199 200 201
    str = buf->content;
    memset(buf, 0, sizeof(*buf));
    return str;
D
Daniel Veillard 已提交
202 203
}

204 205 206 207 208 209
/**
 * virBufferFreeAndReset:
 * @buf: the buffer to free and reset
 *
 * Frees the buffer content and resets the buffer structure.
 */
E
Eric Blake 已提交
210
void virBufferFreeAndReset(virBufferPtr buf)
211 212 213 214 215 216
{
    char *str = virBufferContentAndReset(buf);

    VIR_FREE(str);
}

217
/**
218 219
 * virBufferError:
 * @buf: the buffer
220
 *
221
 * Check to see if the buffer is in an error state due
E
Eric Blake 已提交
222
 * to failed memory allocation or usage error
223
 *
E
Eric Blake 已提交
224
 * Return positive errno value or -1 on usage error, 0 if normal
225
 */
226
int
D
Daniel P. Berrange 已提交
227
virBufferError(const virBufferPtr buf)
D
Daniel Veillard 已提交
228
{
229
    if (buf == NULL)
E
Eric Blake 已提交
230
        return -1;
231 232

    return buf->error;
D
Daniel Veillard 已提交
233 234 235
}

/**
236 237
 * virBufferUse:
 * @buf: the usage of the string in the buffer
D
Daniel Veillard 已提交
238
 *
239
 * Return the string usage in bytes
D
Daniel Veillard 已提交
240
 */
241 242
unsigned int
virBufferUse(const virBufferPtr buf)
D
Daniel Veillard 已提交
243
{
244
    if (buf == NULL)
245
        return 0;
D
Daniel Veillard 已提交
246

247
    return buf->use;
D
Daniel Veillard 已提交
248 249 250
}

/**
251
 * virBufferAsprintf:
E
Eric Blake 已提交
252
 * @buf: the buffer to append to
D
Daniel Veillard 已提交
253
 * @format:  the format
254
 * @...:  the variable list of arguments
D
Daniel Veillard 已提交
255
 *
E
Eric Blake 已提交
256
 * Do a formatted print to an XML buffer.  Auto indentation may be applied.
D
Daniel Veillard 已提交
257
 */
258
void
E
Eric Blake 已提交
259
virBufferAsprintf(virBufferPtr buf, const char *format, ...)
D
Daniel Veillard 已提交
260
{
261
    va_list argptr;
E
Eric Blake 已提交
262 263 264 265 266 267 268
    va_start(argptr, format);
    virBufferVasprintf(buf, format, argptr);
    va_end(argptr);
}

/**
 * virBufferVasprintf:
E
Eric Blake 已提交
269
 * @buf: the buffer to append to
E
Eric Blake 已提交
270 271 272
 * @format:  the format
 * @argptr:  the variable list of arguments
 *
E
Eric Blake 已提交
273
 * Do a formatted print to an XML buffer.  Auto indentation may be applied.
E
Eric Blake 已提交
274 275
 */
void
E
Eric Blake 已提交
276
virBufferVasprintf(virBufferPtr buf, const char *format, va_list argptr)
E
Eric Blake 已提交
277 278 279
{
    int size, count, grow_size;
    va_list copy;
D
Daniel Veillard 已提交
280

281 282 283 284 285
    if ((format == NULL) || (buf == NULL))
        return;

    if (buf->error)
        return;
286

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

289 290
    if (buf->size == 0 &&
        virBufferGrow(buf, 100) < 0)
291
        return;
292

E
Eric Blake 已提交
293
    va_copy(copy, argptr);
294 295 296

    size = buf->size - buf->use;
    if ((count = vsnprintf(&buf->content[buf->use],
E
Eric Blake 已提交
297
                           size, format, copy)) < 0) {
E
Eric Blake 已提交
298
        virBufferSetError(buf, errno);
E
Eric Blake 已提交
299 300
        va_end(copy);
        return;
301
    }
E
Eric Blake 已提交
302
    va_end(copy);
303 304 305

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

308
        grow_size = (count + 1 > 1000) ? count + 1 : 1000;
309
        if (virBufferGrow(buf, grow_size) < 0) {
E
Eric Blake 已提交
310
            return;
311
        }
312

313 314 315
        size = buf->size - buf->use;
        if ((count = vsnprintf(&buf->content[buf->use],
                               size, format, argptr)) < 0) {
E
Eric Blake 已提交
316
            virBufferSetError(buf, errno);
E
Eric Blake 已提交
317
            return;
318
        }
D
Daniel Veillard 已提交
319 320 321 322
    }
    buf->use += count;
}

323 324
/**
 * virBufferEscapeString:
E
Eric Blake 已提交
325
 * @buf: the buffer to append to
326
 * @format: a printf like format string but with only one %s parameter
E
Eric Blake 已提交
327
 * @str: the string argument which needs to be escaped
328
 *
E
Eric Blake 已提交
329 330 331 332
 * 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.
333
 */
334
void
E
Eric Blake 已提交
335
virBufferEscapeString(virBufferPtr buf, const char *format, const char *str)
336
{
337
    int len;
338 339 340
    char *escaped, *out;
    const char *cur;

341 342 343 344 345
    if ((format == NULL) || (buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;
346 347

    len = strlen(str);
348
    if (strcspn(str, "<>&'\"") == len) {
349
        virBufferAsprintf(buf, format, str);
350 351 352
        return;
    }

353 354
    if (xalloc_oversized(6, len) ||
        VIR_ALLOC_N(escaped, 6 * len + 1) < 0) {
E
Eric Blake 已提交
355
        virBufferSetError(buf, errno);
356
        return;
357
    }
358

359 360 361 362
    cur = str;
    out = escaped;
    while (*cur != 0) {
        if (*cur == '<') {
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
            *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++ = ';';
378 379 380 381 382 383 384 385 386 387 388 389 390 391
        } 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++ = ';';
392
        } else if (((unsigned char)*cur >= 0x20) || (*cur == '\n') || (*cur == '\t') ||
393 394 395 396 397 398 399 400 401 402
                   (*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++;
403 404 405
    }
    *out = 0;

406
    virBufferAsprintf(buf, format, escaped);
407
    VIR_FREE(escaped);
408 409
}

410 411
/**
 * virBufferEscapeSexpr:
E
Eric Blake 已提交
412
 * @buf: the buffer to append to
413
 * @format: a printf like format string but with only one %s parameter
E
Eric Blake 已提交
414
 * @str: the string argument which needs to be escaped
415
 *
E
Eric Blake 已提交
416 417 418 419
 * 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.
420 421
 */
void
E
Eric Blake 已提交
422
virBufferEscapeSexpr(virBufferPtr buf,
423 424
                     const char *format,
                     const char *str)
S
Sage Weil 已提交
425
{
426
    virBufferEscape(buf, '\\', "\\'", format, str);
S
Sage Weil 已提交
427 428 429 430
}

/**
 * virBufferEscape:
E
Eric Blake 已提交
431
 * @buf: the buffer to append to
432
 * @escape: the escape character to inject
E
Eric Blake 已提交
433
 * @toescape: NUL-terminated list of characters to escape
S
Sage Weil 已提交
434
 * @format: a printf like format string but with only one %s parameter
E
Eric Blake 已提交
435
 * @str: the string argument which needs to be escaped
S
Sage Weil 已提交
436 437
 *
 * Do a formatted print with a single string to a buffer.  Any characters
438
 * in the provided list are escaped with the given escape.  Auto indentation
E
Eric Blake 已提交
439
 * may be applied.
S
Sage Weil 已提交
440 441
 */
void
442
virBufferEscape(virBufferPtr buf, char escape, const char *toescape,
E
Eric Blake 已提交
443
                const char *format, const char *str)
444 445 446 447 448 449 450 451 452 453 454 455
{
    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 已提交
456
    if (strcspn(str, toescape) == len) {
457
        virBufferAsprintf(buf, format, str);
458 459 460
        return;
    }

461 462
    if (xalloc_oversized(2, len) ||
        VIR_ALLOC_N(escaped, 2 * len + 1) < 0) {
E
Eric Blake 已提交
463
        virBufferSetError(buf, errno);
464 465 466 467 468 469
        return;
    }

    cur = str;
    out = escaped;
    while (*cur != 0) {
470 471 472 473 474
        /* 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))
475
            *out++ = escape;
S
Sage Weil 已提交
476
        *out++ = *cur;
477 478 479 480
        cur++;
    }
    *out = 0;

481
    virBufferAsprintf(buf, format, escaped);
482 483 484
    VIR_FREE(escaped);
}

485 486
/**
 * virBufferURIEncodeString:
E
Eric Blake 已提交
487
 * @buf: the buffer to append to
488 489 490 491
 * @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 已提交
492
 * with '%xx' hex sequences).  Auto indentation may be applied.
493
 */
494
void
E
Eric Blake 已提交
495
virBufferURIEncodeString(virBufferPtr buf, const char *str)
496 497 498 499 500 501
{
    int grow_size = 0;
    const char *p;
    unsigned char uc;
    const char *hex = "0123456789abcdef";

502 503 504 505 506 507
    if ((buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

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

510
    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
511
        if (c_isalnum(*p))
512 513 514 515 516
            grow_size++;
        else
            grow_size += 3; /* %ab */
    }

E
Eric Blake 已提交
517
    if (virBufferGrow(buf, grow_size) < 0)
518
        return;
519 520

    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
521
        if (c_isalnum(*p))
522 523 524 525 526 527 528 529 530 531 532 533
            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 已提交
534 535
/**
 * virBufferEscapeShell:
E
Eric Blake 已提交
536 537
 * @buf: the buffer to append to
 * @str: an unquoted string
G
Guido Günther 已提交
538 539
 *
 * Quotes a string so that the shell (/bin/sh) will interpret the
E
Eric Blake 已提交
540
 * quoted string to mean str.  Auto indentation may be applied.
G
Guido Günther 已提交
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
 */
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. */
556
    if (*str && !strpbrk(str, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~")) {
G
Guido Günther 已提交
557 558 559 560
        virBufferAdd(buf, str, -1);
        return;
    }

561 562 563 564
    if (*str) {
        len = strlen(str);
        if (xalloc_oversized(4, len) ||
            VIR_ALLOC_N(escaped, 4 * len + 3) < 0) {
E
Eric Blake 已提交
565
            virBufferSetError(buf, errno);
566 567 568
            return;
        }
    } else {
569
        virBufferAddLit(buf, "''");
G
Guido Günther 已提交
570 571 572 573 574 575 576 577 578
        return;
    }

    cur = str;
    out = escaped;

    *out++ = '\'';
    while (*cur != 0) {
        if (*cur == '\'') {
579
            *out++ = '\'';
G
Guido Günther 已提交
580 581 582 583
            /* Replace literal ' with a close ', a \', and a open ' */
            *out++ = '\\';
            *out++ = '\'';
        }
584
        *out++ = *cur++;
G
Guido Günther 已提交
585 586 587 588 589 590 591 592
    }
    *out++ = '\'';
    *out = 0;

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

D
Daniel Veillard 已提交
593
/**
D
Daniel P. Berrange 已提交
594
 * virBufferStrcat:
E
Eric Blake 已提交
595
 * @buf: the buffer to append to
596
 * @...:  the variable list of strings, the last argument must be NULL
D
Daniel Veillard 已提交
597
 *
E
Eric Blake 已提交
598 599
 * Concatenate strings to an XML buffer.  Auto indentation may be applied
 * after each string argument.
D
Daniel Veillard 已提交
600
 */
601
void
D
Daniel P. Berrange 已提交
602
virBufferStrcat(virBufferPtr buf, ...)
D
Daniel Veillard 已提交
603 604 605 606
{
    va_list ap;
    char *str;

607 608 609
    if (buf->error)
        return;

D
Daniel Veillard 已提交
610
    va_start(ap, buf);
E
Eric Blake 已提交
611 612
    while ((str = va_arg(ap, char *)) != NULL)
        virBufferAdd(buf, str, -1);
D
Daniel Veillard 已提交
613 614
    va_end(ap);
}
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650

/**
 * 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;
}