sexpr.c 13.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * sexpr.c : S-Expression routines to communicate with the Xen Daemon
 *
 * Copyright (C) 2005
 *
 *      Anthony Liguori <aliguori@us.ibm.com>
 *
 *  This file is subject to the terms and conditions of the GNU Lesser General
 *  Public License. See the file COPYING.LIB in the main directory of this
 *  archive for more details.
 */

13
#define _GNU_SOURCE /* for strndup */
14 15

#include "sexpr.h"
16
#include "internal.h"
17 18 19 20 21 22 23

#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>

24 25 26 27 28 29 30 31 32
/**
 * virSexprError:
 * @conn: the connection if available
 * @error: the error noumber
 * @info: extra information string
 *
 * Handle an error in the S-Expression code
 */
static void
33 34
virSexprError(virErrorNumber error, const char *info)
{
35
    const char *errmsg;
36

37 38 39 40
    if (error == VIR_ERR_OK)
        return;

    errmsg = __virErrorMsg(error, info);
41
    __virRaiseError(NULL, NULL, NULL, VIR_FROM_SEXPR, error, VIR_ERR_ERROR,
42 43 44
                    errmsg, info, NULL, 0, 0, errmsg, info);
}

45 46 47 48 49 50 51 52 53 54 55 56 57 58
/**
 * sexpr_new:
 *
 * Create a new S-Expression
 *
 * Returns the new node or NULL in case of memory allocation error
 */
static struct sexpr *
sexpr_new(void)
{
    struct sexpr *ret;

    ret = (struct sexpr *) malloc(sizeof(*ret));
    if (ret == NULL) {
59
        virSexprError(VIR_ERR_NO_MEMORY, _("failed to allocate a node"));
60
        return (NULL);
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    }
    ret->kind = SEXPR_NIL;
    return ret;
}

/**
 * sexpr_free:
 * @sexpr: the S-Expression pointer
 *
 * Free an S-Expression
 */
void
sexpr_free(struct sexpr *sexpr)
{
    int serrno = errno;

    if (sexpr == NULL) {
        return;
    }

    switch (sexpr->kind) {
        case SEXPR_CONS:
83 84
            sexpr_free(sexpr->u.s.car);
            sexpr_free(sexpr->u.s.cdr);
85 86
            break;
        case SEXPR_VALUE:
87
            free(sexpr->u.value);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
            break;
        case SEXPR_NIL:
            break;
    }

    free(sexpr);

    errno = serrno;
}

/**
 * sexpr_nil:
 *
 * Provide a NIL S-Expression (the pointer is not shared so NIL equality
 * testing won't work at the pointer level).
 *
 * Returns a new NIL S-Expression of NULL in case of error.
 */
struct sexpr *
sexpr_nil(void)
{
    return sexpr_new();
}

/**
 * sexpr_string:
 * @str:  the input string, assumed to be UTF-8
 * @len:  the length in bytes of the input
 *
 * Parse the input S-Expression and return a pointer to the result
 *
 * Returns the S-Expression pointer or NULL in case of error
 */
struct sexpr *
sexpr_string(const char *str, ssize_t len)
{
    struct sexpr *ret = sexpr_new();

    if (ret == NULL)
        return ret;
    ret->kind = SEXPR_VALUE;
    if (len > 0) {
130
        ret->u.value = strndup(str, len);
131
    } else {
132
        ret->u.value = strdup(str);
133 134
    }

135
    if (ret->u.value == NULL) {
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
        return NULL;
    }

    return ret;
}

/**
 * sexpr_cons:
 * @car: the left operand
 * @cdr: the right operand
 *
 * Implement the CONS operation assembling 2 existing S-Expressions.
 * Note that in case of error the input data are not freed.
 *
 * Returns the resulting S-Expression pointer or NULL in case of error.
 */
struct sexpr *
sexpr_cons(struct sexpr *car, struct sexpr *cdr)
{
    struct sexpr *ret = sexpr_new();

    if (ret == NULL)
        return ret;
    ret->kind = SEXPR_CONS;
160 161
    ret->u.s.car = car;
    ret->u.s.cdr = cdr;
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

    return ret;
}

