/* * sexpr.c : S-Expression routines to communicate with the Xen Daemon * * Copyright (C) 2005 * * Anthony Liguori * * 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. */ #define _GNU_SOURCE #include "sexpr.h" #include "internal.h" #include #include #include #include #include /** * virSexprError: * @conn: the connection if available * @error: the error noumber * @info: extra information string * * Handle an error in the S-Expression code */ static void virSexprError(virErrorNumber error, const char *info) { const char *errmsg; if (error == VIR_ERR_OK) return; errmsg = __virErrorMsg(error, info); __virRaiseError(NULL, NULL, VIR_FROM_SEXPR, error, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, errmsg, info); } /** * 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) { virSexprError(VIR_ERR_NO_MEMORY, "failed to allocate a node"); return (NULL); } 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: sexpr_free(sexpr->car); sexpr_free(sexpr->cdr); break; case SEXPR_VALUE: free(sexpr->value); 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) { ret->value = strndup(str, len); } else { ret->value = strdup(str); } if (ret->value == NULL) { 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; ret->car = car; ret->cdr = cdr; 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) { lst = lst->cdr; } lst->kind = SEXPR_CONS; lst->car = value; lst->cdr = sexpr_nil(); } /** * @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) return (NULL); if (value == NULL) return (lst); append(lst, value); return (lst); } /** * 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)) return (0); switch (sexpr->kind) { case SEXPR_CONS: tmp = snprintf(buffer + ret, n_buffer - ret, "("); if (tmp == 0) goto error; ret += tmp; tmp = sexpr2string(sexpr->car, buffer + ret, n_buffer - ret); if (tmp == 0) goto error; ret += tmp; while (sexpr->cdr->kind != SEXPR_NIL) { sexpr = sexpr->cdr; tmp = snprintf(buffer + ret, n_buffer - ret, " "); if (tmp == 0) goto error; ret += tmp; tmp = sexpr2string(sexpr->car, buffer + ret, n_buffer - ret); if (tmp == 0) goto error; ret += tmp; } tmp = snprintf(buffer + ret, n_buffer - ret, ")"); if (tmp == 0) goto error; ret += tmp; break; case SEXPR_VALUE: if (strchr(sexpr->value, ' ')) tmp = snprintf(buffer + ret, n_buffer - ret, "'%s'", sexpr->value); else tmp = snprintf(buffer + ret, n_buffer - ret, "%s", sexpr->value); if (tmp == 0) goto error; ret += tmp; break; case SEXPR_NIL: break; default: goto error; } return (ret); error: buffer[n_buffer - 1] = 0; virSexprError(VIR_ERR_SEXPR_SERIAL, buffer); return (0); } #define IS_SPACE(c) ((c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA)) static const char * trim(const char *string) { while (IS_SPACE(*string)) string++; return (string); } /** * _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++; } ret->value = strndup(start, ptr - start); if (ret->value == NULL) { virSexprError(VIR_ERR_NO_MEMORY, "failed to copy a string"); } if (*ptr == '\'') ptr++; } else { start = ptr; while (*ptr && !isspace(*ptr) && *ptr != ')' && *ptr != '(') { ptr++; } ret->value = strndup(start, ptr - start); if (ret->value == NULL) { virSexprError(VIR_ERR_NO_MEMORY, "failed to copy a string"); } } ret->kind = SEXPR_VALUE; if (ret->value == NULL) goto error; } *end = ptr - buffer; return ret; error: sexpr_free(ret); return (NULL); } /** * 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); } /** * 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) { char buffer[4096], *ptr, *token; if ((node == NULL) || (sexpr == NULL)) return (NULL); snprintf(buffer, sizeof(buffer), "%s", node); ptr = buffer; token = strsep(&ptr, "/"); if (sexpr->kind != SEXPR_CONS || sexpr->car->kind != SEXPR_VALUE) { return NULL; } if (strcmp(sexpr->car->value, token) != 0) { return NULL; } for (token = strsep(&ptr, "/"); token; token = strsep(&ptr, "/")) { struct sexpr *i; if (token == NULL) continue; sexpr = sexpr->cdr; for (i = sexpr; i->kind != SEXPR_NIL; i = i->cdr) { if (i->kind != SEXPR_CONS || i->car->kind != SEXPR_CONS || i->car->car->kind != SEXPR_VALUE) { continue; } if (strcmp(i->car->car->value, token) == 0) { sexpr = i->car; break; } } if (i->kind == SEXPR_NIL) { break; } } if (token != NULL) { return NULL; } if (sexpr->kind != SEXPR_CONS || sexpr->cdr->kind != SEXPR_CONS) return NULL; return sexpr->cdr; } /** * 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); return (n && n->car->kind == SEXPR_VALUE) ? n->car->value : NULL; }