geom_gate userland utility improvements
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

253 lines
6.9 KiB

  1. /*
  2. * Copyright (C) 2015 Patrick Monnerat, D+H <patrick.monnerat@dh.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms,
  6. * with or without modification, are permitted provided
  7. * that the following conditions are met:
  8. *
  9. * Redistributions of source code must retain the above
  10. * copyright notice, this list of conditions and the
  11. * following disclaimer.
  12. *
  13. * Redistributions in binary form must reproduce the above
  14. * copyright notice, this list of conditions and the following
  15. * disclaimer in the documentation and/or other materials
  16. * provided with the distribution.
  17. *
  18. * Neither the name of the copyright holder nor the names
  19. * of any other contributors may be used to endorse or
  20. * promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  24. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  25. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  26. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  28. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  29. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  30. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  32. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  33. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  35. * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  36. * OF SUCH DAMAGE.
  37. */
  38. /* Character encoding wrappers. */
  39. #include "libssh2_priv.h"
  40. #include "libssh2_ccsid.h"
  41. #include <qtqiconv.h>
  42. #include <iconv.h>
  43. #include <errno.h>
  44. #include <string.h>
  45. #include <stdio.h>
  46. #define CCSID_UTF8 1208
  47. #define CCSID_UTF16BE 13488
  48. #define STRING_GRANULE 256
  49. #define MAX_CHAR_SIZE 4
  50. #define OFFSET_OF(t, f) ((size_t) ((char *) &((t *) 0)->f - (char *) 0))
  51. struct _libssh2_string_cache {
  52. libssh2_string_cache * next;
  53. char string[1];
  54. };
  55. static const QtqCode_T utf8code = { CCSID_UTF8 };
  56. static ssize_t
  57. terminator_size(unsigned short ccsid)
  58. {
  59. QtqCode_T outcode;
  60. iconv_t cd;
  61. char *inp;
  62. char *outp;
  63. size_t ilen;
  64. size_t olen;
  65. char buf[MAX_CHAR_SIZE];
  66. /* Return the null-terminator size for the given CCSID. */
  67. /* Fast check usual CCSIDs. */
  68. switch (ccsid) {
  69. case CCSID_UTF8:
  70. case 0: /* Job CCSID is SBCS EBCDIC. */
  71. return 1;
  72. case CCSID_UTF16BE:
  73. return 2;
  74. }
  75. /* Convert an UTF-8 NUL to the target CCSID: use the converted size as
  76. result. */
  77. memset((void *) &outcode, 0, sizeof outcode);
  78. outcode.CCSID = ccsid;
  79. cd = QtqIconvOpen(&outcode, (QtqCode_T *) &utf8code);
  80. if (cd.return_value == -1)
  81. return -1;
  82. inp = "";
  83. ilen = 1;
  84. outp = buf;
  85. olen = sizeof buf;
  86. iconv(cd, &inp, &ilen, &outp, &olen);
  87. iconv_close(cd);
  88. olen = sizeof buf - olen;
  89. return olen? olen: -1;
  90. }
  91. static char *
  92. convert_ccsid(LIBSSH2_SESSION *session, libssh2_string_cache **cache,
  93. unsigned short outccsid, unsigned short inccsid,
  94. const char *instring, ssize_t inlen, size_t *outlen)
  95. {
  96. char *inp;
  97. char *outp;
  98. size_t olen;
  99. size_t ilen;
  100. size_t buflen;
  101. size_t curlen;
  102. ssize_t termsize;
  103. int i;
  104. char *dst;
  105. libssh2_string_cache *outstring;
  106. QtqCode_T incode;
  107. QtqCode_T outcode;
  108. iconv_t cd;
  109. if (!instring) {
  110. if (outlen)
  111. *outlen = 0;
  112. return NULL;
  113. }
  114. if (outlen)
  115. *outlen = -1;
  116. if (!session || !cache)
  117. return NULL;
  118. /* Get terminator size. */
  119. termsize = terminator_size(outccsid);
  120. if (termsize < 0)
  121. return NULL;
  122. /* Prepare conversion parameters. */
  123. memset((void *) &incode, 0, sizeof incode);
  124. memset((void *) &outcode, 0, sizeof outcode);
  125. incode.CCSID = inccsid;
  126. outcode.CCSID = outccsid;
  127. curlen = OFFSET_OF(libssh2_string_cache, string);
  128. inp = (char *) instring;
  129. ilen = inlen;
  130. buflen = inlen + curlen;
  131. if (inlen < 0) {
  132. incode.length_option = 1;
  133. buflen = STRING_GRANULE;
  134. ilen = 0;
  135. }
  136. /* Allocate output string buffer and open conversion descriptor. */
  137. dst = LIBSSH2_ALLOC(session, buflen + termsize);
  138. if (!dst)
  139. return NULL;
  140. cd = QtqIconvOpen(&outcode, &incode);
  141. if (cd.return_value == -1) {
  142. LIBSSH2_FREE(session, (char *) dst);
  143. return NULL;
  144. }
  145. /* Convert string. */
  146. for (;;) {
  147. outp = dst + curlen;
  148. olen = buflen - curlen;
  149. i = iconv(cd, &inp, &ilen, &outp, &olen);
  150. if (inlen < 0 && olen == buflen - curlen) {
  151. /* Special case: converted 0-length (sub)strings do not store the
  152. terminator. */
  153. if (termsize) {
  154. memset(outp, 0, termsize);
  155. olen -= termsize;
  156. }
  157. }
  158. curlen = buflen - olen;
  159. if (i >= 0 || errno != E2BIG)
  160. break;
  161. /* Must expand buffer. */
  162. buflen += STRING_GRANULE;
  163. outp = LIBSSH2_REALLOC(session, dst, buflen + termsize);
  164. if (!outp)
  165. break;
  166. dst = outp;
  167. }
  168. iconv_close(cd);
  169. /* Check for error. */
  170. if (i < 0 || !outp) {
  171. LIBSSH2_FREE(session, dst);
  172. return NULL;
  173. }
  174. /* Process terminator. */
  175. if (inlen < 0)
  176. curlen -= termsize;
  177. else if (termsize)
  178. memset(dst + curlen, 0, termsize);
  179. /* Shorten buffer if possible. */
  180. if (curlen < buflen)
  181. dst = LIBSSH2_REALLOC(session, dst, curlen + termsize);
  182. /* Link to cache. */
  183. outstring = (libssh2_string_cache *) dst;
  184. outstring->next = *cache;
  185. *cache = outstring;
  186. /* Return length if required. */
  187. if (outlen)
  188. *outlen = curlen - OFFSET_OF(libssh2_string_cache, string);
  189. return outstring->string;
  190. }
  191. LIBSSH2_API char *
  192. libssh2_from_ccsid(LIBSSH2_SESSION *session, libssh2_string_cache **cache,
  193. unsigned short ccsid, const char *string, ssize_t inlen,
  194. size_t *outlen)
  195. {
  196. return convert_ccsid(session, cache,
  197. CCSID_UTF8, ccsid, string, inlen, outlen);
  198. }
  199. LIBSSH2_API char *
  200. libssh2_to_ccsid(LIBSSH2_SESSION *session, libssh2_string_cache **cache,
  201. unsigned short ccsid, const char *string, ssize_t inlen,
  202. size_t *outlen)
  203. {
  204. return convert_ccsid(session, cache,
  205. ccsid, CCSID_UTF8, string, inlen, outlen);
  206. }
  207. LIBSSH2_API void
  208. libssh2_release_string_cache(LIBSSH2_SESSION *session,
  209. libssh2_string_cache **cache)
  210. {
  211. libssh2_string_cache *p;
  212. if (session && cache)
  213. while ((p = *cache)) {
  214. *cache = p->next;
  215. LIBSSH2_FREE(session, (char *) p);
  216. }
  217. }
  218. /* vim: set expandtab ts=4 sw=4: */