Lichen

Annotated templates/native/iconv.c

610:2c1ae8f292a9
2017-02-22 Paul Boddie Fixed the testing of attribute usage. method-wrapper-for-context
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
}