/*
* opencog/atoms/sensory-v0/TextFileStream.cc
*
* Copyright (C) 2020 Linas Vepstas
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License v3 as
* published by the Free Software Foundation and including the exceptions
* at http://opencog.org/wiki/Licenses
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to:
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
#include <string.h> // for strerror()
#include <opencog/util/exceptions.h>
#include <opencog/util/oc_assert.h>
#include <opencog/atoms/base/Node.h>
#include <opencog/atoms/value/StringValue.h>
#include <opencog/atoms/value/ValueFactory.h>
#include <opencog/sensory-v0/types/atom_types.h>
#include "TextFileStream.h"
using namespace opencog;
TextFileStream::TextFileStream(Type t, const std::string& str)
: OutputStream(t)
{
OC_ASSERT(nameserver().isA(_type, TEXT_FILE_STREAM),
"Bad TextFileStream constructor!");
init(str);
}
TextFileStream::TextFileStream(const std::string& str)
: OutputStream(TEXT_FILE_STREAM)
{
init(str);
}
TextFileStream::TextFileStream(const Handle& senso)
: OutputStream(TEXT_FILE_STREAM)
{
if (SENSORY_NODE != senso->get_type())
throw RuntimeException(TRACE_INFO,
"Expecting SensoryNode, got %s\n", senso->to_string().c_str());
init(senso->get_name());
}
TextFileStream::~TextFileStream()
{
if (_fh)
fclose (_fh);
}
/// Attempt to open the URL for reading and writing.
/// The URL format is described in
/// https://en.wikipedia.org/wiki/File_URI_scheme
/// and we adhere to that.
///
/// URI formats are:
/// file:/path ; Not currently supported
/// file:///path ; Yes, use this
/// file://host/path ; Not currently supported
/// file://./path ; Dot means localhost
///
/// Possible extensions:
/// file:mode//...
/// where mode is one of the modes described in `man 3 fopen`
void TextFileStream::init(const std::string& url)
{
_fresh = true;
_fh = nullptr;
if (0 != url.compare(0, 8, "file:///"))
throw RuntimeException(TRACE_INFO,
"Unsupported URL \"%s\"\n", url.c_str());
// Make a copy, for debuggingg purposes.
_uri = url;
// Ignore the first 7 chars "file://"
const char* fpath = url.substr(7).c_str();
_fh = fopen(fpath, "a+");
if (nullptr == _fh)
{
int norr = errno;
char buff[80];
buff[0] = 0;
// Apparently, we are getting the Gnu version of strerror_r
// and not the XSI version. I suppose it doesn't matter.
char * ers = strerror_r(norr, buff, 80);
throw RuntimeException(TRACE_INFO,
"Unable to open URL \"%s\"\nError was \"%s\"\n",
url.c_str(), ers);
}
}
// ==============================================================
ValuePtr TextFileStream::describe(AtomSpace* as, bool silent)
{
throw RuntimeException(TRACE_INFO, "Not implemeneted");
return Handle::UNDEFINED;
}
// ==============================================================
// This will read one line from the file stream, and return that line.
// So, a line-oriented, buffered interface. For now.
void TextFileStream::update() const
{
if (nullptr == _fh) { _value.clear(); return; }
// The very first call after opening a file will typically
// be a bogus update, so as to give the caller something,
// anything. There will be trouble down the line, when
// actually reading. So first time through, return the URL
if (_fresh)
{
_fresh = false;
_value.resize(1);
_value[0] = createNode(ITEM_NODE, _uri);
return;
}
#define BUFSZ 4080
char buff[BUFSZ];
char* rd = fgets(buff, BUFSZ, _fh);
if (nullptr == rd)
{
fclose(_fh);
_fh = nullptr;
_value.clear();
return;
}
_value.resize(1);
_value[0] = createNode(ITEM_NODE, buff);
}
// ==============================================================
// Write stuff to a file.
void TextFileStream::do_write(const std::string& str)
{
fprintf(_fh, "%s", str.c_str());
}
// Write stuff to a file.
ValuePtr TextFileStream::write_out(AtomSpace* as, bool silent,
const Handle& cref)
{
if (nullptr == _fh)
throw RuntimeException(TRACE_INFO,
"Text stream not open: URI \"%s\"\n", _uri.c_str());
return do_write_out(as, silent, cref);
}
// ==============================================================
// Adds factory when library is loaded.
DEFINE_VALUE_FACTORY(TEXT_FILE_STREAM, createTextFileStream, std::string)
DEFINE_VALUE_FACTORY(TEXT_FILE_STREAM, createTextFileStream, Handle)
// ====================================================================
void opencog_sensory_v0_filedir_init(void)
{
// Force shared lib ctors to run
};