580 lines
15 KiB
C++
580 lines
15 KiB
C++
/*
|
|
* $Id$
|
|
*
|
|
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-Sed.hpp>
|
|
#include <hawk-fio.h>
|
|
#include <hawk-sio.h>
|
|
#include "sed-prv.h"
|
|
|
|
/////////////////////////////////
|
|
HAWK_BEGIN_NAMESPACE(HAWK)
|
|
/////////////////////////////////
|
|
|
|
static hawk_sio_t* open_sio (SedStd::Stream::Data& io, const hawk_ooch_t* file, int flags)
|
|
{
|
|
hawk_sio_t* sio;
|
|
|
|
sio = hawk_sio_open((hawk_gem_t*)io, 0, file, flags);
|
|
if (sio == HAWK_NULL)
|
|
{
|
|
const hawk_ooch_t* old_errmsg = hawk_sed_backuperrmsg((hawk_sed_t*)io);
|
|
((Sed*)io)->formatError (HAWK_SED_EIOFIL, HAWK_NULL, "unable to open %js - %js", file, old_errmsg);
|
|
}
|
|
return sio;
|
|
}
|
|
|
|
static hawk_sio_t* open_sio_std (SedStd::Stream::Data& io, hawk_sio_std_t std, int flags)
|
|
{
|
|
hawk_sio_t* sio;
|
|
static const hawk_ooch_t* std_names[] =
|
|
{
|
|
HAWK_T("stdin"),
|
|
HAWK_T("stdout"),
|
|
HAWK_T("stderr"),
|
|
};
|
|
|
|
sio = hawk_sio_openstd((hawk_gem_t*)io, 0, std, flags);
|
|
if (sio == HAWK_NULL)
|
|
{
|
|
const hawk_ooch_t* old_errmsg = hawk_sed_backuperrmsg((hawk_sed_t*)io);
|
|
((Sed*)io)->formatError (HAWK_SED_EIOFIL, HAWK_NULL, "unable to open %js - %js", std_names[std], old_errmsg);
|
|
}
|
|
return sio;
|
|
}
|
|
|
|
SedStd::FileStream::FileStream (const hawk_uch_t* file, hawk_cmgr_t* cmgr):
|
|
_type(NAME_UCH), _file(file), file(HAWK_NULL), cmgr(cmgr)
|
|
{
|
|
}
|
|
|
|
SedStd::FileStream::FileStream (const hawk_bch_t* file, hawk_cmgr_t* cmgr ):
|
|
_type(NAME_BCH), _file(file), file(HAWK_NULL), cmgr(cmgr)
|
|
{
|
|
}
|
|
SedStd::FileStream::~FileStream ()
|
|
{
|
|
HAWK_ASSERT (this->file == HAWK_NULL);
|
|
}
|
|
|
|
int SedStd::FileStream::open (Data& io)
|
|
{
|
|
hawk_sio_t* sio;
|
|
const hawk_ooch_t* ioname = io.getName();
|
|
hawk_sio_std_t std_sio;
|
|
int oflags;
|
|
hawk_gem_t* gem = (hawk_gem_t*)io;
|
|
|
|
if (io.getMode() == READ)
|
|
{
|
|
oflags = HAWK_SIO_READ | HAWK_SIO_IGNOREECERR;
|
|
std_sio = HAWK_SIO_STDIN;
|
|
}
|
|
else
|
|
{
|
|
oflags = HAWK_SIO_WRITE | HAWK_SIO_CREATE | HAWK_SIO_TRUNCATE | HAWK_SIO_IGNOREECERR | HAWK_SIO_LINEBREAK;
|
|
std_sio = HAWK_SIO_STDOUT;
|
|
}
|
|
|
|
if (ioname == HAWK_NULL)
|
|
{
|
|
//
|
|
// a normal console is indicated by a null name or a dash
|
|
//
|
|
|
|
if (this->_file)
|
|
{
|
|
if (this->_type == NAME_UCH)
|
|
{
|
|
const hawk_uch_t* tmp = (const hawk_uch_t*)this->_file;
|
|
if (tmp[0] == '-' || tmp[1] == '\0') goto sio_stdio;
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
this->file = hawk_gem_dupucstr(gem, tmp, HAWK_NULL);
|
|
#else
|
|
this->file = hawk_duputobcstr(gem, tmp, HAWK_NULL);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
const hawk_bch_t* tmp = (const hawk_bch_t*)this->_file;
|
|
if (tmp[0] == '-' || tmp[1] == '\0') goto sio_stdio;
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
this->file = hawk_gem_dupbtoucstr(gem, tmp, HAWK_NULL, 0);
|
|
#else
|
|
this->file = hawk_gem_dupbcstr(gem, tmp, HAWK_NULL);
|
|
#endif
|
|
}
|
|
if (!this->file) return -1;
|
|
sio = open_sio(io, this->file, oflags);
|
|
if (!sio) hawk_gem_freemem (gem, this->file);
|
|
}
|
|
else
|
|
{
|
|
sio_stdio:
|
|
sio = open_sio_std(io, std_sio, oflags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if ioname is not empty, it is a file name
|
|
//
|
|
sio = open_sio(io, ioname, oflags);
|
|
}
|
|
if (sio == HAWK_NULL) return -1;
|
|
|
|
if (this->cmgr) hawk_sio_setcmgr (sio, this->cmgr);
|
|
io.setHandle (sio);
|
|
return 1;
|
|
}
|
|
|
|
int SedStd::FileStream::close (Data& io)
|
|
{
|
|
hawk_sio_close ((hawk_sio_t*)io.getHandle());
|
|
|
|
// this stream object may get called more than once and is merely a proxy
|
|
// object that has its own lifespan. while io.getHandle() returns a unique
|
|
// handle value as set by io.setHandle() in the open method, this object
|
|
// is resued for calls over multiple I/O objects created. When releasing
|
|
// extra resources stored in the object, we must take extra care to know
|
|
// the context of this close method.
|
|
|
|
if (!io.getName())
|
|
{
|
|
// for a master stream that's not started by r or w
|
|
if (this->file)
|
|
{
|
|
hawk_gem_freemem ((hawk_gem_t*)io, this->file);
|
|
this->file = HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
hawk_ooi_t SedStd::FileStream::read (Data& io, hawk_ooch_t* buf, hawk_oow_t len)
|
|
{
|
|
hawk_ooi_t n = hawk_sio_getoochars((hawk_sio_t*)io.getHandle(), buf, len);
|
|
|
|
if (n <= -1)
|
|
{
|
|
if (!io.getName() && this->file) // io.getMode() must be READ
|
|
{
|
|
const hawk_ooch_t* old_errmsg = hawk_sed_backuperrmsg((hawk_sed_t*)io);
|
|
((Sed*)io)->formatError (HAWK_SED_EIOFIL, HAWK_NULL, "unable to read %js - %js", this->file, old_errmsg);
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
hawk_ooi_t SedStd::FileStream::write (Data& io, const hawk_ooch_t* buf, hawk_oow_t len)
|
|
{
|
|
hawk_ooi_t n = hawk_sio_putoochars((hawk_sio_t*)io.getHandle(), buf, len);
|
|
|
|
if (n <= -1)
|
|
{
|
|
if (!io.getName() && this->file) // io.getMode() must be WRITE
|
|
{
|
|
const hawk_ooch_t* old_errmsg = hawk_sed_backuperrmsg((hawk_sed_t*)io);
|
|
((Sed*)io)->formatError (HAWK_SED_EIOFIL, HAWK_NULL, "unable to read %js - %js", this->file, old_errmsg);
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
SedStd::StringStream::StringStream (hawk_cmgr_t* cmgr): _type(STR_UCH) // this type isn't import for this
|
|
{
|
|
this->cmgr = cmgr;
|
|
|
|
this->in._sed = HAWK_NULL;
|
|
this->in._str = HAWK_NULL;
|
|
this->in._end = HAWK_NULL;
|
|
this->in.str = HAWK_NULL;
|
|
this->in.end = HAWK_NULL;
|
|
this->in.ptr = HAWK_NULL;
|
|
|
|
this->out._sed = HAWK_NULL;
|
|
this->out.sed_ecb.next = HAWK_NULL;
|
|
this->out.inited = false;
|
|
this->out.alt_buf = HAWK_NULL;
|
|
this->out.alt_sed = HAWK_NULL;
|
|
}
|
|
|
|
SedStd::StringStream::StringStream (const hawk_uch_t* in, hawk_cmgr_t* cmgr): _type(STR_UCH)
|
|
{
|
|
this->cmgr = cmgr;
|
|
|
|
this->in._sed = HAWK_NULL;
|
|
this->in._str = in;
|
|
this->in._end = in + hawk_count_ucstr(in);
|
|
this->in.str = HAWK_NULL;
|
|
this->in.end = HAWK_NULL;
|
|
this->in.ptr = HAWK_NULL;
|
|
|
|
this->out._sed = HAWK_NULL;
|
|
this->out.sed_ecb.next = HAWK_NULL;
|
|
this->out.inited = false;
|
|
this->out.alt_buf = HAWK_NULL;
|
|
this->out.alt_sed = HAWK_NULL;
|
|
}
|
|
|
|
SedStd::StringStream::StringStream (const hawk_uch_t* in, hawk_oow_t len, hawk_cmgr_t* cmgr): _type(STR_UCH)
|
|
{
|
|
this->cmgr = cmgr;
|
|
|
|
this->in._sed = HAWK_NULL;
|
|
this->in._str = in;
|
|
this->in._end = in + len;
|
|
this->in.str = HAWK_NULL;
|
|
this->in.end = HAWK_NULL;
|
|
this->in.ptr = HAWK_NULL;
|
|
|
|
this->out._sed = HAWK_NULL;
|
|
this->out.sed_ecb.next = HAWK_NULL;
|
|
this->out.inited = false;
|
|
this->out.alt_buf = HAWK_NULL;
|
|
this->out.alt_sed = HAWK_NULL;
|
|
|
|
}
|
|
|
|
SedStd::StringStream::StringStream (const hawk_bch_t* in, hawk_cmgr_t* cmgr): _type(STR_BCH)
|
|
{
|
|
this->cmgr = cmgr;
|
|
|
|
this->in._sed = HAWK_NULL;
|
|
this->in._str = in;
|
|
this->in._end = in + hawk_count_bcstr(in);
|
|
this->in.str = HAWK_NULL;
|
|
this->in.end = HAWK_NULL;
|
|
this->in.ptr = HAWK_NULL;
|
|
|
|
this->out._sed = HAWK_NULL;
|
|
this->out.sed_ecb.next = HAWK_NULL;
|
|
this->out.inited = false;
|
|
this->out.alt_buf = HAWK_NULL;
|
|
this->out.alt_sed = HAWK_NULL;
|
|
}
|
|
|
|
SedStd::StringStream::StringStream (const hawk_bch_t* in, hawk_oow_t len, hawk_cmgr_t* cmgr): _type(STR_BCH)
|
|
{
|
|
this->cmgr = cmgr;
|
|
|
|
this->in._sed = HAWK_NULL;
|
|
this->in._str = in;
|
|
this->in._end = in + len;
|
|
this->in.str = HAWK_NULL;
|
|
this->in.end = HAWK_NULL;
|
|
this->in.ptr = HAWK_NULL;
|
|
|
|
this->out._sed = HAWK_NULL;
|
|
this->out.inited = false;
|
|
this->out.alt_buf = HAWK_NULL;
|
|
this->out.alt_sed = HAWK_NULL;
|
|
|
|
this->out.sed_ecb.next = HAWK_NULL;
|
|
}
|
|
|
|
SedStd::StringStream::~StringStream ()
|
|
{
|
|
HAWK_ASSERT (this->in._sed == HAWK_NULL);
|
|
HAWK_ASSERT (this->in.str == HAWK_NULL);
|
|
|
|
this->clearOutputData (true);
|
|
HAWK_ASSERT (this->out._sed == HAWK_NULL);
|
|
HAWK_ASSERT (this->out.inited == false);
|
|
}
|
|
|
|
int SedStd::StringStream::open (Data& io)
|
|
{
|
|
const hawk_ooch_t* ioname = io.getName ();
|
|
|
|
if (ioname == HAWK_NULL)
|
|
{
|
|
// open a main data stream
|
|
if (io.getMode() == READ)
|
|
{
|
|
HAWK_ASSERT (this->in.str == HAWK_NULL);
|
|
|
|
if (this->in._str == HAWK_NULL)
|
|
{
|
|
// no input data was passed to this object for construction
|
|
|
|
if (this->out.inited)
|
|
{
|
|
// this object is being reused for input after output
|
|
// use the output data as input
|
|
hawk_oocs_t out;
|
|
hawk_ooecs_yield (&this->out.buf, &out, 0);
|
|
this->in.str = out.ptr;
|
|
this->in.ptr = out.ptr;
|
|
this->in.end = this->in.str + out.len;
|
|
this->clearOutputData (true);
|
|
}
|
|
else
|
|
{
|
|
((Sed*)io)->formatError (HAWK_EINVAL, HAWK_NULL, "no input data available to open");
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_oow_t len;
|
|
hawk_gem_t* gem = hawk_sed_getgem((hawk_sed_t*)io);
|
|
|
|
if (this->_type == STR_UCH)
|
|
{
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
this->in.str = hawk_gem_dupucstr(gem, (const hawk_uch_t*)this->in._str, &len);
|
|
#else
|
|
this->in.str = hawk_duputobcstr(gem, (const hawk_uch_t*)this->in._str, &len);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT (this->_type == STR_BCH);
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
this->in.str = hawk_gem_dupbtoucstr(gem, (const hawk_bch_t*)this->in._str, &len, 0);
|
|
#else
|
|
this->in.str = hawk_gem_dupbcstr(gem, (const hawk_bch_t*)this->in._str, &len);
|
|
#endif
|
|
}
|
|
if (HAWK_UNLIKELY(!this->in.str)) return -1;
|
|
|
|
this->in.end = this->in.str + len;
|
|
}
|
|
|
|
this->in.ptr = this->in.str;
|
|
this->in._sed = (hawk_sed_t*)io;
|
|
}
|
|
else
|
|
{
|
|
this->clearOutputData (true);
|
|
// preserving this previous output data is a bit tricky when hawk_ooecs_init() fails.
|
|
// let's not try to preserve the old output data for now.
|
|
// full clearing is needed because this object may get passed to different sed objects
|
|
// in succession.
|
|
|
|
if (hawk_ooecs_init(&this->out.buf, (hawk_gem_t*)io, 256) <= -1) return -1;
|
|
this->out.inited = true;
|
|
this->out._sed = (hawk_sed_t*)io;
|
|
|
|
// only if it's not already pushed
|
|
this->out.sed_ecb.close = this->on_sed_close;
|
|
this->out.sed_ecb.ctx = this;
|
|
hawk_sed_pushecb ((hawk_sed_t*)io, &this->out.sed_ecb);
|
|
}
|
|
|
|
io.setHandle (this);
|
|
}
|
|
else
|
|
{
|
|
// open files for a r or w command
|
|
hawk_sio_t* sio;
|
|
int mode = (io.getMode() == READ)?
|
|
HAWK_SIO_READ:
|
|
(HAWK_SIO_WRITE|HAWK_SIO_CREATE|HAWK_SIO_TRUNCATE);
|
|
|
|
sio = hawk_sio_open((hawk_gem_t*)io, 0, ioname, mode);
|
|
if (sio == HAWK_NULL) return -1;
|
|
|
|
io.setHandle (sio);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int SedStd::StringStream::close (Data& io)
|
|
{
|
|
const void* handle = io.getHandle();
|
|
if (handle == this)
|
|
{
|
|
if (io.getMode() == READ)
|
|
{
|
|
this->clearInputData();
|
|
}
|
|
else if (io.getMode() == WRITE && this->out.inited)
|
|
{
|
|
// don't clear anyting here as some output fields are required
|
|
// until this object is destroyed or the associated sed object is closed.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_sio_close ((hawk_sio_t*)handle);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
hawk_ooi_t SedStd::StringStream::read (Data& io, hawk_ooch_t* buf, hawk_oow_t len)
|
|
{
|
|
const void* handle = io.getHandle();
|
|
|
|
if (len == (hawk_oow_t)-1) len--; // shrink buffer if too long
|
|
if (handle == this)
|
|
{
|
|
HAWK_ASSERT (this->in.str != HAWK_NULL);
|
|
hawk_oow_t n = 0;
|
|
while (this->in.ptr < this->in.end && n < len)
|
|
buf[n++] = *this->in.ptr++;
|
|
return (hawk_ooi_t)n;
|
|
}
|
|
else
|
|
{
|
|
return hawk_sio_getoochars((hawk_sio_t*)handle, buf, len);
|
|
}
|
|
}
|
|
|
|
hawk_ooi_t SedStd::StringStream::write (Data& io, const hawk_ooch_t* data, hawk_oow_t len)
|
|
{
|
|
const void* handle = io.getHandle();
|
|
|
|
if (len == (hawk_oow_t)-1) len--; // shrink data if too long
|
|
|
|
if (handle == this)
|
|
{
|
|
HAWK_ASSERT (this->out.inited != 0);
|
|
if (hawk_ooecs_ncat(&this->out.buf, data, len) == (hawk_oow_t)-1) return -1;
|
|
return len;
|
|
}
|
|
else
|
|
{
|
|
return hawk_sio_putoochars((hawk_sio_t*)handle, data, len);
|
|
}
|
|
}
|
|
|
|
const hawk_ooch_t* SedStd::StringStream::getOutput (hawk_oow_t* len) const
|
|
{
|
|
if (this->out.inited)
|
|
{
|
|
if (len) *len = HAWK_OOECS_LEN(&this->out.buf);
|
|
return HAWK_OOECS_PTR(&this->out.buf);
|
|
}
|
|
else
|
|
{
|
|
if (len) *len = 0;
|
|
return HAWK_T("");
|
|
}
|
|
}
|
|
|
|
const hawk_uch_t* SedStd::StringStream::getOutputU (hawk_oow_t* len)
|
|
{
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
return this->getOutput(len);
|
|
#else
|
|
if (this->out.inited)
|
|
{
|
|
hawk_uch_t* tmp = hawk_gem_dupbtoucharswithcmgr(hawk_sed_getgem(this->out._sed), HAWK_OOECS_PTR(&this->out.buf), HAWK_OOECS_LEN(&this->out.buf), len, this->cmgr, 1);
|
|
if (tmp)
|
|
{
|
|
if (this->out.alt_buf) hawk_sed_freemem(this->out._sed, this->out.alt_buf);
|
|
this->out.alt_buf = (void*)tmp;
|
|
this->out.alt_sed = this->out._sed;
|
|
}
|
|
return tmp;
|
|
}
|
|
else
|
|
{
|
|
if (len) *len = 0;
|
|
return HAWK_UT("");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const hawk_bch_t* SedStd::StringStream::getOutputB (hawk_oow_t* len)
|
|
{
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
if (this->out.inited)
|
|
{
|
|
hawk_bch_t* tmp = hawk_gem_duputobcharswithcmgr(hawk_sed_getgem(this->out._sed), HAWK_OOECS_PTR(&this->out.buf), HAWK_OOECS_LEN(&this->out.buf), len, this->cmgr);
|
|
if (tmp)
|
|
{
|
|
if (this->out.alt_buf) hawk_sed_freemem(this->out._sed, this->out.alt_buf);
|
|
this->out.alt_buf = (void*)tmp;
|
|
this->out.alt_sed = this->out._sed;
|
|
}
|
|
return tmp;
|
|
}
|
|
else
|
|
{
|
|
if (len) *len = 0;
|
|
return HAWK_BT("");
|
|
}
|
|
#else
|
|
return this->getOutput(len);
|
|
#endif
|
|
}
|
|
|
|
void SedStd::StringStream::clearInputData ()
|
|
{
|
|
if (this->in.str)
|
|
{
|
|
hawk_sed_freemem (this->in._sed, this->in.str);
|
|
this->in.str = HAWK_NULL;
|
|
this->in.end = HAWK_NULL;
|
|
this->in.ptr = HAWK_NULL;
|
|
this->in._sed = HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
void SedStd::StringStream::clearOutputData (bool kill_ecb)
|
|
{
|
|
if (this->out.alt_buf)
|
|
{
|
|
HAWK_ASSERT (this->out.alt_sed != HAWK_NULL);
|
|
hawk_sed_freemem(this->out.alt_sed, this->out.alt_buf);
|
|
this->out.alt_buf = HAWK_NULL;
|
|
this->out.alt_sed = HAWK_NULL;
|
|
}
|
|
|
|
if (this->out.inited)
|
|
{
|
|
hawk_ooecs_fini (&this->out.buf);
|
|
if (this->out.alt_buf)
|
|
{
|
|
hawk_sed_freemem (this->out._sed, this->out.alt_buf);
|
|
this->out.alt_buf = HAWK_NULL;
|
|
}
|
|
this->out.inited = false;
|
|
|
|
if (kill_ecb && this->out.sed_ecb.next)
|
|
hawk_sed_killecb (this->out._sed, &this->out.sed_ecb);
|
|
|
|
this->out._sed = HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
void SedStd::StringStream::on_sed_close (hawk_sed_t* sed, void* ctx)
|
|
{
|
|
SedStd::StringStream* strm = (SedStd::StringStream*)ctx;
|
|
strm->clearOutputData (false);
|
|
}
|
|
|
|
/////////////////////////////////
|
|
HAWK_END_NAMESPACE(HAWK)
|
|
/////////////////////////////////
|