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