#include <stdio.h>
/*=============================================================================
                              xmlrpc_string
===============================================================================
  Routines for the "string" type of xmlrpc_value.

  By Bryan Henderson.

  Contributed to the public domain by its author.
=============================================================================*/

#include "xmlrpc_config.h"

#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#include "bool.h"
#include "mallocvar.h"

#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/string_int.h"
#include "xmlrpc-c/util.h"



void
xmlrpc_destroyString(xmlrpc_value * const valueP) {

    if (valueP->_wcs_block)
        xmlrpc_mem_block_free(valueP->_wcs_block);

    xmlrpc_mem_block_free(valueP->blockP);
}



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");
}



#if HAVE_UNICODE_WCHAR

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");
}
#endif



static void
validateStringType(xmlrpc_env *         const envP,
                   const xmlrpc_value * const valueP) {

    if (valueP->_type != XMLRPC_TYPE_STRING) {
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
            "string type was expected.",
            xmlrpc_type_name(valueP->_type));
    }
}



static void
accessStringValue(xmlrpc_env *         const envP,
                  const xmlrpc_value * const valueP,
                  size_t *             const lengthP,
                  const char **        const contentsP) {

    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        size_t const size =
            XMLRPC_MEMBLOCK_SIZE(char, valueP->blockP);
        const char * const contents =
            XMLRPC_MEMBLOCK_CONTENTS(char, valueP->blockP);
        size_t const len = size - 1;
            /* The memblock has a null character added to the end */

        verifyNoNulls(envP, contents, len);

        *lengthP = len;
        *contentsP = contents;
    }
}



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 as an ASCIIZ string, with
   LF for line delimiters.

   Return the string in newly malloc'ed storage that Caller must free.

   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).
-----------------------------------------------------------------------------*/
    size_t length;
    const char * contents;

    accessStringValue(envP, valueP, &length, &contents);

    if (!envP->fault_occurred) {
        char * stringValue;

        MALLOCARRAY(stringValue, length + 1);
        if (stringValue == NULL)
            xmlrpc_faultf(envP, "Unable to allocate space "
                          "for %u-character string", (unsigned)length);
        else {
            memcpy(stringValue, contents, length);
            stringValue[length] = '\0';

            *stringValueP = stringValue;
        }
    }
}



static unsigned int
lineDelimCount(const char * const start,
               const char * const end) {

    unsigned int count;
    const char * p;

    for (p = start, count = 0; p < end; ) {
        const char * const nlPos = memchr(p, '\n', end-p);
        if (nlPos) {
            ++count;
            p = nlPos + 1;
        } else
            p = end;
    }

    return count;
}



static void
copyAndConvertLfToCrlf(xmlrpc_env *  const envP,
                       size_t        const srcLen,
                       const char *  const src,
                       size_t *      const dstLenP,
                       const char ** const dstP) {

    const char * const srcEnd = src + srcLen;
    unsigned int const nLineDelim = lineDelimCount(src, srcEnd);
    size_t const dstLen = srcLen + nLineDelim;
    char * dst;

    MALLOCARRAY(dst, dstLen + 1);
    if (dst == NULL)
        xmlrpc_faultf(envP, "Unable to allocate space "
                      "for %u-character string", (unsigned)dstLen + 1);
    else {
        const char * p;  /* source pointer */
        char * q;        /* destination pointer */

        for (p = &src[0], q = &dst[0]; p < srcEnd; ++p) {
            if (*p == '\n')
                *q++ = '\r';

            *q++ = *p;
        }
        XMLRPC_ASSERT(q == dst + dstLen);

        *q = '\0';

        *dstP    = dst;
        *dstLenP = dstLen;
    }
}



void
xmlrpc_read_string_crlf(xmlrpc_env *         const envP,
                        const xmlrpc_value * const valueP,
                        const char **        const stringValueP) {
/*----------------------------------------------------------------------------
   Same as xmlrpc_read_string(), but return CRLF instead of LF for
   line delimiters.
-----------------------------------------------------------------------------*/
    size_t length;
    const char * contents;

    accessStringValue(envP, valueP, &length, &contents);

    if (!envP->fault_occurred) {
        size_t stringLen;

        copyAndConvertLfToCrlf(envP, length, contents,
                               &stringLen, stringValueP);
    }
}



