|
- /*
- * Copyright (C) 2015 Patrick Monnerat, D+H <patrick.monnerat@dh.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms,
- * with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the
- * following disclaimer.
- *
- * 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.
- *
- * Neither the name of the copyright holder nor the names
- * of any other contributors may be used to endorse or
- * promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
- */
-
- /* Character encoding wrappers. */
-
- #include "libssh2_priv.h"
- #include "libssh2_ccsid.h"
-
- #include <qtqiconv.h>
- #include <iconv.h>
- #include <errno.h>
- #include <string.h>
- #include <stdio.h>
-
-
-
- #define CCSID_UTF8 1208
- #define CCSID_UTF16BE 13488
- #define STRING_GRANULE 256
- #define MAX_CHAR_SIZE 4
-
- #define OFFSET_OF(t, f) ((size_t) ((char *) &((t *) 0)->f - (char *) 0))
-
-
- struct _libssh2_string_cache {
- libssh2_string_cache * next;
- char string[1];
- };
-
-
- static const QtqCode_T utf8code = { CCSID_UTF8 };
-
-
- static ssize_t
- terminator_size(unsigned short ccsid)
- {
- QtqCode_T outcode;
- iconv_t cd;
- char *inp;
- char *outp;
- size_t ilen;
- size_t olen;
- char buf[MAX_CHAR_SIZE];
-
- /* Return the null-terminator size for the given CCSID. */
-
- /* Fast check usual CCSIDs. */
- switch (ccsid) {
- case CCSID_UTF8:
- case 0: /* Job CCSID is SBCS EBCDIC. */
- return 1;
- case CCSID_UTF16BE:
- return 2;
- }
-
- /* Convert an UTF-8 NUL to the target CCSID: use the converted size as
- result. */
- memset((void *) &outcode, 0, sizeof outcode);
- outcode.CCSID = ccsid;
- cd = QtqIconvOpen(&outcode, (QtqCode_T *) &utf8code);
- if (cd.return_value == -1)
- return -1;
- inp = "";
- ilen = 1;
- outp = buf;
- olen = sizeof buf;
- iconv(cd, &inp, &ilen, &outp, &olen);
- iconv_close(cd);
- olen = sizeof buf - olen;
- return olen? olen: -1;
- }
-
- static char *
- convert_ccsid(LIBSSH2_SESSION *session, libssh2_string_cache **cache,
- unsigned short outccsid, unsigned short inccsid,
- const char *instring, ssize_t inlen, size_t *outlen)
- {
- char *inp;
- char *outp;
- size_t olen;
- size_t ilen;
- size_t buflen;
- size_t curlen;
- ssize_t termsize;
- int i;
- char *dst;
- libssh2_string_cache *outstring;
- QtqCode_T incode;
- QtqCode_T outcode;
- iconv_t cd;
-
- if (!instring) {
- if (outlen)
- *outlen = 0;
- return NULL;
- }
- if (outlen)
- *outlen = -1;
- if (!session || !cache)
- return NULL;
-
- /* Get terminator size. */
- termsize = terminator_size(outccsid);
- if (termsize < 0)
- return NULL;
-
- /* Prepare conversion parameters. */
- memset((void *) &incode, 0, sizeof incode);
- memset((void *) &outcode, 0, sizeof outcode);
- incode.CCSID = inccsid;
- outcode.CCSID = outccsid;
- curlen = OFFSET_OF(libssh2_string_cache, string);
- inp = (char *) instring;
- ilen = inlen;
- buflen = inlen + curlen;
- if (inlen < 0) {
- incode.length_option = 1;
- buflen = STRING_GRANULE;
- ilen = 0;
- }
-
- /* Allocate output string buffer and open conversion descriptor. */
- dst = LIBSSH2_ALLOC(session, buflen + termsize);
- if (!dst)
- return NULL;
- cd = QtqIconvOpen(&outcode, &incode);
- if (cd.return_value == -1) {
- LIBSSH2_FREE(session, (char *) dst);
- return NULL;
- }
-
- /* Convert string. */
- for (;;) {
- outp = dst + curlen;
- olen = buflen - curlen;
- i = iconv(cd, &inp, &ilen, &outp, &olen);
- if (inlen < 0 && olen == buflen - curlen) {
- /* Special case: converted 0-length (sub)strings do not store the
- terminator. */
- if (termsize) {
- memset(outp, 0, termsize);
- olen -= termsize;
- }
- }
- curlen = buflen - olen;
- if (i >= 0 || errno != E2BIG)
- break;
- /* Must expand buffer. */
- buflen += STRING_GRANULE;
- outp = LIBSSH2_REALLOC(session, dst, buflen + termsize);
- if (!outp)
- break;
- dst = outp;
- }
-
- iconv_close(cd);
-
- /* Check for error. */
- if (i < 0 || !outp) {
- LIBSSH2_FREE(session, dst);
- return NULL;
- }
-
- /* Process terminator. */
- if (inlen < 0)
- curlen -= termsize;
- else if (termsize)
- memset(dst + curlen, 0, termsize);
-
- /* Shorten buffer if possible. */
- if (curlen < buflen)
- dst = LIBSSH2_REALLOC(session, dst, curlen + termsize);
-
- /* Link to cache. */
- outstring = (libssh2_string_cache *) dst;
- outstring->next = *cache;
- *cache = outstring;
-
- /* Return length if required. */
- if (outlen)
- *outlen = curlen - OFFSET_OF(libssh2_string_cache, string);
-
- return outstring->string;
- }
-
- LIBSSH2_API char *
- libssh2_from_ccsid(LIBSSH2_SESSION *session, libssh2_string_cache **cache,
- unsigned short ccsid, const char *string, ssize_t inlen,
- size_t *outlen)
- {
- return convert_ccsid(session, cache,
- CCSID_UTF8, ccsid, string, inlen, outlen);
- }
-
- LIBSSH2_API char *
- libssh2_to_ccsid(LIBSSH2_SESSION *session, libssh2_string_cache **cache,
- unsigned short ccsid, const char *string, ssize_t inlen,
- size_t *outlen)
- {
- return convert_ccsid(session, cache,
- ccsid, CCSID_UTF8, string, inlen, outlen);
- }
-
- LIBSSH2_API void
- libssh2_release_string_cache(LIBSSH2_SESSION *session,
- libssh2_string_cache **cache)
- {
- libssh2_string_cache *p;
-
- if (session && cache)
- while ((p = *cache)) {
- *cache = p->next;
- LIBSSH2_FREE(session, (char *) p);
- }
- }
-
- /* vim: set expandtab ts=4 sw=4: */
|