699 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			699 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
    Copyright (c) 2006-2020 Chung, Hyung-Hwan. 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.
 | 
						|
 | 
						|
    THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 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.
 | 
						|
 */
 | 
						|
 | 
						|
#include <hawk-tio.h>
 | 
						|
#include "hawk-prv.h"
 | 
						|
 | 
						|
#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 (hawk_tio_t* tio, int fini);
 | 
						|
static int detach_out (hawk_tio_t* tio, int fini);
 | 
						|
 | 
						|
hawk_tio_t* hawk_tio_open (hawk_gem_t* gem, hawk_oow_t xtnsize, int flags)
 | 
						|
{
 | 
						|
	hawk_tio_t* tio;
 | 
						|
 | 
						|
	tio = (hawk_tio_t*)hawk_gem_allocmem(gem, HAWK_SIZEOF(hawk_tio_t) + xtnsize);
 | 
						|
	if (tio)
 | 
						|
	{
 | 
						|
		if (hawk_tio_init(tio, gem, flags) <= -1)
 | 
						|
		{
 | 
						|
			hawk_gem_freemem (gem, tio);
 | 
						|
			return HAWK_NULL;
 | 
						|
		}
 | 
						|
		else HAWK_MEMSET (tio + 1, 0, xtnsize);
 | 
						|
	}
 | 
						|
	return tio;
 | 
						|
}
 | 
						|
 | 
						|
int hawk_tio_close (hawk_tio_t* tio)
 | 
						|
{
 | 
						|
	int n = hawk_tio_fini(tio);
 | 
						|
	hawk_gem_freemem (tio->gem, tio);
 | 
						|
	return n;
 | 
						|
}
 | 
						|
 | 
						|
int hawk_tio_init (hawk_tio_t* tio, hawk_gem_t* gem, int flags)
 | 
						|
{
 | 
						|
	HAWK_MEMSET (tio, 0, HAWK_SIZEOF(*tio));
 | 
						|
 | 
						|
	tio->gem = gem;
 | 
						|
	tio->cmgr = gem->cmgr;
 | 
						|
 | 
						|
	tio->flags = flags;
 | 
						|
 | 
						|
	/*
 | 
						|
	tio->input_func = HAWK_NULL;
 | 
						|
	tio->input_arg = HAWK_NULL;
 | 
						|
	tio->output_func = HAWK_NULL;
 | 
						|
	tio->output_arg = HAWK_NULL;
 | 
						|
 | 
						|
	tio->status = 0;
 | 
						|
	tio->inbuf_cur = 0;
 | 
						|
	tio->inbuf_len = 0;
 | 
						|
	tio->outbuf_len = 0;
 | 
						|
	*/
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int hawk_tio_fini (hawk_tio_t* tio)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	hawk_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;
 | 
						|
}
 | 
						|
 | 
						|
hawk_cmgr_t* hawk_tio_getcmgr (hawk_tio_t* tio)
 | 
						|
{
 | 
						|
	return tio->cmgr;
 | 
						|
}
 | 
						|
 | 
						|
void hawk_tio_setcmgr (hawk_tio_t* tio, hawk_cmgr_t* cmgr)
 | 
						|
{
 | 
						|
	tio->cmgr = cmgr;
 | 
						|
}
 | 
						|
 | 
						|
