+++ /dev/null
-#include "base.h"
-#include "poll.h"
-
-using namespace std;
-
-namespace Msp {
-namespace IO {
-
-Base::Base():
- mode(M_READ),
- events(P_NONE),
- eof_flag(false)
-{ }
-
-Base::~Base()
-{
- signal_deleted.emit();
-}
-
-bool Base::getline(string &line)
-{
- line.clear();
-
- if(eof_flag)
- return false;
-
- while(1)
- {
- int c = get();
- if(c==-1 || c=='\n')
- break;
- line += c;
- }
-
- return !eof_flag || !line.empty();
-}
-
-int Base::get()
-{
- char c;
- if(do_read(&c, 1)==0)
- return -1;
- return static_cast<unsigned char>(c);
-}
-
-void Base::set_events(PollEvent e)
-{
- events = e;
- signal_events_changed.emit(events);
-}
-
-void Base::event(PollEvent ev)
-{
- if(ev&P_INPUT)
- signal_data_available.emit();
-
- on_event(ev);
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_BASE_H_
-#define MSP_IO_BASE_H_
-
-#include <sigc++/sigc++.h>
-#include "mode.h"
-#include "poll.h"
-#include "types.h"
-
-namespace Msp {
-namespace IO {
-
-/**
-Common interface for all I/O objects.
-
-A derived class must call set_events(P_NONE) before it is destroyed to avoid
-leaving stale pointers in an EventDispatcher.
-*/
-class Base
-{
-public:
- /** Emitted when there is data available for reading. If all data is not
- read, the signal is emitted again immediately. */
- sigc::signal<void> signal_data_available;
-
- /** Emitted when there is no more data to be read. */
- sigc::signal<void> signal_end_of_file;
-
- /** Emitted when there is a nonlinearity in I/O (such as a file being
- seeked) and any data buffered by upper layers needs to be flushed. */
- sigc::signal<void> signal_flush_required;
-
- /** Emitted when the I/O object has closed. */
- sigc::signal<void> signal_closed;
-
- /** Emitted when the mask of interesting events changes. Mainly for use by
- EventDispatcher. */
- sigc::signal<void, PollEvent> signal_events_changed;
-
- /** Emitted when the object is deleted. Mainly for use by
- EventDispatcher. */
- sigc::signal<void> signal_deleted;
-
-protected:
- Mode mode;
- PollEvent events;
- bool eof_flag;
-
- Base();
-private:
- Base(const Base &);
- Base &operator=(const Base &);
-public:
- virtual ~Base();
-
- /** Sets blocking mode. When blocking is enabled, most operations won't
- return until they can make progress. When blocking is disabled, these
- operations may return immediately with a return code indicating that nothing
- was done.
-
- Blocking is enabled by default. */
- virtual void set_block(bool) { }
-
- /** Returns the current mode flags. */
- Mode get_mode() const { return mode; }
-
-protected:
- virtual unsigned do_write(const char *, unsigned) =0;
- virtual unsigned do_read(char *, unsigned) =0;
-
-public:
- /** Writes data from a buffer. Subject to blocking. Returns the number of
- bytes written, which may be zero for a non-blockin operation. */
- unsigned write(const char *b, unsigned c) { return do_write(b, c); }
-
- /** Writes a string. This is identical to calling
- write(s.data(), s.size()). */
- unsigned write(const std::string &s) { return do_write(s.data(), s.size()); }
-
- /** Writes a single character. This is identical to calling
- write(&c, 1). */
- virtual unsigned put(char c) { return do_write(&c, 1); }
-
- /** Reads data into a buffer. Subject to blocking. Returns the number of
- bytes read, which may be zero for a non-blocking operation. */
- unsigned read(char *b, unsigned c) { return do_read(b, c); }
-
- /** Reads characters up to the next linefeed or end-of-file. The linefeed
- is not included in the line. Returns true if a line was successfully read,
- false otherwise. */
- virtual bool getline(std::string &);
-
- /** Reads a single character. Returns -1 if no character was available due
- to end-of-file or non-blocking operation. */
- virtual int get();
-
- /** Returns the end-of-file flag. */
- bool eof() const { return eof_flag; }
-
-protected:
- void set_events(PollEvent);
-
-public:
- /** Returns a mask of the currently interesting events. Used by
- EventDispatcher. */
- PollEvent get_events() const { return events; }
-
- /** Returns a handle for polling. Should throw if the object does not have
- an event handle. */
- virtual Handle get_event_handle() =0;
-
- /** Notifies the object of an event. Used by EventDispatcher. */
- void event(PollEvent);
-
-protected:
- virtual void on_event(PollEvent) { }
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#include <cstring>
-#include "buffered.h"
-#include "except.h"
-
-using namespace std;
-
-namespace Msp {
-namespace IO {
-
-Buffered::Buffered(Base &b, unsigned s):
- below(b),
- buf_size(s),
- buf(new char[buf_size]),
- begin(buf),
- end(buf),
- cur_op(M_NONE)
-{
- mode = below.get_mode();
- below.signal_flush_required.connect(sigc::mem_fun(this, &Buffered::flush));
-}
-
-Buffered::~Buffered()
-{
- try
- {
- flush();
- }
- catch(...)
- { }
-
- delete[] buf;
-}
-
-void Buffered::flush()
-{
- if(cur_op==M_WRITE)
- {
- unsigned used = end-begin;
- if(used)
- {
- unsigned len = below.write(begin, used);
-
- begin=end = buf;
-
- if(len<used)
- throw Exception("Couldn't flush all data");
- }
- }
- else if(cur_op==M_READ)
- begin=end = buf;
-}
-
-unsigned Buffered::do_write(const char *data, unsigned size)
-{
- set_op(M_WRITE);
-
- if(end+size<buf+buf_size)
- {
- // All data fits in buffer with whatever is already there
- memcpy(end, data, size);
- end += size;
-
- return size;
- }
- else
- {
- // Clear the buffer to make more room
- flush();
-
- if(size<buf_size)
- {
- // Put new data in the buffer to wait for more
- memcpy(end, data, size);
- end += size;
-
- return size;
- }
- else
- // New data still doesn't fit in the buffer, so write it directly
- return below.write(data, size);
- }
-}
-
-unsigned Buffered::do_read(char *data, unsigned size)
-{
- set_op(M_READ);
-
- if(begin+size<=end)
- {
- // The request can be served from the buffer
- memcpy(data, begin, size);
- begin += size;
-
- eof_flag = (below.eof() && begin==end);
-
- return size;
- }
- else
- {
- // Give out whatever is in the buffer already
- memcpy(data, begin, end-begin);
- unsigned ret = end-begin;
- begin=end = buf;
-
- data += ret;
- size -= ret;
-
- if(size<buf_size)
- {
- // Fill the buffer and serve the rest of the request from it
- unsigned len = below.read(end, buf+buf_size-end);
- end += len;
-
- len = min(static_cast<unsigned>(end-begin), size);
- memcpy(data, begin, len);
- begin += len;
- ret += len;
- }
- else
- // Read the rest directly from the underlying object
- ret += below.read(data, size);
-
- eof_flag = (below.eof() && begin==end);
-
- return ret;
- }
-}
-
-unsigned Buffered::put(char c)
-{
- set_op(M_WRITE);
-
- if(end<buf+buf_size)
- {
- *end++ = c;
- return 1;
- }
- else
- return do_write(&c, 1);
-}
-
-bool Buffered::getline(std::string &line)
-{
- set_op(M_READ);
-
- for(char *i=begin; i!=end; ++i)
- if(*i=='\n')
- {
- line.assign(begin, i-begin);
- begin = i+1;
- return true;
- }
-
- return Base::getline(line);
-}
-
-int Buffered::get()
-{
- set_op(M_READ);
-
- if(begin<end)
- return static_cast<unsigned char>(*begin++);
-
- char c;
- if(do_read(&c, 1)==0)
- return -1;
- return static_cast<unsigned char>(c);
-}
-
-void Buffered::set_op(Mode op)
-{
- if(op!=cur_op)
- flush();
- cur_op = op;
-}
-
-unsigned Buffered::get_current_size() const
-{
- return end-begin;
-}
-
-Handle Buffered::get_event_handle()
-{
- throw Exception("Buffered doesn't support events");
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_BUFFERED_H_
-#define MSP_IO_BUFFERED_H_
-
-#include "base.h"
-
-namespace Msp {
-namespace IO {
-
-class Buffered: public Base
-{
-private:
- Base &below;
- unsigned buf_size;
- char *buf;
- char *begin;
- char *end;
- Mode cur_op;
-
-public:
- Buffered(Base &, unsigned =8192);
- ~Buffered();
-
- void flush();
-
-protected:
- unsigned do_write(const char *, unsigned);
- unsigned do_read(char *, unsigned);
-public:
- unsigned put(char);
-
- bool getline(std::string &);
- int get();
-
-private:
- void set_op(Mode);
-public:
- Mode get_current_op() const { return cur_op; }
- unsigned get_current_size() const;
-
- virtual Handle get_event_handle();
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef WIN32
-#include <errno.h>
-#include <fcntl.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#endif
-#include <msp/core/except.h>
-#include "console.h"
-
-namespace {
-
-#ifndef WIN32
-termios orig_attr;
-#endif
-
-} // namespace
-
-namespace Msp {
-namespace IO {
-
-Console::Console(unsigned n)
-{
- if(n>2)
- throw InvalidParameterValue("Invalid parameter for Console::Console");
-
- mode = (n==0 ? M_READ : M_WRITE);
-
-#ifdef WIN32
- switch(n)
- {
- case 0: handle = GetStdHandle(STD_INPUT_HANDLE); break;
- case 1: handle = GetStdHandle(STD_OUTPUT_HANDLE); break;
- case 2: handle = GetStdHandle(STD_ERROR_HANDLE); break;
- }
-#else
- handle = n;
-
- if(handle==0)
- tcgetattr(handle, &orig_attr);
-#endif
-
- if(n==0)
- set_events(P_INPUT);
-}
-
-Console::~Console()
-{
-#ifndef WIN32
- if(handle==0)
- tcsetattr(handle, TCSADRAIN, &orig_attr);
-#endif
-}
-
-void Console::set_block(bool b)
-{
-#ifdef WIN32
- // XXX Dunno how to do this in win32
- (void)b;
-#else
- int flags = fcntl(0, F_GETFL);
- flags = (flags&~O_NONBLOCK) | (b?0:O_NONBLOCK);
- fcntl(0, F_SETFL, flags);
-#endif
-}
-
-void Console::set_local_echo(bool e)
-{
- if(!(mode&M_READ))
- throw InvalidState("Local echo can only be set on input console");
-
-#ifdef WIN32
- DWORD m;
- GetConsoleMode(handle, &m);
- SetConsoleMode(handle, (m&~ENABLE_ECHO_INPUT) | (e?ENABLE_ECHO_INPUT:0));
-#else
- termios t;
- tcgetattr(0, &t);
- t.c_lflag = (t.c_lflag&~ECHO) | (e?ECHO:0);
- tcsetattr(0, TCSADRAIN, &t);
-#endif
-}
-
-void Console::set_line_buffer(bool l)
-{
- if(!(mode&M_READ))
- throw InvalidState("Line buffering can only be set on input console");
-
-#ifdef WIN32
- DWORD m;
- GetConsoleMode(handle, &m);
- SetConsoleMode(handle, (m&~ENABLE_LINE_INPUT) | (l?ENABLE_LINE_INPUT:0));
-#else
- // XXX ICANON does more than just set line buffering, may need a bit more thought
- termios t;
- tcgetattr(0, &t);
- t.c_lflag = (t.c_lflag&~ICANON) | (l?ICANON:0);
- tcsetattr(0, TCSADRAIN, &t);
-#endif
-}
-
-void Console::get_size(unsigned &rows, unsigned &cols)
-{
- if(!(mode&M_WRITE))
- throw InvalidState("Size can only be queried from an output console");
-
-#ifdef WIN32
- // XXX Figure out how to do this
- rows = 24;
- cols = 80;
-#else
- struct winsize wsz;
- ioctl(handle, TIOCGWINSZ, &wsz);
- rows = wsz.ws_row;
- cols = wsz.ws_col;
-#endif
-}
-
-unsigned Console::do_write(const char *buf, unsigned len)
-{
- if(!(mode&M_WRITE))
- throw InvalidState("Console is not writable");
-
-#ifdef WIN32
- DWORD ret;
- if(!WriteFile(handle, buf, len, &ret, 0))
- throw SystemError("Writing to console failed", GetLastError());
-#else
- int ret = ::write(handle, buf, len);
- if(ret==-1)
- throw SystemError("Writing to console failed", errno);
-#endif
-
- return ret;
-}
-
-unsigned Console::do_read(char *buf, unsigned len)
-{
- if(!(mode&M_READ))
- throw InvalidState("Console is not readable");
-
-#ifdef WIN32
- DWORD ret;
- if(!ReadFile(handle, buf, len, &ret, 0))
- throw SystemError("Reading from console failed", GetLastError());
-#else
- int ret = ::read(handle, buf, len);
- if(ret==-1)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Reading from console failed", errno);
- }
- else if(ret==0)
- eof_flag = true;
-#endif
-
- return ret;
-}
-
-Handle Console::get_event_handle()
-{
- return 0;
-}
-
-Console &Console::instance(unsigned n)
-{
- static Console in(0);
- static Console out(1);
- static Console err(2);
-
- switch(n)
- {
- case 0: return in;
- case 1: return out;
- case 2: return err;
- }
-
- throw InvalidParameterValue("Unknown Console instance requested");
-}
-
-Console &cin = Console::instance(0);
-Console &cout = Console::instance(1);
-Console &cerr = Console::instance(2);
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_CONSOLE_H_
-#define MSP_IO_CONSOLE_H_
-
-#include "base.h"
-
-namespace Msp {
-namespace IO {
-
-/**
-Provides access to standard input, output and error streams. This class can't
-be instantiated directly - use one of the cin, cout and cerr references
-instead.
-*/
-class Console: public Base
-{
-private:
- Handle handle;
-
- Console(unsigned);
-public:
- ~Console();
-
- virtual void set_block(bool);
-
- /** If local echo is enabled, characters will appear on the console as they
- are typed. Can only be used on an input Console. */
- void set_local_echo(bool);
-
- /** If line buffering is enabled, input will only be available when a
- newline is encountered. On some systems, this may also enable line editing.
- Can only be used on an input Console.
- */
- void set_line_buffer(bool);
-
- /** Retrieves the size of the Console. Can only be used on an output
- Console. */
- void get_size(unsigned &rows, unsigned &cols);
-
-protected:
- virtual unsigned do_write(const char *, unsigned);
- virtual unsigned do_read(char *, unsigned);
-
-public:
- virtual Handle get_event_handle();
-
- static Console &instance(unsigned);
-};
-
-extern Console &cin;
-extern Console &cout;
-extern Console &cerr;
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#include <msp/time/units.h>
-#include "base.h"
-#include "eventdispatcher.h"
-#include "poll.h"
-
-namespace Msp {
-namespace IO {
-
-EventDispatcher::EventDispatcher()
-{ }
-
-void EventDispatcher::add(Base &obj)
-{
- SlotMap::iterator i = objects.find(&obj);
- if(i==objects.end())
- {
- i = objects.insert(SlotMap::value_type(&obj, Slot(&obj))).first;
- i->second.evch_conn = obj.signal_events_changed.connect(sigc::bind(sigc::mem_fun(this, &EventDispatcher::object_events_changed), &obj));
- i->second.del_conn = obj.signal_deleted.connect(sigc::bind(sigc::mem_fun(this, &EventDispatcher::object_deleted), &obj));
-
- if(obj.get_events())
- poller.set_object(obj, obj.get_events());
- }
-}
-
-void EventDispatcher::remove(Base &obj)
-{
- SlotMap::iterator i = objects.find(&obj);
- if(i!=objects.end())
- {
- i->second.evch_conn.disconnect();
- i->second.del_conn.disconnect();
- objects.erase(i);
-
- poller.set_object(obj, P_NONE);
- }
-}
-
-void EventDispatcher::tick()
-{
- if(objects.empty())
- return;
-
- if(poller.poll()>0)
- dispatch();
-}
-
-void EventDispatcher::tick(const Time::TimeDelta &dt)
-{
- if(objects.empty())
- return;
-
- if(poller.poll(dt)>0)
- dispatch();
-}
-
-void EventDispatcher::object_events_changed(PollEvent ev, Base *obj)
-{
- poller.set_object(*obj, ev);
-}
-
-void EventDispatcher::object_deleted(Base *obj)
-{
- remove(*obj);
-}
-
-void EventDispatcher::dispatch()
-{
- const Poller::SlotSeq &result = poller.get_result();
- for(Poller::SlotSeq::const_iterator i=result.begin(); i!=result.end(); ++i)
- i->object->event(i->events);
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef EVENTDISPATCHER_H_
-#define EVENTDISPATCHER_H_
-
-#include <sigc++/connection.h>
-#include <sigc++/trackable.h>
-#include "poll.h"
-
-namespace Msp {
-namespace IO {
-
-/**
-Put your I/O objects inside one of these to get signaled when something happens
-on some of them.
-*/
-class EventDispatcher: public sigc::trackable
-{
-private:
- struct Slot
- {
- Base *obj;
- sigc::connection evch_conn;
- sigc::connection del_conn;
-
- Slot(Base *o): obj(o) { }
- };
-
- typedef std::map<Base *, Slot> SlotMap;
-
- Poller poller;
- SlotMap objects;
-
-public:
- EventDispatcher();
-
- void add(Base &);
- void remove(Base &);
-
- /** Checks for and dispatches events. If there are no events available,
- blocks until there are. */
- void tick();
-
- /** Checks for and dispatches events. If there are no events available,
- waits at most the specified time before returning. */
- void tick(const Time::TimeDelta &);
-
-private:
- void object_events_changed(PollEvent, Base *);
- void object_deleted(Base *);
- void dispatch();
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef MSP_IO_EXCEPT_H_
-#define MSP_IO_EXCEPT_H_
-
-#include <msp/core/except.h>
-
-namespace Msp {
-namespace IO {
-
-class FileNotFound: public Exception
-{
-public:
- FileNotFound(const std::string &w_, const std::string &f): Exception(w_), filename(f) { }
- const std::string &get_filename() { return filename; }
- ~FileNotFound() throw() { }
-private:
- std::string filename;
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef WIN32
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif
-#include <msp/strings/formatter.h>
-#include "except.h"
-#include "file.h"
-
-using namespace std;
-
-namespace Msp {
-namespace IO {
-
-File::File(const string &fn, Mode m, CreateMode cm)
-{
- if(!(m&M_RDWR))
- throw InvalidParameterValue("Invalid read/write mode");
- if(cm&~(C_CREATE|C_TRUNCATE))
- throw InvalidParameterValue("Invalid create mode");
-
- mode = m;
-
-#ifdef WIN32
- int flags = 0;
- int create_flags = OPEN_EXISTING;
-
- if(mode&M_READ)
- flags |= GENERIC_READ;
- else if(mode&M_WRITE)
- {
- flags |= GENERIC_WRITE;
-
- switch(static_cast<int>(cm))
- {
- case C_NONE: create_flags = OPEN_EXISTING; break;
- case C_CREATE: create_flags = OPEN_ALWAYS; break;
- case C_TRUNCATE: create_flags = TRUNCATE_EXISTING; break;
- case C_CREATE+C_TRUNCATE: create_flags = CREATE_ALWAYS; break;
- }
- }
-
- handle = CreateFile(fn.c_str(), flags, 0, 0, create_flags, FILE_ATTRIBUTE_NORMAL, 0);
- if(handle==INVALID_HANDLE_VALUE)
- {
- int err = GetLastError();
- if(err==ERROR_FILE_NOT_FOUND)
- throw FileNotFound("Can't find file "+fn, fn);
- else
- throw SystemError(format("Can't open file '%s'", fn), GetLastError());
- }
-#else
- int flags = 0;
- switch(mode&M_RDWR)
- {
- case M_READ: flags |= O_RDONLY; break;
- case M_WRITE: flags |= O_WRONLY; break;
- case M_RDWR: flags |= O_RDWR; break;
- default:;
- }
-
- if(mode&M_WRITE)
- {
- if(cm&C_CREATE)
- flags |= O_CREAT;
- if(cm&C_TRUNCATE)
- flags |= O_TRUNC;
- }
- if(mode&M_APPEND)
- flags |= O_APPEND;
- if(mode&M_NONBLOCK)
- flags |= O_NONBLOCK;
-
- handle = ::open(fn.c_str(), flags, 0666);
- if(handle==-1)
- {
- int err = errno;
- if(err==ENOENT)
- throw FileNotFound("Can't find file "+fn, fn);
- else
- throw SystemError(format("Can't open file '%s'", fn), err);
- }
-#endif
-
- set_events(P_INPUT);
-}
-
-File::~File()
-{
- close();
-}
-
-void File::close()
-{
- if(handle==MSP_IO_INVALID_HANDLE)
- return;
-
- set_events(P_NONE);
-
- signal_flush_required.emit();
-
-#ifdef WIN32
- CloseHandle(handle);
-#else
- ::close(handle);
-#endif
-
- handle = MSP_IO_INVALID_HANDLE;
- signal_closed.emit();
-}
-
-void File::set_block(bool b)
-{
- check_access(M_NONE);
-
- mode = (mode&~M_NONBLOCK);
- if(b)
- mode = (mode|M_NONBLOCK);
-#ifndef WIN32
- int flags = fcntl(handle, F_GETFD);
- fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
-#endif
-}
-
-unsigned File::do_write(const char *buf, unsigned size)
-{
- check_access(M_WRITE);
-
- if(size==0)
- return 0;
-
-#ifdef WIN32
- if(mode&M_APPEND)
- seek(0, S_END);
- DWORD ret;
- if(WriteFile(handle, buf, size, &ret, 0)==0)
- throw SystemError("Writing to file failed", GetLastError());
-#else
- int ret = ::write(handle, buf, size);
- if(ret==-1)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Writing to file failed", errno);
- }
-#endif
-
- return ret;
-}
-
-unsigned File::do_read(char *buf, unsigned size)
-{
- check_access(M_READ);
-
- if(size==0)
- return 0;
-
-#ifdef WIN32
- DWORD ret;
- if(ReadFile(handle, buf, size, &ret, 0)==0)
- throw SystemError("Reading from file failed", GetLastError());
-#else
- int ret = ::read(handle, buf, size);
- if(ret==-1)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Reading from file failed", errno);
- }
-#endif
-
- if(ret==0)
- {
- eof_flag = true;
- signal_end_of_file.emit();
- }
-
- return ret;
-}
-
-void File::sync()
-{
-#ifndef WIN32
- signal_flush_required.emit();
-
- fsync(handle);
-#endif
-}
-
-int File::seek(int off, SeekType st)
-{
- check_access(M_NONE);
-
- signal_flush_required.emit();
-
- int type = sys_seek_type(st);
-#ifdef WIN32
- DWORD ret = SetFilePointer(handle, off, 0, type);
- if(ret==INVALID_SET_FILE_POINTER)
- throw SystemError("Seek failed", GetLastError());
-#else
- int ret = lseek(handle, off, type);
- if(ret==-1)
- throw SystemError("Seek failed", errno);
-#endif
-
- eof_flag = false;
-
- return ret;
-}
-
-int File::tell() const
-{
- check_access(M_NONE);
-
-#ifdef WIN32
- DWORD ret = SetFilePointer(handle, 0, 0, FILE_CURRENT);
- if(ret==INVALID_SET_FILE_POINTER)
- throw SystemError("Tell failed", GetLastError());
-#else
- int ret = lseek(handle, 0, SEEK_CUR);
- if(ret==-1)
- throw SystemError("Tell failed", errno);
-#endif
-
- return ret;
-}
-
-void File::check_access(Mode m) const
-{
- if(handle==MSP_IO_INVALID_HANDLE)
- throw InvalidState("File is not open");
- if(m==M_READ && !(mode&M_READ))
- throw InvalidState("File is not readable");
- if(m==M_WRITE && !(mode&M_WRITE))
- throw InvalidState("File is not writable");
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_FILE_H_
-#define MSP_IO_FILE_H_
-
-#include <string>
-#include "base.h"
-#include "buffered.h"
-#include "filtered.h"
-#include "seek.h"
-
-namespace Msp {
-namespace IO {
-
-/**
-A class for reading and writing files.
-
-Non-blocking mode is not supported on Win32.
-*/
-class File: public Base
-{
-public:
- enum CreateMode
- {
- C_NONE = 0,
- C_CREATE = 1,
- C_TRUNCATE = 2
- };
-
-private:
- Handle handle;
-
-public:
- /** Creates a new file object and opens it. If the create flag is set and
- write access is requested and the file does exist, it is created. Otherwise
- a missing file is an error. */
- File(const std::string &, Mode = M_READ, CreateMode = CreateMode(C_CREATE+C_TRUNCATE));
- virtual ~File();
-
- /** Closes the file. Any attempt to access the file after this will cause
- an exception to be thrown. */
- void close();
-
- void set_block(bool);
-
-protected:
- virtual unsigned do_write(const char *, unsigned);
- virtual unsigned do_read(char *, unsigned);
-
-public:
- virtual void sync();
-
- /** Changes the read/write offset of the file. Returns the new offset. */
- virtual int seek(int, SeekType);
-
- /** Returns the current read/write offset of the file. */
- virtual int tell() const;
-
- virtual Handle get_event_handle() { return handle; }
-
-private:
- void check_access(Mode) const;
-};
-
-inline File::CreateMode operator|(File::CreateMode m, File::CreateMode n)
-{ return File::CreateMode(static_cast<int>(m)|static_cast<int>(n)); }
-
-inline File::CreateMode operator&(File::CreateMode m, File::CreateMode n)
-{ return File::CreateMode(static_cast<int>(m)&static_cast<int>(n)); }
-
-inline File::CreateMode operator~(File::CreateMode m)
-{ return File::CreateMode(~static_cast<int>(m)); }
-
-typedef Filtered<File, Buffered> BufferedFile;
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef MSP_IO_FILTERED_H_
-#define MSP_IO_FILTERED_H_
-
-namespace Msp {
-namespace IO {
-
-template<typename B, typename F>
-class Filtered: public B
-{
-private:
- struct Activator
- {
- Filtered &f;
-
- Activator(Filtered &f_): f(f_) { f.active = true; }
- ~Activator() { f.active = false; }
- };
-
- F filter;
- bool active;
-
-public:
- Filtered(): filter(*this), active(false) { }
- ~Filtered() { active = true; }
-
- template<typename A0>
- Filtered(A0 a0): B(a0), filter(*this), active(false) { }
-
- template<typename A0, typename A1>
- Filtered(A0 a0, A1 a1): B(a0, a1), filter(*this), active(false) { }
-
-protected:
- virtual unsigned do_write(const char *b, unsigned s)
- {
- if(!active)
- {
- Activator a(*this);
- return filter.write(b, s);
- }
- else
- return B::do_write(b, s);
- }
-
- virtual unsigned do_read(char *b, unsigned s)
- {
- if(!active)
- {
- Activator a(*this);
- return filter.read(b, s);
- }
- else
- return B::do_read(b, s);
- }
-
-public:
- virtual unsigned put(char c) { return filter.put(c); }
- virtual bool getline(std::string &l) { return filter.getline(l); }
- virtual int get() { return filter.get(); }
-
- F &get_filter() { return filter; }
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
--- /dev/null
+#include "base.h"
+#include "poll.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+Base::Base():
+ mode(M_READ),
+ events(P_NONE),
+ eof_flag(false)
+{ }
+
+Base::~Base()
+{
+ signal_deleted.emit();
+}
+
+bool Base::getline(string &line)
+{
+ line.clear();
+
+ if(eof_flag)
+ return false;
+
+ while(1)
+ {
+ int c = get();
+ if(c==-1 || c=='\n')
+ break;
+ line += c;
+ }
+
+ return !eof_flag || !line.empty();
+}
+
+int Base::get()
+{
+ char c;
+ if(do_read(&c, 1)==0)
+ return -1;
+ return static_cast<unsigned char>(c);
+}
+
+void Base::set_events(PollEvent e)
+{
+ events = e;
+ signal_events_changed.emit(events);
+}
+
+void Base::event(PollEvent ev)
+{
+ if(ev&P_INPUT)
+ signal_data_available.emit();
+
+ on_event(ev);
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_BASE_H_
+#define MSP_IO_BASE_H_
+
+#include <sigc++/sigc++.h>
+#include "mode.h"
+#include "poll.h"
+#include "types.h"
+
+namespace Msp {
+namespace IO {
+
+/**
+Common interface for all I/O objects.
+
+A derived class must call set_events(P_NONE) before it is destroyed to avoid
+leaving stale pointers in an EventDispatcher.
+*/
+class Base
+{
+public:
+ /** Emitted when there is data available for reading. If all data is not
+ read, the signal is emitted again immediately. */
+ sigc::signal<void> signal_data_available;
+
+ /** Emitted when there is no more data to be read. */
+ sigc::signal<void> signal_end_of_file;
+
+ /** Emitted when there is a nonlinearity in I/O (such as a file being
+ seeked) and any data buffered by upper layers needs to be flushed. */
+ sigc::signal<void> signal_flush_required;
+
+ /** Emitted when the I/O object has closed. */
+ sigc::signal<void> signal_closed;
+
+ /** Emitted when the mask of interesting events changes. Mainly for use by
+ EventDispatcher. */
+ sigc::signal<void, PollEvent> signal_events_changed;
+
+ /** Emitted when the object is deleted. Mainly for use by
+ EventDispatcher. */
+ sigc::signal<void> signal_deleted;
+
+protected:
+ Mode mode;
+ PollEvent events;
+ bool eof_flag;
+
+ Base();
+private:
+ Base(const Base &);
+ Base &operator=(const Base &);
+public:
+ virtual ~Base();
+
+ /** Sets blocking mode. When blocking is enabled, most operations won't
+ return until they can make progress. When blocking is disabled, these
+ operations may return immediately with a return code indicating that nothing
+ was done.
+
+ Blocking is enabled by default. */
+ virtual void set_block(bool) { }
+
+ /** Returns the current mode flags. */
+ Mode get_mode() const { return mode; }
+
+protected:
+ virtual unsigned do_write(const char *, unsigned) =0;
+ virtual unsigned do_read(char *, unsigned) =0;
+
+public:
+ /** Writes data from a buffer. Subject to blocking. Returns the number of
+ bytes written, which may be zero for a non-blockin operation. */
+ unsigned write(const char *b, unsigned c) { return do_write(b, c); }
+
+ /** Writes a string. This is identical to calling
+ write(s.data(), s.size()). */
+ unsigned write(const std::string &s) { return do_write(s.data(), s.size()); }
+
+ /** Writes a single character. This is identical to calling
+ write(&c, 1). */
+ virtual unsigned put(char c) { return do_write(&c, 1); }
+
+ /** Reads data into a buffer. Subject to blocking. Returns the number of
+ bytes read, which may be zero for a non-blocking operation. */
+ unsigned read(char *b, unsigned c) { return do_read(b, c); }
+
+ /** Reads characters up to the next linefeed or end-of-file. The linefeed
+ is not included in the line. Returns true if a line was successfully read,
+ false otherwise. */
+ virtual bool getline(std::string &);
+
+ /** Reads a single character. Returns -1 if no character was available due
+ to end-of-file or non-blocking operation. */
+ virtual int get();
+
+ /** Returns the end-of-file flag. */
+ bool eof() const { return eof_flag; }
+
+protected:
+ void set_events(PollEvent);
+
+public:
+ /** Returns a mask of the currently interesting events. Used by
+ EventDispatcher. */
+ PollEvent get_events() const { return events; }
+
+ /** Returns a handle for polling. Should throw if the object does not have
+ an event handle. */
+ virtual Handle get_event_handle() =0;
+
+ /** Notifies the object of an event. Used by EventDispatcher. */
+ void event(PollEvent);
+
+protected:
+ virtual void on_event(PollEvent) { }
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <cstring>
+#include "buffered.h"
+#include "except.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+Buffered::Buffered(Base &b, unsigned s):
+ below(b),
+ buf_size(s),
+ buf(new char[buf_size]),
+ begin(buf),
+ end(buf),
+ cur_op(M_NONE)
+{
+ mode = below.get_mode();
+ below.signal_flush_required.connect(sigc::mem_fun(this, &Buffered::flush));
+}
+
+Buffered::~Buffered()
+{
+ try
+ {
+ flush();
+ }
+ catch(...)
+ { }
+
+ delete[] buf;
+}
+
+void Buffered::flush()
+{
+ if(cur_op==M_WRITE)
+ {
+ unsigned used = end-begin;
+ if(used)
+ {
+ unsigned len = below.write(begin, used);
+
+ begin=end = buf;
+
+ if(len<used)
+ throw Exception("Couldn't flush all data");
+ }
+ }
+ else if(cur_op==M_READ)
+ begin=end = buf;
+}
+
+unsigned Buffered::do_write(const char *data, unsigned size)
+{
+ set_op(M_WRITE);
+
+ if(end+size<buf+buf_size)
+ {
+ // All data fits in buffer with whatever is already there
+ memcpy(end, data, size);
+ end += size;
+
+ return size;
+ }
+ else
+ {
+ // Clear the buffer to make more room
+ flush();
+
+ if(size<buf_size)
+ {
+ // Put new data in the buffer to wait for more
+ memcpy(end, data, size);
+ end += size;
+
+ return size;
+ }
+ else
+ // New data still doesn't fit in the buffer, so write it directly
+ return below.write(data, size);
+ }
+}
+
+unsigned Buffered::do_read(char *data, unsigned size)
+{
+ set_op(M_READ);
+
+ if(begin+size<=end)
+ {
+ // The request can be served from the buffer
+ memcpy(data, begin, size);
+ begin += size;
+
+ eof_flag = (below.eof() && begin==end);
+
+ return size;
+ }
+ else
+ {
+ // Give out whatever is in the buffer already
+ memcpy(data, begin, end-begin);
+ unsigned ret = end-begin;
+ begin=end = buf;
+
+ data += ret;
+ size -= ret;
+
+ if(size<buf_size)
+ {
+ // Fill the buffer and serve the rest of the request from it
+ unsigned len = below.read(end, buf+buf_size-end);
+ end += len;
+
+ len = min(static_cast<unsigned>(end-begin), size);
+ memcpy(data, begin, len);
+ begin += len;
+ ret += len;
+ }
+ else
+ // Read the rest directly from the underlying object
+ ret += below.read(data, size);
+
+ eof_flag = (below.eof() && begin==end);
+
+ return ret;
+ }
+}
+
+unsigned Buffered::put(char c)
+{
+ set_op(M_WRITE);
+
+ if(end<buf+buf_size)
+ {
+ *end++ = c;
+ return 1;
+ }
+ else
+ return do_write(&c, 1);
+}
+
+bool Buffered::getline(std::string &line)
+{
+ set_op(M_READ);
+
+ for(char *i=begin; i!=end; ++i)
+ if(*i=='\n')
+ {
+ line.assign(begin, i-begin);
+ begin = i+1;
+ return true;
+ }
+
+ return Base::getline(line);
+}
+
+int Buffered::get()
+{
+ set_op(M_READ);
+
+ if(begin<end)
+ return static_cast<unsigned char>(*begin++);
+
+ char c;
+ if(do_read(&c, 1)==0)
+ return -1;
+ return static_cast<unsigned char>(c);
+}
+
+void Buffered::set_op(Mode op)
+{
+ if(op!=cur_op)
+ flush();
+ cur_op = op;
+}
+
+unsigned Buffered::get_current_size() const
+{
+ return end-begin;
+}
+
+Handle Buffered::get_event_handle()
+{
+ throw Exception("Buffered doesn't support events");
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_BUFFERED_H_
+#define MSP_IO_BUFFERED_H_
+
+#include "base.h"
+
+namespace Msp {
+namespace IO {
+
+class Buffered: public Base
+{
+private:
+ Base &below;
+ unsigned buf_size;
+ char *buf;
+ char *begin;
+ char *end;
+ Mode cur_op;
+
+public:
+ Buffered(Base &, unsigned =8192);
+ ~Buffered();
+
+ void flush();
+
+protected:
+ unsigned do_write(const char *, unsigned);
+ unsigned do_read(char *, unsigned);
+public:
+ unsigned put(char);
+
+ bool getline(std::string &);
+ int get();
+
+private:
+ void set_op(Mode);
+public:
+ Mode get_current_op() const { return cur_op; }
+ unsigned get_current_size() const;
+
+ virtual Handle get_event_handle();
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef WIN32
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#endif
+#include <msp/core/except.h>
+#include "console.h"
+
+namespace {
+
+#ifndef WIN32
+termios orig_attr;
+#endif
+
+} // namespace
+
+namespace Msp {
+namespace IO {
+
+Console::Console(unsigned n)
+{
+ if(n>2)
+ throw InvalidParameterValue("Invalid parameter for Console::Console");
+
+ mode = (n==0 ? M_READ : M_WRITE);
+
+#ifdef WIN32
+ switch(n)
+ {
+ case 0: handle = GetStdHandle(STD_INPUT_HANDLE); break;
+ case 1: handle = GetStdHandle(STD_OUTPUT_HANDLE); break;
+ case 2: handle = GetStdHandle(STD_ERROR_HANDLE); break;
+ }
+#else
+ handle = n;
+
+ if(handle==0)
+ tcgetattr(handle, &orig_attr);
+#endif
+
+ if(n==0)
+ set_events(P_INPUT);
+}
+
+Console::~Console()
+{
+#ifndef WIN32
+ if(handle==0)
+ tcsetattr(handle, TCSADRAIN, &orig_attr);
+#endif
+}
+
+void Console::set_block(bool b)
+{
+#ifdef WIN32
+ // XXX Dunno how to do this in win32
+ (void)b;
+#else
+ int flags = fcntl(0, F_GETFL);
+ flags = (flags&~O_NONBLOCK) | (b?0:O_NONBLOCK);
+ fcntl(0, F_SETFL, flags);
+#endif
+}
+
+void Console::set_local_echo(bool e)
+{
+ if(!(mode&M_READ))
+ throw InvalidState("Local echo can only be set on input console");
+
+#ifdef WIN32
+ DWORD m;
+ GetConsoleMode(handle, &m);
+ SetConsoleMode(handle, (m&~ENABLE_ECHO_INPUT) | (e?ENABLE_ECHO_INPUT:0));
+#else
+ termios t;
+ tcgetattr(0, &t);
+ t.c_lflag = (t.c_lflag&~ECHO) | (e?ECHO:0);
+ tcsetattr(0, TCSADRAIN, &t);
+#endif
+}
+
+void Console::set_line_buffer(bool l)
+{
+ if(!(mode&M_READ))
+ throw InvalidState("Line buffering can only be set on input console");
+
+#ifdef WIN32
+ DWORD m;
+ GetConsoleMode(handle, &m);
+ SetConsoleMode(handle, (m&~ENABLE_LINE_INPUT) | (l?ENABLE_LINE_INPUT:0));
+#else
+ // XXX ICANON does more than just set line buffering, may need a bit more thought
+ termios t;
+ tcgetattr(0, &t);
+ t.c_lflag = (t.c_lflag&~ICANON) | (l?ICANON:0);
+ tcsetattr(0, TCSADRAIN, &t);
+#endif
+}
+
+void Console::get_size(unsigned &rows, unsigned &cols)
+{
+ if(!(mode&M_WRITE))
+ throw InvalidState("Size can only be queried from an output console");
+
+#ifdef WIN32
+ // XXX Figure out how to do this
+ rows = 24;
+ cols = 80;
+#else
+ struct winsize wsz;
+ ioctl(handle, TIOCGWINSZ, &wsz);
+ rows = wsz.ws_row;
+ cols = wsz.ws_col;
+#endif
+}
+
+unsigned Console::do_write(const char *buf, unsigned len)
+{
+ if(!(mode&M_WRITE))
+ throw InvalidState("Console is not writable");
+
+#ifdef WIN32
+ DWORD ret;
+ if(!WriteFile(handle, buf, len, &ret, 0))
+ throw SystemError("Writing to console failed", GetLastError());
+#else
+ int ret = ::write(handle, buf, len);
+ if(ret==-1)
+ throw SystemError("Writing to console failed", errno);
+#endif
+
+ return ret;
+}
+
+unsigned Console::do_read(char *buf, unsigned len)
+{
+ if(!(mode&M_READ))
+ throw InvalidState("Console is not readable");
+
+#ifdef WIN32
+ DWORD ret;
+ if(!ReadFile(handle, buf, len, &ret, 0))
+ throw SystemError("Reading from console failed", GetLastError());
+#else
+ int ret = ::read(handle, buf, len);
+ if(ret==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+ else
+ throw SystemError("Reading from console failed", errno);
+ }
+ else if(ret==0)
+ eof_flag = true;
+#endif
+
+ return ret;
+}
+
+Handle Console::get_event_handle()
+{
+ return 0;
+}
+
+Console &Console::instance(unsigned n)
+{
+ static Console in(0);
+ static Console out(1);
+ static Console err(2);
+
+ switch(n)
+ {
+ case 0: return in;
+ case 1: return out;
+ case 2: return err;
+ }
+
+ throw InvalidParameterValue("Unknown Console instance requested");
+}
+
+Console &cin = Console::instance(0);
+Console &cout = Console::instance(1);
+Console &cerr = Console::instance(2);
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_CONSOLE_H_
+#define MSP_IO_CONSOLE_H_
+
+#include "base.h"
+
+namespace Msp {
+namespace IO {
+
+/**
+Provides access to standard input, output and error streams. This class can't
+be instantiated directly - use one of the cin, cout and cerr references
+instead.
+*/
+class Console: public Base
+{
+private:
+ Handle handle;
+
+ Console(unsigned);
+public:
+ ~Console();
+
+ virtual void set_block(bool);
+
+ /** If local echo is enabled, characters will appear on the console as they
+ are typed. Can only be used on an input Console. */
+ void set_local_echo(bool);
+
+ /** If line buffering is enabled, input will only be available when a
+ newline is encountered. On some systems, this may also enable line editing.
+ Can only be used on an input Console.
+ */
+ void set_line_buffer(bool);
+
+ /** Retrieves the size of the Console. Can only be used on an output
+ Console. */
+ void get_size(unsigned &rows, unsigned &cols);
+
+protected:
+ virtual unsigned do_write(const char *, unsigned);
+ virtual unsigned do_read(char *, unsigned);
+
+public:
+ virtual Handle get_event_handle();
+
+ static Console &instance(unsigned);
+};
+
+extern Console &cin;
+extern Console &cout;
+extern Console &cerr;
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <msp/time/units.h>
+#include "base.h"
+#include "eventdispatcher.h"
+#include "poll.h"
+
+namespace Msp {
+namespace IO {
+
+EventDispatcher::EventDispatcher()
+{ }
+
+void EventDispatcher::add(Base &obj)
+{
+ SlotMap::iterator i = objects.find(&obj);
+ if(i==objects.end())
+ {
+ i = objects.insert(SlotMap::value_type(&obj, Slot(&obj))).first;
+ i->second.evch_conn = obj.signal_events_changed.connect(sigc::bind(sigc::mem_fun(this, &EventDispatcher::object_events_changed), &obj));
+ i->second.del_conn = obj.signal_deleted.connect(sigc::bind(sigc::mem_fun(this, &EventDispatcher::object_deleted), &obj));
+
+ if(obj.get_events())
+ poller.set_object(obj, obj.get_events());
+ }
+}
+
+void EventDispatcher::remove(Base &obj)
+{
+ SlotMap::iterator i = objects.find(&obj);
+ if(i!=objects.end())
+ {
+ i->second.evch_conn.disconnect();
+ i->second.del_conn.disconnect();
+ objects.erase(i);
+
+ poller.set_object(obj, P_NONE);
+ }
+}
+
+void EventDispatcher::tick()
+{
+ if(objects.empty())
+ return;
+
+ if(poller.poll()>0)
+ dispatch();
+}
+
+void EventDispatcher::tick(const Time::TimeDelta &dt)
+{
+ if(objects.empty())
+ return;
+
+ if(poller.poll(dt)>0)
+ dispatch();
+}
+
+void EventDispatcher::object_events_changed(PollEvent ev, Base *obj)
+{
+ poller.set_object(*obj, ev);
+}
+
+void EventDispatcher::object_deleted(Base *obj)
+{
+ remove(*obj);
+}
+
+void EventDispatcher::dispatch()
+{
+ const Poller::SlotSeq &result = poller.get_result();
+ for(Poller::SlotSeq::const_iterator i=result.begin(); i!=result.end(); ++i)
+ i->object->event(i->events);
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef EVENTDISPATCHER_H_
+#define EVENTDISPATCHER_H_
+
+#include <sigc++/connection.h>
+#include <sigc++/trackable.h>
+#include "poll.h"
+
+namespace Msp {
+namespace IO {
+
+/**
+Put your I/O objects inside one of these to get signaled when something happens
+on some of them.
+*/
+class EventDispatcher: public sigc::trackable
+{
+private:
+ struct Slot
+ {
+ Base *obj;
+ sigc::connection evch_conn;
+ sigc::connection del_conn;
+
+ Slot(Base *o): obj(o) { }
+ };
+
+ typedef std::map<Base *, Slot> SlotMap;
+
+ Poller poller;
+ SlotMap objects;
+
+public:
+ EventDispatcher();
+
+ void add(Base &);
+ void remove(Base &);
+
+ /** Checks for and dispatches events. If there are no events available,
+ blocks until there are. */
+ void tick();
+
+ /** Checks for and dispatches events. If there are no events available,
+ waits at most the specified time before returning. */
+ void tick(const Time::TimeDelta &);
+
+private:
+ void object_events_changed(PollEvent, Base *);
+ void object_deleted(Base *);
+ void dispatch();
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_IO_EXCEPT_H_
+#define MSP_IO_EXCEPT_H_
+
+#include <msp/core/except.h>
+
+namespace Msp {
+namespace IO {
+
+class FileNotFound: public Exception
+{
+public:
+ FileNotFound(const std::string &w_, const std::string &f): Exception(w_), filename(f) { }
+ const std::string &get_filename() { return filename; }
+ ~FileNotFound() throw() { }
+private:
+ std::string filename;
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef WIN32
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+#include <msp/strings/formatter.h>
+#include "except.h"
+#include "file.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+File::File(const string &fn, Mode m, CreateMode cm)
+{
+ if(!(m&M_RDWR))
+ throw InvalidParameterValue("Invalid read/write mode");
+ if(cm&~(C_CREATE|C_TRUNCATE))
+ throw InvalidParameterValue("Invalid create mode");
+
+ mode = m;
+
+#ifdef WIN32
+ int flags = 0;
+ int create_flags = OPEN_EXISTING;
+
+ if(mode&M_READ)
+ flags |= GENERIC_READ;
+ else if(mode&M_WRITE)
+ {
+ flags |= GENERIC_WRITE;
+
+ switch(static_cast<int>(cm))
+ {
+ case C_NONE: create_flags = OPEN_EXISTING; break;
+ case C_CREATE: create_flags = OPEN_ALWAYS; break;
+ case C_TRUNCATE: create_flags = TRUNCATE_EXISTING; break;
+ case C_CREATE+C_TRUNCATE: create_flags = CREATE_ALWAYS; break;
+ }
+ }
+
+ handle = CreateFile(fn.c_str(), flags, 0, 0, create_flags, FILE_ATTRIBUTE_NORMAL, 0);
+ if(handle==INVALID_HANDLE_VALUE)
+ {
+ int err = GetLastError();
+ if(err==ERROR_FILE_NOT_FOUND)
+ throw FileNotFound("Can't find file "+fn, fn);
+ else
+ throw SystemError(format("Can't open file '%s'", fn), GetLastError());
+ }
+#else
+ int flags = 0;
+ switch(mode&M_RDWR)
+ {
+ case M_READ: flags |= O_RDONLY; break;
+ case M_WRITE: flags |= O_WRONLY; break;
+ case M_RDWR: flags |= O_RDWR; break;
+ default:;
+ }
+
+ if(mode&M_WRITE)
+ {
+ if(cm&C_CREATE)
+ flags |= O_CREAT;
+ if(cm&C_TRUNCATE)
+ flags |= O_TRUNC;
+ }
+ if(mode&M_APPEND)
+ flags |= O_APPEND;
+ if(mode&M_NONBLOCK)
+ flags |= O_NONBLOCK;
+
+ handle = ::open(fn.c_str(), flags, 0666);
+ if(handle==-1)
+ {
+ int err = errno;
+ if(err==ENOENT)
+ throw FileNotFound("Can't find file "+fn, fn);
+ else
+ throw SystemError(format("Can't open file '%s'", fn), err);
+ }
+#endif
+
+ set_events(P_INPUT);
+}
+
+File::~File()
+{
+ close();
+}
+
+void File::close()
+{
+ if(handle==MSP_IO_INVALID_HANDLE)
+ return;
+
+ set_events(P_NONE);
+
+ signal_flush_required.emit();
+
+#ifdef WIN32
+ CloseHandle(handle);
+#else
+ ::close(handle);
+#endif
+
+ handle = MSP_IO_INVALID_HANDLE;
+ signal_closed.emit();
+}
+
+void File::set_block(bool b)
+{
+ check_access(M_NONE);
+
+ mode = (mode&~M_NONBLOCK);
+ if(b)
+ mode = (mode|M_NONBLOCK);
+#ifndef WIN32
+ int flags = fcntl(handle, F_GETFD);
+ fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
+#endif
+}
+
+unsigned File::do_write(const char *buf, unsigned size)
+{
+ check_access(M_WRITE);
+
+ if(size==0)
+ return 0;
+
+#ifdef WIN32
+ if(mode&M_APPEND)
+ seek(0, S_END);
+ DWORD ret;
+ if(WriteFile(handle, buf, size, &ret, 0)==0)
+ throw SystemError("Writing to file failed", GetLastError());
+#else
+ int ret = ::write(handle, buf, size);
+ if(ret==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+ else
+ throw SystemError("Writing to file failed", errno);
+ }
+#endif
+
+ return ret;
+}
+
+unsigned File::do_read(char *buf, unsigned size)
+{
+ check_access(M_READ);
+
+ if(size==0)
+ return 0;
+
+#ifdef WIN32
+ DWORD ret;
+ if(ReadFile(handle, buf, size, &ret, 0)==0)
+ throw SystemError("Reading from file failed", GetLastError());
+#else
+ int ret = ::read(handle, buf, size);
+ if(ret==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+ else
+ throw SystemError("Reading from file failed", errno);
+ }
+#endif
+
+ if(ret==0)
+ {
+ eof_flag = true;
+ signal_end_of_file.emit();
+ }
+
+ return ret;
+}
+
+void File::sync()
+{
+#ifndef WIN32
+ signal_flush_required.emit();
+
+ fsync(handle);
+#endif
+}
+
+int File::seek(int off, SeekType st)
+{
+ check_access(M_NONE);
+
+ signal_flush_required.emit();
+
+ int type = sys_seek_type(st);
+#ifdef WIN32
+ DWORD ret = SetFilePointer(handle, off, 0, type);
+ if(ret==INVALID_SET_FILE_POINTER)
+ throw SystemError("Seek failed", GetLastError());
+#else
+ int ret = lseek(handle, off, type);
+ if(ret==-1)
+ throw SystemError("Seek failed", errno);
+#endif
+
+ eof_flag = false;
+
+ return ret;
+}
+
+int File::tell() const
+{
+ check_access(M_NONE);
+
+#ifdef WIN32
+ DWORD ret = SetFilePointer(handle, 0, 0, FILE_CURRENT);
+ if(ret==INVALID_SET_FILE_POINTER)
+ throw SystemError("Tell failed", GetLastError());
+#else
+ int ret = lseek(handle, 0, SEEK_CUR);
+ if(ret==-1)
+ throw SystemError("Tell failed", errno);
+#endif
+
+ return ret;
+}
+
+void File::check_access(Mode m) const
+{
+ if(handle==MSP_IO_INVALID_HANDLE)
+ throw InvalidState("File is not open");
+ if(m==M_READ && !(mode&M_READ))
+ throw InvalidState("File is not readable");
+ if(m==M_WRITE && !(mode&M_WRITE))
+ throw InvalidState("File is not writable");
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_FILE_H_
+#define MSP_IO_FILE_H_
+
+#include <string>
+#include "base.h"
+#include "buffered.h"
+#include "filtered.h"
+#include "seek.h"
+
+namespace Msp {
+namespace IO {
+
+/**
+A class for reading and writing files.
+
+Non-blocking mode is not supported on Win32.
+*/
+class File: public Base
+{
+public:
+ enum CreateMode
+ {
+ C_NONE = 0,
+ C_CREATE = 1,
+ C_TRUNCATE = 2
+ };
+
+private:
+ Handle handle;
+
+public:
+ /** Creates a new file object and opens it. If the create flag is set and
+ write access is requested and the file does exist, it is created. Otherwise
+ a missing file is an error. */
+ File(const std::string &, Mode = M_READ, CreateMode = CreateMode(C_CREATE+C_TRUNCATE));
+ virtual ~File();
+
+ /** Closes the file. Any attempt to access the file after this will cause
+ an exception to be thrown. */
+ void close();
+
+ void set_block(bool);
+
+protected:
+ virtual unsigned do_write(const char *, unsigned);
+ virtual unsigned do_read(char *, unsigned);
+
+public:
+ virtual void sync();
+
+ /** Changes the read/write offset of the file. Returns the new offset. */
+ virtual int seek(int, SeekType);
+
+ /** Returns the current read/write offset of the file. */
+ virtual int tell() const;
+
+ virtual Handle get_event_handle() { return handle; }
+
+private:
+ void check_access(Mode) const;
+};
+
+inline File::CreateMode operator|(File::CreateMode m, File::CreateMode n)
+{ return File::CreateMode(static_cast<int>(m)|static_cast<int>(n)); }
+
+inline File::CreateMode operator&(File::CreateMode m, File::CreateMode n)
+{ return File::CreateMode(static_cast<int>(m)&static_cast<int>(n)); }
+
+inline File::CreateMode operator~(File::CreateMode m)
+{ return File::CreateMode(~static_cast<int>(m)); }
+
+typedef Filtered<File, Buffered> BufferedFile;
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_IO_FILTERED_H_
+#define MSP_IO_FILTERED_H_
+
+namespace Msp {
+namespace IO {
+
+template<typename B, typename F>
+class Filtered: public B
+{
+private:
+ struct Activator
+ {
+ Filtered &f;
+
+ Activator(Filtered &f_): f(f_) { f.active = true; }
+ ~Activator() { f.active = false; }
+ };
+
+ F filter;
+ bool active;
+
+public:
+ Filtered(): filter(*this), active(false) { }
+ ~Filtered() { active = true; }
+
+ template<typename A0>
+ Filtered(A0 a0): B(a0), filter(*this), active(false) { }
+
+ template<typename A0, typename A1>
+ Filtered(A0 a0, A1 a1): B(a0, a1), filter(*this), active(false) { }
+
+protected:
+ virtual unsigned do_write(const char *b, unsigned s)
+ {
+ if(!active)
+ {
+ Activator a(*this);
+ return filter.write(b, s);
+ }
+ else
+ return B::do_write(b, s);
+ }
+
+ virtual unsigned do_read(char *b, unsigned s)
+ {
+ if(!active)
+ {
+ Activator a(*this);
+ return filter.read(b, s);
+ }
+ else
+ return B::do_read(b, s);
+ }
+
+public:
+ virtual unsigned put(char c) { return filter.put(c); }
+ virtual bool getline(std::string &l) { return filter.getline(l); }
+ virtual int get() { return filter.get(); }
+
+ F &get_filter() { return filter; }
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <algorithm>
+#include <cstring>
+#include "except.h"
+#include "memory.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+Memory::Memory(char *d, unsigned s)
+{
+ init(d, d+s, M_RDWR);
+}
+
+Memory::Memory(char *b, char *e)
+{
+ init(b, e, M_RDWR);
+}
+
+Memory::Memory(const char *cd, unsigned s)
+{
+ char *d = const_cast<char *>(cd);
+ init(d, d+s, M_READ);
+}
+
+Memory::Memory(const char *b, const char *e)
+{
+ init(const_cast<char *>(b), const_cast<char *>(e), M_READ);
+}
+
+void Memory::init(char *b, char *e, Mode m)
+{
+ begin = b;
+ end = e;
+ pos = begin;
+ mode = m;
+}
+
+unsigned Memory::do_write(const char *buf, unsigned size)
+{
+ check_mode(M_WRITE);
+
+ size = min<unsigned>(size, end-pos);
+ memcpy(pos, buf, size);
+ pos += size;
+ return size;
+}
+
+unsigned Memory::do_read(char *buf, unsigned size)
+{
+ if(pos==end)
+ {
+ eof_flag = true;
+ return 0;
+ }
+
+ size = min<unsigned>(size, end-pos);
+ memcpy(buf, pos, size);
+ pos += size;
+ return size;
+}
+
+unsigned Memory::put(char c)
+{
+ check_mode(M_WRITE);
+ *pos++ = c;
+ return 1;
+}
+
+bool Memory::getline(string &line)
+{
+ char *nl = find(pos, end, '\n');
+ line.assign(pos, nl);
+ bool result = (nl!=pos);
+ pos = nl;
+ return result;
+}
+
+int Memory::get()
+{
+ if(pos==end)
+ {
+ eof_flag = true;
+ return -1;
+ }
+
+ return static_cast<unsigned char>(*pos++);
+}
+
+unsigned Memory::seek(int off, SeekType type)
+{
+ char *new_pos;
+ if(type==S_BEG)
+ new_pos = begin+off;
+ else if(type==S_CUR)
+ new_pos = pos+off;
+ else if(type==S_END)
+ new_pos = end+off;
+ else
+ throw InvalidParameterValue("Invalid seek type");
+
+ if(new_pos<begin || new_pos>end)
+ throw InvalidParameterValue("Invalid seek offset");
+
+ pos = new_pos;
+ return pos-begin;
+}
+
+Handle Memory::get_event_handle()
+{
+ throw Exception("Memory doesn't support events");
+}
+
+void Memory::check_mode(Mode m) const
+{
+ if(m==M_WRITE)
+ {
+ if(!(mode&M_WRITE))
+ throw InvalidState("Memory is not writable");
+ if(pos==end)
+ throw InvalidState("Attempt to write past end of Memory");
+ }
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_MEMORY_H_
+#define MSP_IO_MEMORY_H_
+
+#include "base.h"
+#include "seek.h"
+
+namespace Msp {
+namespace IO {
+
+class Memory: public Base
+{
+private:
+ char *begin;
+ char *end;
+ char *pos;
+
+public:
+ Memory(char *, unsigned);
+ Memory(char *, char *);
+ Memory(const char *, unsigned);
+ Memory(const char *, const char *);
+private:
+ void init(char *, char *, Mode);
+
+ virtual unsigned do_write(const char *, unsigned);
+ virtual unsigned do_read(char *, unsigned);
+public:
+ virtual unsigned put(char);
+ virtual bool getline(std::string &);
+ virtual int get();
+
+ unsigned seek(int, SeekType);
+ unsigned tell() const { return pos-begin; }
+
+ virtual Handle get_event_handle();
+
+private:
+ void check_mode(Mode) const;
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_IO_MODE_H_
+#define MSP_IO_MODE_H_
+
+namespace Msp {
+namespace IO {
+
+enum Mode
+{
+ M_NONE = 0,
+ M_READ = 1,
+ M_WRITE = 2,
+ M_RDWR = M_READ|M_WRITE,
+ M_APPEND = 4,
+ M_NONBLOCK = 8
+};
+
+inline Mode operator|(Mode m, Mode n)
+{ return Mode(static_cast<int>(m)|static_cast<int>(n)); }
+
+inline Mode operator&(Mode m, Mode n)
+{ return Mode(static_cast<int>(m)&static_cast<int>(n)); }
+
+inline Mode operator~(Mode m)
+{ return Mode(~static_cast<int>(m)); }
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef WIN32
+#include <fcntl.h>
+#include <errno.h>
+#endif
+#include <msp/strings/formatter.h>
+#include "pipe.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+Pipe::Pipe()
+{
+#ifdef WIN32
+ string name = format("\\\\.\\pipe\\%u.%p", GetCurrentProcessId(), this);
+ handle[0] = CreateNamedPipe(name.c_str(), PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024, 0, 0);
+ if(handle[0]==INVALID_HANDLE_VALUE)
+ throw SystemError("Unable to create pipe", GetLastError());
+
+ handle[1] = CreateFile(name.c_str(), GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
+ if(handle[1]==INVALID_HANDLE_VALUE)
+ {
+ unsigned err = GetLastError();
+ CloseHandle(handle[0]);
+ throw SystemError("Unable to create pipe", err);
+ }
+
+ overlapped = 0;
+ event = CreateEvent(0, true, false, 0);
+ buf_size = 1024;
+ buffer = new char[buf_size];
+ buf_avail = 0;
+ buf_next = buffer;
+#else
+ if(pipe(handle)==-1)
+ throw SystemError("Unable to create pipe", errno);
+#endif
+
+ set_events(P_INPUT);
+}
+
+Pipe::~Pipe()
+{
+ close();
+}
+
+void Pipe::close()
+{
+ set_events(P_NONE);
+
+ signal_flush_required.emit();
+#ifdef WIN32
+ CloseHandle(handle[0]);
+ CloseHandle(handle[1]);
+#else
+ ::close(handle[0]);
+ ::close(handle[1]);
+ signal_closed.emit();
+#endif
+}
+
+void Pipe::set_block(bool b)
+{
+ mode = (mode&~M_NONBLOCK);
+ if(b)
+ mode = (mode|M_NONBLOCK);
+
+#ifndef WIN32
+ int flags = fcntl(handle[0], F_GETFD);
+ fcntl(handle[0], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
+ flags = fcntl(handle[1], F_GETFD);
+ fcntl(handle[1], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
+#endif
+}
+
+unsigned Pipe::do_write(const char *buf, unsigned size)
+{
+ if(size==0)
+ return 0;
+
+#ifdef WIN32
+ DWORD ret;
+ if(!WriteFile(handle[1], buf, size, &ret, 0))
+ throw SystemError("Writing to pipe failed", GetLastError());
+#else
+ int ret = ::write(handle[1], buf, size);
+ if(ret==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+ else
+ throw SystemError("Writing to pipe failed", errno);
+ }
+#endif
+
+ return ret;
+}
+
+unsigned Pipe::do_read(char *buf, unsigned size)
+{
+ if(size==0)
+ return 0;
+
+#ifdef WIN32
+ // Initiate overlapped read if needed
+ get_event_handle();
+
+ if(overlapped)
+ {
+ DWORD ret;
+ if(!GetOverlappedResult(handle[0], overlapped, &ret, !buf_avail))
+ throw SystemError("Reading from pipe failed", GetLastError());
+ else
+ {
+ buf_avail += ret;
+ delete overlapped;
+ overlapped = 0;
+ }
+ }
+
+ unsigned ret = min(buf_avail, size);
+ memcpy(buf, buf_next, ret);
+ buf_next += ret;
+ buf_avail -= ret;
+
+ // Initiate another overlapped read in case someone is polling us
+ get_event_handle();
+#else
+ int ret = ::read(handle[0], buf, size);
+ if(ret==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+ else
+ throw SystemError("Reading from pipe failed", errno);
+ }
+#endif
+
+ if(ret==0)
+ {
+ eof_flag = true;
+ signal_end_of_file.emit();
+ }
+
+ return ret;
+}
+
+Handle Pipe::get_event_handle()
+{
+#ifdef WIN32
+ if(!overlapped && !buf_avail)
+ {
+ overlapped = new OVERLAPPED;
+ memset(overlapped, 0, sizeof(OVERLAPPED));
+ overlapped->hEvent = event;
+
+ DWORD ret;
+ buf_next = buffer;
+ if(!ReadFile(handle[0], buffer, buf_size, &ret, overlapped))
+ {
+ unsigned err = GetLastError();
+ if(err!=ERROR_IO_PENDING)
+ throw SystemError("Failed to start an overlapped read", err);
+ }
+ else
+ {
+ buf_avail = ret;
+ delete overlapped;
+ overlapped = 0;
+ SetEvent(event);
+ }
+ }
+
+ return event;
+#else
+ return handle[0];
+#endif
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_PIPE_H_
+#define MSP_IO_PIPE_H_
+
+#include "base.h"
+
+namespace Msp {
+namespace IO {
+
+class Pipe: public Base
+{
+private:
+ Handle handle[2];
+#ifdef WIN32
+ OVERLAPPED *overlapped;
+ Handle event;
+ unsigned buf_size;
+ char *buffer;
+ unsigned buf_avail;
+ char *buf_next;
+#endif
+
+public:
+ Pipe();
+ ~Pipe();
+
+ void close();
+
+ void set_block(bool);
+
+protected:
+ virtual unsigned do_write(const char *, unsigned);
+ virtual unsigned do_read(char *, unsigned);
+
+public:
+ virtual Handle get_event_handle();
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <errno.h>
+#include <msp/strings/formatter.h>
+#include <msp/time/units.h>
+#include "except.h"
+#include "base.h"
+#include "poll.h"
+
+namespace {
+
+using namespace Msp;
+using namespace Msp::IO;
+
+inline int sys_poll_event(PollEvent event)
+{
+ int result = 0;
+
+ if(event&~(P_INPUT|P_PRIO|P_OUTPUT))
+ throw InvalidParameterValue("Invalid poll events");
+
+#ifndef WIN32
+ if(event&P_INPUT)
+ result |= POLLIN;
+ if(event&P_PRIO)
+ result |= POLLPRI;
+ if(event&P_OUTPUT)
+ result |= POLLOUT;
+#endif
+
+ return result;
+}
+
+inline PollEvent poll_event_from_sys(int event)
+{
+ PollEvent result = P_NONE;
+
+#ifdef WIN32
+ // Stop the compiler from complaining about unused parameter
+ event = event;
+#else
+ if(event&POLLIN)
+ result = result|P_INPUT;
+ if(event&POLLPRI)
+ result = result|P_PRIO;
+ if(event&POLLOUT)
+ result = result|P_OUTPUT;
+ if(event&POLLERR)
+ result = result|P_ERROR;
+#endif
+
+ return result;
+}
+
+inline PollEvent do_poll(Base &obj, PollEvent pe, int timeout)
+{
+#ifdef WIN32
+ if(timeout<0)
+ timeout = INFINITE;
+
+ DWORD ret = WaitForSingleObject(obj.get_event_handle(), timeout);
+ if(ret==WAIT_OBJECT_0)
+ return pe;
+ else if(ret==WAIT_FAILED)
+ throw SystemError("Poll failed", GetLastError());
+
+ return P_NONE;
+#else
+ pollfd pfd = {obj.get_event_handle(), sys_poll_event(pe), 0};
+
+ int ret = ::poll(&pfd, 1, timeout);
+ if(ret==-1)
+ {
+ if(errno==EINTR)
+ return P_NONE;
+ else
+ throw SystemError("Poll failed", errno);
+ }
+
+ return poll_event_from_sys(pfd.revents);
+#endif
+}
+
+}
+
+namespace Msp {
+namespace IO {
+
+Poller::Poller():
+ pfd_dirty(false)
+{ }
+
+void Poller::set_object(Base &obj, PollEvent ev)
+{
+ // Verify that the object has an event handle
+ if(ev)
+ obj.get_event_handle();
+
+ SlotMap::iterator i = objects.find(&obj);
+ if(i!=objects.end())
+ {
+ if(ev)
+ i->second.events = ev;
+ else
+ objects.erase(i);
+
+ pfd_dirty = true;
+ }
+ else if(ev)
+ {
+#ifdef WIN32
+ if(objects.size()>=MAXIMUM_WAIT_OBJECTS)
+ throw InvalidState("Maximum number of wait objects reached");
+#endif
+ objects.insert(SlotMap::value_type(&obj, Slot(&obj, ev)));
+
+ pfd_dirty = true;
+ }
+}
+
+int Poller::poll()
+{
+ return do_poll(-1);
+}
+
+int Poller::poll(const Time::TimeDelta &timeout)
+{
+ if(timeout<Time::zero)
+ throw InvalidParameterValue("Invalid timeout");
+
+ return do_poll(static_cast<int>(timeout/Time::msec));
+}
+
+void Poller::rebuild_pfd()
+{
+ pfd.clear();
+
+ pollfd p;
+
+ for(SlotMap::iterator i=objects.begin(); i!=objects.end(); ++i)
+ {
+ p.fd = i->second.object->get_event_handle();
+#ifndef WIN32
+ p.events = sys_poll_event(i->second.events);
+#endif
+ pfd.push_back(p);
+ }
+
+ pfd_dirty = false;
+}
+
+int Poller::do_poll(int timeout)
+{
+ if(pfd_dirty)
+ rebuild_pfd();
+
+ poll_result.clear();
+
+#ifdef WIN32
+ if(timeout<0)
+ timeout = INFINITE;
+
+ DWORD ret = WaitForMultipleObjects(pfd.size(), &pfd.front().fd, false, timeout);
+ if(/*ret>=WAIT_OBJECT_0 &&*/ ret<WAIT_OBJECT_0+pfd.size())
+ {
+ SlotMap::iterator i = objects.begin();
+ advance(i, ret-WAIT_OBJECT_0);
+ poll_result.push_back(Slot(i->second.object, i->second.events));
+
+ return 1;
+ }
+ else if(ret==WAIT_FAILED)
+ throw SystemError("Poll failed", GetLastError());
+
+ return 0;
+#else
+ int ret = ::poll(&pfd.front(), pfd.size(), timeout);
+ if(ret==-1)
+ {
+ if(errno==EINTR)
+ return 0;
+ else
+ throw SystemError("Poll failed", errno);
+ }
+
+ int n = ret;
+ SlotMap::iterator j = objects.begin();
+ for(std::vector<pollfd>::iterator i=pfd.begin(); (i!=pfd.end() && n>0); ++i,++j)
+ if(i->revents)
+ {
+ poll_result.push_back(Slot(j->second.object, poll_event_from_sys(i->revents)));
+ --n;
+ }
+
+ return ret;
+#endif
+}
+
+
+PollEvent poll(Base &obj, PollEvent pe)
+{
+ return do_poll(obj, pe, -1);
+}
+
+PollEvent poll(Base &obj, PollEvent pe, const Time::TimeDelta &timeout)
+{
+ if(timeout<Time::zero)
+ throw InvalidParameterValue("Invalid timeout");
+
+ return do_poll(obj, pe, static_cast<int>(timeout/Time::msec));
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_POLL_H_
+#define MSP_IO_POLL_H_
+
+#ifndef WIN32
+#include <poll.h>
+#endif
+#include <list>
+#include <map>
+#include <vector>
+#include <msp/time/timedelta.h>
+#include "types.h"
+
+namespace Msp {
+namespace IO {
+
+class Base;
+
+enum PollEvent
+{
+ P_NONE = 0,
+ P_INPUT = 1,
+ P_PRIO = 2,
+ P_OUTPUT = 4,
+ P_ERROR = 8
+};
+
+inline PollEvent operator|(PollEvent e, PollEvent f)
+{ return PollEvent(static_cast<int>(e)|static_cast<int>(f)); }
+
+inline PollEvent operator&(PollEvent e, PollEvent f)
+{ return PollEvent(static_cast<int>(e)&static_cast<int>(f)); }
+
+inline PollEvent operator~(PollEvent e)
+{ return PollEvent(~static_cast<int>(e)); }
+
+
+class Poller
+{
+public:
+ struct Slot
+ {
+ Base *object;
+ PollEvent events;
+
+ Slot(Base *o, PollEvent e): object(o), events(e) { }
+ };
+
+ typedef std::list<Slot> SlotSeq;
+private:
+ typedef std::map<Base *, Slot> SlotMap;
+
+#ifdef WIN32
+ struct pollfd
+ {
+ Handle fd;
+ };
+#endif
+
+ SlotMap objects;
+ std::vector<pollfd> pfd;
+ bool pfd_dirty;
+ SlotSeq poll_result;
+
+ void rebuild_pfd();
+ int do_poll(int);
+
+public:
+ Poller();
+
+ void set_object(Base &, PollEvent);
+ int poll();
+ int poll(const Time::TimeDelta &);
+ const SlotSeq &get_result() const { return poll_result; }
+};
+
+PollEvent poll(Base &, PollEvent);
+PollEvent poll(Base &, PollEvent, const Time::TimeDelta &);
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_IO_PRINT_H_
+#define MSP_IO_PRINT_H_
+
+#include <msp/strings/formatter.h>
+#include "base.h"
+#include "console.h"
+
+namespace Msp {
+namespace IO {
+
+/**
+Writes a string to an I/O object. Same as o.write(f). Provided for
+completeness with the other print functions.
+*/
+inline unsigned print(Base &o, const std::string &f)
+{ return o.write(f); }
+
+template<typename A1>
+inline unsigned print(Base &o, const std::string &f, A1 a1)
+{ return print(o, format(f, a1)); }
+
+template<typename A1, typename A2>
+inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2)
+{ return print(o, format(f, a1, a2)); }
+
+template<typename A1, typename A2, typename A3>
+inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2, A3 a3)
+{ return print(o, format(f, a1, a2, a3)); }
+
+template<typename A1, typename A2, typename A3, typename A4>
+inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4)
+{ return print(o, format(f, a1, a2, a3, a4)); }
+
+template<typename A1, typename A2, typename A3, typename A4, typename A5>
+inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
+{ return print(o, format(f, a1, a2, a3, a4, a5)); }
+
+/* The following functions print to console */
+
+inline unsigned print(const std::string &f)
+{ return print(cout, f); }
+
+template<typename A1>
+inline unsigned print(const std::string &f, A1 a1)
+{ return print(cout, f, a1); }
+
+template<typename A1, typename A2>
+inline unsigned print(const std::string &f, A1 a1, A2 a2)
+{ return print(cout, f, a1, a2); }
+
+template<typename A1, typename A2, typename A3>
+inline unsigned print(const std::string &f, A1 a1, A2 a2, A3 a3)
+{ return print(cout, f, a1, a2, a3); }
+
+template<typename A1, typename A2, typename A3, typename A4>
+inline unsigned print(const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4)
+{ return print(cout, f, a1, a2, a3, a4); }
+
+template<typename A1, typename A2, typename A3, typename A4, typename A5>
+inline unsigned print(const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
+{ return print(cout, f, a1, a2, a3, a4, a5); }
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifdef WIN32
+#include <windows.h>
+#endif
+#include "except.h"
+#include "seek.h"
+
+namespace Msp {
+namespace IO {
+
+int sys_seek_type(SeekType st)
+{
+#ifdef WIN32
+ if(st==S_BEG)
+ return FILE_BEGIN;
+ else if(st==S_CUR)
+ return FILE_CURRENT;
+ else if(st==S_END)
+ return FILE_END;
+#else
+ if(st==S_BEG)
+ return SEEK_SET;
+ else if(st==S_CUR)
+ return SEEK_CUR;
+ else if(st==S_END)
+ return SEEK_END;
+#endif
+
+ throw InvalidParameterValue("Invalid seek type");
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_SEEK_H_
+#define MSP_IO_SEEK_H_
+
+namespace Msp {
+namespace IO {
+
+enum SeekType
+{
+ S_BEG,
+ S_CUR,
+ S_END
+};
+
+extern int sys_seek_type(SeekType);
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <termios.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+#include <msp/strings/formatter.h>
+#include "except.h"
+#include "serial.h"
+
+using namespace std;
+
+namespace {
+
+using namespace Msp;
+using namespace Msp::IO;
+
+#ifdef WIN32
+typedef DCB DeviceState;
+#else
+typedef termios DeviceState;
+#endif
+
+void get_state(Handle handle, DeviceState &state)
+{
+#ifdef WIN32
+ GetCommState(handle, &state);
+#else
+ tcgetattr(handle, &state);
+#endif
+}
+
+void set_state(Handle handle, DeviceState &state)
+{
+#ifdef WIN32
+ if(SetCommState(handle, &state)==0)
+ throw SystemError("Cannot set serial port parameters", GetLastError());
+#else
+ if(tcsetattr(handle, TCSADRAIN, &state)==-1)
+ throw SystemError("Cannot set serial port parameters", errno);
+#endif
+}
+
+void set_baud_rate(DeviceState &state, unsigned baud)
+{
+#ifdef WIN32
+ state.BaudRate = baud;
+#else
+ speed_t speed;
+ switch(baud)
+ {
+ case 0: speed = B0; break;
+ case 50: speed = B50; break;
+ case 75: speed = B75; break;
+ case 110: speed = B110; break;
+ case 134: speed = B134; break;
+ case 150: speed = B150; break;
+ case 200: speed = B200; break;
+ case 300: speed = B300; break;
+ case 600: speed = B600; break;
+ case 1200: speed = B1200; break;
+ case 1800: speed = B1800; break;
+ case 2400: speed = B2400; break;
+ case 4800: speed = B4800; break;
+ case 9600: speed = B9600; break;
+ case 19200: speed = B19200; break;
+ case 38400: speed = B38400; break;
+ case 57600: speed = B57600; break;
+ case 115200: speed = B115200; break;
+ case 230400: speed = B230400; break;
+ default: throw InvalidParameterValue("Invalid baud rate");
+ }
+
+ cfsetospeed(&state, speed);
+ cfsetispeed(&state, speed);
+#endif
+}
+
+void set_data_bits(DeviceState &state, unsigned bits)
+{
+#ifdef WIN32
+ state.ByteSize = bits;
+#else
+ tcflag_t flag;
+ switch(bits)
+ {
+ case 5: flag = CS5; break;
+ case 6: flag = CS6; break;
+ case 7: flag = CS7; break;
+ case 8: flag = CS8; break;
+ default: throw InvalidParameterValue("Invalid data bit count");
+ }
+
+ state.c_cflag = (state.c_cflag&~CSIZE)|flag;
+#endif
+}
+
+void set_parity(DeviceState &state, Serial::Parity par)
+{
+#ifdef WIN32
+ switch(par)
+ {
+ case Serial::NONE: state.Parity = NOPARITY; break;
+ case Serial::EVEN: state.Parity = EVENPARITY; break;
+ case Serial::ODD: state.Parity = ODDPARITY; break;
+ default: throw InvalidParameterValue("Invalid parity");
+ }
+#else
+ tcflag_t flag;
+ switch(par)
+ {
+ case Serial::NONE: flag = 0; break;
+ case Serial::EVEN: flag = PARENB; break;
+ case Serial::ODD: flag = PARENB|PARODD; break;
+ default: throw InvalidParameterValue("Invalid parity");
+ }
+
+ state.c_cflag = (state.c_cflag&~(PARENB|PARODD))|flag;
+#endif
+}
+
+void set_stop_bits(DeviceState &state, unsigned bits)
+{
+#ifdef WIN32
+ switch(bits)
+ {
+ case 1: state.StopBits = ONESTOPBIT; break;
+ case 2: state.StopBits = TWOSTOPBITS; break;
+ default: throw InvalidParameterValue("Invalid stop bit count");
+ }
+#else
+ tcflag_t flag;
+ switch(bits)
+ {
+ case 1: flag = 0; break;
+ case 2: flag = CSTOPB; break;
+ default: throw InvalidParameterValue("Invalid stop bit count");
+ }
+
+ state.c_cflag = (state.c_cflag&~CSTOPB)|flag;
+#endif
+}
+
+}
+
+
+namespace Msp {
+namespace IO {
+
+Serial::Serial(const string &descr)
+{
+ string::size_type comma = descr.find(',');
+ string port = descr.substr(0, comma);
+
+#ifdef WIN32
+ port = "\\\\.\\"+port;
+
+ handle = CreateFile(port.c_str(), GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if(handle==INVALID_HANDLE_VALUE)
+ throw SystemError(format("Can't open serial port '%s'", port), GetLastError());
+ mode = M_READ|M_WRITE;
+
+ COMMTIMEOUTS timeouts;
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+ timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+ timeouts.ReadTotalTimeoutConstant = MAXDWORD-1;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+ SetCommTimeouts(handle, &timeouts);
+#else
+ if(port.compare(0, 5, "/dev/"))
+ port = "/dev/"+port;
+
+ handle = open(port.c_str(), O_RDWR);
+ if(handle==-1)
+ throw SystemError(format("Can't open serial port '%s'", port), errno);
+ mode = M_READ|M_WRITE;
+
+ termios t;
+ tcgetattr(handle, &t);
+ t.c_lflag &= ~(ECHO|ICANON);
+ t.c_oflag &= ~OPOST;
+ tcsetattr(handle, TCSADRAIN, &t);
+#endif
+
+ if(comma!=string::npos)
+ {
+ try
+ {
+ set_parameters(descr.substr(comma+1));
+ }
+ catch(...)
+ {
+ close();
+ throw;
+ }
+ }
+
+ set_events(P_INPUT);
+}
+
+Serial::~Serial()
+{
+ close();
+}
+
+void Serial::close()
+{
+#ifdef WIN32
+ CloseHandle(handle);
+#else
+ ::close(handle);
+#endif
+}
+
+void Serial::set_block(bool b)
+{
+ if(b)
+ mode = mode|M_NONBLOCK;
+ else
+ mode = mode&~M_NONBLOCK;
+
+#ifndef WIN32
+ int flags = fcntl(handle, F_GETFD);
+ fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
+#endif
+}
+
+void Serial::set_baud_rate(unsigned rate)
+{
+ DeviceState state;
+ get_state(handle, state);
+ ::set_baud_rate(state, rate);
+ set_state(handle, state);
+}
+
+void Serial::set_data_bits(unsigned bits)
+{
+ DeviceState state;
+ get_state(handle, state);
+ ::set_data_bits(state, bits);
+ set_state(handle, state);
+}
+
+void Serial::set_parity(Parity par)
+{
+ DeviceState state;
+ get_state(handle, state);
+ ::set_parity(state, par);
+ set_state(handle, state);
+}
+
+void Serial::set_stop_bits(unsigned bits)
+{
+ DeviceState state;
+ get_state(handle, state);
+ ::set_stop_bits(state, bits);
+ set_state(handle, state);
+}
+
+void Serial::set_parameters(const string ¶ms)
+{
+ unsigned i;
+ for(i=0; i<params.size() && isdigit(params[i]); ++i) ;
+ if(i+4!=params.size() || params[i]!=',')
+ throw InvalidParameterValue("Invalid parameter string");
+ if(params[i+1]<'5' || params[i+1]>'8')
+ throw InvalidParameterValue("Invalid data bit count");
+ if(params[i+2]!='N' && params[i+2]!='E' && params[i+2]!='O')
+ throw InvalidParameterValue("Invalid parity");
+ if(params[i+3]!='1' && params[i+3]!='2')
+ throw InvalidParameterValue("Invalid stop bit count");
+
+ DeviceState state;
+ get_state(handle, state);
+ ::set_baud_rate(state, lexical_cast<unsigned>(params.substr(0, i)));
+ ::set_data_bits(state, params[i+1]-'0');
+ ::set_parity(state, (params[i+2]=='E' ? EVEN : params[i+2]=='O' ? ODD : NONE));
+ ::set_stop_bits(state, params[i+3]-'0');
+ set_state(handle, state);
+}
+
+unsigned Serial::do_write(const char *buf, unsigned size)
+{
+ if(size==0)
+ return 0;
+
+#ifdef WIN32
+ DWORD ret;
+ if(WriteFile(handle, buf, size, &ret, 0)==0)
+ throw SystemError("Writing to serial port failed", GetLastError());
+#else
+ int ret = ::write(handle, buf, size);
+ if(ret==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+ else
+ throw SystemError("Writing to serial port failed", errno);
+ }
+#endif
+
+ return ret;
+}
+
+unsigned Serial::do_read(char *buf, unsigned size)
+{
+ if(size==0)
+ return 0;
+
+#ifdef WIN32
+ DWORD ret;
+ if(ReadFile(handle, buf, size, &ret, 0)==0)
+ throw SystemError("Reading from serial port failed", GetLastError());
+#else
+ int ret = ::read(handle, buf, size);
+ if(ret==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+ else
+ throw SystemError("Reading from serial port failed", errno);
+ }
+#endif
+
+ return ret;
+}
+
+Handle Serial::get_event_handle()
+{
+#ifdef WIN32
+ throw Exception("Serial port events not supported on win32 yet");
+#else
+ return handle;
+#endif
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_SERIAL_H_
+#define MSP_IO_SERIAL_H_
+
+#include "base.h"
+
+namespace Msp {
+namespace IO {
+
+class Serial: public Base
+{
+public:
+ enum Parity
+ {
+ NONE,
+ EVEN,
+ ODD
+ };
+
+private:
+ Handle handle;
+
+public:
+ Serial(const std::string &);
+ virtual ~Serial();
+
+private:
+ void close();
+
+public:
+ virtual void set_block(bool);
+
+ void set_baud_rate(unsigned);
+ void set_data_bits(unsigned);
+ void set_parity(Parity);
+ void set_stop_bits(unsigned);
+ void set_parameters(const std::string &);
+
+private:
+ virtual unsigned do_write(const char *, unsigned);
+ virtual unsigned do_read(char *, unsigned);
+
+public:
+ virtual Handle get_event_handle();
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_IO_TYPES_H_
+#define MSP_IO_TYPES_H_
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+namespace Msp {
+namespace IO {
+
+#ifdef WIN32
+typedef HANDLE Handle;
+#define MSP_IO_INVALID_HANDLE INVALID_HANDLE_VALUE
+#else
+typedef int Handle;
+#define MSP_IO_INVALID_HANDLE -1
+#endif
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "base.h"
+#include "utils.h"
+
+namespace Msp {
+namespace IO {
+
+unsigned read_all(Base &obj, char *buf, unsigned size)
+{
+ unsigned pos = 0;
+ while(pos<size)
+ pos += obj.read(buf+pos, size-pos);
+
+ return pos;
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_UTILS_H_
+#define MSP_IO_UTILS_H_
+
+namespace Msp {
+namespace IO {
+
+class Base;
+
+/** Reads data from an object. Does not return until the requested amount of
+data is read, regardless of the blocking mode of the object.
+
+Note: If the data is not immediately available and the object is in non-blocking
+mode, this function effectively becomes a busyloop until it can get more
+data. */
+unsigned read_all(Base &, char *, unsigned);
+
+} // namespace IO
+} // namespace Msp
+
+#endif
+++ /dev/null
-#include <algorithm>
-#include <cstring>
-#include "except.h"
-#include "memory.h"
-
-using namespace std;
-
-namespace Msp {
-namespace IO {
-
-Memory::Memory(char *d, unsigned s)
-{
- init(d, d+s, M_RDWR);
-}
-
-Memory::Memory(char *b, char *e)
-{
- init(b, e, M_RDWR);
-}
-
-Memory::Memory(const char *cd, unsigned s)
-{
- char *d = const_cast<char *>(cd);
- init(d, d+s, M_READ);
-}
-
-Memory::Memory(const char *b, const char *e)
-{
- init(const_cast<char *>(b), const_cast<char *>(e), M_READ);
-}
-
-void Memory::init(char *b, char *e, Mode m)
-{
- begin = b;
- end = e;
- pos = begin;
- mode = m;
-}
-
-unsigned Memory::do_write(const char *buf, unsigned size)
-{
- check_mode(M_WRITE);
-
- size = min<unsigned>(size, end-pos);
- memcpy(pos, buf, size);
- pos += size;
- return size;
-}
-
-unsigned Memory::do_read(char *buf, unsigned size)
-{
- if(pos==end)
- {
- eof_flag = true;
- return 0;
- }
-
- size = min<unsigned>(size, end-pos);
- memcpy(buf, pos, size);
- pos += size;
- return size;
-}
-
-unsigned Memory::put(char c)
-{
- check_mode(M_WRITE);
- *pos++ = c;
- return 1;
-}
-
-bool Memory::getline(string &line)
-{
- char *nl = find(pos, end, '\n');
- line.assign(pos, nl);
- bool result = (nl!=pos);
- pos = nl;
- return result;
-}
-
-int Memory::get()
-{
- if(pos==end)
- {
- eof_flag = true;
- return -1;
- }
-
- return static_cast<unsigned char>(*pos++);
-}
-
-unsigned Memory::seek(int off, SeekType type)
-{
- char *new_pos;
- if(type==S_BEG)
- new_pos = begin+off;
- else if(type==S_CUR)
- new_pos = pos+off;
- else if(type==S_END)
- new_pos = end+off;
- else
- throw InvalidParameterValue("Invalid seek type");
-
- if(new_pos<begin || new_pos>end)
- throw InvalidParameterValue("Invalid seek offset");
-
- pos = new_pos;
- return pos-begin;
-}
-
-Handle Memory::get_event_handle()
-{
- throw Exception("Memory doesn't support events");
-}
-
-void Memory::check_mode(Mode m) const
-{
- if(m==M_WRITE)
- {
- if(!(mode&M_WRITE))
- throw InvalidState("Memory is not writable");
- if(pos==end)
- throw InvalidState("Attempt to write past end of Memory");
- }
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_MEMORY_H_
-#define MSP_IO_MEMORY_H_
-
-#include "base.h"
-#include "seek.h"
-
-namespace Msp {
-namespace IO {
-
-class Memory: public Base
-{
-private:
- char *begin;
- char *end;
- char *pos;
-
-public:
- Memory(char *, unsigned);
- Memory(char *, char *);
- Memory(const char *, unsigned);
- Memory(const char *, const char *);
-private:
- void init(char *, char *, Mode);
-
- virtual unsigned do_write(const char *, unsigned);
- virtual unsigned do_read(char *, unsigned);
-public:
- virtual unsigned put(char);
- virtual bool getline(std::string &);
- virtual int get();
-
- unsigned seek(int, SeekType);
- unsigned tell() const { return pos-begin; }
-
- virtual Handle get_event_handle();
-
-private:
- void check_mode(Mode) const;
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef MSP_IO_MODE_H_
-#define MSP_IO_MODE_H_
-
-namespace Msp {
-namespace IO {
-
-enum Mode
-{
- M_NONE = 0,
- M_READ = 1,
- M_WRITE = 2,
- M_RDWR = M_READ|M_WRITE,
- M_APPEND = 4,
- M_NONBLOCK = 8
-};
-
-inline Mode operator|(Mode m, Mode n)
-{ return Mode(static_cast<int>(m)|static_cast<int>(n)); }
-
-inline Mode operator&(Mode m, Mode n)
-{ return Mode(static_cast<int>(m)&static_cast<int>(n)); }
-
-inline Mode operator~(Mode m)
-{ return Mode(~static_cast<int>(m)); }
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef WIN32
-#include <fcntl.h>
-#include <errno.h>
-#endif
-#include <msp/strings/formatter.h>
-#include "pipe.h"
-
-using namespace std;
-
-namespace Msp {
-namespace IO {
-
-Pipe::Pipe()
-{
-#ifdef WIN32
- string name = format("\\\\.\\pipe\\%u.%p", GetCurrentProcessId(), this);
- handle[0] = CreateNamedPipe(name.c_str(), PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024, 0, 0);
- if(handle[0]==INVALID_HANDLE_VALUE)
- throw SystemError("Unable to create pipe", GetLastError());
-
- handle[1] = CreateFile(name.c_str(), GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
- if(handle[1]==INVALID_HANDLE_VALUE)
- {
- unsigned err = GetLastError();
- CloseHandle(handle[0]);
- throw SystemError("Unable to create pipe", err);
- }
-
- overlapped = 0;
- event = CreateEvent(0, true, false, 0);
- buf_size = 1024;
- buffer = new char[buf_size];
- buf_avail = 0;
- buf_next = buffer;
-#else
- if(pipe(handle)==-1)
- throw SystemError("Unable to create pipe", errno);
-#endif
-
- set_events(P_INPUT);
-}
-
-Pipe::~Pipe()
-{
- close();
-}
-
-void Pipe::close()
-{
- set_events(P_NONE);
-
- signal_flush_required.emit();
-#ifdef WIN32
- CloseHandle(handle[0]);
- CloseHandle(handle[1]);
-#else
- ::close(handle[0]);
- ::close(handle[1]);
- signal_closed.emit();
-#endif
-}
-
-void Pipe::set_block(bool b)
-{
- mode = (mode&~M_NONBLOCK);
- if(b)
- mode = (mode|M_NONBLOCK);
-
-#ifndef WIN32
- int flags = fcntl(handle[0], F_GETFD);
- fcntl(handle[0], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
- flags = fcntl(handle[1], F_GETFD);
- fcntl(handle[1], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
-#endif
-}
-
-unsigned Pipe::do_write(const char *buf, unsigned size)
-{
- if(size==0)
- return 0;
-
-#ifdef WIN32
- DWORD ret;
- if(!WriteFile(handle[1], buf, size, &ret, 0))
- throw SystemError("Writing to pipe failed", GetLastError());
-#else
- int ret = ::write(handle[1], buf, size);
- if(ret==-1)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Writing to pipe failed", errno);
- }
-#endif
-
- return ret;
-}
-
-unsigned Pipe::do_read(char *buf, unsigned size)
-{
- if(size==0)
- return 0;
-
-#ifdef WIN32
- // Initiate overlapped read if needed
- get_event_handle();
-
- if(overlapped)
- {
- DWORD ret;
- if(!GetOverlappedResult(handle[0], overlapped, &ret, !buf_avail))
- throw SystemError("Reading from pipe failed", GetLastError());
- else
- {
- buf_avail += ret;
- delete overlapped;
- overlapped = 0;
- }
- }
-
- unsigned ret = min(buf_avail, size);
- memcpy(buf, buf_next, ret);
- buf_next += ret;
- buf_avail -= ret;
-
- // Initiate another overlapped read in case someone is polling us
- get_event_handle();
-#else
- int ret = ::read(handle[0], buf, size);
- if(ret==-1)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Reading from pipe failed", errno);
- }
-#endif
-
- if(ret==0)
- {
- eof_flag = true;
- signal_end_of_file.emit();
- }
-
- return ret;
-}
-
-Handle Pipe::get_event_handle()
-{
-#ifdef WIN32
- if(!overlapped && !buf_avail)
- {
- overlapped = new OVERLAPPED;
- memset(overlapped, 0, sizeof(OVERLAPPED));
- overlapped->hEvent = event;
-
- DWORD ret;
- buf_next = buffer;
- if(!ReadFile(handle[0], buffer, buf_size, &ret, overlapped))
- {
- unsigned err = GetLastError();
- if(err!=ERROR_IO_PENDING)
- throw SystemError("Failed to start an overlapped read", err);
- }
- else
- {
- buf_avail = ret;
- delete overlapped;
- overlapped = 0;
- SetEvent(event);
- }
- }
-
- return event;
-#else
- return handle[0];
-#endif
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_PIPE_H_
-#define MSP_IO_PIPE_H_
-
-#include "base.h"
-
-namespace Msp {
-namespace IO {
-
-class Pipe: public Base
-{
-private:
- Handle handle[2];
-#ifdef WIN32
- OVERLAPPED *overlapped;
- Handle event;
- unsigned buf_size;
- char *buffer;
- unsigned buf_avail;
- char *buf_next;
-#endif
-
-public:
- Pipe();
- ~Pipe();
-
- void close();
-
- void set_block(bool);
-
-protected:
- virtual unsigned do_write(const char *, unsigned);
- virtual unsigned do_read(char *, unsigned);
-
-public:
- virtual Handle get_event_handle();
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#include <errno.h>
-#include <msp/strings/formatter.h>
-#include <msp/time/units.h>
-#include "except.h"
-#include "base.h"
-#include "poll.h"
-
-namespace {
-
-using namespace Msp;
-using namespace Msp::IO;
-
-inline int sys_poll_event(PollEvent event)
-{
- int result = 0;
-
- if(event&~(P_INPUT|P_PRIO|P_OUTPUT))
- throw InvalidParameterValue("Invalid poll events");
-
-#ifndef WIN32
- if(event&P_INPUT)
- result |= POLLIN;
- if(event&P_PRIO)
- result |= POLLPRI;
- if(event&P_OUTPUT)
- result |= POLLOUT;
-#endif
-
- return result;
-}
-
-inline PollEvent poll_event_from_sys(int event)
-{
- PollEvent result = P_NONE;
-
-#ifdef WIN32
- // Stop the compiler from complaining about unused parameter
- event = event;
-#else
- if(event&POLLIN)
- result = result|P_INPUT;
- if(event&POLLPRI)
- result = result|P_PRIO;
- if(event&POLLOUT)
- result = result|P_OUTPUT;
- if(event&POLLERR)
- result = result|P_ERROR;
-#endif
-
- return result;
-}
-
-inline PollEvent do_poll(Base &obj, PollEvent pe, int timeout)
-{
-#ifdef WIN32
- if(timeout<0)
- timeout = INFINITE;
-
- DWORD ret = WaitForSingleObject(obj.get_event_handle(), timeout);
- if(ret==WAIT_OBJECT_0)
- return pe;
- else if(ret==WAIT_FAILED)
- throw SystemError("Poll failed", GetLastError());
-
- return P_NONE;
-#else
- pollfd pfd = {obj.get_event_handle(), sys_poll_event(pe), 0};
-
- int ret = ::poll(&pfd, 1, timeout);
- if(ret==-1)
- {
- if(errno==EINTR)
- return P_NONE;
- else
- throw SystemError("Poll failed", errno);
- }
-
- return poll_event_from_sys(pfd.revents);
-#endif
-}
-
-}
-
-namespace Msp {
-namespace IO {
-
-Poller::Poller():
- pfd_dirty(false)
-{ }
-
-void Poller::set_object(Base &obj, PollEvent ev)
-{
- // Verify that the object has an event handle
- if(ev)
- obj.get_event_handle();
-
- SlotMap::iterator i = objects.find(&obj);
- if(i!=objects.end())
- {
- if(ev)
- i->second.events = ev;
- else
- objects.erase(i);
-
- pfd_dirty = true;
- }
- else if(ev)
- {
-#ifdef WIN32
- if(objects.size()>=MAXIMUM_WAIT_OBJECTS)
- throw InvalidState("Maximum number of wait objects reached");
-#endif
- objects.insert(SlotMap::value_type(&obj, Slot(&obj, ev)));
-
- pfd_dirty = true;
- }
-}
-
-int Poller::poll()
-{
- return do_poll(-1);
-}
-
-int Poller::poll(const Time::TimeDelta &timeout)
-{
- if(timeout<Time::zero)
- throw InvalidParameterValue("Invalid timeout");
-
- return do_poll(static_cast<int>(timeout/Time::msec));
-}
-
-void Poller::rebuild_pfd()
-{
- pfd.clear();
-
- pollfd p;
-
- for(SlotMap::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- p.fd = i->second.object->get_event_handle();
-#ifndef WIN32
- p.events = sys_poll_event(i->second.events);
-#endif
- pfd.push_back(p);
- }
-
- pfd_dirty = false;
-}
-
-int Poller::do_poll(int timeout)
-{
- if(pfd_dirty)
- rebuild_pfd();
-
- poll_result.clear();
-
-#ifdef WIN32
- if(timeout<0)
- timeout = INFINITE;
-
- DWORD ret = WaitForMultipleObjects(pfd.size(), &pfd.front().fd, false, timeout);
- if(/*ret>=WAIT_OBJECT_0 &&*/ ret<WAIT_OBJECT_0+pfd.size())
- {
- SlotMap::iterator i = objects.begin();
- advance(i, ret-WAIT_OBJECT_0);
- poll_result.push_back(Slot(i->second.object, i->second.events));
-
- return 1;
- }
- else if(ret==WAIT_FAILED)
- throw SystemError("Poll failed", GetLastError());
-
- return 0;
-#else
- int ret = ::poll(&pfd.front(), pfd.size(), timeout);
- if(ret==-1)
- {
- if(errno==EINTR)
- return 0;
- else
- throw SystemError("Poll failed", errno);
- }
-
- int n = ret;
- SlotMap::iterator j = objects.begin();
- for(std::vector<pollfd>::iterator i=pfd.begin(); (i!=pfd.end() && n>0); ++i,++j)
- if(i->revents)
- {
- poll_result.push_back(Slot(j->second.object, poll_event_from_sys(i->revents)));
- --n;
- }
-
- return ret;
-#endif
-}
-
-
-PollEvent poll(Base &obj, PollEvent pe)
-{
- return do_poll(obj, pe, -1);
-}
-
-PollEvent poll(Base &obj, PollEvent pe, const Time::TimeDelta &timeout)
-{
- if(timeout<Time::zero)
- throw InvalidParameterValue("Invalid timeout");
-
- return do_poll(obj, pe, static_cast<int>(timeout/Time::msec));
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_POLL_H_
-#define MSP_IO_POLL_H_
-
-#ifndef WIN32
-#include <poll.h>
-#endif
-#include <list>
-#include <map>
-#include <vector>
-#include <msp/time/timedelta.h>
-#include "types.h"
-
-namespace Msp {
-namespace IO {
-
-class Base;
-
-enum PollEvent
-{
- P_NONE = 0,
- P_INPUT = 1,
- P_PRIO = 2,
- P_OUTPUT = 4,
- P_ERROR = 8
-};
-
-inline PollEvent operator|(PollEvent e, PollEvent f)
-{ return PollEvent(static_cast<int>(e)|static_cast<int>(f)); }
-
-inline PollEvent operator&(PollEvent e, PollEvent f)
-{ return PollEvent(static_cast<int>(e)&static_cast<int>(f)); }
-
-inline PollEvent operator~(PollEvent e)
-{ return PollEvent(~static_cast<int>(e)); }
-
-
-class Poller
-{
-public:
- struct Slot
- {
- Base *object;
- PollEvent events;
-
- Slot(Base *o, PollEvent e): object(o), events(e) { }
- };
-
- typedef std::list<Slot> SlotSeq;
-private:
- typedef std::map<Base *, Slot> SlotMap;
-
-#ifdef WIN32
- struct pollfd
- {
- Handle fd;
- };
-#endif
-
- SlotMap objects;
- std::vector<pollfd> pfd;
- bool pfd_dirty;
- SlotSeq poll_result;
-
- void rebuild_pfd();
- int do_poll(int);
-
-public:
- Poller();
-
- void set_object(Base &, PollEvent);
- int poll();
- int poll(const Time::TimeDelta &);
- const SlotSeq &get_result() const { return poll_result; }
-};
-
-PollEvent poll(Base &, PollEvent);
-PollEvent poll(Base &, PollEvent, const Time::TimeDelta &);
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef MSP_IO_PRINT_H_
-#define MSP_IO_PRINT_H_
-
-#include <msp/strings/formatter.h>
-#include "base.h"
-#include "console.h"
-
-namespace Msp {
-namespace IO {
-
-/**
-Writes a string to an I/O object. Same as o.write(f). Provided for
-completeness with the other print functions.
-*/
-inline unsigned print(Base &o, const std::string &f)
-{ return o.write(f); }
-
-template<typename A1>
-inline unsigned print(Base &o, const std::string &f, A1 a1)
-{ return print(o, format(f, a1)); }
-
-template<typename A1, typename A2>
-inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2)
-{ return print(o, format(f, a1, a2)); }
-
-template<typename A1, typename A2, typename A3>
-inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2, A3 a3)
-{ return print(o, format(f, a1, a2, a3)); }
-
-template<typename A1, typename A2, typename A3, typename A4>
-inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4)
-{ return print(o, format(f, a1, a2, a3, a4)); }
-
-template<typename A1, typename A2, typename A3, typename A4, typename A5>
-inline unsigned print(Base &o, const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
-{ return print(o, format(f, a1, a2, a3, a4, a5)); }
-
-/* The following functions print to console */
-
-inline unsigned print(const std::string &f)
-{ return print(cout, f); }
-
-template<typename A1>
-inline unsigned print(const std::string &f, A1 a1)
-{ return print(cout, f, a1); }
-
-template<typename A1, typename A2>
-inline unsigned print(const std::string &f, A1 a1, A2 a2)
-{ return print(cout, f, a1, a2); }
-
-template<typename A1, typename A2, typename A3>
-inline unsigned print(const std::string &f, A1 a1, A2 a2, A3 a3)
-{ return print(cout, f, a1, a2, a3); }
-
-template<typename A1, typename A2, typename A3, typename A4>
-inline unsigned print(const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4)
-{ return print(cout, f, a1, a2, a3, a4); }
-
-template<typename A1, typename A2, typename A3, typename A4, typename A5>
-inline unsigned print(const std::string &f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
-{ return print(cout, f, a1, a2, a3, a4, a5); }
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifdef WIN32
-#include <windows.h>
-#endif
-#include "except.h"
-#include "seek.h"
-
-namespace Msp {
-namespace IO {
-
-int sys_seek_type(SeekType st)
-{
-#ifdef WIN32
- if(st==S_BEG)
- return FILE_BEGIN;
- else if(st==S_CUR)
- return FILE_CURRENT;
- else if(st==S_END)
- return FILE_END;
-#else
- if(st==S_BEG)
- return SEEK_SET;
- else if(st==S_CUR)
- return SEEK_CUR;
- else if(st==S_END)
- return SEEK_END;
-#endif
-
- throw InvalidParameterValue("Invalid seek type");
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_SEEK_H_
-#define MSP_IO_SEEK_H_
-
-namespace Msp {
-namespace IO {
-
-enum SeekType
-{
- S_BEG,
- S_CUR,
- S_END
-};
-
-extern int sys_seek_type(SeekType);
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifdef WIN32
-#include <windows.h>
-#else
-#include <termios.h>
-#include <fcntl.h>
-#include <errno.h>
-#endif
-#include <msp/strings/formatter.h>
-#include "except.h"
-#include "serial.h"
-
-using namespace std;
-
-namespace {
-
-using namespace Msp;
-using namespace Msp::IO;
-
-#ifdef WIN32
-typedef DCB DeviceState;
-#else
-typedef termios DeviceState;
-#endif
-
-void get_state(Handle handle, DeviceState &state)
-{
-#ifdef WIN32
- GetCommState(handle, &state);
-#else
- tcgetattr(handle, &state);
-#endif
-}
-
-void set_state(Handle handle, DeviceState &state)
-{
-#ifdef WIN32
- if(SetCommState(handle, &state)==0)
- throw SystemError("Cannot set serial port parameters", GetLastError());
-#else
- if(tcsetattr(handle, TCSADRAIN, &state)==-1)
- throw SystemError("Cannot set serial port parameters", errno);
-#endif
-}
-
-void set_baud_rate(DeviceState &state, unsigned baud)
-{
-#ifdef WIN32
- state.BaudRate = baud;
-#else
- speed_t speed;
- switch(baud)
- {
- case 0: speed = B0; break;
- case 50: speed = B50; break;
- case 75: speed = B75; break;
- case 110: speed = B110; break;
- case 134: speed = B134; break;
- case 150: speed = B150; break;
- case 200: speed = B200; break;
- case 300: speed = B300; break;
- case 600: speed = B600; break;
- case 1200: speed = B1200; break;
- case 1800: speed = B1800; break;
- case 2400: speed = B2400; break;
- case 4800: speed = B4800; break;
- case 9600: speed = B9600; break;
- case 19200: speed = B19200; break;
- case 38400: speed = B38400; break;
- case 57600: speed = B57600; break;
- case 115200: speed = B115200; break;
- case 230400: speed = B230400; break;
- default: throw InvalidParameterValue("Invalid baud rate");
- }
-
- cfsetospeed(&state, speed);
- cfsetispeed(&state, speed);
-#endif
-}
-
-void set_data_bits(DeviceState &state, unsigned bits)
-{
-#ifdef WIN32
- state.ByteSize = bits;
-#else
- tcflag_t flag;
- switch(bits)
- {
- case 5: flag = CS5; break;
- case 6: flag = CS6; break;
- case 7: flag = CS7; break;
- case 8: flag = CS8; break;
- default: throw InvalidParameterValue("Invalid data bit count");
- }
-
- state.c_cflag = (state.c_cflag&~CSIZE)|flag;
-#endif
-}
-
-void set_parity(DeviceState &state, Serial::Parity par)
-{
-#ifdef WIN32
- switch(par)
- {
- case Serial::NONE: state.Parity = NOPARITY; break;
- case Serial::EVEN: state.Parity = EVENPARITY; break;
- case Serial::ODD: state.Parity = ODDPARITY; break;
- default: throw InvalidParameterValue("Invalid parity");
- }
-#else
- tcflag_t flag;
- switch(par)
- {
- case Serial::NONE: flag = 0; break;
- case Serial::EVEN: flag = PARENB; break;
- case Serial::ODD: flag = PARENB|PARODD; break;
- default: throw InvalidParameterValue("Invalid parity");
- }
-
- state.c_cflag = (state.c_cflag&~(PARENB|PARODD))|flag;
-#endif
-}
-
-void set_stop_bits(DeviceState &state, unsigned bits)
-{
-#ifdef WIN32
- switch(bits)
- {
- case 1: state.StopBits = ONESTOPBIT; break;
- case 2: state.StopBits = TWOSTOPBITS; break;
- default: throw InvalidParameterValue("Invalid stop bit count");
- }
-#else
- tcflag_t flag;
- switch(bits)
- {
- case 1: flag = 0; break;
- case 2: flag = CSTOPB; break;
- default: throw InvalidParameterValue("Invalid stop bit count");
- }
-
- state.c_cflag = (state.c_cflag&~CSTOPB)|flag;
-#endif
-}
-
-}
-
-
-namespace Msp {
-namespace IO {
-
-Serial::Serial(const string &descr)
-{
- string::size_type comma = descr.find(',');
- string port = descr.substr(0, comma);
-
-#ifdef WIN32
- port = "\\\\.\\"+port;
-
- handle = CreateFile(port.c_str(), GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if(handle==INVALID_HANDLE_VALUE)
- throw SystemError(format("Can't open serial port '%s'", port), GetLastError());
- mode = M_READ|M_WRITE;
-
- COMMTIMEOUTS timeouts;
- timeouts.ReadIntervalTimeout = MAXDWORD;
- timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
- timeouts.ReadTotalTimeoutConstant = MAXDWORD-1;
- timeouts.WriteTotalTimeoutMultiplier = 0;
- timeouts.WriteTotalTimeoutConstant = 0;
- SetCommTimeouts(handle, &timeouts);
-#else
- if(port.compare(0, 5, "/dev/"))
- port = "/dev/"+port;
-
- handle = open(port.c_str(), O_RDWR);
- if(handle==-1)
- throw SystemError(format("Can't open serial port '%s'", port), errno);
- mode = M_READ|M_WRITE;
-
- termios t;
- tcgetattr(handle, &t);
- t.c_lflag &= ~(ECHO|ICANON);
- t.c_oflag &= ~OPOST;
- tcsetattr(handle, TCSADRAIN, &t);
-#endif
-
- if(comma!=string::npos)
- {
- try
- {
- set_parameters(descr.substr(comma+1));
- }
- catch(...)
- {
- close();
- throw;
- }
- }
-
- set_events(P_INPUT);
-}
-
-Serial::~Serial()
-{
- close();
-}
-
-void Serial::close()
-{
-#ifdef WIN32
- CloseHandle(handle);
-#else
- ::close(handle);
-#endif
-}
-
-void Serial::set_block(bool b)
-{
- if(b)
- mode = mode|M_NONBLOCK;
- else
- mode = mode&~M_NONBLOCK;
-
-#ifndef WIN32
- int flags = fcntl(handle, F_GETFD);
- fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
-#endif
-}
-
-void Serial::set_baud_rate(unsigned rate)
-{
- DeviceState state;
- get_state(handle, state);
- ::set_baud_rate(state, rate);
- set_state(handle, state);
-}
-
-void Serial::set_data_bits(unsigned bits)
-{
- DeviceState state;
- get_state(handle, state);
- ::set_data_bits(state, bits);
- set_state(handle, state);
-}
-
-void Serial::set_parity(Parity par)
-{
- DeviceState state;
- get_state(handle, state);
- ::set_parity(state, par);
- set_state(handle, state);
-}
-
-void Serial::set_stop_bits(unsigned bits)
-{
- DeviceState state;
- get_state(handle, state);
- ::set_stop_bits(state, bits);
- set_state(handle, state);
-}
-
-void Serial::set_parameters(const string ¶ms)
-{
- unsigned i;
- for(i=0; i<params.size() && isdigit(params[i]); ++i) ;
- if(i+4!=params.size() || params[i]!=',')
- throw InvalidParameterValue("Invalid parameter string");
- if(params[i+1]<'5' || params[i+1]>'8')
- throw InvalidParameterValue("Invalid data bit count");
- if(params[i+2]!='N' && params[i+2]!='E' && params[i+2]!='O')
- throw InvalidParameterValue("Invalid parity");
- if(params[i+3]!='1' && params[i+3]!='2')
- throw InvalidParameterValue("Invalid stop bit count");
-
- DeviceState state;
- get_state(handle, state);
- ::set_baud_rate(state, lexical_cast<unsigned>(params.substr(0, i)));
- ::set_data_bits(state, params[i+1]-'0');
- ::set_parity(state, (params[i+2]=='E' ? EVEN : params[i+2]=='O' ? ODD : NONE));
- ::set_stop_bits(state, params[i+3]-'0');
- set_state(handle, state);
-}
-
-unsigned Serial::do_write(const char *buf, unsigned size)
-{
- if(size==0)
- return 0;
-
-#ifdef WIN32
- DWORD ret;
- if(WriteFile(handle, buf, size, &ret, 0)==0)
- throw SystemError("Writing to serial port failed", GetLastError());
-#else
- int ret = ::write(handle, buf, size);
- if(ret==-1)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Writing to serial port failed", errno);
- }
-#endif
-
- return ret;
-}
-
-unsigned Serial::do_read(char *buf, unsigned size)
-{
- if(size==0)
- return 0;
-
-#ifdef WIN32
- DWORD ret;
- if(ReadFile(handle, buf, size, &ret, 0)==0)
- throw SystemError("Reading from serial port failed", GetLastError());
-#else
- int ret = ::read(handle, buf, size);
- if(ret==-1)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Reading from serial port failed", errno);
- }
-#endif
-
- return ret;
-}
-
-Handle Serial::get_event_handle()
-{
-#ifdef WIN32
- throw Exception("Serial port events not supported on win32 yet");
-#else
- return handle;
-#endif
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_SERIAL_H_
-#define MSP_IO_SERIAL_H_
-
-#include "base.h"
-
-namespace Msp {
-namespace IO {
-
-class Serial: public Base
-{
-public:
- enum Parity
- {
- NONE,
- EVEN,
- ODD
- };
-
-private:
- Handle handle;
-
-public:
- Serial(const std::string &);
- virtual ~Serial();
-
-private:
- void close();
-
-public:
- virtual void set_block(bool);
-
- void set_baud_rate(unsigned);
- void set_data_bits(unsigned);
- void set_parity(Parity);
- void set_stop_bits(unsigned);
- void set_parameters(const std::string &);
-
-private:
- virtual unsigned do_write(const char *, unsigned);
- virtual unsigned do_read(char *, unsigned);
-
-public:
- virtual Handle get_event_handle();
-};
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef MSP_IO_TYPES_H_
-#define MSP_IO_TYPES_H_
-
-#ifdef WIN32
-#include <windows.h>
-#endif
-
-namespace Msp {
-namespace IO {
-
-#ifdef WIN32
-typedef HANDLE Handle;
-#define MSP_IO_INVALID_HANDLE INVALID_HANDLE_VALUE
-#else
-typedef int Handle;
-#define MSP_IO_INVALID_HANDLE -1
-#endif
-
-} // namespace IO
-} // namespace Msp
-
-#endif
+++ /dev/null
-#include "base.h"
-#include "utils.h"
-
-namespace Msp {
-namespace IO {
-
-unsigned read_all(Base &obj, char *buf, unsigned size)
-{
- unsigned pos = 0;
- while(pos<size)
- pos += obj.read(buf+pos, size-pos);
-
- return pos;
-}
-
-} // namespace IO
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_IO_UTILS_H_
-#define MSP_IO_UTILS_H_
-
-namespace Msp {
-namespace IO {
-
-class Base;
-
-/** Reads data from an object. Does not return until the requested amount of
-data is read, regardless of the blocking mode of the object.
-
-Note: If the data is not immediately available and the object is in non-blocking
-mode, this function effectively becomes a busyloop until it can get more
-data. */
-unsigned read_all(Base &, char *, unsigned);
-
-} // namespace IO
-} // namespace Msp
-
-#endif