/**
 * append:
 * @lst: an existing list
 * @value: the value
 *
 * Internal operation appending a value at the end of an existing list
 */
static void
append(struct sexpr *lst, struct sexpr *value)
{
    while (lst->kind != SEXPR_NIL) {
177
        lst = lst->u.s.cdr;
178 179 180
    }

    lst->kind = SEXPR_CONS;
181 182
    lst->u.s.car = value;
    lst->u.s.cdr = sexpr_nil();
183 184 185 186 187 188 189 190 191 192 193 194 195 196
}

/**
 * @lst: an existing list
 * @value: the value
 *
 * Append a value at the end of an existing list
 *
 * Returns lst or NULL in case of error
 */
struct sexpr *
sexpr_append(struct sexpr *lst, struct sexpr *value)
{
    if (lst == NULL)
197
        return (NULL);
198
    if (value == NULL)
199
        return (lst);
200
    append(lst, value);
201
    return (lst);
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
}

/**
 * sexpr2string:
 * @sexpr: an S-Expression pointer
 * @buffer: the output buffer
 * @n_buffer: the size of the buffer in bytes
 *
 * Serialize the S-Expression in the buffer.
 * Note that the output may be truncated if @n_buffer is too small
 * resulting in an unparseable value.
 *
 * Returns the number of bytes used by the serialization in the buffer or
 *         0 in case of error.
 */
size_t
sexpr2string(struct sexpr * sexpr, char *buffer, size_t n_buffer)
{
    size_t ret = 0, tmp;

    if ((sexpr == NULL) || (buffer == NULL) || (n_buffer <= 0))
223
        return (0);
224 225 226 227

    switch (sexpr->kind) {
        case SEXPR_CONS:
            tmp = snprintf(buffer + ret, n_buffer - ret, "(");
228 229 230
            if (tmp == 0)
                goto error;
            ret += tmp;
231
            tmp = sexpr2string(sexpr->u.s.car, buffer + ret, n_buffer - ret);
232 233 234
            if (tmp == 0)
                goto error;
            ret += tmp;
235 236
            while (sexpr->u.s.cdr->kind != SEXPR_NIL) {
                sexpr = sexpr->u.s.cdr;
237
                tmp = snprintf(buffer + ret, n_buffer - ret, " ");
238 239 240 241
                if (tmp == 0)
                    goto error;
                ret += tmp;
                tmp =
242
                    sexpr2string(sexpr->u.s.car, buffer + ret, n_buffer - ret);
243 244 245
                if (tmp == 0)
                    goto error;
                ret += tmp;
246 247
            }
            tmp = snprintf(buffer + ret, n_buffer - ret, ")");
248 249 250
            if (tmp == 0)
                goto error;
            ret += tmp;
251 252
            break;
        case SEXPR_VALUE:
253
            if (strchr(sexpr->u.value, ' '))
254
                tmp = snprintf(buffer + ret, n_buffer - ret, "'%s'",
255
                               sexpr->u.value);
256 257
            else
                tmp = snprintf(buffer + ret, n_buffer - ret, "%s",
258
                               sexpr->u.value);
259 260 261
            if (tmp == 0)
                goto error;
            ret += tmp;
262 263 264
            break;
        case SEXPR_NIL:
            break;
265 266
        default:
            goto error;
267 268
    }

269 270
    return (ret);
  error:
271 272
    buffer[n_buffer - 1] = 0;
    virSexprError(VIR_ERR_SEXPR_SERIAL, buffer);
273
    return (0);
274 275 276 277 278 279 280 281 282
}

#define IS_SPACE(c) ((c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA))

static const char *
trim(const char *string)
{
    while (IS_SPACE(*string))
        string++;
283
    return (string);
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
}

