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