void
xmlrpc_read_string_old(xmlrpc_env *         const envP,
                       const xmlrpc_value * const valueP,
                       const char **        const stringValueP) {
/*----------------------------------------------------------------------------
   Like xmlrpc_read_string(), except it returns as *stringValueP a pointer
   into memory owned by *valueP, rather than new memory to be owned by
   Caller.

   This is for internal use; it's necessary to implement the deprecated
   xmlrpc_parse_value(), which also returns pointers to someone else's
   storage.
-----------------------------------------------------------------------------*/
    size_t length;
    accessStringValue(envP, valueP, &length, stringValueP);
}



void
xmlrpc_read_string_lp(xmlrpc_env *         const envP,
                      const xmlrpc_value * const valueP,
                      size_t *             const lengthP,
                      const char **        const stringValueP) {

    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        size_t const size =
            XMLRPC_MEMBLOCK_SIZE(char, valueP->blockP);
        const char * const contents =
            XMLRPC_MEMBLOCK_CONTENTS(char, valueP->blockP);

        char * stringValue;

        stringValue = malloc(size);
        if (stringValue == NULL)
            xmlrpc_faultf(envP, "Unable to allocate %u bytes for string.",
                          (unsigned int)size);
        else {
            memcpy(stringValue, contents, size);
            *stringValueP = stringValue;
            *lengthP = size - 1;  /* Size includes terminating NUL */
        }
    }
}



void
xmlrpc_read_string_lp_crlf(xmlrpc_env *         const envP,
                           const xmlrpc_value * const valueP,
                           size_t *             const lengthP,
                           const char **        const stringValueP) {

    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        size_t const size =
            XMLRPC_MEMBLOCK_SIZE(char, valueP->blockP); /* Includes NUL */
        const char * const contents =
            XMLRPC_MEMBLOCK_CONTENTS(char, valueP->blockP);

        copyAndConvertLfToCrlf(envP, size-1, contents,
                               lengthP, stringValueP);
    }
}



void
xmlrpc_read_string_lp_old(xmlrpc_env *         const envP,
                          const xmlrpc_value * const valueP,
                          size_t *             const lengthP,
                          const char **        const stringValueP) {
/*----------------------------------------------------------------------------
  This is to xmlrpc_read_string_lp() as xmlrpc_read_string_old() is
  to xmlrpc_read_string().
-----------------------------------------------------------------------------*/
    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        *lengthP =      XMLRPC_MEMBLOCK_SIZE(char, valueP->blockP) - 1;
        *stringValueP = XMLRPC_MEMBLOCK_CONTENTS(char, valueP->blockP);
    }
}



static __inline__ void
setupWcsBlock(xmlrpc_env *   const envP,
              xmlrpc_value * const valueP) {
/*----------------------------------------------------------------------------
   Add a wcs block (wchar_t string) to the indicated xmlrpc_value if it
   doesn't have one already.
-----------------------------------------------------------------------------*/
    if (!valueP->_wcs_block) {
        char * const contents =
            XMLRPC_MEMBLOCK_CONTENTS(char, valueP->blockP);
        size_t const len =
            XMLRPC_MEMBLOCK_SIZE(char, valueP->blockP) - 1;
        valueP->_wcs_block =
            xmlrpc_utf8_to_wcs(envP, contents, len + 1);
    }
}



#if HAVE_UNICODE_WCHAR

static void
accessStringValueW(xmlrpc_env *     const envP,
                   xmlrpc_value *   const valueP,
                   size_t *         const lengthP,
                   const wchar_t ** const stringValueP) {

    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        setupWcsBlock(envP, valueP);

        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;

            verifyNoNullsW(envP, wcontents, len);

            *lengthP = len;
            *stringValueP = wcontents;
        }
    }
}



void
xmlrpc_read_string_w(xmlrpc_env *     const envP,
                     xmlrpc_value *   const valueP,
                     const wchar_t ** const stringValueP) {

    size_t length;
    const wchar_t * wcontents;

    accessStringValueW(envP, valueP, &length, &wcontents);

    if (!envP->fault_occurred) {
        wchar_t * stringValue;
        MALLOCARRAY(stringValue, length + 1);
        if (stringValue == NULL)
            xmlrpc_faultf(envP, "Unable to allocate space for %u-byte string",
                          (unsigned)length);
        else {
            memcpy(stringValue, wcontents, length * sizeof(wchar_t));
            stringValue[length] = '\0';

            *stringValueP = stringValue;
        }
    }
}