/**
 * _string2sexpr:
 * @buffer: a zero terminated buffer containing an S-Expression in UTF-8
 * @end: pointer to an index in the buffer for the already parsed bytes
 *
 * Internal routine implementing the parse of S-Expression
 * Note that failure in this function is catrosphic.  If it returns
 * NULL, you've leaked memory and you're currently OOM.  It will always
 * parse an SEXPR given a buffer
 *
 * Returns a pointer to the resulting parsed S-Expression, or NULL in case of
 *         hard error.
 */
static struct sexpr *
_string2sexpr(const char *buffer, size_t * end)
{
    const char *ptr = buffer + *end;
    struct sexpr *ret = sexpr_new();

    if (ret == NULL)
        return NULL;

    ptr = trim(ptr);

    if (ptr[0] == '(') {
        ret->kind = SEXPR_NIL;

        ptr = trim(ptr + 1);
        while (*ptr && *ptr != ')') {
            struct sexpr *tmp;
            size_t tmp_len = 0;

            tmp = _string2sexpr(ptr, &tmp_len);
            if (tmp == NULL)
                return NULL;
            append(ret, tmp);
#if 0
            if (0) {
                char buf[4096];

                sexpr2string(ret, buf, sizeof(buf));
                printf("%s\n", buffer);
            }
#endif
            ptr = trim(ptr + tmp_len);
        }

        if (*ptr == ')') {
            ptr++;
        }
    } else {
        const char *start;

        if (*ptr == '\'') {
            ptr++;
            start = ptr;

            while (*ptr && *ptr != '\'') {
                if (*ptr == '\\' && ptr[1])
                    ptr++;
                ptr++;
            }

349 350
            ret->u.value = strndup(start, ptr - start);
            if (ret->u.value == NULL) {
351
                virSexprError(VIR_ERR_NO_MEMORY,
352
                              _("failed to copy a string"));
353
            }
354 355 356 357 358 359 360 361 362 363

            if (*ptr == '\'')
                ptr++;
        } else {
            start = ptr;

            while (*ptr && !isspace(*ptr) && *ptr != ')' && *ptr != '(') {
                ptr++;
            }

364 365
            ret->u.value = strndup(start, ptr - start);
            if (ret->u.value == NULL) {
366
                virSexprError(VIR_ERR_NO_MEMORY,
367
                              _("failed to copy a string"));
368
            }
369 370 371
        }

        ret->kind = SEXPR_VALUE;
372
        if (ret->u.value == NULL)
373
            goto error;
374 375 376 377 378
    }

    *end = ptr - buffer;

    return ret;
379

380
  error:
381
    sexpr_free(ret);
382
    return (NULL);
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
}

/**
 * string2sexpr:
 * @buffer: a zero terminated buffer containing an S-Expression in UTF-8
 *
 * Parse the S-Expression in the buffer.
 * Note that failure in this function is catrosphic.  If it returns
 * NULL, you've leaked memory and you're currently OOM.  It will always
 * parse an SEXPR given a buffer
 *
 * Returns a pointer to the resulting parsed S-Expression, or NULL in case of
 *         hard error.
 */
struct sexpr *
string2sexpr(const char *buffer)
{
    size_t dummy = 0;

    return _string2sexpr(buffer, &dummy);
}


/**
D
Daniel P. Berrange 已提交
407
 * sexpr_lookup_key:
408 409 410 411
 * @sexpr: a pointer to a parsed S-Expression
 * @node: a path for the sub expression to lookup in the S-Expression
 *
 * Search a sub expression in the S-Expression based on its path
D
Daniel P. Berrange 已提交
412
 * Returns the key node, rather than the data node.
413 414 415 416
 * NOTE: path are limited to 4096 bytes.
 *
 * Returns the pointer to the sub expression or NULL if not found.
 */
