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