diff --git a/qse/lib/xli/std.c b/qse/lib/xli/std.c
new file mode 100644
index 00000000..54e261fa
--- /dev/null
+++ b/qse/lib/xli/std.c
@@ -0,0 +1,570 @@
+/*
+ * $Id$
+ *
+ Copyright 2006-2012 Chung, Hyung-Hwan.
+ This file is part of QSE.
+
+ 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.
+
+ 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with QSE. If not, see .
+ */
+
+#include "xli.h"
+#include
+#include
+#include "../cmn/mem.h"
+
+typedef struct xtn_t
+{
+ struct
+ {
+ struct
+ {
+ qse_xli_iostd_t* x;
+
+ union
+ {
+ struct
+ {
+ qse_sio_t* sio; /* the handle to an open file */
+ qse_cstr_t dir;
+ } file;
+ struct
+ {
+ const qse_char_t* ptr;
+ const qse_char_t* end;
+ } str;
+ } u;
+ } in;
+
+ struct
+ {
+ qse_xli_iostd_t* x;
+ union
+ {
+ struct
+ {
+ qse_sio_t* sio;
+ } file;
+ struct
+ {
+ qse_str_t* buf;
+ } str;
+ } u;
+ } out;
+ } s;
+
+ qse_xli_ecb_t ecb;
+} xtn_t;
+
+qse_xli_t* qse_xli_openstd (qse_size_t xtnsize)
+{
+ return qse_xli_openstdwithmmgr (QSE_MMGR_GETDFL(), xtnsize);
+}
+
+static void fini_xtn (qse_xli_t* xli)
+{
+ /* nothing to do */
+}
+
+static void clear_xtn (qse_xli_t* xli)
+{
+ /* nothing to do */
+}
+
+qse_xli_t* qse_xli_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize)
+{
+ qse_xli_t* xli;
+ xtn_t* xtn;
+
+ /* create an object */
+ xli = qse_xli_open (mmgr, QSE_SIZEOF(xtn_t) + xtnsize);
+ if (xli == QSE_NULL) goto oops;
+
+ /* initialize extension */
+ xtn = (xtn_t*) QSE_XTN (xli);
+
+ xtn->ecb.close = fini_xtn;
+ xtn->ecb.clear = clear_xtn;
+ qse_xli_pushecb (xli, &xtn->ecb);
+
+ return xli;
+
+oops:
+ if (xli) qse_xli_close (xli);
+ return QSE_NULL;
+}
+
+void* qse_xli_getxtnstd (qse_xli_t* xli)
+{
+ return (void*)((xtn_t*)QSE_XTN(xli) + 1);
+}
+
+static qse_sio_t* open_sio (qse_xli_t* xli, const qse_char_t* file, int flags)
+{
+ qse_sio_t* sio;
+ sio = qse_sio_open (xli->mmgr, 0, file, flags);
+ if (sio == QSE_NULL)
+ {
+ qse_cstr_t errarg;
+ errarg.ptr = file;
+ errarg.len = qse_strlen(file);
+ qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &errarg);
+ }
+ return sio;
+}
+
+struct sio_std_name_t
+{
+ const qse_char_t* ptr;
+ qse_size_t len;
+};
+
+static struct sio_std_name_t sio_std_names[] =
+{
+ { QSE_T("stdin"), 5 },
+ { QSE_T("stdout"), 6 },
+ { QSE_T("stderr"), 6 }
+};
+
+static qse_sio_t* open_sio_std (qse_xli_t* xli, qse_sio_std_t std, int flags)
+{
+ qse_sio_t* sio;
+ sio = qse_sio_openstd (xli->mmgr, 0, std, flags);
+ if (sio == QSE_NULL)
+ {
+ qse_cstr_t ea;
+ ea.ptr = sio_std_names[std].ptr;
+ ea.len = sio_std_names[std].len;
+ qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea);
+ }
+ return sio;
+}
+
+static int open_readstd (qse_xli_t* xli, xtn_t* xtn)
+{
+ qse_xli_iostd_t* psin = xtn->s.in.x;
+
+ switch (psin->type)
+ {
+ /* normal source files */
+
+ case QSE_XLI_IOSTD_FILE:
+ if (psin->u.file.path == QSE_NULL ||
+ (psin->u.file.path[0] == QSE_T('-') &&
+ psin->u.file.path[1] == QSE_T('\0')))
+ {
+ /* no path name or - -> stdin */
+ qse_sio_t* tmp;
+
+ tmp = open_sio_std (xli, QSE_SIO_STDIN, QSE_SIO_READ | QSE_SIO_IGNOREMBWCERR);
+ if (tmp == QSE_NULL) return -1;
+
+ xtn->s.in.u.file.sio = tmp;
+ }
+ else
+ {
+ qse_sio_t* tmp;
+ const qse_char_t* base;
+
+ tmp = open_sio (xli, psin->u.file.path, QSE_SIO_READ | QSE_SIO_IGNOREMBWCERR);
+ if (tmp == QSE_NULL) return -1;
+
+ xtn->s.in.u.file.sio = tmp;
+
+ base = qse_basename (psin->u.file.path);
+ if (base != psin->u.file.path)
+ {
+ xtn->s.in.u.file.dir.ptr = psin->u.file.path;
+ xtn->s.in.u.file.dir.len = base - psin->u.file.path;
+ }
+
+ }
+ if (psin->u.file.cmgr)
+ qse_sio_setcmgr (xtn->s.in.u.file.sio, psin->u.file.cmgr);
+ return 0;
+
+ case QSE_XLI_IOSTD_STR:
+ xtn->s.in.u.str.ptr = psin->u.str.ptr;
+ xtn->s.in.u.str.end = psin->u.str.ptr + psin->u.str.len;
+ return 0;
+
+ default:
+ qse_xli_seterrnum (xli, QSE_XLI_EINTERN, QSE_NULL);
+ return -1;
+ }
+}
+
+static qse_ssize_t sf_in_open (
+ qse_xli_t* xli, qse_xli_io_arg_t* arg, xtn_t* xtn)
+{
+ if (arg == QSE_NULL || !(arg->flags & QSE_XLI_IO_INCLUDED))
+ {
+ /* handle normal source input streams specified
+ * to qse_xli_readstd() */
+
+ qse_ssize_t x;
+
+ x = open_readstd (xli, xtn);
+ if (x >= 0)
+ {
+ /* perform some manipulation about the top-level input information */
+ if (xtn->s.in.x->type == QSE_XLI_IOSTD_FILE)
+ xli->sio.arg.name = xtn->s.in.x->u.file.path;
+ else
+ xli->sio.arg.name = QSE_NULL;
+ }
+
+ return x;
+ }
+ else
+ {
+ /* handle the included file - @include */
+ const qse_char_t* file;
+ qse_char_t fbuf[64];
+ qse_char_t* dbuf = QSE_NULL;
+
+ QSE_ASSERT (arg->name != QSE_NULL);
+
+ file = arg->name;
+ if (xtn->s.in.u.file.dir.len > 0 && arg->name[0] != QSE_T('/'))
+ {
+ qse_size_t tmplen, totlen;
+
+ totlen = qse_strlen(arg->name) + xtn->s.in.u.file.dir.len;
+ if (totlen >= QSE_COUNTOF(fbuf))
+ {
+ dbuf = qse_xli_allocmem (
+ xli, QSE_SIZEOF(qse_char_t) * (totlen + 1)
+ );
+ if (dbuf == QSE_NULL) return -1;
+
+ file = dbuf;
+ }
+ else file = fbuf;
+
+ tmplen = qse_strncpy (
+ (qse_char_t*)file,
+ xtn->s.in.u.file.dir.ptr,
+ xtn->s.in.u.file.dir.len
+ );
+ qse_strcpy ((qse_char_t*)file + tmplen, arg->name);
+ }
+
+ arg->handle = qse_sio_open (
+ xli->mmgr, 0, file, QSE_SIO_READ | QSE_SIO_IGNOREMBWCERR
+ );
+
+ if (dbuf) QSE_MMGR_FREE (xli->mmgr, dbuf);
+ if (arg->handle == QSE_NULL)
+ {
+ qse_cstr_t ea;
+ ea.ptr = arg->name;
+ ea.len = qse_strlen(ea.ptr);
+ qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea);
+ return -1;
+ }
+
+ return 0;
+ }
+}
+
+static qse_ssize_t sf_in_close (
+ qse_xli_t* xli, qse_xli_io_arg_t* arg, xtn_t* xtn)
+{
+ if (arg == QSE_NULL || !(arg->flags & QSE_XLI_IO_INCLUDED))
+ {
+ switch (xtn->s.in.x->type)
+ {
+ case QSE_XLI_IOSTD_FILE:
+ qse_sio_close (xtn->s.in.u.file.sio);
+ break;
+
+ case QSE_XLI_IOSTD_STR:
+ /* nothing to close */
+ break;
+
+ default:
+ /* nothing to close */
+ break;
+ }
+ }
+ else
+ {
+ /* handle the included source file - @include */
+ QSE_ASSERT (arg->handle != QSE_NULL);
+ qse_sio_close (arg->handle);
+ }
+
+ return 0;
+}
+
+static qse_ssize_t sf_in_read (
+ qse_xli_t* xli, qse_xli_io_arg_t* arg,
+ qse_char_t* data, qse_size_t size, xtn_t* xtn)
+{
+
+ if (arg == QSE_NULL || !(arg->flags & QSE_XLI_IO_INCLUDED))
+ {
+ qse_ssize_t n;
+
+ switch (xtn->s.in.x->type)
+ {
+ case QSE_XLI_IOSTD_FILE:
+ QSE_ASSERT (xtn->s.in.u.file.sio != QSE_NULL);
+ n = qse_sio_getstrn (xtn->s.in.u.file.sio, data, size);
+ if (n <= -1)
+ {
+ qse_cstr_t ea;
+ if (xtn->s.in.x->u.file.path)
+ {
+ ea.ptr = xtn->s.in.x->u.file.path;
+ ea.len = qse_strlen(ea.ptr);
+ }
+ else
+ {
+ ea.ptr = sio_std_names[QSE_SIO_STDIN].ptr;
+ ea.len = sio_std_names[QSE_SIO_STDIN].len;
+ }
+ qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea);
+ }
+ break;
+
+ case QSE_XLI_IOSTD_STR:
+ n = 0;
+ while (n < size && xtn->s.in.u.str.ptr < xtn->s.in.u.str.end)
+ {
+ data[n++] = *xtn->s.in.u.str.ptr++;
+ }
+ break;
+
+ default:
+ /* this should never happen */
+ qse_xli_seterrnum (xli, QSE_XLI_EINTERN, QSE_NULL);
+ n = -1;
+ break;
+ }
+
+ return n;
+ }
+ else
+ {
+ /* handle the included source file - @include */
+ qse_ssize_t n;
+
+ QSE_ASSERT (arg->name != QSE_NULL);
+ QSE_ASSERT (arg->handle != QSE_NULL);
+
+ n = qse_sio_getstrn (arg->handle, data, size);
+ if (n <= -1)
+ {
+ qse_cstr_t ea;
+ ea.ptr = arg->name;
+ ea.len = qse_strlen(ea.ptr);
+ qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea);
+ }
+ return n;
+ }
+}
+
+static qse_ssize_t sf_in (
+ qse_xli_t* xli, qse_xli_io_cmd_t cmd,
+ qse_xli_io_arg_t* arg, qse_char_t* data, qse_size_t size)
+{
+ xtn_t* xtn = QSE_XTN (xli);
+
+ switch (cmd)
+ {
+ case QSE_XLI_IO_OPEN:
+ return sf_in_open (xli, arg, xtn);
+
+ case QSE_XLI_IO_CLOSE:
+ return sf_in_close (xli, arg, xtn);
+
+ case QSE_XLI_IO_READ:
+ return sf_in_read (xli, arg, data, size, xtn);
+
+ default:
+ qse_xli_seterrnum (xli, QSE_XLI_EINTERN, QSE_NULL);
+ return -1;
+ }
+}
+
+static qse_ssize_t sf_out (
+ qse_xli_t* xli, qse_xli_io_cmd_t cmd,
+ qse_xli_io_arg_t* arg, qse_char_t* data, qse_size_t size)
+{
+ xtn_t* xtn = QSE_XTN (xli);
+
+ switch (cmd)
+ {
+ case QSE_XLI_IO_OPEN:
+ {
+ switch (xtn->s.out.x->type)
+ {
+ case QSE_XLI_IOSTD_FILE:
+ if (xtn->s.out.x->u.file.path == QSE_NULL ||
+ (xtn->s.out.x->u.file.path[0] == QSE_T('-') &&
+ xtn->s.out.x->u.file.path[1] == QSE_T('\0')))
+
+ {
+ /* no path name or - -> stdout */
+ xtn->s.out.u.file.sio = open_sio_std (
+ xli, QSE_SIO_STDOUT,
+ QSE_SIO_WRITE | QSE_SIO_IGNOREMBWCERR
+ );
+ if (xtn->s.out.u.file.sio == QSE_NULL) return -1;
+ }
+ else
+ {
+ xtn->s.out.u.file.sio = open_sio (
+ xli, xtn->s.out.x->u.file.path,
+ QSE_SIO_WRITE | QSE_SIO_CREATE |
+ QSE_SIO_TRUNCATE | QSE_SIO_IGNOREMBWCERR
+ );
+ if (xtn->s.out.u.file.sio == QSE_NULL) return -1;
+ }
+
+ if (xtn->s.out.x->u.file.cmgr)
+ qse_sio_setcmgr (xtn->s.out.u.file.sio, xtn->s.out.x->u.file.cmgr);
+ return 1;
+
+ case QSE_XLI_IOSTD_STR:
+ xtn->s.out.u.str.buf = qse_str_open (xli->mmgr, 0, 512);
+ if (xtn->s.out.u.str.buf == QSE_NULL)
+ {
+ qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
+ return -1;
+ }
+
+ return 1;
+ }
+
+ break;
+ }
+
+
+ case QSE_XLI_IO_CLOSE:
+ {
+ switch (xtn->s.out.x->type)
+ {
+ case QSE_XLI_IOSTD_FILE:
+ qse_sio_close (xtn->s.out.u.file.sio);
+ return 0;
+
+ case QSE_XLI_IOSTD_STR:
+ /* i don't close xtn->s.out.u.str.buf intentionally here.
+ * it will be closed at the end of qse_xli_readstd() */
+ return 0;
+ }
+
+ break;
+ }
+
+ case QSE_XLI_IO_WRITE:
+ {
+ switch (xtn->s.out.x->type)
+ {
+ case QSE_XLI_IOSTD_FILE:
+ {
+ qse_ssize_t n;
+ QSE_ASSERT (xtn->s.out.u.file.sio != QSE_NULL);
+ n = qse_sio_putstrn (xtn->s.out.u.file.sio, data, size);
+ if (n <= -1)
+ {
+ qse_cstr_t ea;
+ if (xtn->s.out.x->u.file.path)
+ {
+ ea.ptr = xtn->s.out.x->u.file.path;
+ ea.len = qse_strlen(ea.ptr);
+ }
+ else
+ {
+ ea.ptr = sio_std_names[QSE_SIO_STDOUT].ptr;
+ ea.len = sio_std_names[QSE_SIO_STDOUT].len;
+ }
+ qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea);
+ }
+
+ return n;
+ }
+
+ case QSE_XLI_IOSTD_STR:
+ {
+ if (size > QSE_TYPE_MAX(qse_ssize_t)) size = QSE_TYPE_MAX(qse_ssize_t);
+ if (qse_str_ncat (xtn->s.out.u.str.buf, data, size) == (qse_size_t)-1)
+ {
+ qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
+ return -1;
+ }
+ return size;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ /* other code must not trigger this function */
+ break;
+ }
+
+ qse_xli_seterrnum (xli, QSE_XLI_EINTERN, QSE_NULL);
+ return -1;
+}
+
+int qse_xli_readstd (qse_xli_t* xli, qse_xli_iostd_t* in)
+{
+ xtn_t* xtn = (xtn_t*) QSE_XTN (xli);
+
+ if (in == QSE_NULL || (in->type != QSE_XLI_IOSTD_FILE &&
+ in->type != QSE_XLI_IOSTD_STR))
+ {
+ /* the input is a must. at least 1 file or 1 string
+ * must be specified */
+ qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
+ return -1;
+ }
+
+ xtn->s.in.x = in;
+ return qse_xli_read (xli, sf_in);
+}
+
+int qse_xli_writestd (qse_xli_t* xli, qse_xli_iostd_t* out)
+{
+ int n;
+ xtn_t* xtn = (xtn_t*) QSE_XTN (xli);
+
+ if (out == QSE_NULL || (out->type != QSE_XLI_IOSTD_FILE &&
+ out->type != QSE_XLI_IOSTD_STR))
+ {
+ /* the input is a must. at least 1 file or 1 string
+ * must be specified */
+ qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
+ return -1;
+ }
+
+ xtn->s.out.x = out;
+ n = qse_xli_write (xli, sf_out);
+
+ if (out->type == QSE_XLI_IOSTD_STR)
+ {
+ if (n >= 0)
+ {
+ QSE_ASSERT (xtn->s.out.u.str.buf != QSE_NULL);
+ qse_str_yield (xtn->s.out.u.str.buf, &out->u.str, 0);
+ }
+ if (xtn->s.out.u.str.buf) qse_str_close (xtn->s.out.u.str.buf);
+ }
+
+ return n;
+}