qse/qse/lib/cmn/tio.c

753 lines
16 KiB
C
Raw Normal View History

2008-10-14 05:32:58 +00:00
/*
2012-08-16 03:47:55 +00:00
* $Id$
*
2013-12-31 10:24:12 +00:00
Copyright 2006-2014 Chung, Hyung-Hwan.
2009-09-16 04:01:02 +00:00
This file is part of QSE.
2009-09-16 04:01:02 +00:00
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
2009-09-16 04:01:02 +00:00
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
2009-09-16 04:01:02 +00:00
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
2008-10-14 05:32:58 +00:00
*/
2008-12-21 21:35:07 +00:00
#include <qse/cmn/tio.h>
2012-01-03 14:41:15 +00:00
#include <qse/cmn/mbwc.h>
2008-10-14 05:32:58 +00:00
#include "mem.h"
2012-04-30 15:15:41 +00:00
#define STATUS_OUTPUT_DYNBUF (1 << 0)
#define STATUS_INPUT_DYNBUF (1 << 1)
#define STATUS_INPUT_ILLSEQ (1 << 2)
#define STATUS_INPUT_EOF (1 << 3)
static int detach_in (qse_tio_t* tio, int fini);
static int detach_out (qse_tio_t* tio, int fini);
2011-09-11 20:48:21 +00:00
qse_tio_t* qse_tio_open (qse_mmgr_t* mmgr, qse_size_t xtnsize, int flags)
2008-10-14 05:32:58 +00:00
{
2008-12-21 21:35:07 +00:00
qse_tio_t* tio;
2008-10-14 05:32:58 +00:00
2011-09-01 09:43:46 +00:00
tio = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_tio_t) + xtnsize);
2008-12-21 21:35:07 +00:00
if (tio == QSE_NULL) return QSE_NULL;
2008-10-14 05:32:58 +00:00
2011-09-11 20:48:21 +00:00
if (qse_tio_init (tio, mmgr, flags) <= -1)
2008-10-14 05:32:58 +00:00
{
2008-12-21 21:35:07 +00:00
QSE_MMGR_FREE (mmgr, tio);
return QSE_NULL;
2008-10-14 05:32:58 +00:00
}
QSE_MEMSET (tio + 1, 0, xtnsize);
2008-10-14 05:32:58 +00:00
return tio;
}
2008-12-21 21:35:07 +00:00
int qse_tio_close (qse_tio_t* tio)
2008-10-14 05:32:58 +00:00
{
2008-12-21 21:35:07 +00:00
int n = qse_tio_fini (tio);
QSE_MMGR_FREE (tio->mmgr, tio);
2008-10-14 05:32:58 +00:00
return n;
}
2011-09-11 20:48:21 +00:00
int qse_tio_init (qse_tio_t* tio, qse_mmgr_t* mmgr, int flags)
2008-10-14 05:32:58 +00:00
{
2008-12-21 21:35:07 +00:00
QSE_MEMSET (tio, 0, QSE_SIZEOF(*tio));
2008-10-14 05:32:58 +00:00
tio->mmgr = mmgr;
2012-01-03 14:41:15 +00:00
tio->cmgr = qse_getdflcmgr();
2012-04-30 15:15:41 +00:00
tio->flags = flags;
2008-10-14 05:32:58 +00:00
/*
2008-12-21 21:35:07 +00:00
tio->input_func = QSE_NULL;
tio->input_arg = QSE_NULL;
tio->output_func = QSE_NULL;
tio->output_arg = QSE_NULL;
2008-10-14 05:32:58 +00:00
2012-04-30 15:15:41 +00:00
tio->status = 0;
tio->inbuf_cur = 0;
2011-09-01 09:43:46 +00:00
tio->inbuf_len = 0;
tio->outbuf_len = 0;
2008-10-14 05:32:58 +00:00
*/
2008-12-21 21:35:07 +00:00
tio->errnum = QSE_TIO_ENOERR;
2011-09-01 09:43:46 +00:00
return 0;
2008-10-14 05:32:58 +00:00
}
2008-12-21 21:35:07 +00:00
int qse_tio_fini (qse_tio_t* tio)
2008-10-14 05:32:58 +00:00
{
int ret = 0;
2008-12-21 21:35:07 +00:00
qse_tio_flush (tio); /* don't care about the result */
if (detach_in (tio, 1) <= -1) ret = -1;
if (detach_out (tio, 1) <= -1) ret = -1;
return ret;
2008-10-14 05:32:58 +00:00
}
qse_mmgr_t* qse_tio_getmmgr (qse_tio_t* tio)
{
return tio->mmgr;
}
void* qse_tio_getxtn (qse_tio_t* tio)
{
return QSE_XTN (tio);
}
qse_tio_errnum_t qse_tio_geterrnum (const qse_tio_t* tio)
2008-10-14 05:32:58 +00:00
{
return tio->errnum;
}
void qse_tio_seterrnum (qse_tio_t* tio, qse_tio_errnum_t errnum)
{
tio->errnum = errnum;
}
qse_cmgr_t* qse_tio_getcmgr (qse_tio_t* tio)
{
return tio->cmgr;
}
void qse_tio_setcmgr (qse_tio_t* tio, qse_cmgr_t* cmgr)
{
tio->cmgr = cmgr;
}
int qse_tio_attachin (
qse_tio_t* tio, qse_tio_io_impl_t input,
qse_mchar_t* bufptr, qse_size_t bufcapa)
2008-10-14 05:32:58 +00:00
{
qse_mchar_t* xbufptr;
if (input == QSE_NULL || bufcapa < QSE_TIO_MININBUFCAPA)
{
tio->errnum = QSE_TIO_EINVAL;
return -1;
}
if (qse_tio_detachin(tio) <= -1) return -1;
QSE_ASSERT (tio->in.fun == QSE_NULL);
2008-10-14 05:32:58 +00:00
xbufptr = bufptr;
if (xbufptr == QSE_NULL)
{
xbufptr = QSE_MMGR_ALLOC (
tio->mmgr, QSE_SIZEOF(qse_mchar_t) * bufcapa);
if (xbufptr == QSE_NULL)
{
tio->errnum = QSE_TIO_ENOMEM;
return -1;
}
}
2008-10-14 05:32:58 +00:00
2012-02-12 13:20:39 +00:00
tio->errnum = QSE_TIO_ENOERR;
if (input (tio, QSE_TIO_OPEN, QSE_NULL, 0) <= -1)
2008-10-14 05:32:58 +00:00
{
2012-02-12 13:20:39 +00:00
if (tio->errnum == QSE_TIO_ENOERR) tio->errnum = QSE_TIO_EOTHER;
if (xbufptr != bufptr) QSE_MMGR_FREE (tio->mmgr, xbufptr);
2008-10-14 05:32:58 +00:00
return -1;
}
/* if i defined tio->io[2] instead of tio->in and tio-out,
* i would be able to shorten code amount. but fields to initialize
* are not symmetric between input and output.
* so it's just a bit clumsy that i repeat almost the same code
* in qse_tio_attachout().
*/
tio->in.fun = input;
tio->in.buf.ptr = xbufptr;
tio->in.buf.capa = bufcapa;
2008-10-14 05:32:58 +00:00
2012-04-30 15:15:41 +00:00
tio->status &= ~(STATUS_INPUT_ILLSEQ | STATUS_INPUT_EOF);
tio->inbuf_cur = 0;
2011-09-01 09:43:46 +00:00
tio->inbuf_len = 0;
2008-10-14 05:32:58 +00:00
2012-04-30 15:15:41 +00:00
if (xbufptr != bufptr) tio->status |= STATUS_INPUT_DYNBUF;
2008-10-14 05:32:58 +00:00
return 0;
}
static int detach_in (qse_tio_t* tio, int fini)
2008-10-14 05:32:58 +00:00
{
int ret = 0;
if (tio->in.fun)
2008-10-14 05:32:58 +00:00
{
2012-02-12 13:20:39 +00:00
tio->errnum = QSE_TIO_ENOERR;
if (tio->in.fun (tio, QSE_TIO_CLOSE, QSE_NULL, 0) <= -1)
2008-10-14 05:32:58 +00:00
{
2012-02-12 13:20:39 +00:00
if (tio->errnum == QSE_TIO_ENOERR) tio->errnum = QSE_TIO_EOTHER;
/* returning with an error here allows you to retry detaching */
if (!fini) return -1;
/* otherwise, you can't retry since the input handler information
* is reset below */
ret = -1;
2008-10-14 05:32:58 +00:00
}
2012-04-30 15:15:41 +00:00
if (tio->status & STATUS_INPUT_DYNBUF)
{
QSE_MMGR_FREE (tio->mmgr, tio->in.buf.ptr);
2012-04-30 15:15:41 +00:00
tio->status &= ~STATUS_INPUT_DYNBUF;
}
tio->in.fun = QSE_NULL;
tio->in.buf.ptr = QSE_NULL;
tio->in.buf.capa = 0;
2008-10-14 05:32:58 +00:00
}
return ret;
2008-10-14 05:32:58 +00:00
}
int qse_tio_detachin (qse_tio_t* tio)
2008-10-14 05:32:58 +00:00
{
return detach_in (tio, 0);
}
int qse_tio_attachout (
qse_tio_t* tio, qse_tio_io_impl_t output,
qse_mchar_t* bufptr, qse_size_t bufcapa)
{
qse_mchar_t* xbufptr;
if (output == QSE_NULL || bufcapa < QSE_TIO_MINOUTBUFCAPA)
{
tio->errnum = QSE_TIO_EINVAL;
return -1;
}
2008-12-21 21:35:07 +00:00
if (qse_tio_detachout(tio) == -1) return -1;
2008-10-14 05:32:58 +00:00
QSE_ASSERT (tio->out.fun == QSE_NULL);
xbufptr = bufptr;
if (xbufptr == QSE_NULL)
{
xbufptr = QSE_MMGR_ALLOC (
tio->mmgr, QSE_SIZEOF(qse_mchar_t) * bufcapa);
if (xbufptr == QSE_NULL)
{
tio->errnum = QSE_TIO_ENOMEM;
return -1;
}
}
2008-10-14 05:32:58 +00:00
2012-02-12 13:20:39 +00:00
tio->errnum = QSE_TIO_ENOERR;
if (output (tio, QSE_TIO_OPEN, QSE_NULL, 0) <= -1)
2008-10-14 05:32:58 +00:00
{
2012-02-12 13:20:39 +00:00
if (tio->errnum == QSE_TIO_ENOERR) tio->errnum = QSE_TIO_EOTHER;
if (xbufptr != bufptr) QSE_MMGR_FREE (tio->mmgr, xbufptr);
2008-10-14 05:32:58 +00:00
return -1;
}
tio->out.fun = output;
tio->out.buf.ptr = xbufptr;
tio->out.buf.capa = bufcapa;
tio->outbuf_len = 0;
2008-10-14 05:32:58 +00:00
2012-04-30 15:15:41 +00:00
if (xbufptr != bufptr) tio->status |= STATUS_OUTPUT_DYNBUF;
2008-10-14 05:32:58 +00:00
return 0;
}
static int detach_out (qse_tio_t* tio, int fini)
2008-10-14 05:32:58 +00:00
{
int ret = 0;
if (tio->out.fun)
2008-10-14 05:32:58 +00:00
{
2008-12-21 21:35:07 +00:00
qse_tio_flush (tio); /* don't care about the result */
2008-10-14 05:32:58 +00:00
2012-02-12 13:20:39 +00:00
tio->errnum = QSE_TIO_ENOERR;
if (tio->out.fun (tio, QSE_TIO_CLOSE, QSE_NULL, 0) <= -1)
2008-10-14 05:32:58 +00:00
{
2012-02-12 13:20:39 +00:00
if (tio->errnum == QSE_TIO_ENOERR) tio->errnum = QSE_TIO_EOTHER;
/* returning with an error here allows you to retry detaching */
if (!fini) return -1;
/* otherwise, you can't retry since the input handler information
* is reset below */
ret = -1;
}
2012-04-30 15:15:41 +00:00
if (tio->status & STATUS_OUTPUT_DYNBUF)
{
QSE_MMGR_FREE (tio->mmgr, tio->out.buf.ptr);
2012-04-30 15:15:41 +00:00
tio->status &= ~STATUS_OUTPUT_DYNBUF;
2008-10-14 05:32:58 +00:00
}
tio->out.fun = QSE_NULL;
tio->out.buf.ptr = QSE_NULL;
tio->out.buf.capa = 0;
2008-10-14 05:32:58 +00:00
}
return ret;
}
int qse_tio_detachout (qse_tio_t* tio)
{
return detach_out (tio, 0);
2008-10-14 05:32:58 +00:00
}
2008-12-21 21:35:07 +00:00
qse_ssize_t qse_tio_flush (qse_tio_t* tio)
2008-10-14 05:32:58 +00:00
{
2008-12-21 21:35:07 +00:00
qse_size_t left, count;
qse_ssize_t n;
qse_mchar_t* cur;
2008-10-14 05:32:58 +00:00
if (tio->out.fun == QSE_NULL)
2008-10-14 05:32:58 +00:00
{
2008-12-21 21:35:07 +00:00
tio->errnum = QSE_TIO_ENOUTF;
return (qse_ssize_t)-1;
2008-10-14 05:32:58 +00:00
}
left = tio->outbuf_len;
cur = tio->out.buf.ptr;
2008-10-14 05:32:58 +00:00
while (left > 0)
{
2012-02-12 13:20:39 +00:00
tio->errnum = QSE_TIO_ENOERR;
n = tio->out.fun (tio, QSE_TIO_DATA, cur, left);
2008-10-14 05:32:58 +00:00
if (n <= -1)
{
2012-02-12 13:20:39 +00:00
if (tio->errnum == QSE_TIO_ENOERR) tio->errnum = QSE_TIO_EOTHER;
2012-04-29 15:26:44 +00:00
if (cur != tio->out.buf.ptr)
{
QSE_MEMCPY (tio->out.buf.ptr, cur, left);
tio->outbuf_len = left;
}
2008-10-14 05:32:58 +00:00
return -1;
}
if (n == 0)
{
2012-04-29 15:26:44 +00:00
if (cur != tio->out.buf.ptr)
QSE_MEMCPY (tio->out.buf.ptr, cur, left);
break;
}
2008-10-14 05:32:58 +00:00
left -= n;
cur += n;
2008-10-14 05:32:58 +00:00
}
count = tio->outbuf_len - left;
tio->outbuf_len = left;
2008-12-21 21:35:07 +00:00
return (qse_ssize_t)count;
2008-10-14 05:32:58 +00:00
}
void qse_tio_drain (qse_tio_t* tio)
2008-10-14 05:32:58 +00:00
{
2012-04-30 15:15:41 +00:00
tio->status &= ~(STATUS_INPUT_ILLSEQ | STATUS_INPUT_EOF);
tio->inbuf_cur = 0;
tio->inbuf_len = 0;
tio->outbuf_len = 0;
2008-12-21 21:35:07 +00:00
tio->errnum = QSE_TIO_ENOERR;
2008-10-14 05:32:58 +00:00
}
2012-04-30 15:15:41 +00:00
/* ------------------------------------------------------------- */
qse_ssize_t qse_tio_readmbs (qse_tio_t* tio, qse_mchar_t* buf, qse_size_t size)
{
qse_size_t nread;
qse_ssize_t n;
/*QSE_ASSERT (tio->in.fun != QSE_NULL);*/
if (tio->in.fun == QSE_NULL)
{
tio->errnum = QSE_TIO_ENINPF;
return -1;
}
/* note that this function doesn't check if
* tio->status is set with STATUS_INPUT_ILLSEQ
* since this function can simply return the next
* available byte. */
if (size > QSE_TYPE_MAX(qse_ssize_t)) size = QSE_TYPE_MAX(qse_ssize_t);
nread = 0;
while (nread < size)
{
if (tio->inbuf_cur >= tio->inbuf_len)
{
tio->errnum = QSE_TIO_ENOERR;
n = tio->in.fun (
tio, QSE_TIO_DATA,
tio->in.buf.ptr, tio->in.buf.capa);
if (n == 0) break;
if (n <= -1)
{
if (tio->errnum == QSE_TIO_ENOERR) tio->errnum = QSE_TIO_EOTHER;
return -1;
}
tio->inbuf_cur = 0;
tio->inbuf_len = (qse_size_t)n;
}
do
{
buf[nread] = tio->in.buf.ptr[tio->inbuf_cur++];
/* TODO: support a different line terminator */
if (buf[nread++] == QSE_MT('\n')) goto done;
}
while (tio->inbuf_cur < tio->inbuf_len && nread < size);
}
done:
return nread;
}
static QSE_INLINE qse_ssize_t tio_read_widechars (
qse_tio_t* tio, qse_wchar_t* buf, qse_size_t bufsize)
{
qse_size_t mlen, wlen;
qse_ssize_t n;
int x;
if (tio->inbuf_cur >= tio->inbuf_len)
{
tio->inbuf_cur = 0;
tio->inbuf_len = 0;
getc_conv:
if (tio->status & STATUS_INPUT_EOF) n = 0;
else
{
tio->errnum = QSE_TIO_ENOERR;
n = tio->in.fun (
tio, QSE_TIO_DATA,
&tio->in.buf.ptr[tio->inbuf_len],
tio->in.buf.capa - tio->inbuf_len);
}
if (n == 0)
{
tio->status |= STATUS_INPUT_EOF;
if (tio->inbuf_cur < tio->inbuf_len)
{
/* no more input from the underlying input handler.
* but some incomplete bytes in the buffer. */
if (tio->flags & QSE_TIO_IGNOREMBWCERR)
{
/* tread them as illegal sequence */
goto ignore_illseq;
}
else
{
tio->errnum = QSE_TIO_EICSEQ;
return -1;
}
}
return 0;
}
if (n <= -1)
{
if (tio->errnum == QSE_TIO_ENOERR) tio->errnum = QSE_TIO_EOTHER;
return -1;
}
tio->inbuf_len += n;
}
mlen = tio->inbuf_len - tio->inbuf_cur;
wlen = bufsize;
x = qse_mbsntowcsnuptowithcmgr (
&tio->in.buf.ptr[tio->inbuf_cur],
&mlen, buf, &wlen, QSE_WT('\n'), tio->cmgr);
tio->inbuf_cur += mlen;
if (x == -3)
{
/* incomplete sequence */
if (wlen <= 0)
{
/* not even a single character was handled.
* shift bytes in the buffer to the head. */
QSE_ASSERT (mlen <= 0);
tio->inbuf_len = tio->inbuf_len - tio->inbuf_cur;
QSE_MEMCPY (&tio->in.buf.ptr[0],
&tio->in.buf.ptr[tio->inbuf_cur],
tio->inbuf_len * QSE_SIZEOF(tio->in.buf.ptr[0]));
tio->inbuf_cur = 0;
goto getc_conv; /* and read more */
}
/* get going if some characters are handled */
}
else if (x == -2)
{
/* buffer not large enough */
QSE_ASSERT (wlen > 0);
/* the wide-character buffer is not just large enough to
* hold the entire conversion result. lets's go on so long as
* 1 wide-character is produced though it may be inefficient.
*/
}
else if (x <= -1)
{
/* illegal sequence */
if (tio->flags & QSE_TIO_IGNOREMBWCERR)
{
ignore_illseq:
tio->inbuf_cur++; /* skip one byte */
buf[wlen++] = QSE_WT('?');
}
else if (wlen <= 0)
{
tio->errnum = QSE_TIO_EILSEQ;
return -1;
}
else
{
/* some characters are already handled.
* mark that an illegal sequence encountered
* and carry on. */
tio->status |= STATUS_INPUT_ILLSEQ;
}
}
return wlen;
}
qse_ssize_t qse_tio_readwcs (qse_tio_t* tio, qse_wchar_t* buf, qse_size_t size)
{
qse_size_t nread = 0;
qse_ssize_t n;
/*QSE_ASSERT (tio->in.fun != QSE_NULL);*/
if (tio->in.fun == QSE_NULL)
{
tio->errnum = QSE_TIO_ENINPF;
return -1;
}
if (size > QSE_TYPE_MAX(qse_ssize_t)) size = QSE_TYPE_MAX(qse_ssize_t);
while (nread < size)
{
if (tio->status & STATUS_INPUT_ILLSEQ)
{
tio->status &= ~STATUS_INPUT_ILLSEQ;
tio->errnum = QSE_TIO_EILSEQ;
return -1;
}
n = tio_read_widechars (tio, &buf[nread], size - nread);
if (n == 0) break;
if (n <= -1) return -1;
nread += n;
if (buf[nread-1] == QSE_WT('\n')) break;
}
return nread;
}
/* ------------------------------------------------------------- */
qse_ssize_t qse_tio_writembs (
qse_tio_t* tio, const qse_mchar_t* mptr, qse_size_t mlen)
{
if (tio->outbuf_len >= tio->out.buf.capa)
{
/* maybe, previous flush operation has failed a few
* times previously. so the buffer is full.
*/
tio->errnum = QSE_TIO_ENOSPC;
return -1;
}
if (mlen == (qse_size_t)-1)
{
qse_size_t pos = 0;
if (tio->flags & QSE_TIO_NOAUTOFLUSH)
{
2013-10-21 14:53:53 +00:00
while (mptr[pos] != QSE_MT('\0'))
2012-04-30 15:15:41 +00:00
{
tio->out.buf.ptr[tio->outbuf_len++] = mptr[pos++];
if (tio->outbuf_len >= tio->out.buf.capa &&
qse_tio_flush (tio) <= -1) return -1;
if (pos >= QSE_TYPE_MAX(qse_ssize_t)) break;
}
}
else
{
int nl = 0;
2013-10-21 14:53:53 +00:00
while (mptr[pos] != QSE_MT('\0'))
2012-04-30 15:15:41 +00:00
{
tio->out.buf.ptr[tio->outbuf_len++] = mptr[pos];
if (tio->outbuf_len >= tio->out.buf.capa)
{
if (qse_tio_flush (tio) <= -1) return -1;
nl = 0;
}
else if (mptr[pos] == QSE_T('\n')) nl = 1;
/* TODO: different line terminator */
if (++pos >= QSE_TYPE_MAX(qse_ssize_t)) break;
}
if (nl && qse_tio_flush(tio) <= -1) return -1;
}
return pos;
}
else
{
const qse_mchar_t* xptr, * xend;
qse_size_t capa;
int nl = 0;
/* adjust mlen for the type difference between the parameter
* and the return value */
if (mlen > QSE_TYPE_MAX(qse_ssize_t)) mlen = QSE_TYPE_MAX(qse_ssize_t);
xptr = mptr;
/* handle the parts that can't fit into the internal buffer */
while (mlen >= (capa = tio->out.buf.capa - tio->outbuf_len))
{
for (xend = xptr + capa; xptr < xend; xptr++)
tio->out.buf.ptr[tio->outbuf_len++] = *xptr;
if (qse_tio_flush (tio) <= -1) return -1;
mlen -= capa;
}
if (tio->flags & QSE_TIO_NOAUTOFLUSH)
{
/* handle the last part that can fit into the internal buffer */
for (xend = xptr + mlen; xptr < xend; xptr++)
tio->out.buf.ptr[tio->outbuf_len++] = *xptr;
}
else
{
/* handle the last part that can fit into the internal buffer */
for (xend = xptr + mlen; xptr < xend; xptr++)
{
/* TODO: support different line terminating characeter */
if (*xptr == QSE_MT('\n'))
2012-04-30 15:15:41 +00:00
{
nl = 1;
break;
}
tio->out.buf.ptr[tio->outbuf_len++] = *xptr;
2012-04-30 15:15:41 +00:00
}
/* continue copying without checking for nl */
2012-04-30 15:15:41 +00:00
while (xptr < xend) tio->out.buf.ptr[tio->outbuf_len++] = *xptr++;
}
/* if the last part contains a new line, flush the internal
* buffer. note that this flushes characters after nl also.*/
if (nl && qse_tio_flush (tio) <= -1) return -1;
/* returns the number multi-byte characters handled */
2012-04-30 15:15:41 +00:00
return xptr - mptr;
}
}
qse_ssize_t qse_tio_writewcs (
qse_tio_t* tio, const qse_wchar_t* wptr, qse_size_t wlen)
{
qse_size_t capa, wcnt, mcnt, xwlen;
int n, nl = 0;
if (tio->outbuf_len >= tio->out.buf.capa)
{
/* maybe, previous flush operation has failed a few
* times previously. so the buffer is full. */
2012-04-30 15:15:41 +00:00
tio->errnum = QSE_TIO_ENOSPC;
return -1;
}
if (wlen == (qse_size_t)-1) wlen = qse_wcslen(wptr);
if (wlen > QSE_TYPE_MAX(qse_ssize_t)) wlen = QSE_TYPE_MAX(qse_ssize_t);
xwlen = wlen;
while (xwlen > 0)
{
capa = tio->out.buf.capa - tio->outbuf_len;
wcnt = xwlen; mcnt = capa;
n = qse_wcsntombsnwithcmgr (
wptr, &wcnt, &tio->out.buf.ptr[tio->outbuf_len], &mcnt, tio->cmgr);
tio->outbuf_len += mcnt;
if (n == -2)
{
/* the buffer is not large enough to
* convert more. so flush now and continue.
* note that the buffer may not be full though
2013-10-21 14:53:53 +00:00
* it is not large enough in this case */
2012-04-30 15:15:41 +00:00
if (qse_tio_flush (tio) <= -1) return -1;
nl = 0;
}
else
{
if (tio->outbuf_len >= tio->out.buf.capa)
{
/* flush the full buffer regardless of conversion
* result. */
if (qse_tio_flush (tio) <= -1) return -1;
nl = 0;
}
if (n <= -1)
{
/* an invalid wide-character is encountered. */
if (tio->flags & QSE_TIO_IGNOREMBWCERR)
{
/* insert a question mark for an illegal
* character. */
QSE_ASSERT (tio->outbuf_len < tio->out.buf.capa);
tio->out.buf.ptr[tio->outbuf_len++] = QSE_MT('?');
wcnt++; /* skip this illegal character */
/* don't need to increment mcnt since
* it's not used below */
}
else
{
tio->errnum = QSE_TIO_EILCHR;
return -1;
}
}
else
{
if (!(tio->flags & QSE_TIO_NOAUTOFLUSH) && !nl)
{
/* checking for a newline this way looks damn ugly.
* TODO: how can i do this more elegantly? */
qse_size_t i = wcnt;
while (i > 0)
{
/* scan backward assuming a line terminator
* is typically at the back */
if (wptr[--i] == QSE_WT('\n'))
{
/* TOOD: differetn line terminator */
nl = 1;
break;
}
}
}
}
}
wptr += wcnt; xwlen -= wcnt;
}
if (nl && qse_tio_flush (tio) <= -1) return -1;
return wlen;
}