#include <stdio.h>
#include "xmlrpc_config.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "int.h"
#define KEY_ERROR_BUFFER_SZ (32)
static uint32_t
hashStructKey(const char * const key,
size_t const keyLen) {
uint32_t hash;
size_t i;
XMLRPC_ASSERT(key != NULL);
/* This is the Bernstein hash, optimized for lower case ASCII
keys. Note that the bytes of such a key differ only in their
lower 5 bits.
*/
for (hash = 0, i = 0; i < keyLen; ++i)
hash = hash + key[i] + (hash << 5);
return hash;
}
static void
changeMemberValue(xmlrpc_value * const structP,
unsigned int const mbrIndex,
xmlrpc_value * const newValueP) {
/*----------------------------------------------------------------------------
Change the value of an existing member. (But be careful--the original and
new values might be the same object, so watch the order of INCREF and DECREF
calls!)
-----------------------------------------------------------------------------*/
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
_struct_member * const memberP = &members[mbrIndex];
xmlrpc_value * const oldValueP = memberP->value;
/* Juggle our references. */
memberP->value = newValueP;
xmlrpc_INCREF(memberP->value);
xmlrpc_DECREF(oldValueP);
}
static void
addNewMember(xmlrpc_env * const envP,
xmlrpc_value * const structP,
xmlrpc_value * const keyvalP,
xmlrpc_value * const valueP) {
/*----------------------------------------------------------------------------
Add a new member. Assume no member already exists with this key.
-----------------------------------------------------------------------------*/
const char * const key =
XMLRPC_MEMBLOCK_CONTENTS(char, keyvalP->blockP);
size_t const keyLen =
XMLRPC_MEMBLOCK_SIZE(char, keyvalP->blockP) - 1;
_struct_member newMember;
newMember.keyHash = hashStructKey(key, keyLen);
newMember.key = keyvalP;
newMember.value = valueP;
XMLRPC_MEMBLOCK_APPEND(_struct_member, envP, structP->blockP,
&newMember, 1);
if (!envP->fault_occurred) {
xmlrpc_INCREF(keyvalP);
xmlrpc_INCREF(valueP);
}
}
void
xmlrpc_destroyStruct(xmlrpc_value * const structP) {
/*----------------------------------------------------------------------------
Dispose of the contents of struct *structP (but not the struct value
itself). The value is not valid after this.
-----------------------------------------------------------------------------*/
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);
unsigned int i;
for (i = 0; i < size; ++i) {
xmlrpc_DECREF(members[i].key);
xmlrpc_DECREF(members[i].value);
}
XMLRPC_MEMBLOCK_FREE(_struct_member, structP->blockP);
}
/*=========================================================================
** xmlrpc_struct_new
**=========================================================================
** Create a new <struct> value. The corresponding destructor code
** currently lives in xmlrpc_DECREF.
**
** We store the individual members in an array of _struct_member. This
** contains a key, a hash code, and a value. We look up keys by doing
** a linear search of the hash codes.
*/
xmlrpc_value *
xmlrpc_struct_new(xmlrpc_env * const envP) {
xmlrpc_value * valP;
XMLRPC_ASSERT_ENV_OK(envP);
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_STRUCT;
valP->blockP = XMLRPC_MEMBLOCK_NEW(_struct_member, envP, 0);
if (envP->fault_occurred)
free(valP);
}
return valP;
}
xmlrpc_value *
xmlrpc_struct_new_value(xmlrpc_env * const envP,
xmlrpc_value * const valueP) {
xmlrpc_value * structP;
if (valueP->_type != XMLRPC_TYPE_STRUCT) {
xmlrpc_env_set_fault_formatted(envP, XMLRPC_TYPE_ERROR,
"Value is not a structure. "
"It is type #%d", valueP->_type);
structP = NULL;
} else {
xmlrpc_createXmlrpcValue(envP, &structP);
if (!envP->fault_occurred) {
structP->_type = XMLRPC_TYPE_STRUCT;
structP->blockP = XMLRPC_MEMBLOCK_NEW(_struct_member, envP, 0);
if (envP->fault_occurred)
free(structP);
else {
_struct_member * const srcMemberList =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, valueP->blockP);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(_struct_member, valueP->blockP);
unsigned int i;
for (i = 0; i < size && !envP->fault_occurred; ++i) {
const _struct_member * const thisMemberP =
&srcMemberList[i];
xmlrpc_value * keyValP =
xmlrpc_string_new_value(envP, thisMemberP->key);
if (!envP->fault_occurred) {
xmlrpc_value * valueP =
xmlrpc_value_new(envP, thisMemberP->value);
if (!envP->fault_occurred) {
addNewMember(envP, structP, keyValP, valueP);
xmlrpc_DECREF(valueP);
}
xmlrpc_DECREF(keyValP);
}
}
if (envP->fault_occurred)
xmlrpc_destroyStruct(structP);
}
if (envP->fault_occurred)
free(structP);
}
}
return structP;
}
/*=========================================================================
** xmlrpc_struct_size
**=========================================================================
** Return the number of key-value pairs contained in the struct. If the
** value is not a struct, return -1 and set a fault.
*/
int
xmlrpc_struct_size(xmlrpc_env * const envP,
xmlrpc_value * const structP) {
int retval;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
if (structP->_type != XMLRPC_TYPE_STRUCT) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value is not a struct. It is type #%d",
structP->_type);
retval = -1;
} else {
size_t const size =
XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);
assert((size_t)(int)size == size);
/* Because structs are defined to have few enough members */
retval = (int)size;
}
return retval;
}
static void
findMember(xmlrpc_value * const structP,
const char * const key,
size_t const keyLen,
bool * const foundP,
unsigned int * const indexP) {
size_t size, i;
uint32_t searchHash;
_struct_member * contents; /* array */
bool found;
size_t foundIndex; /* Meaningful only when 'found' is true */
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT(key != NULL);
foundIndex = 0; /* defeat used-before-set compiler warning */
/* Look for our key. */
searchHash = hashStructKey(key, keyLen);
size = XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);
contents = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
for (i = 0, found = false; i < size && !found; ++i) {
if (contents[i].keyHash == searchHash) {
xmlrpc_value * const keyvalP = contents[i].key;
const char * const keystr =
XMLRPC_MEMBLOCK_CONTENTS(char, keyvalP->blockP);
size_t const keystrSize =
XMLRPC_MEMBLOCK_SIZE(char, keyvalP->blockP)-1;
if (keystrSize == keyLen && memcmp(key, keystr, keyLen) == 0) {
found = true;
foundIndex = i;
}
}
}
if (found) {
assert((size_t)(int)foundIndex == foundIndex);
/* Definition of structure says it has few enough members */
if (indexP)
*indexP = foundIndex;
}
*foundP = found;
}
/*=========================================================================
** xmlrpc_struct_has_key
**=========================================================================
*/
int
xmlrpc_struct_has_key(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key) {
XMLRPC_ASSERT(key != NULL);
return xmlrpc_struct_has_key_n(envP, strctP, key, strlen(key));
}
int
xmlrpc_struct_has_key_n(xmlrpc_env * const envP,
xmlrpc_value * const structP,
const char * const key,
size_t const keyLen) {
int retval;
/* Suppress a compiler warning about uninitialized variables. */
retval = 0;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT(key != NULL);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault(envP, XMLRPC_TYPE_ERROR,
"Value is not a struct");
else {
bool found;
findMember(structP, key, keyLen, &found, NULL);
retval = found ? 1 : 0;
}
return retval;
}
/*=========================================================================
** xmlrpc_struct_find_value...
**=========================================================================
** These functions look up a specified key value in a specified struct.
** If it exists, they return the value of the struct member. If not,
** they return a NULL to indicate such.
*/
/* It would be a nice extension to be able to look up a key that is
not a text string.
*/
void
xmlrpc_struct_find_value(xmlrpc_env * const envP,
xmlrpc_value * const structP,
const char * const key,
xmlrpc_value ** const valuePP) {
/*----------------------------------------------------------------------------
Given a key, retrieve a value from the struct. If the key is not
present, return NULL as *valuePP.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_PTR_OK(key);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value is not a struct. It is type #%d",
structP->_type);
else {
bool found;
unsigned int index;
/* Get our member index. */
findMember(structP, key, strlen(key), &found, &index);
if (!found)
*valuePP = NULL;
else {
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
*valuePP = members[index].value;
XMLRPC_ASSERT_VALUE_OK(*valuePP);
xmlrpc_INCREF(*valuePP);
}
}
}
void
xmlrpc_struct_find_value_v(xmlrpc_env * const envP,
xmlrpc_value * const structP,
xmlrpc_value * const keyP,
xmlrpc_value ** const valuePP) {
/*----------------------------------------------------------------------------
Given a key, retrieve a value from the struct. If the key is not
present, return NULL as *valuePP.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_VALUE_OK(keyP);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value is not a struct. It is type #%d",
structP->_type);
else {
if (keyP->_type != XMLRPC_TYPE_STRING)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Key value is not a string. "
"It is type #%d",
keyP->_type);
else {
bool found;
unsigned int index;
/* Get our member index. */
findMember(structP,
XMLRPC_MEMBLOCK_CONTENTS(char, keyP->blockP),
XMLRPC_MEMBLOCK_SIZE(char, keyP->blockP)-1,
&found, &index);
if (!found)
*valuePP = NULL;
else {
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
*valuePP = members[index].value;
XMLRPC_ASSERT_VALUE_OK(*valuePP);
xmlrpc_INCREF(*valuePP);
}
}
}
}
/*=========================================================================
** xmlrpc_struct_read_value...
**=========================================================================
** These fail if no member with the specified key exists.
** Otherwise, they are the same as xmlrpc_struct_find_value...
*/
void
xmlrpc_struct_read_value_v(xmlrpc_env * const envP,
xmlrpc_value * const structP,
xmlrpc_value * const keyP,
xmlrpc_value ** const valuePP) {
xmlrpc_struct_find_value_v(envP, structP, keyP, valuePP);
if (!envP->fault_occurred) {
if (*valuePP == NULL) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%.*s'",
(int)XMLRPC_MEMBLOCK_SIZE(char, keyP->blockP),
XMLRPC_MEMBLOCK_CONTENTS(char, keyP->blockP));
}
}
}
void
xmlrpc_struct_read_value(xmlrpc_env * const envP,
xmlrpc_value * const structP,
const char * const key,
xmlrpc_value ** const valuePP) {
xmlrpc_struct_find_value(envP, structP, key, valuePP);
if (!envP->fault_occurred) {
if (*valuePP == NULL) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%s'",
key);
/* We should fix the error message to format the key for display */
}
}
}
/*=========================================================================
** xmlrpc_struct_get_value...
**=========================================================================
** These are for backward compatibility. They used to be the only ones.
** They're deprecated because they don't acquire a reference to the
** value they return.
*/
xmlrpc_value *
xmlrpc_struct_get_value_n(xmlrpc_env * const envP,
xmlrpc_value * const structP,
const char * const key,
size_t const keyLen) {
xmlrpc_value * retval;
xmlrpc_value * keyP; /* 'key' as an XML-RPC string */
keyP = xmlrpc_string_new_lp(envP, keyLen, key);
if (!envP->fault_occurred) {
xmlrpc_struct_find_value_v(envP, structP, keyP, &retval);
if (!envP->fault_occurred) {
if (retval == NULL) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR,
"No member of struct has key '%.*s'",
(int)keyLen, key);
/* We should fix the error message to format the key
for display */
} else
/* For backward compatibility. */
xmlrpc_DECREF(retval);
}
xmlrpc_DECREF(keyP);
}
return retval;
}
xmlrpc_value *
xmlrpc_struct_get_value(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key) {
XMLRPC_ASSERT(key != NULL);
return xmlrpc_struct_get_value_n(envP, strctP, key, strlen(key));
}
/*=========================================================================
** xmlrpc_struct_set_value
**=========================================================================
*/
void
xmlrpc_struct_set_value(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key,
xmlrpc_value * const valueP) {
XMLRPC_ASSERT(key != NULL);
xmlrpc_struct_set_value_n(envP, strctP, key, strlen(key), valueP);
}
void
xmlrpc_struct_set_value_n(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key,
size_t const keyLen,
xmlrpc_value * const valueP) {
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(key != NULL);
if (xmlrpc_value_type(strctP) != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR,
"Trying to set value in something not a struct. "
"Type is %d; struct is %d",
xmlrpc_value_type(strctP), XMLRPC_TYPE_STRUCT);
else {
xmlrpc_value * keyvalP; /* 'key' as an XML-RPC string */
keyvalP = xmlrpc_string_new_lp(envP, keyLen, key);
if (!envP->fault_occurred)
xmlrpc_struct_set_value_v(envP, strctP, keyvalP, valueP);
xmlrpc_DECREF(keyvalP);
}
}
void
xmlrpc_struct_set_value_v(xmlrpc_env * const envP,
xmlrpc_value * const structP,
xmlrpc_value * const keyvalP,
xmlrpc_value * const valueP) {
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_VALUE_OK(keyvalP);
XMLRPC_ASSERT_VALUE_OK(valueP);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault(envP, XMLRPC_TYPE_ERROR,
"Value is not a struct");
else if (keyvalP->_type != XMLRPC_TYPE_STRING)
xmlrpc_env_set_fault(envP, XMLRPC_TYPE_ERROR,
"Key value is not a string");
else {
const char * const key =
XMLRPC_MEMBLOCK_CONTENTS(char, keyvalP->blockP);
size_t const keyLen =
XMLRPC_MEMBLOCK_SIZE(char, keyvalP->blockP) - 1;
bool found;
unsigned int index;
findMember(structP, key, keyLen, &found, &index);
if (found)
changeMemberValue(structP, index, valueP);
else
addNewMember(envP, structP, keyvalP, valueP);
}
}
/* Note that the order of keys and values is undefined, and may change
when you modify the struct.
*/
void
xmlrpc_struct_read_member(xmlrpc_env * const envP,
xmlrpc_value * const structP,
unsigned int const index,
xmlrpc_value ** const keyvalP,
xmlrpc_value ** const valueP) {
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_PTR_OK(keyvalP);
XMLRPC_ASSERT_PTR_OK(valueP);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Attempt to read a struct member "
"of something that is not a struct");
else {
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);
if (index >= size)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "Index %u is beyond the end of "
"the %u-member structure", index, (unsigned int)size);
else {
_struct_member * const memberP = &members[index];
*keyvalP = memberP->key;
xmlrpc_INCREF(memberP->key);
*valueP = memberP->value;
xmlrpc_INCREF(memberP->value);
}
}
}
void
xmlrpc_struct_get_key_and_value(xmlrpc_env * const envP,
xmlrpc_value * const structP,
int const index,
xmlrpc_value ** const keyvalP,
xmlrpc_value ** const valueP) {
/*----------------------------------------------------------------------------
Same as xmlrpc_struct_read_member(), except doesn't take a reference
to the returned value.
This is obsolete.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_PTR_OK(keyvalP);
XMLRPC_ASSERT_PTR_OK(valueP);
if (index < 0)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "Index %d is negative.", index);
else {
xmlrpc_struct_read_member(envP, structP, index, keyvalP, valueP);
if (!envP->fault_occurred) {
xmlrpc_DECREF(*keyvalP);
xmlrpc_DECREF(*valueP);
}
}
if (envP->fault_occurred) {
*keyvalP = NULL;
*valueP = NULL;
}
}
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */