Move files to prepare for assimilation into core
authorMikko Rasa <tdb@tdb.fi>
Fri, 10 Jun 2011 18:01:55 +0000 (21:01 +0300)
committerMikko Rasa <tdb@tdb.fi>
Fri, 10 Jun 2011 18:01:55 +0000 (21:01 +0300)
54 files changed:
source/base.cpp [deleted file]
source/base.h [deleted file]
source/buffered.cpp [deleted file]
source/buffered.h [deleted file]
source/console.cpp [deleted file]
source/console.h [deleted file]
source/eventdispatcher.cpp [deleted file]
source/eventdispatcher.h [deleted file]
source/except.h [deleted file]
source/file.cpp [deleted file]
source/file.h [deleted file]
source/filtered.h [deleted file]
source/io/base.cpp [new file with mode: 0644]
source/io/base.h [new file with mode: 0644]
source/io/buffered.cpp [new file with mode: 0644]
source/io/buffered.h [new file with mode: 0644]
source/io/console.cpp [new file with mode: 0644]
source/io/console.h [new file with mode: 0644]
source/io/eventdispatcher.cpp [new file with mode: 0644]
source/io/eventdispatcher.h [new file with mode: 0644]
source/io/except.h [new file with mode: 0644]
source/io/file.cpp [new file with mode: 0644]
source/io/file.h [new file with mode: 0644]
source/io/filtered.h [new file with mode: 0644]
source/io/memory.cpp [new file with mode: 0644]
source/io/memory.h [new file with mode: 0644]
source/io/mode.h [new file with mode: 0644]
source/io/pipe.cpp [new file with mode: 0644]
source/io/pipe.h [new file with mode: 0644]
source/io/poll.cpp [new file with mode: 0644]
source/io/poll.h [new file with mode: 0644]
source/io/print.h [new file with mode: 0644]
source/io/seek.cpp [new file with mode: 0644]
source/io/seek.h [new file with mode: 0644]
source/io/serial.cpp [new file with mode: 0644]
source/io/serial.h [new file with mode: 0644]
source/io/types.h [new file with mode: 0644]
source/io/utils.cpp [new file with mode: 0644]
source/io/utils.h [new file with mode: 0644]
source/memory.cpp [deleted file]
source/memory.h [deleted file]
source/mode.h [deleted file]
source/pipe.cpp [deleted file]
source/pipe.h [deleted file]
source/poll.cpp [deleted file]
source/poll.h [deleted file]
source/print.h [deleted file]
source/seek.cpp [deleted file]
source/seek.h [deleted file]
source/serial.cpp [deleted file]
source/serial.h [deleted file]
source/types.h [deleted file]
source/utils.cpp [deleted file]
source/utils.h [deleted file]