int hawk_tio_attachin (
 | 
						|
	hawk_tio_t* tio, hawk_tio_io_impl_t input,
 | 
						|
	hawk_bch_t* bufptr, hawk_oow_t bufcapa)
 | 
						|
{
 | 
						|
	hawk_bch_t* xbufptr;
 | 
						|
 | 
						|
	if (input == HAWK_NULL || bufcapa < HAWK_TIO_MININBUFCAPA)
 | 
						|
	{
 | 
						|
		hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EINVAL);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (hawk_tio_detachin(tio) <= -1) return -1;
 | 
						|
 | 
						|
	HAWK_ASSERT(tio->in.fun == HAWK_NULL);
 | 
						|
 | 
						|
	xbufptr = bufptr;
 | 
						|
	if (xbufptr == HAWK_NULL)
 | 
						|
	{
 | 
						|
		xbufptr = hawk_gem_allocmem(tio->gem, HAWK_SIZEOF(hawk_bch_t) * bufcapa);
 | 
						|
		if (xbufptr == HAWK_NULL) return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (input(tio, HAWK_TIO_OPEN, HAWK_NULL, 0) <= -1)
 | 
						|
	{
 | 
						|
		if (xbufptr != bufptr) hawk_gem_freemem (tio->gem, xbufptr);
 | 
						|
		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 hawk_tio_attachout().
 | 
						|
	 */
 | 
						|
 | 
						|
	tio->in.fun = input;
 | 
						|
	tio->in.buf.ptr = xbufptr;
 | 
						|
	tio->in.buf.capa = bufcapa;
 | 
						|
 | 
						|
	tio->status &= ~(STATUS_INPUT_ILLSEQ | STATUS_INPUT_EOF);
 | 
						|
	tio->inbuf_cur = 0;
 | 
						|
	tio->inbuf_len = 0;
 | 
						|
 | 
						|
	if (xbufptr != bufptr) tio->status |= STATUS_INPUT_DYNBUF;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int detach_in (hawk_tio_t* tio, int fini)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	if (tio->in.fun)
 | 
						|
	{
 | 
						|
		if (tio->in.fun(tio, HAWK_TIO_CLOSE, HAWK_NULL, 0) <= -1)
 | 
						|
		{
 | 
						|
			/* 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;
 | 
						|
		}
 | 
						|
 | 
						|
		if (tio->status & STATUS_INPUT_DYNBUF)
 | 
						|
		{
 | 
						|
			hawk_gem_freemem(tio->gem, tio->in.buf.ptr);
 | 
						|
			tio->status &= ~STATUS_INPUT_DYNBUF;
 | 
						|
		}
 | 
						|
 | 
						|
		tio->in.fun = HAWK_NULL;
 | 
						|
		tio->in.buf.ptr = HAWK_NULL;
 | 
						|
		tio->in.buf.capa = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int hawk_tio_detachin (hawk_tio_t* tio)
 | 
						|
{
 | 
						|
	return detach_in (tio, 0);
 | 
						|
}
 | 
						|
 | 
						|
int hawk_tio_attachout (
 | 
						|
	hawk_tio_t* tio, hawk_tio_io_impl_t output,
 | 
						|
	hawk_bch_t* bufptr, hawk_oow_t bufcapa)
 | 
						|
{
 | 
						|
	hawk_bch_t* xbufptr;
 | 
						|
 | 
						|
	if (output == HAWK_NULL || bufcapa < HAWK_TIO_MINOUTBUFCAPA)
 | 
						|
	{
 | 
						|
		hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EINVAL);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (hawk_tio_detachout(tio) == -1) return -1;
 | 
						|
 | 
						|
	HAWK_ASSERT(tio->out.fun == HAWK_NULL);
 | 
						|
 | 
						|
	xbufptr = bufptr;
 | 
						|
	if (xbufptr == HAWK_NULL)
 | 
						|
	{
 | 
						|
		xbufptr = hawk_gem_allocmem(tio->gem, HAWK_SIZEOF(hawk_bch_t) * bufcapa);
 | 
						|
		if (xbufptr == HAWK_NULL) return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (output(tio, HAWK_TIO_OPEN, HAWK_NULL, 0) <= -1)
 | 
						|
	{
 | 
						|
		if (xbufptr != bufptr) hawk_gem_freemem (tio->gem, xbufptr);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	tio->out.fun = output;
 | 
						|
	tio->out.buf.ptr = xbufptr;
 | 
						|
	tio->out.buf.capa = bufcapa;
 | 
						|
 | 
						|
	tio->outbuf_len = 0;
 | 
						|
 | 
						|
	if (xbufptr != bufptr) tio->status |= STATUS_OUTPUT_DYNBUF;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int detach_out (hawk_tio_t* tio, int fini)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	if (tio->out.fun)
 | 
						|
	{
 | 
						|
		hawk_tio_flush (tio); /* don't care about the result */
 | 
						|
 | 
						|
		if (tio->out.fun (tio, HAWK_TIO_CLOSE, HAWK_NULL, 0) <= -1)
 | 
						|
		{
 | 
						|
			/* 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;
 | 
						|
		}
 | 
						|
 | 
						|
		if (tio->status & STATUS_OUTPUT_DYNBUF)
 | 
						|
		{
 | 
						|
			hawk_gem_freemem (tio->gem, tio->out.buf.ptr);
 | 
						|
			tio->status &= ~STATUS_OUTPUT_DYNBUF;
 | 
						|
		}
 | 
						|
 | 
						|
		tio->out.fun = HAWK_NULL;
 | 
						|
		tio->out.buf.ptr = HAWK_NULL;
 | 
						|
		tio->out.buf.capa = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int hawk_tio_detachout (hawk_tio_t* tio)
 | 
						|
{
 | 
						|
	return detach_out(tio, 0);
 | 
						|
}
 | 
						|
 | 
						|
hawk_ooi_t hawk_tio_flush (hawk_tio_t* tio)
 | 
						|
{
 | 
						|
	hawk_oow_t left, count;
 | 
						|
	hawk_ooi_t n;
 | 
						|
	hawk_bch_t* cur;
 | 
						|
 | 
						|
	if (tio->out.fun == HAWK_NULL)
 | 
						|
	{
 | 
						|
		/* no output function */
 | 
						|
		hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EINVAL);
 | 
						|
		return (hawk_ooi_t)-1;
 | 
						|
	}
 | 
						|
 | 
						|
	left = tio->outbuf_len;
 | 
						|
	cur = tio->out.buf.ptr;
 | 
						|
	while (left > 0)
 | 
						|
	{
 | 
						|
		n = tio->out.fun(tio, HAWK_TIO_DATA, cur, left);
 | 
						|
		if (n <= -1)
 | 
						|
		{
 | 
						|
			if (cur != tio->out.buf.ptr)
 | 
						|
			{
 | 
						|
				HAWK_MEMCPY(tio->out.buf.ptr, cur, left);
 | 
						|
				tio->outbuf_len = left;
 | 
						|
			}
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		if (n == 0)
 | 
						|
		{
 | 
						|
			if (cur != tio->out.buf.ptr)
 | 
						|
				HAWK_MEMCPY(tio->out.buf.ptr, cur, left);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		left -= n;
 | 
						|
		cur += n;
 | 
						|
	}
 | 
						|
 | 
						|
	count = tio->outbuf_len - left;
 | 
						|
	tio->outbuf_len = left;
 | 
						|
 | 
						|
	return (hawk_ooi_t)count;
 | 
						|
}
 | 
						|
 | 
						|
void hawk_tio_drain (hawk_tio_t* tio)
 | 
						|
{
 | 
						|
	tio->status &= ~(STATUS_INPUT_ILLSEQ | STATUS_INPUT_EOF);
 | 
						|
	tio->inbuf_cur = 0;
 | 
						|
	tio->inbuf_len = 0;
 | 
						|
	tio->outbuf_len = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* ------------------------------------------------------------- */
 | 
						|
 | 
						|
 | 
						|
hawk_ooi_t hawk_tio_readbchars (hawk_tio_t* tio, hawk_bch_t* buf, hawk_oow_t size)
 | 
						|
{
 | 
						|
	hawk_oow_t nread;
 | 
						|
	hawk_ooi_t n;
 | 
						|
 | 
						|
	/*HAWK_ASSERT(tio->in.fun != HAWK_NULL);*/
 | 
						|
	if (tio->in.fun == HAWK_NULL)
 | 
						|
	{
 | 
						|
		/* no input function */
 | 
						|
		hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EINVAL);
 | 
						|
		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 > HAWK_TYPE_MAX(hawk_ooi_t)) size = HAWK_TYPE_MAX(hawk_ooi_t);
 | 
						|
 | 
						|
	nread = 0;
 | 
						|
	while (nread < size)
 | 
						|
	{
 | 
						|
		if (tio->inbuf_cur >= tio->inbuf_len)
 | 
						|
		{
 | 
						|
			n = tio->in.fun(tio, HAWK_TIO_DATA, tio->in.buf.ptr, tio->in.buf.capa);
 | 
						|
			if (n == 0) break;
 | 
						|
			if (n <= -1) return -1;
 | 
						|
 | 
						|
			tio->inbuf_cur = 0;
 | 
						|
			tio->inbuf_len = (hawk_oow_t)n;
 | 
						|
		}
 | 
						|
 | 
						|
		do
 | 
						|
		{
 | 
						|
			buf[nread] = tio->in.buf.ptr[tio->inbuf_cur++];
 | 
						|
			/* TODO: support a different line terminator */
 | 
						|
			if (buf[nread++] == '\n') goto done;
 | 
						|
		}
 | 
						|
		while (tio->inbuf_cur < tio->inbuf_len && nread < size);
 | 
						|
	}
 | 
						|
 | 
						|
done:
 | 
						|
	return nread;
 | 
						|
}
 | 
						|
 | 
						|
static HAWK_INLINE hawk_ooi_t tio_read_uchars (hawk_tio_t* tio, hawk_uch_t* buf, hawk_oow_t bufsize)
 | 
						|
{
 | 
						|
	hawk_oow_t mlen, wlen;
 | 
						|
	hawk_ooi_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
 | 
						|
		{
 | 
						|
			n = tio->in.fun(tio, HAWK_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 & HAWK_TIO_IGNOREECERR)
 | 
						|
				{
 | 
						|
					wlen = 0;
 | 
						|
					goto ignore_illseq;
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					/* treat them as illegal sequence */
 | 
						|
					hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EECERR);
 | 
						|
					return -1;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		if (n <= -1) return -1;
 | 
						|
 | 
						|
		tio->inbuf_len += n;
 | 
						|
	}
 | 
						|
 | 
						|
	mlen = tio->inbuf_len - tio->inbuf_cur;
 | 
						|
	wlen = bufsize;
 | 
						|
 | 
						|
	x = hawk_conv_bchars_to_uchars_upto_stopper_with_cmgr(
 | 
						|
		&tio->in.buf.ptr[tio->inbuf_cur],
 | 
						|
		&mlen, buf, &wlen, '\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. */
 | 
						|
			HAWK_ASSERT(mlen <= 0);
 | 
						|
			tio->inbuf_len = tio->inbuf_len - tio->inbuf_cur;
 | 
						|
			HAWK_MEMCPY(&tio->in.buf.ptr[0],
 | 
						|
			            &tio->in.buf.ptr[tio->inbuf_cur],
 | 
						|
			            tio->inbuf_len * HAWK_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 */
 | 
						|
		HAWK_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 & HAWK_TIO_IGNOREECERR)
 | 
						|
		{
 | 
						|
		ignore_illseq:
 | 
						|
			tio->inbuf_cur++; /* skip one byte */
 | 
						|
			buf[wlen++] = '?';
 | 
						|
		}
 | 
						|
		else if (wlen <= 0)
 | 
						|
		{
 | 
						|
			/* really illegal sequence */
 | 
						|
			hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EECERR);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			/* some characters are already handled.
 | 
						|
			 * mark that an illegal sequence encountered
 | 
						|
			 * and carry on. */
 | 
						|
			tio->status |= STATUS_INPUT_ILLSEQ;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return wlen;
 | 
						|
}
 | 
						|
 | 
						|
hawk_ooi_t hawk_tio_readuchars (hawk_tio_t* tio, hawk_uch_t* buf, hawk_oow_t size)
 | 
						|
{
 | 
						|
	hawk_oow_t nread = 0;
 | 
						|
	hawk_ooi_t n;
 | 
						|
 | 
						|
	/*HAWK_ASSERT(tio->in.fun != HAWK_NULL);*/
 | 
						|
	if (tio->in.fun == HAWK_NULL)
 | 
						|
	{
 | 
						|
		/* no input handler function */
 | 
						|
		hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EINVAL);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (size > HAWK_TYPE_MAX(hawk_ooi_t)) size = HAWK_TYPE_MAX(hawk_ooi_t);
 | 
						|
 | 
						|
	while (nread < size)
 | 
						|
	{
 | 
						|
		if (tio->status & STATUS_INPUT_ILLSEQ)
 | 
						|
		{
 | 
						|
			tio->status &= ~STATUS_INPUT_ILLSEQ;
 | 
						|
			hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EECERR);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		n = tio_read_uchars(tio, &buf[nread], size - nread);
 | 
						|
		if (n == 0) break;
 | 
						|
		if (n <= -1) return -1;
 | 
						|
 | 
						|
		nread += n;
 | 
						|
		if (buf[nread-1] == '\n') break;
 | 
						|
	}
 | 
						|
 | 
						|
	return nread;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ------------------------------------------------------------- */
 | 
						|
hawk_ooi_t hawk_tio_writebchars (hawk_tio_t* tio, const hawk_bch_t* mptr, hawk_oow_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.
 | 
						|
		 */
 | 
						|
		hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EBUFFULL);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (mlen == (hawk_oow_t)-1)
 | 
						|
	{
 | 
						|
		hawk_oow_t pos = 0;
 | 
						|
 | 
						|
		if (tio->flags & HAWK_TIO_NOAUTOFLUSH)
 | 
						|
		{
 | 
						|
			while (mptr[pos] != '\0')
 | 
						|
			{
 | 
						|
				tio->out.buf.ptr[tio->outbuf_len++] = mptr[pos++];
 | 
						|
				if (tio->outbuf_len >= tio->out.buf.capa &&
 | 
						|
				    hawk_tio_flush(tio) <= -1) return -1;
 | 
						|
				if (pos >= HAWK_TYPE_MAX(hawk_ooi_t)) break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			int nl = 0;
 | 
						|
			while (mptr[pos] != '\0')
 | 
						|
			{
 | 
						|
				tio->out.buf.ptr[tio->outbuf_len++] = mptr[pos];
 | 
						|
				if (tio->outbuf_len >= tio->out.buf.capa)
 | 
						|
				{
 | 
						|
					if (hawk_tio_flush(tio) <= -1) return -1;
 | 
						|
					nl = 0;
 | 
						|
				}
 | 
						|
				else if (mptr[pos] == '\n') nl = 1;
 | 
						|
				/* TODO: different line terminator */
 | 
						|
				if (++pos >= HAWK_TYPE_MAX(hawk_ooi_t)) break;
 | 
						|
			}
 | 
						|
			if (nl && hawk_tio_flush(tio) <= -1) return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		return pos;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		const hawk_bch_t* xptr, * xend;
 | 
						|
		hawk_oow_t capa;
 | 
						|
		int nl = 0;
 | 
						|
 | 
						|
		/* adjust mlen for the type difference between the parameter
 | 
						|
		 * and the return value */
 | 
						|
		if (mlen > HAWK_TYPE_MAX(hawk_ooi_t)) mlen = HAWK_TYPE_MAX(hawk_ooi_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 (hawk_tio_flush(tio) <= -1) return -1;
 | 
						|
			mlen -= capa;
 | 
						|
		}
 | 
						|
 | 
						|
		if (tio->flags & HAWK_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 == '\n')
 | 
						|
				{
 | 
						|
					nl = 1;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
				tio->out.buf.ptr[tio->outbuf_len++] = *xptr;
 | 
						|
			}
 | 
						|
 | 
						|
			/* continue copying without checking for nl */
 | 
						|
			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 && hawk_tio_flush(tio) <= -1) return -1;
 | 
						|
 | 
						|
		/* returns the number multi-byte characters handled */
 | 
						|
		return xptr - mptr;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
hawk_ooi_t hawk_tio_writeuchars (hawk_tio_t* tio, const hawk_uch_t* wptr, hawk_oow_t wlen)
 | 
						|
{
 | 
						|
	hawk_oow_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. */
 | 
						|
		hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EBUFFULL);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (wlen == (hawk_oow_t)-1) wlen = hawk_count_ucstr(wptr);
 | 
						|
	if (wlen > HAWK_TYPE_MAX(hawk_ooi_t)) wlen = HAWK_TYPE_MAX(hawk_ooi_t);
 | 
						|
 | 
						|
	xwlen = wlen;
 | 
						|
	while (xwlen > 0)
 | 
						|
	{
 | 
						|
		capa = tio->out.buf.capa - tio->outbuf_len;
 | 
						|
		wcnt = xwlen; mcnt = capa;
 | 
						|
 | 
						|
		n = hawk_conv_uchars_to_bchars_with_cmgr(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
 | 
						|
			 * it is not large enough in this case */
 | 
						|
			if (hawk_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 (hawk_tio_flush (tio) <= -1) return -1;
 | 
						|
				nl = 0;
 | 
						|
			}
 | 
						|
 | 
						|
			if (n <= -1)
 | 
						|
			{
 | 
						|
				/* an invalid wide-character is encountered. */
 | 
						|
				if (tio->flags & HAWK_TIO_IGNOREECERR)
 | 
						|
				{
 | 
						|
					/* insert a question mark for an illegal
 | 
						|
					 * character. */
 | 
						|
					HAWK_ASSERT(tio->outbuf_len < tio->out.buf.capa);
 | 
						|
					tio->out.buf.ptr[tio->outbuf_len++] = '?';
 | 
						|
					wcnt++; /* skip this illegal character */
 | 
						|
					/* don't need to increment mcnt since
 | 
						|
					 * it's not used below */
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					/* illegal character */
 | 
						|
					hawk_gem_seterrnum (tio->gem, HAWK_NULL, HAWK_EECERR);
 | 
						|
					return -1;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				if (!(tio->flags & HAWK_TIO_NOAUTOFLUSH) && !nl)
 | 
						|
				{
 | 
						|
					/* checking for a newline this way looks damn ugly.
 | 
						|
					 * TODO: how can i do this more elegantly? */
 | 
						|
					hawk_oow_t i = wcnt;
 | 
						|
					while (i > 0)
 | 
						|
					{
 | 
						|
						/* scan backward assuming a line terminator
 | 
						|
						 * is typically at the back */
 | 
						|
						if (wptr[--i] == '\n')
 | 
						|
						{
 | 
						|
							/* TOOD: handle different line terminator */
 | 
						|
							nl = 1;
 | 
						|
							break;
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		wptr += wcnt; xwlen -= wcnt;
 | 
						|
	}
 | 
						|
 | 
						|
	if (nl && hawk_tio_flush(tio) <= -1) return -1;
 | 
						|
	return wlen;
 | 
						|
}
 |