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;
 | |
| }
 |