static unsigned int
lineDelimCountW(const wchar_t * const start,
                const wchar_t * const end) {

    unsigned int count;
    const wchar_t * p;

    count = 0;
    p = start;

    while (p && p < end) {
        /* We used to use memchr(), but Windows doesn't have it */
        p = wcsstr(p, L"\n");
        if (p && p < end) {
            ++count; /* count the newline */
            ++p;  /* skip the newline */
        }
    }

    return count;
}



static void
wCopyAndConvertLfToCrlf(xmlrpc_env *     const envP,
                        size_t           const srcLen,
                        const wchar_t *  const src,
                        size_t *         const dstLenP,
                        const wchar_t ** const dstP) {

    const wchar_t * const srcEnd = src + srcLen;
    unsigned int const nLineDelim = lineDelimCountW(src, srcEnd);
    size_t const dstLen = srcLen + nLineDelim;
    wchar_t * dst;

    MALLOCARRAY(dst, dstLen + 1);
    if (dst == NULL)
        xmlrpc_faultf(envP, "Unable to allocate space "
                      "for %u-character string", (unsigned)dstLen + 1);
    else {
        const wchar_t * p;  /* source pointer */
        wchar_t * q;        /* destination pointer */

        for (p = &src[0], q = &dst[0]; p < srcEnd; ++p) {
            if (*p == '\n')
                *q++ = '\r';

            *q++ = *p;
        }
        XMLRPC_ASSERT(q == dst + dstLen);

        *q = '\0';

        *dstP    = dst;
        *dstLenP = dstLen;
    }
}



void
xmlrpc_read_string_w_crlf(xmlrpc_env *     const envP,
                          xmlrpc_value *   const valueP,
                          const wchar_t ** const stringValueP) {

    size_t size;
    const wchar_t * contents;

    accessStringValueW(envP, valueP, &size, &contents);

    if (!envP->fault_occurred) {
        size_t stringLen;

        wCopyAndConvertLfToCrlf(envP, size, contents,
                                &stringLen, stringValueP);
    }
}



void
xmlrpc_read_string_w_old(xmlrpc_env *     const envP,
                         xmlrpc_value *   const valueP,
                         const wchar_t ** const stringValueP) {
/*----------------------------------------------------------------------------
  This is to xmlrpc_read_string_w() as xmlrpc_read_string_old() is
  to xmlrpc_read_string().
-----------------------------------------------------------------------------*/
    size_t length;

    accessStringValueW(envP, valueP, &length, stringValueP);
}



void
xmlrpc_read_string_w_lp(xmlrpc_env *     const envP,
                        xmlrpc_value *   const valueP,
                        size_t *         const lengthP,
                        const wchar_t ** const stringValueP) {

    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        setupWcsBlock(envP, valueP);

        if (!envP->fault_occurred) {
            wchar_t * const wcontents =
                XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block);
            size_t const size =
                XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block);

            wchar_t * stringValue;

            MALLOCARRAY(stringValue, size);
            if (stringValue == NULL)
                xmlrpc_faultf(envP,
                              "Unable to allocate space for %u-byte string",
                              (unsigned int)size);
            else {
                memcpy(stringValue, wcontents, size * sizeof(wchar_t));

                *lengthP      = size - 1; /* size includes terminating NUL */
                *stringValueP = stringValue;
            }
        }
    }
}



void
xmlrpc_read_string_w_lp_crlf(xmlrpc_env *     const envP,
                             xmlrpc_value *   const valueP,
                             size_t *         const lengthP,
                             const wchar_t ** const stringValueP) {

    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        setupWcsBlock(envP, valueP);

        if (!envP->fault_occurred) {
            size_t const size =
                XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block);
            wchar_t * const wcontents =
                XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block);

            wCopyAndConvertLfToCrlf(envP, size-1, wcontents,
                                   lengthP, stringValueP);
        }
    }
}



