2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A
2N/A#include "lint.h"
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <secdb.h>
2N/A#include <ctype.h>
2N/A#include <alloca.h>
2N/A
2N/Astatic char *do_unescape(char *, boolean_t *);
2N/A
2N/A/*
2N/A * kva_match(): Given a key-value array and a key, return a pointer to the
2N/A * value that matches the key.
2N/A */
2N/Achar *
2N/Akva_match(kva_t *kva, char *key)
2N/A{
2N/A int i;
2N/A kv_t *data;
2N/A
2N/A if (kva == NULL || key == NULL) {
2N/A return (NULL);
2N/A }
2N/A data = kva->data;
2N/A for (i = 0; i < kva->length; i++) {
2N/A if (strcmp(data[i].key, key) == 0) {
2N/A return (data[i].value);
2N/A }
2N/A }
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * _kva_free(): Free up memory.
2N/A */
2N/Avoid
2N/A_kva_free(kva_t *kva)
2N/A{
2N/A int i;
2N/A kv_t *data;
2N/A
2N/A if (kva == NULL) {
2N/A return;
2N/A }
2N/A data = kva->data;
2N/A for (i = 0; i < kva->length; i++) {
2N/A if (data[i].key != NULL) {
2N/A free(data[i].key);
2N/A data[i].key = NULL;
2N/A }
2N/A if (data[i].value != NULL) {
2N/A free(data[i].value);
2N/A data[i].value = NULL;
2N/A }
2N/A }
2N/A free(kva->data);
2N/A free(kva);
2N/A}
2N/A
2N/A/*
2N/A * _kva_free_value(): Free up memory (value) for all the occurrences of
2N/A * the given key.
2N/A */
2N/Avoid
2N/A_kva_free_value(kva_t *kva, char *key)
2N/A{
2N/A int ctr;
2N/A kv_t *data;
2N/A
2N/A if (kva == NULL) {
2N/A return;
2N/A }
2N/A
2N/A ctr = kva->length;
2N/A data = kva->data;
2N/A
2N/A while (ctr--) {
2N/A if (strcmp(data->key, key) == 0 && data->value != NULL) {
2N/A free(data->value);
2N/A data->value = NULL;
2N/A }
2N/A data++;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * new_kva(): Allocate a key-value array.
2N/A */
2N/Akva_t *
2N/A_new_kva(int size)
2N/A{
2N/A kva_t *new_kva;
2N/A
2N/A if ((new_kva = calloc(1, sizeof (kva_t))) == NULL) {
2N/A return (NULL);
2N/A }
2N/A if ((new_kva->data = calloc(1, (size*sizeof (kv_t)))) == NULL) {
2N/A free(new_kva);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (new_kva);
2N/A}
2N/A
2N/A/*
2N/A * _str2kva(): Given a string (s) of key-value pairs, separated by delimeter
2N/A * (del), place the values into the key value array (nkva).
2N/A */
2N/Akva_t *
2N/A_str2kva(char *s, char *ass, char *del)
2N/A{
2N/A int n = 0;
2N/A int m;
2N/A int size = KV_ADD_KEYS;
2N/A char *buf;
2N/A char *p;
2N/A char *pair;
2N/A char *key;
2N/A char *last_pair;
2N/A char *last_key;
2N/A kv_t *data;
2N/A kva_t *nkva;
2N/A boolean_t err = B_FALSE;
2N/A
2N/A if (s == NULL ||
2N/A ass == NULL ||
2N/A del == NULL ||
2N/A *s == '\0' ||
2N/A *s == '\n' ||
2N/A (strlen(s) <= 1)) {
2N/A return (NULL);
2N/A }
2N/A p = s;
2N/A while ((p = _strpbrk_escape(p, ass)) != NULL) {
2N/A n++;
2N/A p++;
2N/A }
2N/A if (n > size) {
2N/A m = n/size;
2N/A if (n%size) {
2N/A ++m;
2N/A }
2N/A size = m * KV_ADD_KEYS;
2N/A }
2N/A if ((nkva = _new_kva(size)) == NULL) {
2N/A return (NULL);
2N/A }
2N/A data = nkva->data;
2N/A nkva->length = 0;
2N/A if ((buf = strdup(s)) == NULL) {
2N/A return (NULL);
2N/A }
2N/A pair = _strtok_escape(buf, del, &last_pair);
2N/A do {
2N/A key = _strtok_escape(pair, ass, &last_key);
2N/A if (key != NULL) {
2N/A data[nkva->length].key = do_unescape(key, &err);
2N/A data[nkva->length].value = do_unescape(last_key, &err);
2N/A nkva->length++;
2N/A }
2N/A } while ((pair = _strtok_escape(NULL, del, &last_pair)) != NULL &&
2N/A !err);
2N/A free(buf);
2N/A if (err) {
2N/A _kva_free(nkva);
2N/A return (NULL);
2N/A }
2N/A return (nkva);
2N/A}
2N/A
2N/A/*
2N/A * Returns the escaped string; if new memory is allocated, it is written
2N/A * to *res else *res will be set to NULL so it is safe to call free(*res).
2N/A * Error is flagged in *err; the application needs to set it to B_FALSE.
2N/A */
2N/Astatic char *
2N/Ado_escape(const char *src, char **res, const char *esc, boolean_t *err)
2N/A{
2N/A char *tmp;
2N/A
2N/A *res = NULL;
2N/A
2N/A /* Nothing to escape. */
2N/A if (src == NULL || src[strcspn(src, esc)] == '\0')
2N/A return ((char *)src);
2N/A
2N/A tmp = _escape(src, esc);
2N/A
2N/A if (tmp == NULL) {
2N/A *err = B_TRUE;
2N/A return ((char *)src);
2N/A }
2N/A
2N/A *res = tmp;
2N/A
2N/A return (tmp);
2N/A}
2N/A
2N/A/*
2N/A * _kva2str(): Given an array of key-value pairs, place them into a string
2N/A * (buf). Use delimeter (del) to separate pairs. Use assignment character
2N/A * (ass) to separate keys and values.
2N/A *
2N/A * This is the inverse of _str2kva; the keys and values must be escaped.
2N/A * This version also escapes the second delimiter, typically a colon.
2N/A *
2N/A * Return Values: 0 Success 1 Buffer too small
2N/A */
2N/Aint
2N/A_kva2strx(kva_t *kva, char *buf, int buflen, const char *ass, const char *del,
2N/A const char *del2)
2N/A{
2N/A int i;
2N/A int len;
2N/A int off = 0;
2N/A kv_t *data;
2N/A boolean_t err = B_FALSE;
2N/A int esclen = strlen(ass) + strlen(del) + strlen(del2) + 1;
2N/A char *esc = alloca(esclen);
2N/A const char *cdel = "";
2N/A
2N/A buf[0] = '\0';
2N/A
2N/A if (kva == NULL)
2N/A return (0);
2N/A
2N/A (void) snprintf(esc, esclen, "%s%s%s", ass, del, del2);
2N/A
2N/A data = kva->data;
2N/A
2N/A for (i = 0; i < kva->length; i++) {
2N/A /* Suppress unset and empty values. */
2N/A if (data[i].key != NULL && data[i].key[0] != '\0' &&
2N/A data[i].value != NULL) {
2N/A char *key, *val;
2N/A len = snprintf(buf + off, buflen - off, "%s%s%s%s",
2N/A cdel, do_escape(data[i].key, &key, esc, &err), ass,
2N/A do_escape(data[i].value, &val, esc, &err));
2N/A
2N/A free(key);
2N/A free(val);
2N/A
2N/A if (len < 0 || len + off >= buflen || err)
2N/A return (1);
2N/A
2N/A off += len;
2N/A
2N/A cdel = del;
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/A_kva2str(kva_t *kva, char *buf, int buflen, const char *ass, const char *del)
2N/A{
2N/A return (_kva2strx(kva, buf, buflen, ass, del, ""));
2N/A}
2N/A
2N/Aint
2N/A_insert2kva(kva_t *kva, char *key, char *value)
2N/A{
2N/A int i;
2N/A kv_t *data;
2N/A
2N/A if (kva == NULL) {
2N/A return (0);
2N/A }
2N/A data = kva->data;
2N/A for (i = 0; i < kva->length; i++) {
2N/A if (strcmp(data[i].key, key) == 0) {
2N/A if (data[i].value != NULL)
2N/A free(data[i].value);
2N/A data[i].value = _strdup_null(value);
2N/A return (0);
2N/A }
2N/A }
2N/A return (1);
2N/A}
2N/A
2N/Akva_t *
2N/A_kva_dup(kva_t *old_kva)
2N/A{
2N/A int i;
2N/A int size;
2N/A kv_t *old_data;
2N/A kv_t *new_data;
2N/A kva_t *nkva = NULL;
2N/A
2N/A if (old_kva == NULL) {
2N/A return (NULL);
2N/A }
2N/A old_data = old_kva->data;
2N/A size = old_kva->length;
2N/A if ((nkva = _new_kva(size)) == NULL) {
2N/A return (NULL);
2N/A }
2N/A new_data = nkva->data;
2N/A nkva->length = old_kva->length;
2N/A for (i = 0; i < nkva->length; i++) {
2N/A new_data[i].key = _strdup_null(old_data[i].key);
2N/A new_data[i].value = _strdup_null(old_data[i].value);
2N/A }
2N/A
2N/A return (nkva);
2N/A}
2N/A
2N/Astatic void
2N/Astrip_spaces(char **valuep)
2N/A{
2N/A char *p, *start;
2N/A
2N/A /* Find first non-white space character and return pointer to it */
2N/A for (p = *valuep; *p != '\0' && isspace((unsigned char)*p); p++)
2N/A ;
2N/A
2N/A *valuep = start = p;
2N/A
2N/A if (*p == '\0')
2N/A return;
2N/A
2N/A p = p + strlen(p) - 1;
2N/A
2N/A /* Remove trailing spaces */
2N/A while (p > start && isspace((unsigned char)*p))
2N/A p--;
2N/A
2N/A p[1] = '\0';
2N/A}
2N/A
2N/Astatic char *
2N/Ado_unescape(char *src, boolean_t *err)
2N/A{
2N/A char *tmp;
2N/A
2N/A if (src == NULL)
2N/A return (src);
2N/A
2N/A strip_spaces(&src);
2N/A /* Unescape should only unescape the standard seperators */
2N/A tmp = _unescape(src, KV_SPECIAL);
2N/A if (tmp == NULL)
2N/A *err = B_TRUE;
2N/A
2N/A return (tmp);
2N/A}
2N/A
2N/A/*
2N/A * Always allocates new memory, for some reason when allocate fails we try
2N/A * to return an empty string (but allocating that might also fail).
2N/A */
2N/Achar *
2N/A_do_unescape(char *src)
2N/A{
2N/A char *tmp;
2N/A boolean_t err = B_FALSE;
2N/A
2N/A tmp = do_unescape(src, &err);
2N/A if (err || tmp == NULL)
2N/A return (_strdup_null(NULL));
2N/A else
2N/A return (tmp);
2N/A}
2N/A
2N/A#ifdef DEBUG
2N/Avoid
2N/Aprint_kva(kva_t *kva)
2N/A{
2N/A int i;
2N/A kv_t *data;
2N/A
2N/A if (kva == NULL) {
2N/A (void) printf(" (empty)\n");
2N/A return;
2N/A }
2N/A data = kva->data;
2N/A for (i = 0; i < kva->length; i++) {
2N/A (void) printf(" %s = %s\n",
2N/A data[i].key != NULL ? data[i].key : "NULL",
2N/A data[i].value != NULL ? data[i].value : "NULL");
2N/A }
2N/A}
2N/A#endif /* DEBUG */