diff --git a/source/base.cpp b/source/base.cpp
deleted file mode 100644 (file)
index 48de3ee..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#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
diff --git a/source/base.h b/source/base.h
deleted file mode 100644 (file)
index 774d034..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-#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
diff --git a/source/buffered.cpp b/source/buffered.cpp
deleted file mode 100644 (file)
index f53d2fc..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-#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
diff --git a/source/buffered.h b/source/buffered.h
deleted file mode 100644 (file)
index 36d2a11..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#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
diff --git a/source/console.cpp b/source/console.cpp
deleted file mode 100644 (file)
index 9db7737..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-#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
diff --git a/source/console.h b/source/console.h
deleted file mode 100644 (file)
index 3c0bb71..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#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
diff --git a/source/eventdispatcher.cpp b/source/eventdispatcher.cpp
deleted file mode 100644 (file)
index 06a594f..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#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
diff --git a/source/eventdispatcher.h b/source/eventdispatcher.h
deleted file mode 100644 (file)
index 7e89d47..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#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
diff --git a/source/except.h b/source/except.h
deleted file mode 100644 (file)
index 5770f64..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#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
diff --git a/source/file.cpp b/source/file.cpp
deleted file mode 100644 (file)
index 2990d5c..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-#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
diff --git a/source/file.h b/source/file.h
deleted file mode 100644 (file)
index 05aaf95..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#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
diff --git a/source/filtered.h b/source/filtered.h
deleted file mode 100644 (file)
index 0d323ec..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#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
diff --git a/source/io/base.cpp b/source/io/base.cpp
new file mode 100644 (file)
index 0000000..48de3ee
--- /dev/null
@@ -0,0 +1,61 @@
+#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
diff --git a/source/io/base.h b/source/io/base.h
new file mode 100644 (file)
index 0000000..774d034
--- /dev/null
@@ -0,0 +1,121 @@
+#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
diff --git a/source/io/buffered.cpp b/source/io/buffered.cpp
new file mode 100644 (file)
index 0000000..f53d2fc
--- /dev/null
@@ -0,0 +1,188 @@
+#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
diff --git a/source/io/buffered.h b/source/io/buffered.h
new file mode 100644 (file)
index 0000000..36d2a11
--- /dev/null
@@ -0,0 +1,46 @@
+#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
diff --git a/source/io/console.cpp b/source/io/console.cpp
new file mode 100644 (file)
index 0000000..9db7737
--- /dev/null
@@ -0,0 +1,187 @@
+#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
diff --git a/source/io/console.h b/source/io/console.h
new file mode 100644 (file)
index 0000000..3c0bb71
--- /dev/null
@@ -0,0 +1,56 @@
+#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
diff --git a/source/io/eventdispatcher.cpp b/source/io/eventdispatcher.cpp
new file mode 100644 (file)
index 0000000..06a594f
--- /dev/null
@@ -0,0 +1,75 @@
+#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
diff --git a/source/io/eventdispatcher.h b/source/io/eventdispatcher.h
new file mode 100644 (file)
index 0000000..7e89d47
--- /dev/null
@@ -0,0 +1,55 @@
+#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
diff --git a/source/io/except.h b/source/io/except.h
new file mode 100644 (file)
index 0000000..5770f64
--- /dev/null
@@ -0,0 +1,22 @@
+#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
diff --git a/source/io/file.cpp b/source/io/file.cpp
new file mode 100644 (file)
index 0000000..2990d5c
--- /dev/null
@@ -0,0 +1,242 @@
+#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
diff --git a/source/io/file.h b/source/io/file.h
new file mode 100644 (file)
index 0000000..05aaf95
--- /dev/null
@@ -0,0 +1,77 @@
+#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
diff --git a/source/io/filtered.h b/source/io/filtered.h
new file mode 100644 (file)
index 0000000..0d323ec
--- /dev/null
@@ -0,0 +1,66 @@
+#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
diff --git a/source/io/memory.cpp b/source/io/memory.cpp
new file mode 100644 (file)
index 0000000..6ea130b
--- /dev/null
@@ -0,0 +1,127 @@
+#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
diff --git a/source/io/memory.h b/source/io/memory.h
new file mode 100644 (file)
index 0000000..ba6c4d8
--- /dev/null
@@ -0,0 +1,44 @@
+#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
diff --git a/source/io/mode.h b/source/io/mode.h
new file mode 100644 (file)
index 0000000..5e103f2
--- /dev/null
@@ -0,0 +1,29 @@
+#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
diff --git a/source/io/pipe.cpp b/source/io/pipe.cpp
new file mode 100644 (file)
index 0000000..b3735aa
--- /dev/null
@@ -0,0 +1,182 @@
+#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
diff --git a/source/io/pipe.h b/source/io/pipe.h
new file mode 100644 (file)
index 0000000..71edf7e
--- /dev/null
@@ -0,0 +1,41 @@
+#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
diff --git a/source/io/poll.cpp b/source/io/poll.cpp
new file mode 100644 (file)
index 0000000..dce1912
--- /dev/null
@@ -0,0 +1,212 @@
+#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
diff --git a/source/io/poll.h b/source/io/poll.h
new file mode 100644 (file)
index 0000000..6367d8f
--- /dev/null
@@ -0,0 +1,82 @@
+#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
diff --git a/source/io/print.h b/source/io/print.h
new file mode 100644 (file)
index 0000000..65744cb
--- /dev/null
@@ -0,0 +1,66 @@
+#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
diff --git a/source/io/seek.cpp b/source/io/seek.cpp
new file mode 100644 (file)
index 0000000..b10c5a0
--- /dev/null
@@ -0,0 +1,32 @@
+#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
diff --git a/source/io/seek.h b/source/io/seek.h
new file mode 100644 (file)
index 0000000..3c6c331
--- /dev/null
@@ -0,0 +1,19 @@
+#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
diff --git a/source/io/serial.cpp b/source/io/serial.cpp
new file mode 100644 (file)
index 0000000..ec6a198
--- /dev/null
@@ -0,0 +1,340 @@
+#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 &params)
+{
+       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
diff --git a/source/io/serial.h b/source/io/serial.h
new file mode 100644 (file)
index 0000000..069789c
--- /dev/null
@@ -0,0 +1,49 @@
+#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
diff --git a/source/io/types.h b/source/io/types.h
new file mode 100644 (file)
index 0000000..f18b7cb
--- /dev/null
@@ -0,0 +1,22 @@
+#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
diff --git a/source/io/utils.cpp b/source/io/utils.cpp
new file mode 100644 (file)
index 0000000..eee51cc
--- /dev/null
@@ -0,0 +1,17 @@
+#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
diff --git a/source/io/utils.h b/source/io/utils.h
new file mode 100644 (file)
index 0000000..8d4f9dd
--- /dev/null
@@ -0,0 +1,20 @@
+#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
diff --git a/source/memory.cpp b/source/memory.cpp
deleted file mode 100644 (file)
index 6ea130b..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-#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
diff --git a/source/memory.h b/source/memory.h
deleted file mode 100644 (file)
index ba6c4d8..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#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
diff --git a/source/mode.h b/source/mode.h
deleted file mode 100644 (file)
index 5e103f2..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#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
diff --git a/source/pipe.cpp b/source/pipe.cpp
deleted file mode 100644 (file)
index b3735aa..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-#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
diff --git a/source/pipe.h b/source/pipe.h
deleted file mode 100644 (file)
index 71edf7e..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#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
diff --git a/source/poll.cpp b/source/poll.cpp
deleted file mode 100644 (file)
index dce1912..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-#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
diff --git a/source/poll.h b/source/poll.h
deleted file mode 100644 (file)
index 6367d8f..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#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
diff --git a/source/print.h b/source/print.h
deleted file mode 100644 (file)
index 65744cb..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#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
diff --git a/source/seek.cpp b/source/seek.cpp
deleted file mode 100644 (file)
index b10c5a0..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#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
diff --git a/source/seek.h b/source/seek.h
deleted file mode 100644 (file)
index 3c6c331..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#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
diff --git a/source/serial.cpp b/source/serial.cpp
deleted file mode 100644 (file)
index ec6a198..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-#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 &params)
-{
-       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
diff --git a/source/serial.h b/source/serial.h
deleted file mode 100644 (file)
index 069789c..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#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
diff --git a/source/types.h b/source/types.h
deleted file mode 100644 (file)
index f18b7cb..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#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
diff --git a/source/utils.cpp b/source/utils.cpp
deleted file mode 100644 (file)
index eee51cc..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#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
diff --git a/source/utils.h b/source/utils.h
deleted file mode 100644 (file)
index 8d4f9dd..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#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