void
xmlrpc_read_string_w_lp_old(xmlrpc_env *     const envP,
                            xmlrpc_value *   const valueP,
                            size_t *         const lengthP,
                            const wchar_t ** const stringValueP) {
/*----------------------------------------------------------------------------
  This is to xmlrpc_read_string_w_lp() as xmlrpc_read_string_old() is
  to xmlrpc_read_string().
-----------------------------------------------------------------------------*/
    validateStringType(envP, valueP);
    if (!envP->fault_occurred) {
        setupWcsBlock(envP, valueP);

        if (!envP->fault_occurred) {
            wchar_t * const wcontents =
                XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block);
            size_t const size =
                XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block);

            *lengthP      = size - 1;  /* size includes terminating NUL */
            *stringValueP = wcontents;
        }
    }
}
#endif   /* HAVE_UNICODE_WCHAR */



void
xmlrpc_string_validate(xmlrpc_env *   const envP,
                       xmlrpc_value * const valueP) {
/*----------------------------------------------------------------------------
   Validate that *valueP is not the kind of value that would serialize into
   invalid XML.

   To wit, the string does not contain any characters that are not valid XML
   characters, which means it does not contain ASCII control characters
   other than carriage return, line feed, and tab.

   Xmlrpc-c has the odd function, because of mistakes that were made in the
   early days of XML-RPC, that it is possible to cause it to generate an
   XML-RPC call or response that is invalid XML, which you do with a string
   value which contains things other than XML characters.  But it's a really
   bad idea to exercise that option, so code that constructs string values can
   use this function to ensure that it does not.  See the discussion of the
   string data type in the Xmlrpc-c user's guide for a detailed explanation of
   this issue.
-----------------------------------------------------------------------------*/
    enum {LF = 0x0a, CR = 0x0d, TAB = 0x08};

    size_t length;
    const char * contents;

    accessStringValue(envP, valueP, &length, &contents);

    if (!envP->fault_occurred) {
        unsigned int i;
        for (i = 0; i < length && !envP->fault_occurred; ++i) {
            char const c = contents[i];
            if (iscntrl(c) && c != LF && c != CR && c != TAB)
                xmlrpc_faultf(envP, "String contains an invalid value "
                              "(Not a Unicode codepoint for a legal XML "
                              "character) x%02x at position %u",
                              c, i);
        }
    }
}



static void
copyLines(xmlrpc_env *        const envP,
          const char *        const src,
          size_t              const srcLen,
          xmlrpc_mem_block ** const dstPP) {
/*----------------------------------------------------------------------------
   Generate the internal representation of string xmlrpc_value contents for
   string 'src', which is 'srcLen' characters long and has lines separated by
   LF, CR, and/or CRLF.  Return it as a new xmlrpc_mem_block at *dstPP.

   Note that the source format differs from the destination format in
   that in the destination format, lines are separated only by newline
   (LF).

   It is tempting to believe that if we just put the user's line
   delimiters in the xmlrpc_value here (i.e. where user has CRLF, the
   xmlrpc_value also has CRLF), the user's line delimiters would go
   all the way across to the XML-RPC partner.  But that won't work,
   because the XML processor on the other side will, following Section
   2.11 of the XML spec, normalize all line endings to LF anyhow.  So
   then you might ask, why do we bother to do all the work to convert
   them here?  Because: besides just being logically cleaner, this way
   xmlrpc_read_string() gets the proper value -- the same one the
   XML-RPC partner would see.
-----------------------------------------------------------------------------*/
    /* Destination format is sometimes smaller than source (because
       CRLF turns into LF), but never smaller.  So we allocate
       destination space equal to source size (plus one for
       terminating NUL), but don't necessarily use it all.
    */

    /* To convert LF, CR, and CRLF to LF, all we have to do is
       copy everything up to a CR verbatim, then insert an LF and
       skip the CR and any following LF, and repeat.
    */

    xmlrpc_mem_block * dstP;

    dstP = XMLRPC_MEMBLOCK_NEW(char, envP, srcLen + 1);

    if (!envP->fault_occurred) {
        const char * const srcEnd = &src[srcLen];
        char * const contents = XMLRPC_MEMBLOCK_CONTENTS(char, dstP);

        const char * srcCursor;
        char * dstCursor;

        for (srcCursor = &src[0], dstCursor = &contents[0];
             srcCursor < srcEnd;) {

            char * const crPos = memchr(srcCursor, '\r', srcEnd - srcCursor);

            if (crPos) {
                size_t const copyLen = crPos - srcCursor;
                memcpy(dstCursor, srcCursor, copyLen);
                srcCursor += copyLen;
                dstCursor += copyLen;

                *(dstCursor++) = '\n';

                XMLRPC_ASSERT(*srcCursor == '\r');
                ++srcCursor;  /* Move past CR */
                if (*srcCursor == '\n')
                    ++srcCursor;  /* Move past LF */
            } else {
                size_t const remainingLen = srcEnd - srcCursor;
                memcpy(dstCursor, srcCursor, remainingLen);
                srcCursor += remainingLen;
                dstCursor += remainingLen;
            }
        }

        *dstCursor++ = '\0';

        XMLRPC_ASSERT((unsigned)(dstCursor - &contents[0]) <= srcLen + 1);

        XMLRPC_MEMBLOCK_RESIZE(char, envP, dstP, dstCursor - &contents[0]);

        if (envP->fault_occurred)
            XMLRPC_MEMBLOCK_FREE(char, dstP);
    }
    *dstPP = dstP;
}