D
Daniel P. Berrange 已提交
417 418
static struct sexpr *
sexpr_lookup_key(struct sexpr *sexpr, const char *node)
419 420 421 422
{
    char buffer[4096], *ptr, *token;

    if ((node == NULL) || (sexpr == NULL))
423
        return (NULL);
424 425 426 427 428 429

    snprintf(buffer, sizeof(buffer), "%s", node);

    ptr = buffer;
    token = strsep(&ptr, "/");

430
    if (sexpr->kind != SEXPR_CONS || sexpr->u.s.car->kind != SEXPR_VALUE) {
431 432 433
        return NULL;
    }

434
    if (strcmp(sexpr->u.s.car->u.value, token) != 0) {
435 436 437 438 439 440 441 442 443
        return NULL;
    }

    for (token = strsep(&ptr, "/"); token; token = strsep(&ptr, "/")) {
        struct sexpr *i;

        if (token == NULL)
            continue;

444 445
        sexpr = sexpr->u.s.cdr;
        for (i = sexpr; i->kind != SEXPR_NIL; i = i->u.s.cdr) {
446
            if (i->kind != SEXPR_CONS ||
447 448
                i->u.s.car->kind != SEXPR_CONS ||
                i->u.s.car->u.s.car->kind != SEXPR_VALUE) {
449 450 451
                continue;
            }

452 453
            if (strcmp(i->u.s.car->u.s.car->u.value, token) == 0) {
                sexpr = i->u.s.car;
454 455 456 457 458 459 460 461 462 463 464 465 466
                break;
            }
        }

        if (i->kind == SEXPR_NIL) {
            break;
        }
    }

    if (token != NULL) {
        return NULL;
    }

D
Daniel P. Berrange 已提交
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
    return sexpr;
}

/**
 * sexpr_lookup:
 * @sexpr: a pointer to a parsed S-Expression
 * @node: a path for the sub expression to lookup in the S-Expression
 *
 * Search a sub expression in the S-Expression based on its path.
 * NOTE: path are limited to 4096 bytes.
 *
 * Returns the pointer to the sub expression or NULL if not found.
 */
struct sexpr *
sexpr_lookup(struct sexpr *sexpr, const char *node)
{
    struct sexpr *s = sexpr_lookup_key(sexpr, node);

    if (s == NULL)
	return NULL;

    if (s->kind != SEXPR_CONS || s->u.s.cdr->kind != SEXPR_CONS)
489 490
        return NULL;

D
Daniel P. Berrange 已提交
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
    return s->u.s.cdr;
}

/**
 * sexpr_has:
 * @sexpr: a pointer to a parsed S-Expression
 * @node: a path for the sub expression to lookup in the S-Expression
 *
 * Search a sub expression in the S-Expression based on its path.
 * NOTE: path are limited to 4096 bytes.
 * NB, even if the key was found sexpr_lookup may return NULL if
 * the corresponding value was empty
 *
 * Returns true if the key was found, false otherwise 
 */
int
sexpr_has(struct sexpr *sexpr, const char *node)
{
    struct sexpr *s = sexpr_lookup_key(sexpr, node);

    if (s == NULL)
	return 0;

    if (s->kind != SEXPR_CONS)
        return 0;

    return 1;
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
}

/**
 * sexpr_node:
 * @sexpr: a pointer to a parsed S-Expression
 * @node: a path for the node to lookup in the S-Expression
 *
 * Search a node value in the S-Expression based on its path
 * NOTE: path are limited to 4096 bytes.
 *
 * Returns the value of the node or NULL if not found.
 */
const char *
sexpr_node(struct sexpr *sexpr, const char *node)
{
    struct sexpr *n = sexpr_lookup(sexpr, node);

535
    return (n && n->u.s.car->kind == SEXPR_VALUE) ? n->u.s.car->u.value : NULL;
536
}
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560

/**
 * sexpr_fmt_node:
 * @sexpr: a pointer to a parsed S-Expression
 * @fmt: a path for the node to lookup in the S-Expression
 * @... extra data to build the path
 *
 * Search a node value in the S-Expression based on its path
 * NOTE: path are limited to 4096 bytes.
 *
 * Returns the value of the node or NULL if not found.
 */
const char *
sexpr_fmt_node(struct sexpr *sexpr, const char *fmt, ...)
{
    va_list ap;
    char node[4096];

    va_start(ap, fmt);
    vsnprintf(node, sizeof(node), fmt, ap);
    va_end(ap);

    return sexpr_node(sexpr, node);
}