/* * $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 "sed-prv.h" #include #include #include #include "../cmn/mem-prv.h" typedef struct xtn_in_t xtn_in_t; struct xtn_in_t { qse_sed_iostd_t* ptr; qse_sed_iostd_t* cur; qse_size_t mempos; }; typedef struct xtn_out_t xtn_out_t; struct xtn_out_t { qse_sed_iostd_t* ptr; qse_str_t* memstr; }; typedef struct xtn_t xtn_t; struct xtn_t { struct { xtn_in_t in; qse_char_t last; int newline_squeezed; } s; struct { xtn_in_t in; xtn_out_t out; } e; }; #if defined(QSE_HAVE_INLINE) static QSE_INLINE xtn_t* GET_XTN(qse_sed_t* sed) { return (xtn_t*)((qse_uint8_t*)qse_sed_getxtn(sed) - QSE_SIZEOF(xtn_t)); } #else #define GET_XTN(sed) ((xtn_t*)((qse_uint8_t*)qse_sed_getxtn(sed) - QSE_SIZEOF(xtn_t))) #endif static int int_to_str (qse_size_t val, qse_char_t* buf, qse_size_t buflen) { qse_size_t t; qse_size_t rlen = 0; t = val; if (t == 0) rlen++; else { /* non-zero values */ if (t < 0) { t = -t; rlen++; } while (t > 0) { rlen++; t /= 10; } } if (rlen >= buflen) return -1; /* buffer too small */ buf[rlen] = QSE_T('\0'); t = val; if (t == 0) buf[0] = QSE_T('0'); else { if (t < 0) t = -t; /* fill in the buffer with digits */ while (t > 0) { buf[--rlen] = (qse_char_t)(t % 10) + QSE_T('0'); t /= 10; } /* insert the negative sign if necessary */ if (val < 0) buf[--rlen] = QSE_T('-'); } return 0; } qse_sed_t* qse_sed_openstd (qse_size_t xtnsize, qse_sed_errnum_t* errnum) { return qse_sed_openstdwithmmgr (QSE_MMGR_GETDFL(), xtnsize, errnum); } qse_sed_t* qse_sed_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize, qse_sed_errnum_t* errnum) { qse_sed_t* sed; sed = qse_sed_open (mmgr, QSE_SIZEOF(xtn_t) + xtnsize, errnum); if (!sed) return QSE_NULL; sed->_instsize += QSE_SIZEOF(xtn_t); return sed; } static int verify_iostd_in (qse_sed_t* sed, qse_sed_iostd_t in[]) { qse_size_t i; if (in[0].type == QSE_SED_IOSTD_NULL) { /* if 'in' is specified, it must contains at least one * valid entry */ qse_sed_seterrnum (sed, QSE_SED_EINVAL, QSE_NULL); return -1; } for (i = 0; in[i].type != QSE_SED_IOSTD_NULL; i++) { if (in[i].type != QSE_SED_IOSTD_FILE && in[i].type != QSE_SED_IOSTD_STR && in[i].type != QSE_SED_IOSTD_SIO) { qse_sed_seterrnum (sed, QSE_SED_EINVAL, QSE_NULL); return -1; } } return 0; } static qse_sio_t* open_sio_file (qse_sed_t* sed, const qse_char_t* file, int flags) { qse_sio_t* sio; sio = qse_sio_open(qse_sed_getmmgr(sed), 0, file, flags); if (sio == QSE_NULL) { qse_cstr_t ea; ea.ptr = file; ea.len = qse_strlen (file); qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } 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_sed_t* sed, qse_sio_std_t std, int flags) { qse_sio_t* sio; sio = qse_sio_openstd(qse_sed_getmmgr(sed), 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_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } return sio; } static void close_main_stream ( qse_sed_t* sed, qse_sed_io_arg_t* arg, qse_sed_iostd_t* io) { switch (io->type) { case QSE_SED_IOSTD_FILE: qse_sio_close (arg->handle); break; case QSE_SED_IOSTD_STR: /* nothing to do for input. * i don't close xtn->e.out.memstr intentionally. * i close this in qse_awk_execstd() */ break; case QSE_SED_IOSTD_SIO: /* nothing to do */ break; default: /* do nothing */ break; } } static int open_input_stream ( qse_sed_t* sed, qse_sed_io_arg_t* arg, qse_sed_iostd_t* io, xtn_in_t* base) { xtn_t* xtn = GET_XTN(sed); QSE_ASSERT (io != QSE_NULL); switch (io->type) { case QSE_SED_IOSTD_FILE: { qse_sio_t* sio; if (io->u.file.path == QSE_NULL || (io->u.file.path[0] == QSE_T('-') && io->u.file.path[1] == QSE_T('\0'))) { sio = open_sio_std ( sed, QSE_SIO_STDIN, QSE_SIO_READ | QSE_SIO_IGNOREMBWCERR ); } else { sio = open_sio_file ( sed, io->u.file.path, QSE_SIO_READ | QSE_SIO_IGNOREMBWCERR); } if (sio == QSE_NULL) return -1; if (io->u.file.cmgr) qse_sio_setcmgr (sio, io->u.file.cmgr); arg->handle = sio; break; } case QSE_SED_IOSTD_STR: /* don't store anything to arg->handle */ base->mempos = 0; break; case QSE_SED_IOSTD_SIO: arg->handle = io->u.sio; break; default: QSE_ASSERTX ( !"should never happen", "io-type must be one of SIO,FILE,STR" ); qse_sed_seterrnum (sed, QSE_SED_EINTERN, QSE_NULL); return -1; } if (base == &xtn->s.in) { /* reset script location */ if (io->type == QSE_SED_IOSTD_FILE) { qse_sed_setcompid ( sed, ((io->u.file.path == QSE_NULL)? sio_std_names[QSE_SIO_STDIN].ptr: io->u.file.path) ); } else { qse_char_t buf[64]; /* format an identifier to be something like M#1, S#5 */ buf[0] = (io->type == QSE_SED_IOSTD_STR)? QSE_T('M'): QSE_T('S'); buf[1] = QSE_T('#'); int_to_str (io - xtn->s.in.ptr, &buf[2], QSE_COUNTOF(buf) - 2); /* don't care about failure int_to_str() though it's not * likely to happen */ qse_sed_setcompid (sed, buf); } sed->src.loc.line = 1; sed->src.loc.colm = 1; } return 0; } static int open_output_stream (qse_sed_t* sed, qse_sed_io_arg_t* arg, qse_sed_iostd_t* io) { xtn_t* xtn = GET_XTN(sed); QSE_ASSERT (io != QSE_NULL); switch (io->type) { case QSE_SED_IOSTD_FILE: { qse_sio_t* sio; if (io->u.file.path == QSE_NULL || (io->u.file.path[0] == QSE_T('-') && io->u.file.path[1] == QSE_T('\0'))) { sio = open_sio_std ( sed, QSE_SIO_STDOUT, QSE_SIO_WRITE | QSE_SIO_CREATE | QSE_SIO_TRUNCATE | QSE_SIO_IGNOREMBWCERR | QSE_SIO_LINEBREAK ); } else { sio = open_sio_file ( sed, io->u.file.path, QSE_SIO_WRITE | QSE_SIO_CREATE | QSE_SIO_TRUNCATE | QSE_SIO_IGNOREMBWCERR ); } if (sio == QSE_NULL) return -1; if (io->u.file.cmgr) qse_sio_setcmgr (sio, io->u.file.cmgr); arg->handle = sio; break; } case QSE_SED_IOSTD_STR: /* don't store anything to arg->handle */ xtn->e.out.memstr = qse_str_open(qse_sed_getmmgr(sed), 0, 512); if (xtn->e.out.memstr == QSE_NULL) { qse_sed_seterrnum (sed, QSE_SED_ENOMEM, QSE_NULL); return -1; } break; case QSE_SED_IOSTD_SIO: arg->handle = io->u.sio; break; default: QSE_ASSERTX ( !"should never happen", "io-type must be one of SIO,FILE,STR" ); qse_sed_seterrnum (sed, QSE_SED_EINTERN, QSE_NULL); return -1; } return 0; } static qse_ssize_t read_input_stream ( qse_sed_t* sed, qse_sed_io_arg_t* arg, qse_char_t* buf, qse_size_t len, xtn_in_t* base) { xtn_t* xtn = GET_XTN(sed); qse_sed_iostd_t* io, * next; void* old, * new; qse_ssize_t n = 0; if (len > QSE_TYPE_MAX(qse_ssize_t)) len = QSE_TYPE_MAX(qse_ssize_t); do { io = base->cur; if (base == &xtn->s.in && xtn->s.newline_squeezed) { xtn->s.newline_squeezed = 0; goto open_next; } QSE_ASSERT (io != QSE_NULL); if (io->type == QSE_SED_IOSTD_STR) { n = 0; while (base->mempos < io->u.str.len && n < len) buf[n++] = io->u.str.ptr[base->mempos++]; } else n = qse_sio_getstrn (arg->handle, buf, len); if (n != 0) { if (n <= -1) { if (io->type == QSE_SED_IOSTD_FILE) { qse_cstr_t ea; if (io->u.file.path) { ea.ptr = io->u.file.path; ea.len = qse_strlen (io->u.file.path); } else { ea.ptr = sio_std_names[QSE_SIO_STDIN].ptr; ea.len = sio_std_names[QSE_SIO_STDIN].len; } qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } } else if (base == &xtn->s.in) { xtn->s.last = buf[n-1]; } break; } /* ============================================= */ /* == end of file on the current input stream == */ /* ============================================= */ if (base == &xtn->s.in && xtn->s.last != QSE_T('\n')) { /* TODO: different line termination convension */ buf[0] = QSE_T('\n'); n = 1; xtn->s.newline_squeezed = 1; break; } open_next: next = base->cur + 1; if (next->type == QSE_SED_IOSTD_NULL) { /* no next stream available - return 0 */ break; } old = arg->handle; /* try to open the next input stream */ if (open_input_stream (sed, arg, next, base) <= -1) { /* failed to open the next input stream */ if (next->type == QSE_SED_IOSTD_FILE) { qse_cstr_t ea; if (next->u.file.path) { ea.ptr = next->u.file.path; ea.len = qse_strlen (next->u.file.path); } else { ea.ptr = sio_std_names[QSE_SIO_STDIN].ptr; ea.len = sio_std_names[QSE_SIO_STDIN].len; } qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } n = -1; break; } /* successfuly opened the next input stream */ new = arg->handle; arg->handle = old; /* close the previous stream */ close_main_stream (sed, arg, io); arg->handle = new; base->cur++; } while (1); return n; } static qse_ssize_t s_in ( qse_sed_t* sed, qse_sed_io_cmd_t cmd, qse_sed_io_arg_t* arg, qse_char_t* buf, qse_size_t len) { xtn_t* xtn = GET_XTN(sed); switch (cmd) { case QSE_SED_IO_OPEN: { if (open_input_stream (sed, arg, xtn->s.in.cur, &xtn->s.in) <= -1) return -1; return 1; } case QSE_SED_IO_CLOSE: { close_main_stream (sed, arg, xtn->s.in.cur); return 0; } case QSE_SED_IO_READ: { return read_input_stream (sed, arg, buf, len, &xtn->s.in); } default: { QSE_ASSERTX ( !"should never happen", "cmd must be one of OPEN,CLOSE,READ" ); qse_sed_seterrnum (sed, QSE_SED_EINTERN, QSE_NULL); return -1; } } } static qse_ssize_t x_in ( qse_sed_t* sed, qse_sed_io_cmd_t cmd, qse_sed_io_arg_t* arg, qse_char_t* buf, qse_size_t len) { qse_sio_t* sio; xtn_t* xtn = GET_XTN(sed); switch (cmd) { case QSE_SED_IO_OPEN: { if (arg->path == QSE_NULL) { /* no file specified. console stream */ if (xtn->e.in.ptr == QSE_NULL) { /* QSE_NULL passed into qse_sed_exec() for input */ sio = open_sio_std ( sed, QSE_SIO_STDIN, QSE_SIO_READ | QSE_SIO_IGNOREMBWCERR); if (sio == QSE_NULL) return -1; arg->handle = sio; } else { if (open_input_stream (sed, arg, xtn->e.in.cur, &xtn->e.in) <= -1) return -1; } } else { sio = open_sio_file (sed, arg->path, QSE_SIO_READ | QSE_SIO_IGNOREMBWCERR); if (sio == QSE_NULL) return -1; arg->handle = sio; } return 1; } case QSE_SED_IO_CLOSE: { if (arg->path == QSE_NULL) { /* main data stream */ if (xtn->e.in.ptr == QSE_NULL) qse_sio_close (arg->handle); else close_main_stream (sed, arg, xtn->e.in.cur); } else { qse_sio_close (arg->handle); } return 0; } case QSE_SED_IO_READ: { if (arg->path == QSE_NULL) { /* main data stream */ if (xtn->e.in.ptr == QSE_NULL) { qse_ssize_t n; n = qse_sio_getstrn (arg->handle, buf, len); if (n <= -1) { qse_cstr_t ea; ea.ptr = sio_std_names[QSE_SIO_STDIN].ptr; ea.len = sio_std_names[QSE_SIO_STDIN].len; qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } return n; } else return read_input_stream (sed, arg, buf, len, &xtn->e.in); } else { qse_ssize_t n; n = qse_sio_getstrn (arg->handle, buf, len); if (n <= -1) { qse_cstr_t ea; ea.ptr = arg->path; ea.len = qse_strlen (arg->path); qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } return n; } } default: QSE_ASSERTX ( !"should never happen", "cmd must be one of OPEN,CLOSE,READ" ); qse_sed_seterrnum (sed, QSE_SED_EINTERN, QSE_NULL); return -1; } } static qse_ssize_t x_out ( qse_sed_t* sed, qse_sed_io_cmd_t cmd, qse_sed_io_arg_t* arg, qse_char_t* dat, qse_size_t len) { xtn_t* xtn = GET_XTN(sed); qse_sio_t* sio; switch (cmd) { case QSE_SED_IO_OPEN: { if (arg->path == QSE_NULL) { /* main data stream */ if (xtn->e.out.ptr == QSE_NULL) { /* QSE_NULL passed into qse_sed_execstd() for output */ sio = open_sio_std ( sed, QSE_SIO_STDOUT, QSE_SIO_WRITE | QSE_SIO_CREATE | QSE_SIO_TRUNCATE | QSE_SIO_IGNOREMBWCERR | QSE_SIO_LINEBREAK ); if (sio == QSE_NULL) return -1; arg->handle = sio; } else { if (open_output_stream (sed, arg, xtn->e.out.ptr) <= -1) return -1; } } else { sio = open_sio_file ( sed, arg->path, QSE_SIO_WRITE | QSE_SIO_CREATE | QSE_SIO_TRUNCATE | QSE_SIO_IGNOREMBWCERR ); if (sio == QSE_NULL) return -1; arg->handle = sio; } return 1; } case QSE_SED_IO_CLOSE: { if (arg->path == QSE_NULL) { if (xtn->e.out.ptr == QSE_NULL) qse_sio_close (arg->handle); else close_main_stream (sed, arg, xtn->e.out.ptr); } else { qse_sio_close (arg->handle); } return 0; } case QSE_SED_IO_WRITE: { if (arg->path == QSE_NULL) { /* main data stream */ if (xtn->e.out.ptr == QSE_NULL) { qse_ssize_t n; n = qse_sio_putstrn (arg->handle, dat, len); if (n <= -1) { qse_cstr_t ea; ea.ptr = sio_std_names[QSE_SIO_STDOUT].ptr; ea.len = sio_std_names[QSE_SIO_STDOUT].len; qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } return n; } else { qse_sed_iostd_t* io = xtn->e.out.ptr; if (io->type == QSE_SED_IOSTD_STR) { if (len > QSE_TYPE_MAX(qse_ssize_t)) len = QSE_TYPE_MAX(qse_ssize_t); if (qse_str_ncat (xtn->e.out.memstr, dat, len) == (qse_size_t)-1) { qse_sed_seterrnum (sed, QSE_SED_ENOMEM, QSE_NULL); return -1; } return len; } else { qse_ssize_t n; n = qse_sio_putstrn (arg->handle, dat, len); if (n <= -1) { qse_cstr_t ea; if (io->u.file.path) { ea.ptr = io->u.file.path; ea.len = qse_strlen(io->u.file.path); } else { ea.ptr = sio_std_names[QSE_SIO_STDOUT].ptr; ea.len = sio_std_names[QSE_SIO_STDOUT].len; } qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } return n; } } } else { qse_ssize_t n; n = qse_sio_putstrn (arg->handle, dat, len); if (n <= -1) { qse_cstr_t ea; ea.ptr = arg->path; ea.len = qse_strlen(arg->path); qse_sed_seterrnum (sed, QSE_SED_EIOFIL, &ea); } return n; } } default: QSE_ASSERTX ( !"should never happen", "cmd must be one of OPEN,CLOSE,WRITE" ); qse_sed_seterrnum (sed, QSE_SED_EINTERN, QSE_NULL); return -1; } } int qse_sed_compstd (qse_sed_t* sed, qse_sed_iostd_t in[], qse_size_t* count) { xtn_t* xtn = GET_XTN(sed); int ret; if (in == QSE_NULL) { /* it requires a valid array unlike qse_sed_execstd(). */ qse_sed_seterrnum (sed, QSE_SED_EINVAL, QSE_NULL); return -1; } if (verify_iostd_in (sed, in) <= -1) return -1; QSE_MEMSET (&xtn->s, 0, QSE_SIZEOF(xtn->s)); xtn->s.in.ptr = in; xtn->s.in.cur = in; ret = qse_sed_comp (sed, s_in); if (count) *count = xtn->s.in.cur - xtn->s.in.ptr; return ret; } int qse_sed_execstd ( qse_sed_t* sed, qse_sed_iostd_t in[], qse_sed_iostd_t* out) { int n; xtn_t* xtn = GET_XTN(sed); if (in && verify_iostd_in (sed, in) <= -1) return -1; if (out) { if (out->type != QSE_SED_IOSTD_FILE && out->type != QSE_SED_IOSTD_STR && out->type != QSE_SED_IOSTD_SIO) { qse_sed_seterrnum (sed, QSE_SED_EINVAL, QSE_NULL); return -1; } } QSE_MEMSET (&xtn->e, 0, QSE_SIZEOF(xtn->e)); xtn->e.in.ptr = in; xtn->e.in.cur = in; xtn->e.out.ptr = out; n = qse_sed_exec (sed, x_in, x_out); if (out && out->type == QSE_SED_IOSTD_STR) { if (n >= 0) { QSE_ASSERT (xtn->e.out.memstr != QSE_NULL); qse_str_yield (xtn->e.out.memstr, &out->u.str, 0); } if (xtn->e.out.memstr) qse_str_close (xtn->e.out.memstr); } return n; } int qse_sed_compstdfile ( qse_sed_t* sed, const qse_char_t* file, qse_cmgr_t* cmgr) { qse_sed_iostd_t in[2]; in[0].type = QSE_SED_IOSTD_FILE; in[0].u.file.path = file; in[0].u.file.cmgr = cmgr; in[1].type = QSE_SED_IOSTD_NULL; return qse_sed_compstd (sed, in, QSE_NULL); } int qse_sed_compstdstr (qse_sed_t* sed, const qse_char_t* script) { qse_sed_iostd_t in[2]; in[0].type = QSE_SED_IOSTD_STR; in[0].u.str.ptr = (qse_char_t*)script; in[0].u.str.len = qse_strlen(script); in[1].type = QSE_SED_IOSTD_NULL; return qse_sed_compstd (sed, in, QSE_NULL); } int qse_sed_compstdxstr (qse_sed_t* sed, const qse_cstr_t* script) { qse_sed_iostd_t in[2]; in[0].type = QSE_SED_IOSTD_STR; in[0].u.str = *script; in[1].type = QSE_SED_IOSTD_NULL; return qse_sed_compstd (sed, in, QSE_NULL); } int qse_sed_execstdfile ( qse_sed_t* sed, const qse_char_t* infile, const qse_char_t* outfile, qse_cmgr_t* cmgr) { qse_sed_iostd_t in[2]; qse_sed_iostd_t out; qse_sed_iostd_t* pin = QSE_NULL, * pout = QSE_NULL; if (infile) { in[0].type = QSE_SED_IOSTD_FILE; in[0].u.file.path = infile; in[0].u.file.cmgr = cmgr; in[1].type = QSE_SED_IOSTD_NULL; pin = in; } if (outfile) { out.type = QSE_SED_IOSTD_FILE; out.u.file.path = outfile; out.u.file.cmgr = cmgr; pout = &out; } return qse_sed_execstd (sed, pin, pout); } int qse_sed_execstdxstr ( qse_sed_t* sed, const qse_cstr_t* instr, qse_cstr_t* outstr, qse_cmgr_t* cmgr) { qse_sed_iostd_t in[2]; qse_sed_iostd_t out; int n; in[0].type = QSE_SED_IOSTD_STR; in[0].u.str = *instr; in[1].type = QSE_SED_IOSTD_NULL; out.type = QSE_SED_IOSTD_STR; n = qse_sed_execstd (sed, in, &out); if (n >= 0) *outstr = out.u.str; return n; }