1135 lines
25 KiB
C
1135 lines
25 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2006-2019 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 <qse/si/sio.h>
|
|
#include <qse/cmn/mbwc.h>
|
|
#include "../cmn/mem-prv.h"
|
|
#include "../cmn/fmt-prv.h"
|
|
|
|
#if defined(_WIN32)
|
|
# include <windows.h> /* for the UGLY hack */
|
|
#elif defined(__OS2__)
|
|
/* nothing */
|
|
#elif defined(__DOS__)
|
|
/* nothing */
|
|
#else
|
|
# include "../cmn/syscall.h"
|
|
#endif
|
|
|
|
|
|
#define LOCK_OUTPUT(sio) do { if ((sio)->mtx) qse_mtx_lock ((sio)->mtx, QSE_NULL); } while(0)
|
|
#define UNLOCK_OUTPUT(sio) do { if ((sio)->mtx) qse_mtx_unlock ((sio)->mtx); } while(0)
|
|
|
|
/* TODO: currently, LOCK_INPUT and LOCK_OUTPUT don't have difference.
|
|
* can i just use two difference mutex objects to differentiate? */
|
|
#define LOCK_INPUT(sio) do { if ((sio)->mtx) qse_mtx_lock ((sio)->mtx, QSE_NULL); } while(0)
|
|
#define UNLOCK_INPUT(sio) do { if ((sio)->mtx) qse_mtx_unlock ((sio)->mtx); } while(0)
|
|
|
|
/* internal status codes */
|
|
enum
|
|
{
|
|
STATUS_UTF8_CONSOLE = (1 << 0),
|
|
STATUS_LINE_BREAK = (1 << 1)
|
|
};
|
|
|
|
static qse_ssize_t file_input (qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size);
|
|
static qse_ssize_t file_output (qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size);
|
|
|
|
static qse_sio_errnum_t fio_errnum_to_sio_errnum (qse_fio_t* fio)
|
|
{
|
|
switch (fio->errnum)
|
|
{
|
|
case QSE_FIO_ENOMEM:
|
|
return QSE_SIO_ENOMEM;
|
|
case QSE_FIO_EINVAL:
|
|
return QSE_SIO_EINVAL;
|
|
case QSE_FIO_EACCES:
|
|
return QSE_SIO_EACCES;
|
|
case QSE_FIO_ENOENT:
|
|
return QSE_SIO_ENOENT;
|
|
case QSE_FIO_EEXIST:
|
|
return QSE_SIO_EEXIST;
|
|
case QSE_FIO_EINTR:
|
|
return QSE_SIO_EINTR;
|
|
case QSE_FIO_EPIPE:
|
|
return QSE_SIO_EPIPE;
|
|
case QSE_FIO_EAGAIN:
|
|
return QSE_SIO_EAGAIN;
|
|
case QSE_FIO_ESYSERR:
|
|
return QSE_SIO_ESYSERR;
|
|
case QSE_FIO_ENOIMPL:
|
|
return QSE_SIO_ENOIMPL;
|
|
default:
|
|
return QSE_SIO_EOTHER;
|
|
}
|
|
}
|
|
|
|
static qse_sio_errnum_t tio_errnum_to_sio_errnum (qse_tio_t* tio)
|
|
{
|
|
switch (tio->errnum)
|
|
{
|
|
case QSE_TIO_ENOMEM:
|
|
return QSE_SIO_ENOMEM;
|
|
case QSE_TIO_EINVAL:
|
|
return QSE_SIO_EINVAL;
|
|
case QSE_TIO_EACCES:
|
|
return QSE_SIO_EACCES;
|
|
case QSE_TIO_ENOENT:
|
|
return QSE_SIO_ENOENT;
|
|
case QSE_TIO_EILSEQ:
|
|
return QSE_SIO_EILSEQ;
|
|
case QSE_TIO_EICSEQ:
|
|
return QSE_SIO_EICSEQ;
|
|
case QSE_TIO_EILCHR:
|
|
return QSE_SIO_EILCHR;
|
|
default:
|
|
return QSE_SIO_EOTHER;
|
|
}
|
|
}
|
|
|
|
qse_sio_t* qse_sio_open (qse_mmgr_t* mmgr, qse_size_t xtnsize, const qse_char_t* file, int flags)
|
|
{
|
|
qse_sio_t* sio;
|
|
|
|
sio = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_sio_t) + xtnsize);
|
|
if (sio)
|
|
{
|
|
if (qse_sio_init (sio, mmgr, file, flags) <= -1)
|
|
{
|
|
QSE_MMGR_FREE (mmgr, sio);
|
|
return QSE_NULL;
|
|
}
|
|
else QSE_MEMSET (QSE_XTN(sio), 0, xtnsize);
|
|
}
|
|
return sio;
|
|
}
|
|
|
|
qse_sio_t* qse_sio_openstd (qse_mmgr_t* mmgr, qse_size_t xtnsize, qse_sio_std_t std, int flags)
|
|
{
|
|
qse_sio_t* sio;
|
|
qse_fio_hnd_t hnd;
|
|
|
|
/* Is this necessary?
|
|
if (flags & QSE_SIO_KEEPATH)
|
|
{
|
|
sio->errnum = QSE_SIO_EINVAL;
|
|
return QSE_NULL;
|
|
}
|
|
*/
|
|
|
|
if (qse_get_stdfiohandle (std, &hnd) <= -1) return QSE_NULL;
|
|
|
|
sio = qse_sio_open (mmgr, xtnsize, (const qse_char_t*)&hnd, flags | QSE_SIO_HANDLE | QSE_SIO_NOCLOSE);
|
|
|
|
#if defined(_WIN32)
|
|
if (sio)
|
|
{
|
|
DWORD mode;
|
|
if (GetConsoleMode (sio->file.handle, &mode) == TRUE &&
|
|
GetConsoleOutputCP() == CP_UTF8)
|
|
{
|
|
sio->status |= STATUS_UTF8_CONSOLE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return sio;
|
|
}
|
|
|
|
void qse_sio_close (qse_sio_t* sio)
|
|
{
|
|
qse_sio_fini (sio);
|
|
QSE_MMGR_FREE (sio->mmgr, sio);
|
|
}
|
|
|
|
int qse_sio_init (qse_sio_t* sio, qse_mmgr_t* mmgr, const qse_char_t* path, int flags)
|
|
{
|
|
int mode;
|
|
int topt = 0;
|
|
|
|
QSE_MEMSET (sio, 0, QSE_SIZEOF(*sio));
|
|
sio->mmgr = mmgr;
|
|
|
|
mode = QSE_FIO_RUSR | QSE_FIO_WUSR | QSE_FIO_RGRP | QSE_FIO_ROTH;
|
|
|
|
if (flags & QSE_SIO_REENTRANT)
|
|
{
|
|
sio->mtx = qse_mtx_open (mmgr, 0);
|
|
if (!sio->mtx) goto oops00;
|
|
}
|
|
/* sio flag enumerators redefines most fio flag enumerators and
|
|
* compose a superset of fio flag enumerators. when a user calls
|
|
* this function, a user can specify a sio flag enumerator not
|
|
* present in the fio flag enumerator. mask off such an enumerator. */
|
|
if (qse_fio_init (&sio->file, mmgr, path, (flags & ~QSE_FIO_RESERVED), mode) <= -1)
|
|
{
|
|
sio->errnum = fio_errnum_to_sio_errnum (&sio->file);
|
|
goto oops01;
|
|
}
|
|
|
|
if (flags & QSE_SIO_IGNOREMBWCERR) topt |= QSE_TIO_IGNOREMBWCERR;
|
|
if (flags & QSE_SIO_NOAUTOFLUSH) topt |= QSE_TIO_NOAUTOFLUSH;
|
|
|
|
if ((flags & QSE_SIO_KEEPPATH) && !(flags & QSE_SIO_HANDLE))
|
|
{
|
|
sio->path = qse_strdup (path, sio->mmgr);
|
|
if (sio->path == QSE_NULL)
|
|
{
|
|
sio->errnum = QSE_SIO_ENOMEM;
|
|
goto oops02;
|
|
}
|
|
}
|
|
|
|
if (qse_tio_init(&sio->tio.io, mmgr, topt) <= -1)
|
|
{
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
goto oops03;
|
|
}
|
|
/* store the back-reference to sio in the extension area.*/
|
|
QSE_ASSERT (QSE_XTN(&sio->tio.io) == &sio->tio.xtn);
|
|
*(qse_sio_t**)QSE_XTN(&sio->tio.io) = sio;
|
|
|
|
if (qse_tio_attachin (&sio->tio.io, file_input, sio->inbuf, QSE_COUNTOF(sio->inbuf)) <= -1 ||
|
|
qse_tio_attachout (&sio->tio.io, file_output, sio->outbuf, QSE_COUNTOF(sio->outbuf)) <= -1)
|
|
{
|
|
if (sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
goto oops04;
|
|
}
|
|
|
|
#if defined(__OS2__)
|
|
if (flags & QSE_SIO_LINEBREAK) sio->status |= STATUS_LINE_BREAK;
|
|
#endif
|
|
return 0;
|
|
|
|
oops04:
|
|
qse_tio_fini (&sio->tio.io);
|
|
oops03:
|
|
if (sio->path) QSE_MMGR_FREE (sio->mmgr, sio->path);
|
|
oops02:
|
|
qse_fio_fini (&sio->file);
|
|
oops01:
|
|
if (sio->mtx) qse_mtx_close (sio->mtx);
|
|
oops00:
|
|
return -1;
|
|
}
|
|
|
|
int qse_sio_initstd (
|
|
qse_sio_t* sio, qse_mmgr_t* mmgr, qse_sio_std_t std, int flags)
|
|
{
|
|
int n;
|
|
qse_fio_hnd_t hnd;
|
|
|
|
if (qse_get_stdfiohandle (std, &hnd) <= -1) return -1;
|
|
|
|
n = qse_sio_init (sio, mmgr,
|
|
(const qse_char_t*)&hnd, flags | QSE_SIO_HANDLE | QSE_SIO_NOCLOSE);
|
|
|
|
#if defined(_WIN32)
|
|
if (n >= 0)
|
|
{
|
|
DWORD mode;
|
|
if (GetConsoleMode (sio->file.handle, &mode) == TRUE &&
|
|
GetConsoleOutputCP() == CP_UTF8)
|
|
{
|
|
sio->status |= STATUS_UTF8_CONSOLE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return n;
|
|
}
|
|
|
|
void qse_sio_fini (qse_sio_t* sio)
|
|
{
|
|
/*if (qse_sio_flush (sio) <= -1) return -1;*/
|
|
qse_sio_flush (sio);
|
|
qse_tio_fini (&sio->tio.io);
|
|
qse_fio_fini (&sio->file);
|
|
if (sio->path) QSE_MMGR_FREE (sio->mmgr, sio->path);
|
|
if (sio->mtx) qse_mtx_close (sio->mtx);
|
|
}
|
|
|
|
qse_mmgr_t* qse_sio_getmmgr (qse_sio_t* sio)
|
|
{
|
|
return sio->mmgr;
|
|
}
|
|
|
|
void* qse_sio_getxtn (qse_sio_t* sio)
|
|
{
|
|
return QSE_XTN (sio);
|
|
}
|
|
|
|
qse_sio_errnum_t qse_sio_geterrnum (const qse_sio_t* sio)
|
|
{
|
|
return sio->errnum;
|
|
}
|
|
|
|
qse_cmgr_t* qse_sio_getcmgr (qse_sio_t* sio)
|
|
{
|
|
return qse_tio_getcmgr (&sio->tio.io);
|
|
}
|
|
|
|
void qse_sio_setcmgr (qse_sio_t* sio, qse_cmgr_t* cmgr)
|
|
{
|
|
qse_tio_setcmgr (&sio->tio.io, cmgr);
|
|
}
|
|
|
|
qse_sio_hnd_t qse_sio_gethnd (const qse_sio_t* sio)
|
|
{
|
|
/*return qse_fio_gethnd (&sio->file);*/
|
|
return QSE_FIO_HANDLE(&sio->file);
|
|
}
|
|
|
|
const qse_char_t* qse_sio_getpath (qse_sio_t* sio)
|
|
{
|
|
/* this path is valid if QSE_SIO_HANDLE is off and QSE_SIO_KEEPPATH is on */
|
|
return sio->path;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_flush (qse_sio_t* sio)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
LOCK_OUTPUT (sio);
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_flush(&sio->tio.io);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
|
|
void qse_sio_drain (qse_sio_t* sio)
|
|
{
|
|
LOCK_OUTPUT (sio);
|
|
qse_tio_drain (&sio->tio.io);
|
|
UNLOCK_OUTPUT (sio);
|
|
}
|
|
|
|
qse_ssize_t qse_sio_getmb (qse_sio_t* sio, qse_mchar_t* c)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
LOCK_INPUT (sio);
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_readmbs(&sio->tio.io, c, 1);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
UNLOCK_INPUT (sio);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_getwc (qse_sio_t* sio, qse_wchar_t* c)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
LOCK_INPUT (sio);
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_readwcs(&sio->tio.io, c, 1);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
UNLOCK_INPUT (sio);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_getmbs (qse_sio_t* sio, qse_mchar_t* buf, qse_size_t size)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
if (size <= 0) return 0;
|
|
|
|
#if defined(_WIN32)
|
|
/* Using ReadConsoleA() didn't help at all.
|
|
* so I don't implement any hack here */
|
|
#endif
|
|
|
|
LOCK_INPUT (sio);
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_readmbs(&sio->tio.io, buf, size - 1);
|
|
if (n <= -1)
|
|
{
|
|
if (sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
UNLOCK_INPUT (sio);
|
|
return -1;
|
|
}
|
|
UNLOCK_INPUT (sio);
|
|
|
|
buf[n] = QSE_MT('\0');
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_getmbsn (qse_sio_t* sio, qse_mchar_t* buf, qse_size_t size)
|
|
{
|
|
qse_ssize_t n;
|
|
#if defined(_WIN32)
|
|
/* Using ReadConsoleA() didn't help at all.
|
|
* so I don't implement any hack here */
|
|
#endif
|
|
|
|
LOCK_INPUT (sio);
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_readmbs(&sio->tio.io, buf, size);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
UNLOCK_INPUT (sio);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_getwcs (qse_sio_t* sio, qse_wchar_t* buf, qse_size_t size)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
if (size <= 0) return 0;
|
|
|
|
#if defined(_WIN32)
|
|
/* Using ReadConsoleA() didn't help at all.
|
|
* so I don't implement any hack here */
|
|
#endif
|
|
|
|
LOCK_INPUT (sio);
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_readwcs(&sio->tio.io, buf, size - 1);
|
|
if (n <= -1)
|
|
{
|
|
if (sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
UNLOCK_INPUT (sio);
|
|
return -1;
|
|
}
|
|
UNLOCK_INPUT (sio);
|
|
|
|
buf[n] = QSE_WT('\0');
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_getwcsn(qse_sio_t* sio, qse_wchar_t* buf, qse_size_t size)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
#if defined(_WIN32)
|
|
/* Using ReadConsoleW() didn't help at all.
|
|
* so I don't implement any hack here */
|
|
#endif
|
|
|
|
LOCK_INPUT (sio);
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_readwcs (&sio->tio.io, buf, size);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
UNLOCK_INPUT (sio);
|
|
|
|
return n;
|
|
}
|
|
|
|
static qse_ssize_t putmb_no_mutex (qse_sio_t* sio, qse_mchar_t c)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
|
|
#if defined(__OS2__)
|
|
if (c == QSE_MT('\n') && (sio->status & STATUS_LINE_BREAK))
|
|
n = qse_tio_writembs (&sio->tio.io, QSE_MT("\r\n"), 2);
|
|
else
|
|
n = qse_tio_writembs (&sio->tio.io, &c, 1);
|
|
#else
|
|
n = qse_tio_writembs (&sio->tio.io, &c, 1);
|
|
#endif
|
|
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putmb (qse_sio_t* sio, qse_mchar_t c)
|
|
{
|
|
qse_ssize_t n;
|
|
LOCK_OUTPUT (sio);
|
|
n = putmb_no_mutex(sio, c);
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
|
|
static qse_ssize_t putwc_no_mutex (qse_sio_t* sio, qse_wchar_t c)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
#if defined(__OS2__)
|
|
if (c == QSE_WT('\n') && (sio->status & STATUS_LINE_BREAK))
|
|
n = qse_tio_writewcs (&sio->tio.io, QSE_WT("\r\n"), 2);
|
|
else
|
|
n = qse_tio_writewcs (&sio->tio.io, &c, 1);
|
|
#else
|
|
n = qse_tio_writewcs (&sio->tio.io, &c, 1);
|
|
#endif
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putwc (qse_sio_t* sio, qse_wchar_t c)
|
|
{
|
|
qse_ssize_t n;
|
|
LOCK_OUTPUT (sio);
|
|
n = putwc_no_mutex(sio, c);
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putmbs (qse_sio_t* sio, const qse_mchar_t* str)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
#if defined(_WIN32)
|
|
/* Using WriteConsoleA() didn't help at all.
|
|
* so I don't implement any hacks here */
|
|
#elif defined(__OS2__)
|
|
if (sio->status & STATUS_LINE_BREAK)
|
|
{
|
|
LOCK_OUTPUT (sio);
|
|
for (n = 0; n < QSE_TYPE_MAX(qse_ssize_t) && str[n] != QSE_MT('\0'); n++)
|
|
{
|
|
if ((n = putmb_no_mutex(sio, str[n])) <= -1)
|
|
{
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
}
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
#endif
|
|
|
|
LOCK_OUTPUT (sio);
|
|
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_writembs (&sio->tio.io, str, (qse_size_t)-1);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putmbsn (qse_sio_t* sio, const qse_mchar_t* str, qse_size_t size)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
#if defined(_WIN32)
|
|
/* Using WriteConsoleA() didn't help at all.
|
|
* so I don't implement any hacks here */
|
|
#elif defined(__OS2__)
|
|
if (sio->status & STATUS_LINE_BREAK)
|
|
{
|
|
if (size > QSE_TYPE_MAX(qse_ssize_t)) size = QSE_TYPE_MAX(qse_ssize_t);
|
|
LOCK_OUTPUT (sio);
|
|
for (n = 0; n < size; n++)
|
|
{
|
|
if (putmb_no_mutex(sio, str[n]) <= -1)
|
|
{
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1;
|
|
}
|
|
}
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
#endif
|
|
|
|
LOCK_OUTPUT (sio);
|
|
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_writembs (&sio->tio.io, str, size);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
|
|
UNLOCK_OUTPUT (sio);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putwcs (qse_sio_t* sio, const qse_wchar_t* str)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
#if defined(_WIN32)
|
|
/* DAMN UGLY: See comment in qse_sio_putwcsn() */
|
|
if (sio->status & STATUS_UTF8_CONSOLE)
|
|
{
|
|
DWORD count, left;
|
|
const qse_wchar_t* cur;
|
|
|
|
LOCK_OUTPUT (sio);
|
|
if (qse_sio_flush (sio) <= -1)
|
|
{
|
|
return -1; /* can't do buffering */
|
|
UNLOCK_OUTPUT (sio);
|
|
}
|
|
|
|
for (cur = str, left = qse_wcslen(str); left > 0; cur += count, left -= count)
|
|
{
|
|
if (WriteConsoleW(sio->file.handle, cur, left, &count, QSE_NULL) == FALSE)
|
|
{
|
|
sio->errnum = QSE_SIO_ESYSERR;
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1;
|
|
}
|
|
if (count == 0) break;
|
|
|
|
if (count > left)
|
|
{
|
|
sio->errnum = QSE_SIO_ESYSERR;
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1;
|
|
}
|
|
}
|
|
UNLOCK_OUTPUT (sio);
|
|
return cur - str;
|
|
}
|
|
#elif defined(__OS2__)
|
|
if (sio->status & STATUS_LINE_BREAK)
|
|
{
|
|
LOCK_OUTPUT (sio);
|
|
for (n = 0; n < QSE_TYPE_MAX(qse_ssize_t) && str[n] != QSE_WT('\0'); n++)
|
|
{
|
|
if (putwc_no_mutex(sio, str[n]) <= -1)
|
|
{
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1;
|
|
}
|
|
}
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
#endif
|
|
|
|
LOCK_OUTPUT (sio);
|
|
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_writewcs (&sio->tio.io, str, (qse_size_t)-1);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putwcsn (
|
|
qse_sio_t* sio, const qse_wchar_t* str, qse_size_t size)
|
|
{
|
|
qse_ssize_t n;
|
|
|
|
#if defined(_WIN32)
|
|
/* DAMN UGLY:
|
|
* WriteFile returns wrong number of bytes written if it is
|
|
* requested to write a utf8 string on utf8 console (codepage 65001).
|
|
* it seems to return a number of characters written instead. so
|
|
* i have to use an alternate API for console output for
|
|
* wide-character strings. Conversion to either an OEM codepage or
|
|
* the utf codepage is handled by the API. This hack at least
|
|
* lets you do proper utf8 output on utf8 console using wide-character.
|
|
*
|
|
* Note that the multibyte functions qse_sio_putmbs() and
|
|
* qse_sio_putmbsn() doesn't handle this. So you may still suffer.
|
|
*/
|
|
if (sio->status & STATUS_UTF8_CONSOLE)
|
|
{
|
|
DWORD count, left;
|
|
const qse_wchar_t* cur;
|
|
|
|
LOCK_OUTPUT (sio);
|
|
if (qse_sio_flush (sio) <= -1)
|
|
{
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1; /* can't do buffering */
|
|
}
|
|
|
|
for (cur = str, left = size; left > 0; cur += count, left -= count)
|
|
{
|
|
if (WriteConsoleW(sio->file.handle, cur, left, &count, QSE_NULL) == FALSE)
|
|
{
|
|
sio->errnum = QSE_SIO_ESYSERR;
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1;
|
|
}
|
|
if (count == 0) break;
|
|
|
|
/* Note:
|
|
* WriteConsoleW() in unicosw.dll on win 9x/me returns
|
|
* the number of bytes via 'count'. If a double byte
|
|
* string is given, 'count' can be greater than 'left'.
|
|
* this case is a miserable failure. however, i don't
|
|
* think there is CP_UTF8 codepage for console on win9x/me.
|
|
* so let me make this function fail if that ever happens.
|
|
*/
|
|
if (count > left)
|
|
{
|
|
sio->errnum = QSE_SIO_ESYSERR;
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1;
|
|
}
|
|
}
|
|
UNLOCK_OUTPUT (sio);
|
|
return cur - str;
|
|
}
|
|
|
|
#elif defined(__OS2__)
|
|
if (sio->status & STATUS_LINE_BREAK)
|
|
{
|
|
if (size > QSE_TYPE_MAX(qse_ssize_t)) size = QSE_TYPE_MAX(qse_ssize_t);
|
|
LOCK_OUTPUT (sio);
|
|
for (n = 0; n < size; n++)
|
|
{
|
|
if (putwc_no_mutex(sio, str[n]) <= -1)
|
|
{
|
|
UNLOCK_OUTPUT (sio);
|
|
return -1;
|
|
}
|
|
}
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
#endif
|
|
|
|
LOCK_OUTPUT (sio);
|
|
|
|
sio->errnum = QSE_SIO_ENOERR;
|
|
n = qse_tio_writewcs (&sio->tio.io, str, size);
|
|
if (n <= -1 && sio->errnum == QSE_SIO_ENOERR)
|
|
sio->errnum = tio_errnum_to_sio_errnum (&sio->tio.io);
|
|
|
|
UNLOCK_OUTPUT (sio);
|
|
return n;
|
|
}
|
|
|
|
static int put_wchar (qse_wchar_t c, void* ctx)
|
|
{
|
|
return putwc_no_mutex((qse_sio_t*)ctx, c);
|
|
}
|
|
|
|
static int put_mchar (qse_mchar_t c, void* ctx)
|
|
{
|
|
return putmb_no_mutex((qse_sio_t*)ctx, c);
|
|
}
|
|
|
|
static int wcs_to_mbs (
|
|
const qse_wchar_t* wcs, qse_size_t* wcslen,
|
|
qse_mchar_t* mbs, qse_size_t* mbslen, void* ctx)
|
|
{
|
|
return qse_wcsntombsnwithcmgr (wcs, wcslen, mbs, mbslen, qse_sio_getcmgr ((qse_sio_t*)ctx));
|
|
}
|
|
|
|
static int mbs_to_wcs (
|
|
const qse_mchar_t* mbs, qse_size_t* mbslen,
|
|
qse_wchar_t* wcs, qse_size_t* wcslen, void* ctx)
|
|
{
|
|
return qse_mbsntowcsnwithcmgr (mbs, mbslen, wcs, wcslen, qse_sio_getcmgr ((qse_sio_t*)ctx));
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putmbsf (qse_sio_t* sio, const qse_mchar_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
qse_ssize_t x;
|
|
qse_mfmtout_t fo;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio;
|
|
fo.put = put_mchar;
|
|
fo.conv = wcs_to_mbs;
|
|
|
|
va_start (ap, fmt);
|
|
|
|
LOCK_OUTPUT (sio);
|
|
x = qse_mfmtout(fmt, &fo, ap);
|
|
UNLOCK_OUTPUT (sio);
|
|
|
|
va_end (ap);
|
|
|
|
return (x <= -1)? -1: fo.count;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putwcsf (qse_sio_t* sio, const qse_wchar_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int x;
|
|
qse_wfmtout_t fo;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio;
|
|
fo.put = put_wchar;
|
|
fo.conv = mbs_to_wcs;
|
|
|
|
va_start (ap, fmt);
|
|
|
|
LOCK_OUTPUT (sio);
|
|
x = qse_wfmtout (fmt, &fo, ap);
|
|
UNLOCK_OUTPUT (sio);
|
|
|
|
va_end (ap);
|
|
|
|
return (x <= -1)? -1: fo.count;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putmbsvf (qse_sio_t* sio, const qse_mchar_t* fmt, va_list ap)
|
|
{
|
|
qse_mfmtout_t fo;
|
|
qse_ssize_t n;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio;
|
|
fo.put = put_mchar;
|
|
fo.conv = wcs_to_mbs;
|
|
|
|
LOCK_OUTPUT (sio);
|
|
n = (qse_mfmtout(fmt, &fo, ap) <= -1)? -1: fo.count;
|
|
UNLOCK_OUTPUT (sio);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_sio_putwcsvf (qse_sio_t* sio, const qse_wchar_t* fmt, va_list ap)
|
|
{
|
|
qse_wfmtout_t fo;
|
|
qse_ssize_t n;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio;
|
|
fo.put = put_wchar;
|
|
fo.conv = mbs_to_wcs;
|
|
|
|
LOCK_OUTPUT (sio);
|
|
n = (qse_wfmtout(fmt, &fo, ap) <= -1)? -1: fo.count;
|
|
UNLOCK_OUTPUT (sio);
|
|
|
|
return n;
|
|
}
|
|
|
|
int qse_sio_getpos (qse_sio_t* sio, qse_sio_pos_t* pos)
|
|
{
|
|
qse_fio_off_t off;
|
|
|
|
if (qse_sio_flush(sio) <= -1) return -1;
|
|
|
|
off = qse_fio_seek(&sio->file, 0, QSE_FIO_CURRENT);
|
|
if (off == (qse_fio_off_t)-1)
|
|
{
|
|
sio->errnum = fio_errnum_to_sio_errnum (&sio->file);
|
|
return -1;
|
|
}
|
|
|
|
*pos = off;
|
|
return 0;
|
|
}
|
|
|
|
int qse_sio_setpos (qse_sio_t* sio, qse_sio_pos_t pos)
|
|
{
|
|
qse_fio_off_t off;
|
|
|
|
if (qse_sio_flush(sio) <= -1) return -1;
|
|
|
|
off = qse_fio_seek(&sio->file, pos, QSE_FIO_BEGIN);
|
|
if (off == (qse_fio_off_t)-1)
|
|
{
|
|
sio->errnum = fio_errnum_to_sio_errnum (&sio->file);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qse_sio_truncate (qse_sio_t* sio, qse_sio_pos_t pos)
|
|
{
|
|
if (qse_sio_flush(sio) <= -1) return -1;
|
|
return qse_fio_truncate(&sio->file, pos);
|
|
}
|
|
|
|
int qse_sio_seek (qse_sio_t* sio, qse_sio_pos_t* pos, qse_sio_ori_t origin)
|
|
{
|
|
qse_fio_off_t x;
|
|
|
|
if (qse_sio_flush(sio) <= -1) return -1;
|
|
x = qse_fio_seek(&sio->file, *pos, origin);
|
|
if (x == (qse_fio_off_t)-1) return -1;
|
|
|
|
*pos = x;
|
|
return 0;
|
|
}
|
|
|
|
static qse_ssize_t file_input (qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size)
|
|
{
|
|
if (cmd == QSE_TIO_DATA)
|
|
{
|
|
qse_ssize_t n;
|
|
qse_sio_t* sio;
|
|
|
|
sio = *(qse_sio_t**)QSE_XTN(tio);
|
|
QSE_ASSERT (sio != QSE_NULL);
|
|
|
|
n = qse_fio_read(&sio->file, buf, size);
|
|
if (n <= -1) sio->errnum = fio_errnum_to_sio_errnum (&sio->file);
|
|
return n;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static qse_ssize_t file_output (qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size)
|
|
{
|
|
if (cmd == QSE_TIO_DATA)
|
|
{
|
|
qse_ssize_t n;
|
|
qse_sio_t* sio;
|
|
|
|
sio = *(qse_sio_t**)QSE_XTN(tio);
|
|
QSE_ASSERT (sio != QSE_NULL);
|
|
|
|
n = qse_fio_write(&sio->file, buf, size);
|
|
if (n <= -1) sio->errnum = fio_errnum_to_sio_errnum (&sio->file);
|
|
return n;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------- */
|
|
|
|
static qse_sio_t* sio_stdout = QSE_NULL;
|
|
static qse_sio_t* sio_stderr = QSE_NULL;
|
|
/* TODO: add sio_stdin, qse_getmbs, etc */
|
|
|
|
int qse_open_stdsios ()
|
|
{
|
|
return qse_open_stdsios_with_flags (QSE_SIO_LINEBREAK | QSE_SIO_REENTRANT);
|
|
}
|
|
|
|
int qse_open_stdsios_with_flags (int flags)
|
|
{
|
|
if (sio_stdout == QSE_NULL)
|
|
{
|
|
sio_stdout = qse_sio_openstd (QSE_MMGR_GETDFL(), 0, QSE_SIO_STDOUT, flags);
|
|
}
|
|
if (sio_stderr == QSE_NULL)
|
|
{
|
|
sio_stderr = qse_sio_openstd (QSE_MMGR_GETDFL(), 0, QSE_SIO_STDERR, flags);
|
|
}
|
|
|
|
if (sio_stdout == QSE_NULL || sio_stderr == QSE_NULL)
|
|
{
|
|
qse_close_stdsios ();
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void qse_close_stdsios (void)
|
|
{
|
|
if (sio_stderr)
|
|
{
|
|
qse_sio_close (sio_stderr);
|
|
sio_stderr = QSE_NULL;
|
|
}
|
|
if (sio_stdout)
|
|
{
|
|
qse_sio_close (sio_stdout);
|
|
sio_stdout = QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_sio_t* qse_get_stdout (void)
|
|
{
|
|
return sio_stdout;
|
|
}
|
|
|
|
qse_sio_t* qse_get_stderr (void)
|
|
{
|
|
return sio_stderr;
|
|
}
|
|
|
|
qse_ssize_t qse_putmbsf (const qse_mchar_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int x;
|
|
qse_mfmtout_t fo;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stdout;
|
|
fo.put = put_mchar;
|
|
fo.conv = wcs_to_mbs;
|
|
|
|
va_start (ap, fmt);
|
|
|
|
LOCK_OUTPUT (sio_stdout);
|
|
x = qse_mfmtout(fmt, &fo, ap);
|
|
UNLOCK_OUTPUT (sio_stdout);
|
|
|
|
va_end (ap);
|
|
|
|
return (x <= -1)? -1: fo.count;
|
|
}
|
|
|
|
qse_ssize_t qse_putwcsf (const qse_wchar_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int x;
|
|
qse_wfmtout_t fo;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stdout;
|
|
fo.put = put_wchar;
|
|
fo.conv = mbs_to_wcs;
|
|
|
|
va_start (ap, fmt);
|
|
|
|
LOCK_OUTPUT (sio_stdout);
|
|
x = qse_wfmtout(fmt, &fo, ap);
|
|
UNLOCK_OUTPUT (sio_stdout);
|
|
|
|
va_end (ap);
|
|
|
|
return (x <= -1)? -1: fo.count;
|
|
}
|
|
|
|
qse_ssize_t qse_putmbsvf (const qse_mchar_t* fmt, va_list ap)
|
|
{
|
|
qse_mfmtout_t fo;
|
|
qse_ssize_t n;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stdout;
|
|
fo.put = put_mchar;
|
|
fo.conv = wcs_to_mbs;
|
|
|
|
LOCK_OUTPUT (sio_stdout);
|
|
n = (qse_mfmtout(fmt, &fo, ap) <= -1)? -1: fo.count;
|
|
UNLOCK_OUTPUT (sio_stdout);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_putwcsvf (const qse_wchar_t* fmt, va_list ap)
|
|
{
|
|
qse_wfmtout_t fo;
|
|
qse_ssize_t n;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stdout;
|
|
fo.put = put_wchar;
|
|
fo.conv = mbs_to_wcs;
|
|
|
|
LOCK_OUTPUT (sio_stdout);
|
|
n = (qse_wfmtout(fmt, &fo, ap) <= -1)? -1: fo.count;
|
|
UNLOCK_OUTPUT (sio_stdout);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_errputmbsf (const qse_mchar_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int x;
|
|
qse_mfmtout_t fo;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stderr;
|
|
fo.put = put_mchar;
|
|
fo.conv = wcs_to_mbs;
|
|
|
|
va_start (ap, fmt);
|
|
|
|
LOCK_OUTPUT (sio_stderr);
|
|
x = qse_mfmtout(fmt, &fo, ap);
|
|
UNLOCK_OUTPUT (sio_stderr);
|
|
|
|
va_end (ap);
|
|
|
|
return (x <= -1)? -1: fo.count;
|
|
}
|
|
|
|
qse_ssize_t qse_errputwcsf (const qse_wchar_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int x;
|
|
qse_wfmtout_t fo;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stderr;
|
|
fo.put = put_wchar;
|
|
fo.conv = mbs_to_wcs;
|
|
|
|
va_start (ap, fmt);
|
|
|
|
LOCK_OUTPUT (sio_stderr);
|
|
x = qse_wfmtout(fmt, &fo, ap);
|
|
UNLOCK_OUTPUT (sio_stderr);
|
|
|
|
va_end (ap);
|
|
|
|
return (x <= -1)? -1: fo.count;
|
|
}
|
|
|
|
qse_ssize_t qse_errputmbsvf (const qse_mchar_t* fmt, va_list ap)
|
|
{
|
|
qse_mfmtout_t fo;
|
|
qse_ssize_t n;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stderr;
|
|
fo.put = put_mchar;
|
|
fo.conv = wcs_to_mbs;
|
|
|
|
LOCK_OUTPUT (sio_stderr);
|
|
n = (qse_mfmtout(fmt, &fo, ap) <= -1)? -1: fo.count;
|
|
UNLOCK_OUTPUT (sio_stderr);
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_ssize_t qse_errputwcsvf (const qse_wchar_t* fmt, va_list ap)
|
|
{
|
|
qse_wfmtout_t fo;
|
|
qse_ssize_t n;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_ssize_t);
|
|
fo.ctx = sio_stderr;
|
|
fo.put = put_wchar;
|
|
fo.conv = mbs_to_wcs;
|
|
|
|
LOCK_OUTPUT (sio_stderr);
|
|
n = (qse_wfmtout(fmt, &fo, ap) <= -1)? -1: fo.count;
|
|
UNLOCK_OUTPUT (sio_stderr);
|
|
|
|
return n;
|
|
}
|