1 /* Native functions for character set conversion. 2 3 Copyright (C) 2016, 2017 Paul Boddie <paul@boddie.org.uk> 4 5 This program is free software; you can redistribute it and/or modify it under 6 the terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3 of the License, or (at your option) any later 8 version. 9 10 This program is distributed in the hope that it will be useful, but WITHOUT 11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 details. 14 15 You should have received a copy of the GNU General Public License along with 16 this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include <iconv.h> /* iconv, iconv_close, iconv_open */ 20 #include <string.h> /* memcpy */ 21 #include <errno.h> /* errno */ 22 #include "native/common.h" 23 #include "types.h" 24 #include "exceptions.h" 25 #include "ops.h" 26 #include "progconsts.h" 27 #include "progops.h" 28 #include "progtypes.h" 29 #include "main.h" 30 31 static const size_t OUTBUFSIZE_MIN = 16; 32 33 static void __raise_incomplete_sequence_error(__attr value, __attr arg) 34 { 35 #ifdef __HAVE_posix_iconv_IncompleteSequenceError 36 __attr args[3] = {__NULL, value, arg}; 37 __attr exc = __new_posix_iconv_IncompleteSequenceError(args); 38 __Raise(exc); 39 #endif /* __HAVE_posix_iconv_IncompleteSequenceError */ 40 } 41 42 static void __raise_invalid_sequence_error(__attr value, __attr arg) 43 { 44 #ifdef __HAVE_posix_iconv_InvalidSequenceError 45 __attr args[3] = {__NULL, value, arg}; 46 __attr exc = __new_posix_iconv_InvalidSequenceError(args); 47 __Raise(exc); 48 #endif /* __HAVE_posix_iconv_InvalidSequenceError */ 49 } 50 51 /* Character set conversion. */ 52 53 __attr __fn_native_iconv_iconv(__attr __self, __attr cd, __attr state) 54 { 55 /* cd interpreted as iconv_t */ 56 iconv_t c = (iconv_t) cd.datavalue; 57 /* state.__data__ interpreted as list */ 58 __fragment *f = __load_via_object(state.value, __data__).seqvalue; 59 60 /* Obtain the string, start position, and remaining bytes from the state. */ 61 62 char *inbuf = __load_via_object(f->attrs[0].value, __data__).strvalue; 63 int start = __load_via_object(f->attrs[1].value, __data__).intvalue; 64 int remaining = __load_via_object(f->attrs[2].value, __data__).intvalue; 65 66 /* Allocate a string for the output buffer using the remaining input size 67 as a guide. */ 68 69 size_t outbufsize = remaining < OUTBUFSIZE_MIN ? OUTBUFSIZE_MIN : remaining; 70 size_t outbytesleft = outbufsize; 71 size_t inbytesleft = remaining; 72 73 char buf[outbytesleft]; 74 char *outbuf = buf, *outbufstart = outbuf, *resultbuf; 75 size_t result, outbytestotal; 76 77 /* Convert from the start point. */ 78 79 inbuf += start; 80 81 errno = 0; 82 result = iconv(c, &inbuf, &inbytesleft, &outbuf, &outbytesleft); 83 84 /* Return any string. */ 85 86 if ((result != -1) || (errno == E2BIG) || (errno == EINVAL)) 87 { 88 outbytestotal = outbufsize - outbytesleft; 89 resultbuf = __ALLOCATE(outbytestotal + 1, sizeof(char)); 90 memcpy(resultbuf, outbufstart, outbytestotal); 91 92 /* Mutate the state to indicate the next input buffer position. */ 93 94 f->attrs[1] = __new_int(start + remaining - inbytesleft); 95 f->attrs[2] = __new_int(inbytesleft); 96 97 /* Incomplete sequence: raise the string in an OSError instead. */ 98 99 if (errno == EINVAL) 100 __raise_incomplete_sequence_error(__new_int(errno), __new_str(resultbuf, outbytestotal)); 101 102 return __new_str(resultbuf, outbytestotal); 103 } 104 105 /* Invalid sequence. */ 106 107 if (errno == EILSEQ) 108 { 109 resultbuf = __ALLOCATE(inbytesleft + 1, sizeof(char)); 110 memcpy(resultbuf, inbuf, inbytesleft); 111 __raise_invalid_sequence_error(__new_int(errno), __new_str(resultbuf, inbytesleft)); 112 } 113 114 /* General failure. */ 115 116 else 117 __raise_os_error(__new_int(errno), __builtins___none_None); 118 119 /* Should never be reached: included to satisfy the compiler. */ 120 121 return __builtins___none_None; 122 } 123 124 __attr __fn_native_iconv_iconv_close(__attr __self, __attr cd) 125 { 126 /* cd interpreted as iconv_t */ 127 iconv_t c = (iconv_t) cd.datavalue; 128 129 errno = 0; 130 131 if (iconv_close(c) == -1) 132 __raise_os_error(__new_int(errno), __builtins___none_None); 133 134 return __builtins___none_None; 135 } 136 137 __attr __fn_native_iconv_iconv_open(__attr __self, __attr tocode, __attr fromcode) 138 { 139 /* tocode.__data__ interpreted as string */ 140 char *t = __load_via_object(tocode.value, __data__).strvalue; 141 /* fromcode.__data__ interpreted as string */ 142 char *f = __load_via_object(fromcode.value, __data__).strvalue; 143 iconv_t result; 144 __attr attr; 145 146 errno = 0; 147 result = iconv_open(t, f); 148 149 if (result == (iconv_t) -1) 150 __raise_os_error(__new_int(errno), __builtins___none_None); 151 152 /* Return the descriptor as an opaque value. */ 153 154 attr.datavalue = (void *) result; 155 return attr; 156 } 157 158 __attr __fn_native_iconv_iconv_reset(__attr __self, __attr cd) 159 { 160 /* cd interpreted as iconv_t */ 161 iconv_t c = (iconv_t) cd.datavalue; 162 163 iconv(c, NULL, NULL, NULL, NULL); 164 return __builtins___none_None; 165 } 166 167 /* Module initialisation. */ 168 169 void __main_native_iconv() 170 { 171 }