Lichen

Annotated templates/native/iconv.c

825:8e9566713e1a
2018-06-22 Paul Boddie Fixed accessor tests to raise type errors instead of returning zero.
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
}