static void
copySimple(xmlrpc_env *        const envP,
           const char *        const src,
           size_t              const srcLen,
           xmlrpc_mem_block ** const dstPP) {
/*----------------------------------------------------------------------------
   Generate the internal representation of string xmlrpc_value contents for
   string 'src', which is 'srcLen' characters long and, conveniently enough,
   has the exact same format as the internal representation.

   To wit, 'src' has lines separated by LFs only -- no CR or CRLF.

   Return it as a new xmlrpc_mem_block at *dstPP.
-----------------------------------------------------------------------------*/
    xmlrpc_mem_block * dstP;

    dstP = XMLRPC_MEMBLOCK_NEW(char, envP, srcLen + 1);

    if (!envP->fault_occurred) {
        char * const contents = XMLRPC_MEMBLOCK_CONTENTS(char, dstP);

        memcpy(contents, src, srcLen);
        contents[srcLen] = '\0';
    }
    *dstPP = dstP;
}



enum crTreatment { CR_IS_LINEDELIM, CR_IS_CHAR };

static void
stringNew(xmlrpc_env *     const envP,
          size_t           const length,
          const char *     const value,
          enum crTreatment const crTreatment,
          xmlrpc_value **  const valPP) {

    fprintf(stderr, "stringNew entered\n");
    xmlrpc_value * valP;

    xmlrpc_validate_utf8(envP, value, length);

    if (!envP->fault_occurred) {
        fprintf(stderr, "Going to xmlrpc_createXmlrpcValue\n");
        xmlrpc_createXmlrpcValue(envP, &valP);
        fprintf(stderr, "Back from createXmlrpcValue\n");

        if (!envP->fault_occurred) {
            valP->_type = XMLRPC_TYPE_STRING;
            valP->_wcs_block = NULL;

            /* Note that copyLines() works for strings with no CRs, but
               it's slower.
            */
            if (memchr(value, '\r', length) && crTreatment == CR_IS_LINEDELIM)
                copyLines(envP, value, length, &valP->blockP);
            else
                copySimple(envP, value, length, &valP->blockP);

            if (envP->fault_occurred)
                free(valP);
            else
                *valPP = valP;
        }
    }
    fprintf(stderr, "stringNew exiting\n");
}



xmlrpc_value *
xmlrpc_string_new_lp(xmlrpc_env * const envP,
                     size_t       const length,
                     const char * const value) {

    xmlrpc_value * retval;
    stringNew(envP, length, value, CR_IS_LINEDELIM, &retval);
    return retval;
}



xmlrpc_value *
xmlrpc_string_new_lp_cr(xmlrpc_env * const envP,
                        size_t       const length,
                        const char * const value) {

    xmlrpc_value * retval;
    stringNew(envP, length, value, CR_IS_CHAR, &retval);
    return retval;
}



xmlrpc_value *
xmlrpc_string_new(xmlrpc_env * const envP,
                  const char * const value) {

    xmlrpc_value * retval;
    stringNew(envP, strlen(value), value, CR_IS_LINEDELIM, &retval);
    return retval;
}



xmlrpc_value *
xmlrpc_string_new_cr(xmlrpc_env * const envP,
                     const char * const value) {

    xmlrpc_value * retval;
    stringNew(envP, strlen(value), value, CR_IS_CHAR, &retval);
    return retval;
}



