| /*- |
| * Copyright (c) 1999,2000 |
| * Konstantin Chuguev. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by Konstantin Chuguev |
| * and its contributors. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * iconv (Charset Conversion Library) v1.0 |
| */ |
| |
| #include <errno.h> /* E2BIG, EINVAL, errno */ |
| #include <stdio.h> /* FILE, ferror, fwrite */ |
| #include <stdlib.h> /* free, malloc */ |
| #include <string.h> /* memcpy, memmove */ |
| |
| #include "iconv_stream.h" |
| |
| #define buf_size 4096 |
| |
| iconv_stream *iconv_stream_open(apr_iconv_t cd, void *handle, |
| iconv_stream_func method) |
| { |
| iconv_stream *res = malloc(sizeof(iconv_stream)); |
| if (!res) |
| return NULL; |
| res->cd = cd; |
| res->chars = res->in_bytes = res->out_bytes = 0; |
| res->buffer = res->buf_ptr = NULL; |
| res->handle = handle; |
| res->method = method; |
| return res; |
| } |
| |
| void iconv_stream_close(iconv_stream *stream) |
| { |
| if (!stream) |
| return; |
| if (stream->buffer) |
| free(stream->buffer); |
| free(stream); |
| } |
| |
| apr_ssize_t iconv_write(void *handle, const void *buf, apr_size_t insize) |
| { |
| #define stream ((iconv_stream *)handle) |
| char buffer[4096]; |
| apr_size_t outsize = sizeof(buffer), size; |
| char *outbuf = buffer; |
| const char *inbuf = buf; |
| apr_size_t chars; |
| apr_status_t status; |
| |
| if (!buf) |
| insize = 0; |
| status = apr_iconv(stream->cd, (const char **)&buf, &insize, &outbuf, &outsize, &chars); |
| if ((int)chars < 0) |
| return -1; |
| stream->chars += chars; |
| size = outbuf - buffer; |
| if (size) { |
| apr_ssize_t r; |
| outbuf = buffer; |
| while ((r = stream->method(stream->handle, outbuf, size)) < size) { |
| if (r < 0) |
| return -1; |
| outbuf += r; |
| size -= r; |
| stream->out_bytes += r; |
| } |
| } |
| size = (const char *)buf - inbuf; |
| if (size) |
| stream->in_bytes += size; |
| return size; |
| #undef stream |
| } |
| |
| apr_ssize_t iconv_bwrite(void *handle, const void *buf, apr_size_t insize) |
| { |
| #define stream ((iconv_stream *)handle) |
| apr_ssize_t res = 0; |
| apr_size_t left, size = insize; |
| if (!buf) |
| return iconv_write(handle, NULL, 0); |
| if (stream->buffer && stream->buf_ptr > stream->buffer) { |
| do { |
| left = stream->buffer + buf_size - stream->buf_ptr; |
| if (!left) { |
| errno = E2BIG; |
| return -1; |
| } |
| if (left > size) |
| left = size; |
| memcpy(stream->buf_ptr, buf, left); |
| buf = ((const char *)buf) + left; |
| size -= left; |
| stream->buf_ptr += left; |
| res = iconv_write(handle, stream->buffer, |
| stream->buf_ptr - stream->buffer); |
| if (res < 0) { |
| if (errno != EINVAL) |
| return -1; |
| res = 0; |
| } |
| left = stream->buf_ptr - (stream->buffer + res); |
| if (!res) |
| break; |
| if (left > 0) |
| memmove(stream->buffer, stream->buffer + res, left); |
| stream->buf_ptr -= res; |
| } while (size && left); |
| if (!size) |
| return insize; |
| } |
| do { |
| res = iconv_write(handle, buf, size); |
| if (res <= 0) { |
| if (errno != EINVAL) |
| return -1; |
| res = 0; |
| } |
| buf = ((const char *)buf) + res; |
| size -= res; |
| } while (size && res); |
| if (!size) |
| return insize; |
| if (size > buf_size) |
| return -1; |
| if (!stream->buffer) { |
| if (!(stream->buffer = malloc(buf_size))) |
| return -1; |
| } |
| memcpy(stream->buffer, buf, size); |
| stream->buf_ptr = stream->buffer + size; |
| return insize; |
| #undef stream |
| } |
| |
| static apr_ssize_t fwrite_wrapper(void *handle, void *buf, apr_size_t size) |
| { |
| apr_size_t res = fwrite(buf, 1, size, (FILE *)handle); |
| return (res && !ferror((FILE *)handle)) ? res : -1; |
| } |
| |
| iconv_stream *iconv_ostream_fopen(apr_iconv_t cd, FILE *handle) |
| { |
| return iconv_stream_open(cd, handle, fwrite_wrapper); |
| } |