diff options
author | Andy Cedilnik <andy.cedilnik@kitware.com> | 2005-02-22 18:08:27 (GMT) |
---|---|---|
committer | Andy Cedilnik <andy.cedilnik@kitware.com> | 2005-02-22 18:08:27 (GMT) |
commit | b9b4ea0f7bbf0313d8aa5e354ee56f912969763c (patch) | |
tree | ba5e857cda91949be14f508e6df490e1e2176f40 /Utilities/cmxmlrpc/xmlrpc_data.c | |
parent | bfcb4b693763bb11f841094d2ca9852a64e5d33b (diff) | |
download | CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.zip CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.gz CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.bz2 |
ENH: Initial import
Diffstat (limited to 'Utilities/cmxmlrpc/xmlrpc_data.c')
-rw-r--r-- | Utilities/cmxmlrpc/xmlrpc_data.c | 1331 |
1 files changed, 1331 insertions, 0 deletions
diff --git a/Utilities/cmxmlrpc/xmlrpc_data.c b/Utilities/cmxmlrpc/xmlrpc_data.c new file mode 100644 index 0000000..76713c9 --- /dev/null +++ b/Utilities/cmxmlrpc/xmlrpc_data.c @@ -0,0 +1,1331 @@ +/* Copyright information is at end of file */ + +#include "xmlrpc_config.h" + +#include <stddef.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include "bool.h" +#include "xmlrpc.h" +#include "xmlrpc_int.h" + +/* Borrowed from Python 1.5.2. +** MPW pushes 'extended' for float and double types with varargs */ +#ifdef MPW +typedef extended va_double; +#else +typedef double va_double; +#endif + +/* Borrowed from Python 1.5.2. +** Python copies its va_list objects before using them in certain +** tricky fashions. We don't why Python does this, but since we're +** abusing our va_list objects in a similar fashion, we'll copy them +** too. */ +#if VA_LIST_IS_ARRAY +#define VA_LIST_COPY(dest,src) memcpy((dest), (src), sizeof(va_list)) +#else +#define VA_LIST_COPY(dest,src) ((dest) = (src)) +#endif + + +static void +destroyValue(xmlrpc_value * const valueP) { + + /* First, we need to destroy this value's contents, if any. */ + switch (valueP->_type) { + case XMLRPC_TYPE_INT: + case XMLRPC_TYPE_BOOL: + case XMLRPC_TYPE_DOUBLE: + break; + + case XMLRPC_TYPE_ARRAY: + xmlrpc_destroyArrayContents(valueP); + break; + + case XMLRPC_TYPE_STRING: +#ifdef HAVE_UNICODE_WCHAR + if (valueP->_wcs_block) + xmlrpc_mem_block_free(valueP->_wcs_block); +#endif /* HAVE_UNICODE_WCHAR */ + /* Fall through. */ + + case XMLRPC_TYPE_DATETIME: + case XMLRPC_TYPE_BASE64: + xmlrpc_mem_block_clean(&valueP->_block); + break; + + case XMLRPC_TYPE_STRUCT: + xmlrpc_destroyStruct(valueP); + break; + + case XMLRPC_TYPE_C_PTR: + break; + + case XMLRPC_TYPE_DEAD: + XMLRPC_ASSERT(FALSE); /* Can't happen, per entry conditions */ + + default: + XMLRPC_ASSERT(FALSE); /* There are no other possible values */ + } + + /* Next, we mark this value as invalid, to help catch refcount + ** errors. */ + valueP->_type = XMLRPC_TYPE_DEAD; + + /* Finally, we destroy the value itself. */ + free(valueP); +} + + + +/*========================================================================= +** Reference Counting +**========================================================================= +** Some simple reference-counting code. The xmlrpc_DECREF routine is in +** charge of destroying values when their reference count equals zero. +*/ + +void +xmlrpc_INCREF (xmlrpc_value * const valueP) { + + XMLRPC_ASSERT_VALUE_OK(valueP); + XMLRPC_ASSERT(valueP->_refcount > 0); + + valueP->_refcount++; +} + + + +void +xmlrpc_DECREF (xmlrpc_value * const valueP) { + + XMLRPC_ASSERT_VALUE_OK(valueP); + XMLRPC_ASSERT(valueP->_refcount > 0); + XMLRPC_ASSERT(valueP->_type != XMLRPC_TYPE_DEAD); + + valueP->_refcount--; + + /* If we have no more refs, we need to deallocate this value. */ + if (valueP->_refcount == 0) + destroyValue(valueP); +} + + + +/*========================================================================= + Utiltiies +=========================================================================*/ + +static const char * +typeName(xmlrpc_type const type) { + + switch(type) { + + case XMLRPC_TYPE_INT: return "INT"; + case XMLRPC_TYPE_BOOL: return "BOOL"; + case XMLRPC_TYPE_DOUBLE: return "DOUBLE"; + case XMLRPC_TYPE_DATETIME: return "DATETIME"; + case XMLRPC_TYPE_STRING: return "STRING"; + case XMLRPC_TYPE_BASE64: return "BASE64"; + case XMLRPC_TYPE_ARRAY: return "ARRAY"; + case XMLRPC_TYPE_STRUCT: return "STRUCT"; + case XMLRPC_TYPE_C_PTR: return "C_PTR"; + case XMLRPC_TYPE_DEAD: return "DEAD"; + default: return "???"; + } +} + + + +static void +verifyNoNulls(xmlrpc_env * const envP, + const char * const contents, + unsigned int const len) { +/*---------------------------------------------------------------------------- + Verify that the character array 'contents', which is 'len' bytes long, + does not contain any NUL characters, which means it can be made into + a passable ASCIIZ string just by adding a terminating NUL. + + Fail if the array contains a NUL. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < len && !envP->fault_occurred; i++) + if (contents[i] == '\0') + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_TYPE_ERROR, + "String must not contain NUL characters"); +} + + + +static void +verifyNoNullsW(xmlrpc_env * const envP, + const wchar_t * const contents, + unsigned int const len) { +/*---------------------------------------------------------------------------- + Same as verifyNoNulls(), but for wide characters. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < len && !envP->fault_occurred; i++) + if (contents[i] == '\0') + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_TYPE_ERROR, + "String must not contain NUL characters"); +} + + + +static void +validateType(xmlrpc_env * const envP, + const xmlrpc_value * const valueP, + xmlrpc_type const expectedType) { + + if (valueP->_type != expectedType) { + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where " + "type %s was expected.", + typeName(valueP->_type), typeName(expectedType)); + } +} + + + +/*========================================================================= + Extracting XML-RPC value +=========================================================================== + These routines extract XML-RPC values into ordinary C data types. + + For array and struct values, see the separates files xmlrpc_array.c + and xmlrpc_struct.c. +=========================================================================*/ + +void +xmlrpc_read_int(xmlrpc_env * const envP, + const xmlrpc_value * const valueP, + xmlrpc_int32 * const intValueP) { + + validateType(envP, valueP, XMLRPC_TYPE_INT); + if (!envP->fault_occurred) + *intValueP = valueP->_value.i; +} + + + +void +xmlrpc_read_double(xmlrpc_env * const envP, + const xmlrpc_value * const valueP, + xmlrpc_double * const doubleValueP) { + + validateType(envP, valueP, XMLRPC_TYPE_DOUBLE); + if (!envP->fault_occurred) + *doubleValueP = valueP->_value.d; + +} + + + +void +xmlrpc_read_bool(xmlrpc_env * const envP, + const xmlrpc_value * const valueP, + xmlrpc_bool * const boolValueP) { + + validateType(envP, valueP, XMLRPC_TYPE_BOOL); + if (!envP->fault_occurred) + *boolValueP = valueP->_value.b; +} + + + +void +xmlrpc_read_string(xmlrpc_env * const envP, + const xmlrpc_value * const valueP, + const char ** const stringValueP) { +/*---------------------------------------------------------------------------- + Read the value of an XML-RPC string an ASCIIZ string. + + Fail if the string contains null characters (which means it wasn't + really a string, but XML-RPC doesn't seem to understand what a string + is, and such values are possible). +-----------------------------------------------------------------------------*/ + validateType(envP, valueP, XMLRPC_TYPE_STRING); + if (!envP->fault_occurred) { + unsigned int const size = + XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block); + const char * const contents = + XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block); + + verifyNoNulls(envP, contents, size); + if (!envP->fault_occurred) { + char * stringValue; + + stringValue = malloc(size+1); + if (stringValue == NULL) + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate space " + "for %u-character string", size); + else { + memcpy(stringValue, + XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block), size); + stringValue[size] = '\0'; + + *stringValueP = stringValue; + } + } + } +} + + + +void +xmlrpc_read_string_lp(xmlrpc_env * const envP, + const xmlrpc_value * const valueP, + unsigned int * const lengthP, + const char ** const stringValueP) { + + validateType(envP, valueP, XMLRPC_TYPE_STRING); + if (!envP->fault_occurred) { + unsigned int const size = + XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block); + const char * const contents = + XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block); + + char * stringValue; + + stringValue = malloc(size); + if (stringValue == NULL) + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate %u bytes " + "for string.", size); + else { + memcpy(stringValue, contents, size); + *stringValueP = stringValue; + *lengthP = size; + } + } +} + + + +/*========================================================================= +** Building XML-RPC values. +**========================================================================= +** Build new XML-RPC values from a format string. This code is heavily +** inspired by Py_BuildValue from Python 1.5.2. In particular, our +** particular abuse of the va_list data type is copied from the equivalent +** Python code in modsupport.c. Since Python is portable, our code should +** (in theory) also be portable. +*/ + + +xmlrpc_type xmlrpc_value_type (xmlrpc_value* value) +{ + XMLRPC_ASSERT_VALUE_OK(value); + return value->_type; +} + + + +static void +createXmlrpcValue(xmlrpc_env * const envP, + xmlrpc_value ** const valPP) { +/*---------------------------------------------------------------------------- + Create a blank xmlrpc_value to be filled in. + + Set the reference count to 1. +-----------------------------------------------------------------------------*/ + xmlrpc_value * valP; + + valP = (xmlrpc_value*) malloc(sizeof(xmlrpc_value)); + if (!valP) + xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR, + "Could not allocate memory for xmlrpc_value"); + else + valP->_refcount = 1; + + *valPP = valP; +} + + + +static void +mkInt(xmlrpc_env * const envP, + xmlrpc_int32 const value, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + + createXmlrpcValue(envP, &valP); + + if (!envP->fault_occurred) { + valP->_type = XMLRPC_TYPE_INT; + valP->_value.i = value; + } + *valPP = valP; +} + + + +static void +mkBool(xmlrpc_env * const envP, + xmlrpc_bool const value, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + + createXmlrpcValue(envP, &valP); + + if (!envP->fault_occurred) { + valP->_type = XMLRPC_TYPE_BOOL; + valP->_value.b = value; + } + *valPP = valP; +} + + + +static void +mkDouble(xmlrpc_env * const envP, + double const value, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + + createXmlrpcValue(envP, &valP); + + if (!envP->fault_occurred) { + valP->_type = XMLRPC_TYPE_DOUBLE; + valP->_value.d = value; + } + *valPP = valP; +} + + + +#ifdef HAVE_UNICODE_WCHAR +#define MAKE_WCS_BLOCK_NULL(val) ((val)->_wcs_block = NULL) +#else +#define MAKE_WCS_BLOCK_NULL(val) while (0) do {}; +#endif + + + +static void +mkString(xmlrpc_env * const envP, + const char * const value, + unsigned int const length, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + + createXmlrpcValue(envP, &valP); + + if (!envP->fault_occurred) { + valP->_type = XMLRPC_TYPE_STRING; + MAKE_WCS_BLOCK_NULL(valP); + XMLRPC_MEMBLOCK_INIT(char, envP, &valP->_block, length + 1); + if (!envP->fault_occurred) { + char * const contents = + XMLRPC_MEMBLOCK_CONTENTS(char, &valP->_block); + memcpy(contents, value, length); + contents[length] = '\0'; + } + if (envP->fault_occurred) + free(valP); + } + *valPP = valP; +} + + + +static void +getString(xmlrpc_env * const envP, + const char ** const formatP, + va_list * const args, + xmlrpc_value ** const valPP) { + + const char * str; + unsigned int len; + + str = (const char*) va_arg(*args, char*); + if (**formatP == '#') { + (*formatP)++; + len = (size_t) va_arg(*args, size_t); + } else + len = strlen(str); + + mkString(envP, str, len, valPP); +} + + + +#ifdef HAVE_UNICODE_WCHAR +static void +mkWideString(xmlrpc_env * const envP, + wchar_t * const wcs, + size_t const wcs_len, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + char *contents; + wchar_t *wcs_contents; + int block_is_inited; + xmlrpc_mem_block *utf8_block; + char *utf8_contents; + size_t utf8_len; + + /* Error-handling preconditions. */ + valP = NULL; + utf8_block = NULL; + block_is_inited = 0; + + /* Initialize our XML-RPC value. */ + valP = (xmlrpc_value*) malloc(sizeof(xmlrpc_value)); + XMLRPC_FAIL_IF_NULL(valP, envP, XMLRPC_INTERNAL_ERROR, + "Could not allocate memory for wide string"); + valP->_refcount = 1; + valP->_type = XMLRPC_TYPE_STRING; + + /* More error-handling preconditions. */ + valP->_wcs_block = NULL; + + /* Build our wchar_t block first. */ + valP->_wcs_block = + XMLRPC_TYPED_MEM_BLOCK_NEW(wchar_t, envP, wcs_len + 1); + XMLRPC_FAIL_IF_FAULT(envP); + wcs_contents = + XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, valP->_wcs_block); + memcpy(wcs_contents, wcs, wcs_len * sizeof(wchar_t)); + wcs_contents[wcs_len] = '\0'; + + /* Convert the wcs block to UTF-8. */ + utf8_block = xmlrpc_wcs_to_utf8(envP, wcs_contents, wcs_len + 1); + XMLRPC_FAIL_IF_FAULT(envP); + utf8_contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, utf8_block); + utf8_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, utf8_block); + + /* XXX - We need an extra memcopy to initialize _block. */ + XMLRPC_TYPED_MEM_BLOCK_INIT(char, envP, &valP->_block, utf8_len); + XMLRPC_FAIL_IF_FAULT(envP); + block_is_inited = 1; + contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block); + memcpy(contents, utf8_contents, utf8_len); + + cleanup: + if (utf8_block) + xmlrpc_mem_block_free(utf8_block); + if (envP->fault_occurred) { + if (valP) { + if (valP->_wcs_block) + xmlrpc_mem_block_free(valP->_wcs_block); + if (block_is_inited) + xmlrpc_mem_block_clean(&valP->_block); + free(valP); + } + } + *valPP = valP; +} +#endif /* HAVE_UNICODE_WCHAR */ + + + +static void +getWideString(xmlrpc_env * const envP, + const char ** const formatP, + va_list * const args, + xmlrpc_value ** const valPP) { +#ifdef HAVE_UNICODE_WCHAR + + wchar_t *wcs; + size_t len; + + wcs = (wchar_t*) va_arg(*args, wchar_t*); + if (**formatP == '#') { + (*formatP)++; + len = (size_t) va_arg(*args, size_t); + } else + len = wcslen(wcs); + + mkWideString(envP, wcs, len, valPP); + +#endif /* HAVE_UNICODE_WCHAR */ +} + + + +static void +mkDatetime(xmlrpc_env * const envP, + const char * const value, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + + createXmlrpcValue(envP, &valP); + + if (!envP->fault_occurred) { + valP->_type = XMLRPC_TYPE_DATETIME; + + XMLRPC_TYPED_MEM_BLOCK_INIT( + char, envP, &valP->_block, strlen(value) + 1); + if (!envP->fault_occurred) { + char * const contents = + XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block); + strcpy(contents, value); + } + if (envP->fault_occurred) + free(valP); + } + *valPP = valP; +} + + + +static void +mkBase64(xmlrpc_env * const envP, + const unsigned char * const value, + size_t const length, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + + createXmlrpcValue(envP, &valP); + + if (!envP->fault_occurred) { + valP->_type = XMLRPC_TYPE_BASE64; + + xmlrpc_mem_block_init(envP, &valP->_block, length); + if (!envP->fault_occurred) { + char * const contents = + xmlrpc_mem_block_contents(&valP->_block); + memcpy(contents, value, length); + } + if (envP->fault_occurred) + free(valP); + } + *valPP = valP; +} + + + +static void +getBase64(xmlrpc_env * const envP, + va_list * const args, + xmlrpc_value ** const valPP) { + + unsigned char * value; + size_t length; + + value = (unsigned char*) va_arg(*args, unsigned char*); + length = (size_t) va_arg(*args, size_t); + + mkBase64(envP, value, length, valPP); +} + + + +static void +mkCPtr(xmlrpc_env * const envP, + void * const value, + xmlrpc_value ** const valPP) { + + xmlrpc_value * valP; + + createXmlrpcValue(envP, &valP); + + if (!envP->fault_occurred) { + valP->_type = XMLRPC_TYPE_C_PTR; + valP->_value.c_ptr = value; + } + *valPP = valP; +} + + + +static void +mkArrayFromVal(xmlrpc_env * const envP, + xmlrpc_value * const value, + xmlrpc_value ** const valPP) { + + if (xmlrpc_value_type(value) != XMLRPC_TYPE_ARRAY) + xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR, + "Array format ('A'), non-array xmlrpc_value"); + else + xmlrpc_INCREF(value); + + *valPP = value; +} + + + +static void +mkStructFromVal(xmlrpc_env * const envP, + xmlrpc_value * const value, + xmlrpc_value ** const valPP) { + + if (xmlrpc_value_type(value) != XMLRPC_TYPE_STRUCT) + xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR, + "Struct format ('S'), non-struct xmlrpc_value"); + else + xmlrpc_INCREF(value); + + *valPP = value; +} + + + +static void +getValue(xmlrpc_env * const envP, + const char** const format, + va_list * args, + xmlrpc_value ** const valPP); + + + +static void +createXmlrpcArray(xmlrpc_env * const envP, + xmlrpc_value ** const arrayPP) { +/*---------------------------------------------------------------------------- + Create an empty array xmlrpc_value. +-----------------------------------------------------------------------------*/ + xmlrpc_value * arrayP; + + createXmlrpcValue(envP, &arrayP); + if (!envP->fault_occurred) { + arrayP->_type = XMLRPC_TYPE_ARRAY; + XMLRPC_TYPED_MEM_BLOCK_INIT(xmlrpc_value*, envP, &arrayP->_block, 0); + if (envP->fault_occurred) + free(arrayP); + } + *arrayPP = arrayP; +} + + + +static void +getArray(xmlrpc_env * const envP, + const char ** const formatP, + char const delimiter, + va_list * const args, + xmlrpc_value ** const arrayPP) { + + xmlrpc_value * arrayP; + + createXmlrpcArray(envP, &arrayP); + + /* Add items to the array until we hit our delimiter. */ + + while (**formatP != delimiter && !envP->fault_occurred) { + + xmlrpc_value * itemP; + + if (**formatP == '\0') + xmlrpc_env_set_fault( + envP, XMLRPC_INTERNAL_ERROR, + "format string ended before closing ')'."); + else { + getValue(envP, formatP, args, &itemP); + if (!envP->fault_occurred) { + xmlrpc_array_append_item(envP, arrayP, itemP); + xmlrpc_DECREF(itemP); + } + } + } + if (envP->fault_occurred) + xmlrpc_DECREF(arrayP); + + *arrayPP = arrayP; +} + + + +static void +getStructMember(xmlrpc_env * const envP, + const char ** const formatP, + va_list * const args, + xmlrpc_value ** const keyPP, + xmlrpc_value ** const valuePP) { + + + /* Get the key */ + getValue(envP, formatP, args, keyPP); + if (!envP->fault_occurred) { + if (**formatP != ':') + xmlrpc_env_set_fault( + envP, XMLRPC_INTERNAL_ERROR, + "format string does not have ':' after a " + "structure member key."); + else { + /* Skip over colon that separates key from value */ + (*formatP)++; + + /* Get the value */ + getValue(envP, formatP, args, valuePP); + } + if (envP->fault_occurred) + xmlrpc_DECREF(*keyPP); + } +} + + + +static void +getStruct(xmlrpc_env * const envP, + const char ** const formatP, + char const delimiter, + va_list * const args, + xmlrpc_value ** const structPP) { + + xmlrpc_value * structP; + + structP = xmlrpc_struct_new(envP); + if (!envP->fault_occurred) { + while (**formatP != delimiter && !envP->fault_occurred) { + xmlrpc_value * keyP; + xmlrpc_value * valueP; + + getStructMember(envP, formatP, args, &keyP, &valueP); + + if (!envP->fault_occurred) { + if (**formatP == ',') + (*formatP)++; /* Skip over the comma */ + else if (**formatP == delimiter) { + /* End of the line */ + } else + xmlrpc_env_set_fault( + envP, XMLRPC_INTERNAL_ERROR, + "format string does not have ',' or ')' after " + "a structure member"); + + if (!envP->fault_occurred) + /* Add the new member to the struct. */ + xmlrpc_struct_set_value_v(envP, structP, keyP, valueP); + + xmlrpc_DECREF(valueP); + xmlrpc_DECREF(keyP); + } + } + if (envP->fault_occurred) + xmlrpc_DECREF(structP); + } + *structPP = structP; +} + + + +static void +getValue(xmlrpc_env * const envP, + const char** const formatP, + va_list * const args, + xmlrpc_value ** const valPP) { +/*---------------------------------------------------------------------------- + Get the next value from the list. *formatP points to the specifier + for the next value in the format string (i.e. to the type code + character) and we move *formatP past the whole specifier for the + next value. We read the required arguments from 'args'. We return + the value as *valPP with a reference to it. + + For example, if *formatP points to the "i" in the string "sis", + we read one argument from 'args' and return as *valP an integer whose + value is the argument we read. We advance *formatP to point to the + last 's' and advance 'args' to point to the argument that belongs to + that 's'. +-----------------------------------------------------------------------------*/ + char const formatChar = *(*formatP)++; + + switch (formatChar) { + case 'i': + mkInt(envP, (xmlrpc_int32) va_arg(*args, xmlrpc_int32), valPP); + break; + + case 'b': + mkBool(envP, (xmlrpc_bool) va_arg(*args, xmlrpc_bool), valPP); + break; + + case 'd': + mkDouble(envP, (double) va_arg(*args, va_double), valPP); + break; + + case 's': + getString(envP, formatP, args, valPP); + break; + + case 'w': + getWideString(envP, formatP, args, valPP); + break; + + /* The code 't' is reserved for a better, time_t based + implementation of dateTime conversion. + */ + case '8': + mkDatetime(envP, (char*) va_arg(*args, char*), valPP); + break; + + case '6': + getBase64(envP, args, valPP); + break; + + case 'p': + /* We might someday want to use the code 'p!' to read in a + cleanup function for this pointer. + */ + mkCPtr(envP, (void*) va_arg(*args, void*), valPP); + break; + + case 'A': + mkArrayFromVal(envP, (xmlrpc_value*) va_arg(*args, xmlrpc_value*), + valPP); + break; + + case 'S': + mkStructFromVal(envP, (xmlrpc_value*) va_arg(*args, xmlrpc_value*), + valPP); + break; + + case 'V': + *valPP = (xmlrpc_value*) va_arg(*args, xmlrpc_value*); + xmlrpc_INCREF(*valPP); + break; + + case '(': + getArray(envP, formatP, ')', args, valPP); + if (!envP->fault_occurred) { + XMLRPC_ASSERT(**formatP == ')'); + (*formatP)++; /* Skip over closing parenthesis */ + } + break; + + case '{': + getStruct(envP, formatP, '}', args, valPP); + if (!envP->fault_occurred) { + XMLRPC_ASSERT(**formatP == '}'); + (*formatP)++; /* Skip over closing brace */ + } + break; + + default: { + const char * const badCharacter = xmlrpc_makePrintableChar(formatChar); + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_INTERNAL_ERROR, + "Unexpected character '%s' in format string", badCharacter); + xmlrpc_strfree(badCharacter); + } + } +} + + + +void +xmlrpc_build_value_va(xmlrpc_env * const envP, + const char * const format, + va_list args, + xmlrpc_value ** const valPP, + const char ** const tailP) { + + const char * formatCursor; + va_list args_copy; + + XMLRPC_ASSERT_ENV_OK(envP); + XMLRPC_ASSERT(format != NULL); + + if (strlen(format) == 0) + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_INTERNAL_ERROR, "Format string is empty."); + else { + formatCursor = &format[0]; + VA_LIST_COPY(args_copy, args); + getValue(envP, &formatCursor, &args_copy, valPP); + + if (!envP->fault_occurred) + XMLRPC_ASSERT_VALUE_OK(*valPP); + + *tailP = formatCursor; + } +} + + + +xmlrpc_value * +xmlrpc_build_value(xmlrpc_env * const envP, + const char * const format, + ...) { + + va_list args; + xmlrpc_value* retval; + const char * suffix; + + va_start(args, format); + xmlrpc_build_value_va(envP, format, args, &retval, &suffix); + va_end(args); + + if (!envP->fault_occurred) { + if (*suffix != '\0') + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument " + "specifier: '%s'. There must be exactly one arument.", + suffix); + + if (envP->fault_occurred) + xmlrpc_DECREF(retval); + } + return retval; +} + + +/*========================================================================= +** Parsing XML-RPC values. +**========================================================================= +** Parse an XML-RPC value based on a format string. This code is heavily +** inspired by Py_BuildValue from Python 1.5.2. +*/ + +/* Prototype for recursive invocation: */ + +static void +parsevalue(xmlrpc_env * const env, + xmlrpc_value * const val, + const char ** const format, + va_list * args); + +static void +parsearray(xmlrpc_env * const env, + const xmlrpc_value * const array, + const char ** const format, + char const delimiter, + va_list * args) { + + int size, i; + xmlrpc_value *item; + + /* Fetch the array size. */ + size = xmlrpc_array_size(env, array); + XMLRPC_FAIL_IF_FAULT(env); + + /* Loop over the items in the array. */ + for (i = 0; i < size; i++) { + /* Bail out if the caller didn't care about the rest of the items. */ + if (**format == '*') + break; + + item = xmlrpc_array_get_item(env, array, i); + XMLRPC_FAIL_IF_FAULT(env); + + XMLRPC_ASSERT(**format != '\0'); + if (**format == delimiter) + XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Too many items in array"); + parsevalue(env, item, format, args); + XMLRPC_FAIL_IF_FAULT(env); + } + if (**format == '*') + (*format)++; + if (**format != delimiter) + XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Not enough items in array"); + + cleanup: + return; +} + + + +static void +parsestruct(xmlrpc_env * const env, + xmlrpc_value * const strct, + const char ** const format, + char const delimiter, + va_list * args) { + + xmlrpc_value *key, *value; + char *keystr; + size_t keylen; + + /* Set up error handling preconditions. */ + key = NULL; + + /* Build the members of our struct. */ + while (**format != '*' && **format != delimiter && **format != '\0') { + + /* Get our key, and skip over the ':' character. Notice the + ** sudden call to getValue--we're going in the opposite direction. */ + getValue(env, format, args, &key); + XMLRPC_FAIL_IF_FAULT(env); + XMLRPC_ASSERT(**format == ':'); + (*format)++; + + /* Look up the value for our key. */ + xmlrpc_parse_value(env, key, "s#", &keystr, &keylen); + XMLRPC_FAIL_IF_FAULT(env); + value = xmlrpc_struct_get_value_n(env, strct, keystr, keylen); + XMLRPC_FAIL_IF_FAULT(env); + + /* Get our value, and skip over the ',' character (if present). */ + parsevalue(env, value, format, args); + XMLRPC_FAIL_IF_FAULT(env); + XMLRPC_ASSERT(**format == ',' || **format == delimiter); + if (**format == ',') + (*format)++; + + /* Release our reference, and restore our invariant. */ + xmlrpc_DECREF(key); + key = NULL; + } + if (**format == '*') { + (*format)++; + if (**format != delimiter && **format != '\0') + XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR, + "* can appear only at the end " + "of a structure format specifier"); + } else { + /* Here we're supposed to fail if he didn't extract all the + members. But we don't know how to determine whether he + specified all the members, so we always fail. + */ + XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR, "You must specify '*' as the " + "last member of a structure in a format specifier " + "used for parsing an xmlrpc_value"); + } + XMLRPC_ASSERT(**format == delimiter || **format == '\0'); + +cleanup: + if (key) + xmlrpc_DECREF(key); +} + + +static void +parsevalue(xmlrpc_env * const envP, + xmlrpc_value * const valueP, + const char ** const format, + va_list * args) { + + char formatSpecChar; + + formatSpecChar = *(*format)++; + + switch (formatSpecChar) { + case 'i': + validateType(envP, valueP, XMLRPC_TYPE_INT); + if (!envP->fault_occurred) { + xmlrpc_int32 * const int32ptr = + (xmlrpc_int32*) va_arg(*args, xmlrpc_int32*); + *int32ptr = valueP->_value.i; + } + break; + + case 'b': + validateType(envP, valueP, XMLRPC_TYPE_BOOL); + if (!envP->fault_occurred) { + xmlrpc_bool * const boolptr = + (xmlrpc_bool*) va_arg(*args, xmlrpc_bool*); + *boolptr = valueP->_value.b; + } + break; + + case 'd': + validateType(envP, valueP, XMLRPC_TYPE_DOUBLE); + if (!envP->fault_occurred) { + double * const doubleptr = (double*) va_arg(*args, double*); + *doubleptr = valueP->_value.d; + } + break; + + case 's': + validateType(envP, valueP, XMLRPC_TYPE_STRING); + if (!envP->fault_occurred) { + char * const contents = + XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block); + size_t const len = XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block) - 1; + + char ** const strptr = (char**) va_arg(*args, char**); + if (**format == '#') { + size_t * const sizeptr = (size_t*) va_arg(*args, size_t**); + (*format)++; + *sizeptr = len; + } else + verifyNoNulls(envP, contents, len); + *strptr = contents; + } + break; + +#ifdef HAVE_UNICODE_WCHAR + case 'w': + validateType(envP, valueP, XMLRPC_TYPE_STRING); + if (!envP->fault_occurred) { + if (!valueP->_wcs_block) { + /* Allocate a wchar_t string if we don't have one. */ + char * const contents = + XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block); + size_t const len = + XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block) - 1; + valueP->_wcs_block = + xmlrpc_utf8_to_wcs(envP, contents, len + 1); + } + if (!envP->fault_occurred) { + wchar_t * const wcontents = + XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block); + size_t const len = + XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block) - 1; + + wchar_t ** const wcsptr = (wchar_t**) va_arg(*args, wchar_t**); + if (**format == '#') { + size_t * const sizeptr = (size_t*) va_arg(*args, size_t**); + (*format)++; + *sizeptr = len; + } else + verifyNoNullsW(envP, wcontents, len); + *wcsptr = wcontents; + } + } + break; +#endif /* HAVE_UNICODE_WCHAR */ + + case '8': + /* The code 't' is reserved for a better, time_t based + ** implementation of dateTime conversion. */ + validateType(envP, valueP, XMLRPC_TYPE_DATETIME); + if (!envP->fault_occurred) { + char * const contents = + XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block); + char ** const strptr = (char**) va_arg(*args, char**); + *strptr = contents; + } + break; + + case '6': + validateType(envP, valueP, XMLRPC_TYPE_BASE64); + if (!envP->fault_occurred) { + unsigned char * const bin_data = + XMLRPC_MEMBLOCK_CONTENTS(unsigned char, + &valueP->_block); + size_t const len = XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block); + unsigned char ** const binptr = + (unsigned char**) va_arg(*args, unsigned char**); + size_t * const sizeptr = (size_t*) va_arg(*args, size_t**); + *binptr = bin_data; + *sizeptr = len; + } + break; + + case 'p': + validateType(envP, valueP, XMLRPC_TYPE_C_PTR); + if (!envP->fault_occurred) { + void ** const voidptrptr = (void**) va_arg(*args, void**); + *voidptrptr = valueP->_value.c_ptr; + } + break; + + case 'V': { + xmlrpc_value ** const valptr = + (xmlrpc_value**) va_arg(*args, xmlrpc_value**); + *valptr = valueP; + } + break; + + case 'A': + validateType(envP, valueP, XMLRPC_TYPE_ARRAY); + if (!envP->fault_occurred) { + xmlrpc_value ** const valptr = + (xmlrpc_value**) va_arg(*args, xmlrpc_value**); + *valptr = valueP; + } + break; + + case 'S': + validateType(envP, valueP, XMLRPC_TYPE_STRUCT); + if (!envP->fault_occurred) { + xmlrpc_value ** const valptr = + (xmlrpc_value**) va_arg(*args, xmlrpc_value**); + *valptr = valueP; + } + break; + + case '(': + validateType(envP, valueP, XMLRPC_TYPE_ARRAY); + if (!envP->fault_occurred) { + parsearray(envP, valueP, format, ')', args); + (*format)++; + } + break; + + case '{': + validateType(envP, valueP, XMLRPC_TYPE_STRUCT); + if (!envP->fault_occurred) { + parsestruct(envP, valueP, format, '}', args); + (*format)++; + } + break; + + default: + xmlrpc_env_set_fault_formatted( + envP, XMLRPC_INTERNAL_ERROR, "Invalid format character '%c'", + formatSpecChar); + } +} + + + +void +xmlrpc_parse_value_va(xmlrpc_env * const envP, + xmlrpc_value * const value, + const char * const format, + va_list args) { + + const char *format_copy; + va_list args_copy; + + XMLRPC_ASSERT_ENV_OK(envP); + XMLRPC_ASSERT_VALUE_OK(value); + XMLRPC_ASSERT(format != NULL); + + format_copy = format; + VA_LIST_COPY(args_copy, args); + parsevalue(envP, value, &format_copy, &args_copy); + if (!envP->fault_occurred) { + XMLRPC_ASSERT(*format_copy == '\0'); + } +} + + + +void +xmlrpc_parse_value(xmlrpc_env * const envP, + xmlrpc_value * const value, + const char * const format, + ...) { + + va_list args; + + va_start(args, format); + xmlrpc_parse_value_va(envP, value, format, args); + va_end(args); +} + + + +/* Copyright (C) 2001 by First Peer, Inc. All rights reserved. +** Copyright (C) 2001 by Eric Kidd. 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. */ |