xmlrpc_value *
xmlrpc_string_new_va(xmlrpc_env * const envP,
                     const char * const format,
                     va_list            args) {

    const char * formattedString;
    xmlrpc_value * retvalP;

    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT(format != NULL);

    xmlrpc_vasprintf(&formattedString, format, args);

    if (xmlrpc_strnomem(formattedString)) {
        xmlrpc_faultf(envP, "Out of memory building formatted string");
        retvalP = NULL;  /* defeat compiler warning */
    } else
        retvalP = xmlrpc_string_new(envP, formattedString);

    xmlrpc_strfree(formattedString);

    return retvalP;
}



xmlrpc_value *
xmlrpc_string_new_f(xmlrpc_env * const envP,
                    const char * const format,
                    ...) {

    va_list args;
    xmlrpc_value * retval;

    va_start(args, format);

    retval = xmlrpc_string_new_va(envP, format, args);

    va_end(args);

    return retval;
}



#if HAVE_UNICODE_WCHAR

static void
stringWNew(xmlrpc_env *     const envP,
           size_t           const length,
           const wchar_t *  const value,
           enum crTreatment const crTreatment,
           xmlrpc_value **  const valPP) {

    xmlrpc_mem_block * utf8P;

    utf8P = xmlrpc_wcs_to_utf8(envP, value, length);
    if (!envP->fault_occurred) {
        char * const utf8_value = XMLRPC_MEMBLOCK_CONTENTS(char, utf8P);
        size_t const utf8_len   = XMLRPC_MEMBLOCK_SIZE(char, utf8P);

        if (!envP->fault_occurred) {
            stringNew(envP, utf8_len, utf8_value, crTreatment, valPP);

            XMLRPC_MEMBLOCK_FREE(char, utf8P);
        }
    }
}



xmlrpc_value *
xmlrpc_string_w_new_lp(xmlrpc_env *    const envP,
                       size_t          const length,
                       const wchar_t * const value) {

    xmlrpc_value * valP;
    stringWNew(envP, length, value, CR_IS_LINEDELIM, &valP);
    return valP;
}




xmlrpc_value *
xmlrpc_string_w_new_lp_cr(xmlrpc_env *    const envP,
                          size_t          const length,
                          const wchar_t * const value) {

    xmlrpc_value * valP;
    stringWNew(envP, length, value, CR_IS_CHAR, &valP);
    return valP;
}




xmlrpc_value *
xmlrpc_string_w_new(xmlrpc_env *    const envP,
                    const wchar_t * const value) {

    xmlrpc_value * valP;
    stringWNew(envP, wcslen(value), value, CR_IS_LINEDELIM, &valP);
    return valP;
}



xmlrpc_value *
xmlrpc_string_w_new_cr(xmlrpc_env *    const envP,
                       const wchar_t * const value) {

    xmlrpc_value * valP;
    stringWNew(envP, wcslen(value), value, CR_IS_CHAR, &valP);
    return valP;
}



#endif   /* HAVE_UNICODE_WCHAR */



xmlrpc_value *
xmlrpc_string_new_value(xmlrpc_env *   const envP,
                        xmlrpc_value * const valueP) {

    xmlrpc_value * valP;

    if (valueP->_type != XMLRPC_TYPE_STRING) {
        xmlrpc_env_set_fault_formatted(envP, XMLRPC_TYPE_ERROR,
                                       "Value is not a string.  "
                                       "It is type #%d", valueP->_type);
        valP = NULL;
    } else {
        xmlrpc_createXmlrpcValue(envP, &valP);

        if (!envP->fault_occurred) {
            valP->_type = XMLRPC_TYPE_STRING;

            valP->blockP =
                xmlrpc_mem_block_new(envP,
                                     xmlrpc_mem_block_size(valueP->blockP));

            if (!envP->fault_occurred) {
                memcpy(xmlrpc_mem_block_contents(valP->blockP),
                       xmlrpc_mem_block_contents(valueP->blockP),
                       xmlrpc_mem_block_size(valueP->blockP));
            }
        }
        if (!envP->fault_occurred) {
            if (valueP->_wcs_block) {
                valP->_wcs_block =
                    xmlrpc_mem_block_new(
                        envP,
                        xmlrpc_mem_block_size(valueP->_wcs_block));

                if (!envP->fault_occurred) {
                    memcpy(xmlrpc_mem_block_contents(valP->_wcs_block),
                           xmlrpc_mem_block_contents(valueP->_wcs_block),
                           xmlrpc_mem_block_size(valueP->_wcs_block));
                }
            } else
                valP->_wcs_block = NULL;
        }
    }
    return valP;
}



