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 __Raise(__new_posix_iconv_IncompleteSequenceError(__NULL, value, arg)); 37 #endif /* __HAVE_posix_iconv_IncompleteSequenceError */ 38 } 39 40 static void __raise_invalid_sequence_error(__attr value, __attr arg) 41 { 42 #ifdef __HAVE_posix_iconv_InvalidSequenceError 43 __Raise(__new_posix_iconv_InvalidSequenceError(__NULL, value, arg)); 44 #endif /* __HAVE_posix_iconv_InvalidSequenceError */ 45 } 46 47 /* Character set conversion. */ 48 49 __attr __fn_native_iconv_iconv(__attr __self, __attr cd, __attr state) 50 { 51 /* cd interpreted as iconv_t */ 52 iconv_t c = (iconv_t) cd.datavalue; 53 /* state.__data__ interpreted as list */ 54 __fragment *f = __load_via_object(__VALUE(state), __data__).seqvalue; 55 56 /* Obtain the string, start position, and remaining bytes from the state. */ 57 58 char *inbuf = __load_via_object(__VALUE(f->attrs[0]), __data__).strvalue; 59 int start = __TOINT(f->attrs[1]); 60 int remaining = __TOINT(f->attrs[2]); 61 62 /* Allocate a string for the output buffer using the remaining input size 63 as a guide. */ 64 65 size_t outbufsize = remaining < OUTBUFSIZE_MIN ? OUTBUFSIZE_MIN : remaining; 66 size_t outbytesleft = outbufsize; 67 size_t inbytesleft = remaining; 68 69 char buf[outbytesleft]; 70 char *outbuf = buf, *outbufstart = outbuf, *resultbuf; 71 size_t result, outbytestotal; 72 73 /* Convert from the start point. */ 74 75 inbuf += start; 76 77 errno = 0; 78 result = iconv(c, &inbuf, &inbytesleft, &outbuf, &outbytesleft); 79 80 /* Return any string. */ 81 82 if ((result != -1) || (errno == E2BIG) || (errno == EINVAL)) 83 { 84 outbytestotal = outbufsize - outbytesleft; 85 resultbuf = __ALLOCATE(outbytestotal + 1, sizeof(char)); 86 memcpy(resultbuf, outbufstart, outbytestotal); 87 88 /* Mutate the state to indicate the next input buffer position. */ 89 90 f->attrs[1] = __new_int(start + remaining - inbytesleft); 91 f->attrs[2] = __new_int(inbytesleft); 92 93 /* Incomplete sequence: raise the string in an OSError instead. */ 94 95 if (errno == EINVAL) 96 __raise_incomplete_sequence_error(__new_int(errno), __new_str(resultbuf, outbytestotal)); 97 98 return __new_str(resultbuf, outbytestotal); 99 } 100 101 /* Invalid sequence. */ 102 103 if (errno == EILSEQ) 104 { 105 resultbuf = __ALLOCATE(inbytesleft + 1, sizeof(char)); 106 memcpy(resultbuf, inbuf, inbytesleft); 107 __raise_invalid_sequence_error(__new_int(errno), __new_str(resultbuf, inbytesleft)); 108 } 109 110 /* General failure. */ 111 112 else 113 __raise_os_error(__new_int(errno), __builtins___none_None); 114 115 /* Should never be reached: included to satisfy the compiler. */ 116 117 return __builtins___none_None; 118 } 119 120 __attr __fn_native_iconv_iconv_close(__attr __self, __attr cd) 121 { 122 /* cd interpreted as iconv_t */ 123 iconv_t c = (iconv_t) cd.datavalue; 124 125 errno = 0; 126 127 if (iconv_close(c) == -1) 128 __raise_os_error(__new_int(errno), __builtins___none_None); 129 130 return __builtins___none_None; 131 } 132 133 __attr __fn_native_iconv_iconv_open(__attr __self, __attr tocode, __attr fromcode) 134 { 135 /* tocode.__data__ interpreted as string */ 136 char *t = __load_via_object(__VALUE(tocode), __data__).strvalue; 137 /* fromcode.__data__ interpreted as string */ 138 char *f = __load_via_object(__VALUE(fromcode), __data__).strvalue; 139 iconv_t result; 140 __attr attr; 141 142 errno = 0; 143 result = iconv_open(t, f); 144 145 if (result == (iconv_t) -1) 146 __raise_os_error(__new_int(errno), __builtins___none_None); 147 148 /* Return the descriptor as an opaque value. */ 149 150 attr.datavalue = (void *) result; 151 return attr; 152 } 153 154 __attr __fn_native_iconv_iconv_reset(__attr __self, __attr cd) 155 { 156 /* cd interpreted as iconv_t */ 157 iconv_t c = (iconv_t) cd.datavalue; 158 159 iconv(c, NULL, NULL, NULL, NULL); 160 return __builtins___none_None; 161 } 162 163 /* Module initialisation. */ 164 165 void __main_native_iconv() 166 { 167 }