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
E
Eric Blake 已提交
42
virBufferSetError(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
E
Eric Blake 已提交
91
virBufferAdd(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
/**
 * virBufferAddChar:
E
Eric Blake 已提交
116
 * @buf: the buffer to append to
117 118 119 120 121
 * @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 *
E
Eric Blake 已提交
153
virBufferContentAndReset(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
/**
 * virBufferFreeAndReset:
 * @buf: the buffer to free and reset
 *
 * Frees the buffer content and resets the buffer structure.
 */
E
Eric Blake 已提交
175
void virBufferFreeAndReset(virBufferPtr buf)
176 177 178 179 180 181
{
    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:
E
Eric Blake 已提交
217
 * @buf: the buffer to append to
D
Daniel Veillard 已提交
218
 * @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
E
Eric Blake 已提交
224
virBufferAsprintf(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
    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
E
Eric Blake 已提交
241
virBufferVasprintf(virBufferPtr buf, const char *format, va_list argptr)
E
Eric Blake 已提交
242 243 244
{
    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
/**
 * virBufferEscapeString:
E
Eric Blake 已提交
288
 * @buf: the buffer to append to
289 290 291 292 293 294
 * @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
void
E
Eric Blake 已提交
296
virBufferEscapeString(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
/**
 * virBufferEscapeSexpr:
E
Eric Blake 已提交
373
 * @buf: the buffer to append to
374 375 376 377 378 379 380 381
 * @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
E
Eric Blake 已提交
382
virBufferEscapeSexpr(virBufferPtr buf,
383 384
                     const char *format,
                     const char *str)
S
Sage Weil 已提交
385 386 387 388 389 390
{
    virBufferEscape(buf, "\\'", format, str);
}

/**
 * virBufferEscape:
E
Eric Blake 已提交
391 392
 * @buf: the buffer to append to
 * @toescape: NUL-terminated list of characters to escape
S
Sage Weil 已提交
393
 * @format: a printf like format string but with only one %s parameter
E
Eric Blake 已提交
394
 * @str: the string argument which needs to be escaped
S
Sage Weil 已提交
395 396 397 398 399
 *
 * Do a formatted print with a single string to a buffer.  Any characters
 * in the provided list are escaped with a preceeding \.
 */
void
E
Eric Blake 已提交
400 401
virBufferEscape(virBufferPtr buf, const char *toescape,
                const char *format, const char *str)
402 403 404 405 406 407 408 409 410 411 412 413
{
    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 已提交
414
    if (strcspn(str, toescape) == len) {
415
        virBufferAsprintf(buf, format, str);
416 417 418
        return;
    }

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

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

435
    virBufferAsprintf(buf, format, escaped);
436 437 438
    VIR_FREE(escaped);
}

439 440
/**
 * virBufferURIEncodeString:
E
Eric Blake 已提交
441
 * @buf: the buffer to append to
442 443 444 445 446 447
 * @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).
 */
448
void
E
Eric Blake 已提交
449
virBufferURIEncodeString(virBufferPtr buf, const char *str)
450 451 452 453 454 455
{
    int grow_size = 0;
    const char *p;
    unsigned char uc;
    const char *hex = "0123456789abcdef";

456 457 458 459 460 461
    if ((buf == NULL) || (str == NULL))
        return;

    if (buf->error)
        return;

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

469 470
    if (virBufferGrow (buf, grow_size) < 0)
        return;
471 472

    for (p = str; *p; ++p) {
J
Jim Meyering 已提交
473
        if (c_isalnum(*p))
474 475 476 477 478 479 480 481 482 483 484 485
            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 已提交
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
/**
 * 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. */
508
    if (*str && !strpbrk(str, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~")) {
G
Guido Günther 已提交
509 510 511 512
        virBufferAdd(buf, str, -1);
        return;
    }

513 514 515 516 517 518 519 520
    if (*str) {
        len = strlen(str);
        if (xalloc_oversized(4, len) ||
            VIR_ALLOC_N(escaped, 4 * len + 3) < 0) {
            virBufferSetError(buf);
            return;
        }
    } else {
521
        virBufferAddLit(buf, "''");
G
Guido Günther 已提交
522 523 524 525 526 527 528 529 530
        return;
    }

    cur = str;
    out = escaped;

    *out++ = '\'';
    while (*cur != 0) {
        if (*cur == '\'') {
531
            *out++ = '\'';
G
Guido Günther 已提交
532 533 534 535
            /* Replace literal ' with a close ', a \', and a open ' */
            *out++ = '\\';
            *out++ = '\'';
        }
536
        *out++ = *cur++;
G
Guido Günther 已提交
537 538 539 540 541 542 543 544
    }
    *out++ = '\'';
    *out = 0;

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

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

558 559 560
    if (buf->error)
        return;

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