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