buf.c 14.0 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

/* 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 427 428 429 430
{
    virBufferEscape(buf, "\\'", format, str);
}

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

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

    cur = str;
    out = escaped;
    while (*cur != 0) {
S
Sage Weil 已提交
469
        if (strchr(toescape, *cur))
470
            *out++ = '\\';
S
Sage Weil 已提交
471
        *out++ = *cur;
472 473 474 475
        cur++;
    }
    *out = 0;

476
    virBufferAsprintf(buf, format, escaped);
477 478 479
    VIR_FREE(escaped);
}

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

497 498 499 500 501 502
    if ((buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

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

505
    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
506
        if (c_isalnum(*p))
507 508 509 510 511
            grow_size++;
        else
            grow_size += 3; /* %ab */
    }

E
Eric Blake 已提交
512
    if (virBufferGrow(buf, grow_size) < 0)
513
        return;
514 515

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

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

    cur = str;
    out = escaped;

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

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

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

602 603 604
    if (buf->error)
        return;

D
Daniel Veillard 已提交
605
    va_start(ap, buf);
E
Eric Blake 已提交
606 607
    while ((str = va_arg(ap, char *)) != NULL)
        virBufferAdd(buf, str, -1);
D
Daniel Veillard 已提交
608 609
    va_end(ap);
}