#13795: Initial commit for sflphone-android
includes: libexpat libyaml libdbus-c++ commoncpp ccrtp
libdbus (from android-4.0.4 sources)
TODO:
- git ignores "/jni/sflphone", sflphone repo should be cloned.
- sflphone-android only needs daemon directory. Ideally it should be possible
to clone it without cloning the whole sflphone project.
into sfl-android (commit 6a0fa7a "#13961: Fix cipher handling" has been used here)
- add pjsip-android project as a git submodule
- sflphone-android needs pjsip android project. Ideally daemon git repository
should not embed pjsip. Instead pjsip should be clone from official repositories.
Considering this, structure should have three distincts git repos:
sflphone-android/.git
sflphone-android/jni/ccrtp-1.8.0-android
sflphone-android/jni/commoncpp2-1.8.1-android
sflphone-android/jni/dbus
sflphone-android/jni/libdbus-c++-0.9.0-android
sflphone-android/jni/libexpat
sflphone-android/jni/libyaml
sflphone-android/jni/sflphone-daemon/.git
sflphone-android/jni/sflphone-daemon/src/audio
sflphone-android/jni/sflphone-daemon/src/config
sflphone-android/jni/sflphone-daemon/src/dbus
sflphone-android/jni/sflphone-daemon/src/history
sflphone-android/jni/sflphone-daemon/src/hooks
sflphone-android/jni/sflphone-daemon/src/iax
sflphone-android/jni/sflphone-daemon/src/sip
sflphone-android/jni/sflphone-daemon/src/video
sflphone-android/jni/pjsip-android/.git
Signed-off-by: Emeric Vigier <emeric.vigier@savoirfairelinux.com>
diff --git a/jni/commoncpp2-1.8.1-android/src/serial.cpp b/jni/commoncpp2-1.8.1-android/src/serial.cpp
new file mode 100644
index 0000000..ac72405
--- /dev/null
+++ b/jni/commoncpp2-1.8.1-android/src/serial.cpp
@@ -0,0 +1,1872 @@
+// Copyright (C) 1999-2005 Open Source Telecom Corporation.
+// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+//
+// This exception applies only to the code released under the name GNU
+// Common C++. If you copy code from other releases into a copy of GNU
+// Common C++, as the General Public License permits, the exception does
+// not apply to the code that you add in this way. To avoid misleading
+// anyone as to the status of such modified files, you must delete
+// this exception notice from them.
+//
+// If you write modifications of your own for GNU Common C++, it is your choice
+// whether to permit this exception to apply to your modifications.
+// If you do not wish that, delete this exception notice.
+//
+
+#include <cc++/config.h>
+#ifdef CCXX_WITHOUT_EXTRAS
+#include <cc++/export.h>
+#endif
+#include <cc++/exception.h>
+#include <cc++/thread.h>
+#include <cc++/file.h>
+#ifndef CCXX_WITHOUT_EXTRAS
+#include <cc++/export.h>
+#endif
+#include <cc++/serial.h>
+#include "private.h"
+#include <cstdlib>
+#include <climits>
+
+#ifdef WIN32
+
+#define B256000 CBR_256000
+#define B128000 CBR_128000
+#define B115200 CBR_115200
+#define B57600 CBR_57600
+#define B56000 CBR_56000
+#define B38400 CBR_38400
+#define B19200 CBR_19200
+#define B14400 CBR_14400
+#define B9600 CBR_9600
+#define B4800 CBR_4800
+#define B2400 CBR_2400
+#define B1200 CBR_1200
+#define B600 CBR_600
+#define B300 CBR_300
+#define B110 CBR_110
+
+#include <io.h>
+#include <fcntl.h>
+#else
+#include <sys/ioctl.h>
+#include <termios.h>
+#endif
+
+#include <cerrno>
+#include <iostream>
+
+#ifdef CCXX_NAMESPACES
+namespace ost {
+using std::streambuf;
+using std::iostream;
+using std::ios;
+#endif
+
+#ifndef MAX_INPUT
+#define MAX_INPUT 255
+#endif
+
+#ifndef MAX_CANON
+#define MAX_CANON MAX_INPUT
+#endif
+
+#ifdef __FreeBSD__
+#undef _PC_MAX_INPUT
+#undef _PC_MAX_CANON
+#endif
+
+#if defined(__QNX__)
+#define CRTSCTS (IHFLOW | OHFLOW)
+#endif
+
+#if defined(_THR_UNIXWARE) || defined(__hpux) || defined(_AIX)
+#include <sys/termiox.h>
+#define CRTSCTS (CTSXON | RTSXOFF)
+#endif
+
+// IRIX
+
+#ifndef CRTSCTS
+#ifdef CNEW_RTSCTS
+#define CRTSCTS (CNEW_RTSCTS)
+#endif
+#endif
+
+#if defined(CTSXON) && defined(RTSXOFF) && !defined(CRTSCTS)
+#define CRTSCTS (CTSXON | RTSXOFF)
+#endif
+
+#ifndef CRTSCTS
+#define CRTSCTS 0
+#endif
+
+Serial::Serial(const char *fname)
+{
+ initSerial();
+
+ open(fname);
+
+#ifdef WIN32
+ if(dev == INVALID_HANDLE_VALUE)
+#else
+ if(dev < 0)
+#endif
+ {
+ error(errOpenFailed);
+ return;
+ }
+
+#ifdef WIN32
+ COMMTIMEOUTS CommTimeOuts ;
+
+ GetCommTimeouts(dev, &CommTimeOuts);
+
+// CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
+ CommTimeOuts.ReadIntervalTimeout = 0;
+
+ CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
+ CommTimeOuts.ReadTotalTimeoutConstant = 0;
+ CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
+ CommTimeOuts.WriteTotalTimeoutConstant = 1000;
+
+ SetCommTimeouts(dev, &CommTimeOuts) ;
+
+#else
+
+ if(!isatty(dev)) {
+ Serial::close();
+ error(errOpenNoTty);
+ return;
+ }
+#endif
+}
+
+Serial::~Serial()
+{
+ endSerial();
+}
+
+void Serial::initConfig(void)
+{
+#ifdef WIN32
+
+#define ASCII_XON 0x11
+#define ASCII_XOFF 0x13
+
+ DCB * attr = (DCB *)current;
+ DCB * orig = (DCB *)original;
+
+ attr->DCBlength = sizeof(DCB);
+ orig->DCBlength = sizeof(DCB);
+
+ GetCommState(dev, orig);
+ GetCommState(dev, attr);
+
+ attr->DCBlength = sizeof(DCB);
+ attr->BaudRate = 1200;
+ attr->Parity = NOPARITY;
+ attr->ByteSize = 8;
+
+ attr->XonChar = ASCII_XON;
+ attr->XoffChar = ASCII_XOFF;
+ attr->XonLim = 100;
+ attr->XoffLim = 100;
+ attr->fOutxDsrFlow = 0;
+ attr->fDtrControl = DTR_CONTROL_ENABLE;
+ attr->fOutxCtsFlow = 1;
+ attr->fRtsControl = RTS_CONTROL_ENABLE;
+ attr->fInX = attr->fOutX = 0;
+
+ attr->fBinary = true;
+ attr->fParity = true;
+
+ SetCommState(dev, attr);
+
+#else
+ struct termios *attr = (struct termios *)current;
+ struct termios *orig = (struct termios *)original;
+ long ioflags = fcntl(dev, F_GETFL);
+
+ tcgetattr(dev, (struct termios *)original);
+ tcgetattr(dev, (struct termios *)current);
+
+ attr->c_oflag = attr->c_lflag = 0;
+ attr->c_cflag = CLOCAL | CREAD | HUPCL;
+ attr->c_iflag = IGNBRK;
+
+ memset(attr->c_cc, 0, sizeof(attr->c_cc));
+ attr->c_cc[VMIN] = 1;
+
+ // inherit original settings, maybe we should keep more??
+ cfsetispeed(attr, cfgetispeed(orig));
+ cfsetospeed(attr, cfgetospeed(orig));
+ attr->c_cflag |= orig->c_cflag & (CRTSCTS | CSIZE | PARENB | PARODD | CSTOPB);
+ attr->c_iflag |= orig->c_iflag & (IXON | IXANY | IXOFF);
+
+ tcsetattr(dev, TCSANOW, attr);
+ fcntl(dev, F_SETFL, ioflags & ~O_NDELAY);
+
+#if defined(TIOCM_RTS) && defined(TIOCMODG)
+ int mcs = 0;
+ ioctl(dev, TIOCMODG, &mcs);
+ mcs |= TIOCM_RTS;
+ ioctl(dev, TIOCMODS, &mcs);
+#endif
+
+#ifdef _COHERENT
+ ioctl(dev, TIOCSRTS, 0);
+#endif
+
+#endif // WIN32
+ }
+
+void Serial::restore(void)
+{
+#ifdef WIN32
+ memcpy(current, original, sizeof(DCB));
+ SetCommState(dev, (DCB *)current);
+#else
+ memcpy(current, original, sizeof(struct termios));
+ tcsetattr(dev, TCSANOW, (struct termios *)current);
+#endif
+}
+
+void Serial::initSerial(void)
+{
+ flags.thrown = false;
+ flags.linebuf = false;
+ errid = errSuccess;
+ errstr = NULL;
+
+ dev = INVALID_HANDLE_VALUE;
+#ifdef WIN32
+ current = new DCB;
+ original = new DCB;
+#else
+ current = new struct termios;
+ original = new struct termios;
+#endif
+}
+
+void Serial::endSerial(void)
+{
+#ifdef WIN32
+ if(dev == INVALID_HANDLE_VALUE && original)
+ SetCommState(dev, (DCB *)original);
+
+ if(current)
+ delete (DCB *)current;
+
+ if(original)
+ delete (DCB *)original;
+#else
+ if(dev < 0 && original)
+ tcsetattr(dev, TCSANOW, (struct termios *)original);
+
+ if(current)
+ delete (struct termios *)current;
+
+ if(original)
+ delete (struct termios *)original;
+#endif
+ Serial::close();
+
+ current = NULL;
+ original = NULL;
+}
+
+Serial::Error Serial::error(Error err, char *errs)
+{
+ errid = err;
+ errstr = errs;
+ if(!err)
+ return err;
+
+ if(flags.thrown)
+ return err;
+
+ // prevents recursive throws
+
+ flags.thrown = true;
+#ifdef CCXX_EXCEPTIONS
+ if(Thread::getException() == Thread::throwObject)
+ throw((Serial *)this);
+#ifdef COMMON_STD_EXCEPTION
+ else if(Thread::getException() == Thread::throwException) {
+ if(!errs)
+ errs = (char *)"";
+ throw SerException(String(errs));
+ }
+#endif
+#endif
+ return err;
+}
+
+int Serial::setPacketInput(int size, unsigned char btimer)
+{
+#ifdef WIN32
+ // Still to be done......
+ return 0;
+#else
+
+#ifdef _PC_MAX_INPUT
+ int max = fpathconf(dev, _PC_MAX_INPUT);
+#else
+ int max = MAX_INPUT;
+#endif
+ struct termios *attr = (struct termios *)current;
+
+ if(size > max)
+ size = max;
+
+ attr->c_cc[VEOL] = attr->c_cc[VEOL2] = 0;
+ attr->c_cc[VMIN] = (unsigned char)size;
+ attr->c_cc[VTIME] = btimer;
+ attr->c_lflag &= ~ICANON;
+ tcsetattr(dev, TCSANOW, attr);
+ bufsize = size;
+ return size;
+#endif
+}
+
+int Serial::setLineInput(char newline, char nl1)
+{
+#ifdef WIN32
+ // Still to be done......
+ return 0;
+#else
+
+ struct termios *attr = (struct termios *)current;
+ attr->c_cc[VMIN] = attr->c_cc[VTIME] = 0;
+ attr->c_cc[VEOL] = newline;
+ attr->c_cc[VEOL2] = nl1;
+ attr->c_lflag |= ICANON;
+ tcsetattr(dev, TCSANOW, attr);
+#ifdef _PC_MAX_CANON
+ bufsize = fpathconf(dev, _PC_MAX_CANON);
+#else
+ bufsize = MAX_CANON;
+#endif
+ return bufsize;
+#endif
+}
+
+void Serial::flushInput(void)
+{
+#ifdef WIN32
+ PurgeComm(dev, PURGE_RXABORT | PURGE_RXCLEAR);
+#else
+ tcflush(dev, TCIFLUSH);
+#endif
+}
+
+void Serial::flushOutput(void)
+{
+#ifdef WIN32
+ PurgeComm(dev, PURGE_TXABORT | PURGE_TXCLEAR);
+#else
+ tcflush(dev, TCOFLUSH);
+#endif
+}
+
+void Serial::waitOutput(void)
+{
+#ifdef WIN32
+
+#else
+ tcdrain(dev);
+#endif
+}
+
+Serial &Serial::operator=(const Serial &ser)
+{
+ Serial::close();
+
+ if(ser.dev < 0)
+ return *this;
+
+#ifdef WIN32
+ HANDLE process = GetCurrentProcess();
+
+ int result = DuplicateHandle(process, ser.dev, process, &dev, DUPLICATE_SAME_ACCESS, 0, 0);
+
+ if (0 == result) {
+ memcpy(current, ser.current, sizeof(DCB));
+ memcpy(original, ser.original, sizeof(DCB));
+ }
+#else
+ dev = dup(ser.dev);
+
+ memcpy(current, ser.current, sizeof(struct termios));
+ memcpy(original, ser.original, sizeof(struct termios));
+#endif
+ return *this;
+}
+
+
+void Serial::open(const char * fname)
+{
+
+#ifndef WIN32
+ int cflags = O_RDWR | O_NDELAY;
+ dev = ::open(fname, cflags);
+ if(dev > -1)
+ initConfig();
+#else
+ // open COMM device
+ dev = CreateFile(fname,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, // exclusive access
+ NULL, // no security attrs
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
+ NULL);
+ if(dev != INVALID_HANDLE_VALUE)
+ initConfig();
+#endif
+}
+
+#ifdef WIN32
+int Serial::aRead(char * Data, const int Length)
+{
+
+ unsigned long dwLength = 0, dwError, dwReadLength;
+ COMSTAT cs;
+ OVERLAPPED ol;
+
+ // Return zero if handle is invalid
+ if(dev == INVALID_HANDLE_VALUE)
+ return 0;
+
+ // Read max length or only what is available
+ ClearCommError(dev, &dwError, &cs);
+
+ // If not requiring an exact byte count, get whatever is available
+ if(Length > (int)cs.cbInQue)
+ dwReadLength = cs.cbInQue;
+ else
+ dwReadLength = Length;
+
+ memset(&ol, 0, sizeof(OVERLAPPED));
+ ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if(dwReadLength > 0) {
+ if(ReadFile(dev, Data, dwReadLength, &dwLength, &ol) == FALSE) {
+ if(GetLastError() == ERROR_IO_PENDING) {
+ WaitForSingleObject(ol.hEvent, INFINITE);
+ GetOverlappedResult(dev, &ol, &dwLength, TRUE);
+ }
+ else
+ ClearCommError(dev, &dwError, &cs);
+ }
+ }
+
+ if(ol.hEvent != INVALID_HANDLE_VALUE)
+ CloseHandle(ol.hEvent);
+
+ return dwLength;
+}
+
+int Serial::aWrite(const char * Data, const int Length)
+{
+ COMSTAT cs;
+ unsigned long dwError = 0;
+ OVERLAPPED ol;
+
+ // Clear the com port of any error condition prior to read
+ ClearCommError(dev, &dwError, &cs);
+
+ unsigned long retSize = 0;
+
+ memset(&ol, 0, sizeof(OVERLAPPED));
+ ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if(WriteFile(dev, Data, Length, &retSize, &ol) == FALSE) {
+ if(GetLastError() == ERROR_IO_PENDING) {
+ WaitForSingleObject(ol.hEvent, INFINITE);
+ GetOverlappedResult(dev, &ol, &retSize, TRUE);
+ }
+ else
+ ClearCommError(dev, &dwError, &cs);
+ }
+
+ if(ol.hEvent != INVALID_HANDLE_VALUE)
+ CloseHandle(ol.hEvent);
+
+ return retSize;
+}
+#else
+int Serial::aRead(char *Data, const int Length)
+{
+ return ::read(dev, Data, Length);
+}
+
+int Serial::aWrite(const char *Data, const int Length)
+{
+ return ::write(dev, Data, Length);
+}
+#endif
+
+void Serial::close()
+{
+#ifdef WIN32
+ CloseHandle(dev);
+#else
+ ::close(dev);
+#endif
+
+ dev = INVALID_HANDLE_VALUE;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+const int iAsync::getTimeOuts(unsigned long & readTimeout, unsigned long & writeTimeout)
+{
+ return GetCommTimeouts(_TheHandle, &CommTimeOuts);
+ }
+
+const int iAsync::setTimeOuts(unsigned long readTimeout, unsigned long writeTimeout)
+{
+ COMMTIMEOUTS CommTimeOuts ;
+
+ getTimeOuts(CommTimeOuts);
+
+ CommTimeOuts.ReadIntervalTimeout = readTimeout;
+ CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
+ CommTimeOuts.ReadTotalTimeoutConstant = 0;
+ CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
+ CommTimeOuts.WriteTotalTimeoutConstant = 1000;
+
+ return GetCommTimeouts(_TheHandle, &CommTimeOuts);
+ }
+ return SetCommTimeouts(_TheHandle, &CommTimeOuts) ;
+{
+ DCB dcb ;
+
+ dcb.DCBlength = sizeof(DCB) ;
+
+ GetCommState(_TheHandle, &dcb) ;
+
+ // hardcode this stuff for now.....
+ dcb.BaudRate = _TheBaudrate;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ dcb.fOutxDsrFlow = 0;
+ dcb.fDtrControl = DTR_CONTROL_ENABLE ;
+ dcb.fOutxCtsFlow = 1;
+ dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;
+ dcb.fInX = dcb.fOutX = 0;
+
+ dcb.XonChar = ASCII_XON;
+ dcb.XoffChar = ASCII_XOFF;
+ dcb.XonLim = 100;
+ dcb.XoffLim = 100;
+
+ // other various settings
+
+ dcb.fBinary = TRUE;
+ dcb.fParity = TRUE;
+
+ GetCommState(_TheHandle, &dcb) ;
+ dcb.DCBlength = sizeof(DCB) ;
+ dcb.BaudRate = _TheBaudrate;
+ SetCommState(_TheHandle, &dcb) ;
+
+ }
+*/
+
+Serial::Error Serial::setSpeed(unsigned long speed)
+{
+ unsigned long rate;
+
+ switch(speed) {
+#ifdef B115200
+ case 115200:
+ rate = B115200;
+ break;
+#endif
+#ifdef B57600
+ case 57600:
+ rate = B57600;
+ break;
+#endif
+#ifdef B38400
+ case 38400:
+ rate = B38400;
+ break;
+#endif
+ case 19200:
+ rate = B19200;
+ break;
+ case 9600:
+ rate = B9600;
+ break;
+ case 4800:
+ rate = B4800;
+ break;
+ case 2400:
+ rate = B2400;
+ break;
+ case 1200:
+ rate = B1200;
+ break;
+ case 600:
+ rate = B600;
+ break;
+ case 300:
+ rate = B300;
+ break;
+ case 110:
+ rate = B110;
+ break;
+#ifdef B0
+ case 0:
+ rate = B0;
+ break;
+#endif
+ default:
+ return error(errSpeedInvalid);
+ }
+
+#ifdef WIN32
+
+ DCB * dcb = (DCB *)current;
+ dcb->DCBlength = sizeof(DCB);
+ GetCommState(dev, dcb);
+
+ dcb->BaudRate = rate;
+ SetCommState(dev, dcb) ;
+
+#else
+ struct termios *attr = (struct termios *)current;
+ cfsetispeed(attr, rate);
+ cfsetospeed(attr, rate);
+ tcsetattr(dev, TCSANOW, attr);
+#endif
+ return errSuccess;
+ }
+
+Serial::Error Serial::setFlowControl(Flow flow)
+{
+#ifdef WIN32
+
+ DCB * attr = (DCB *)current;
+ attr->XonChar = ASCII_XON;
+ attr->XoffChar = ASCII_XOFF;
+ attr->XonLim = 100;
+ attr->XoffLim = 100;
+
+ switch(flow) {
+ case flowSoft:
+ attr->fInX = attr->fOutX = 1;
+ break;
+ case flowBoth:
+ attr->fInX = attr->fOutX = 1;
+ case flowHard:
+ attr->fOutxCtsFlow = 1;
+ attr->fRtsControl = RTS_CONTROL_HANDSHAKE;
+ break;
+ case flowNone:
+ break;
+ default:
+ return error(errFlowInvalid);
+ }
+
+ SetCommState(dev, attr);
+#else
+
+ struct termios *attr = (struct termios *)current;
+
+ attr->c_cflag &= ~CRTSCTS;
+ attr->c_iflag &= ~(IXON | IXANY | IXOFF);
+
+ switch(flow) {
+ case flowSoft:
+ attr->c_iflag |= (IXON | IXANY | IXOFF);
+ break;
+ case flowBoth:
+ attr->c_iflag |= (IXON | IXANY | IXOFF);
+ case flowHard:
+ attr->c_cflag |= CRTSCTS;
+ break;
+ case flowNone:
+ break;
+ default:
+ return error(errFlowInvalid);
+ }
+
+ tcsetattr(dev, TCSANOW, attr);
+
+#endif
+ return errSuccess;
+ }
+
+Serial::Error Serial::setStopBits(int bits)
+{
+#ifdef WIN32
+
+ DCB * attr = (DCB *)current;
+ switch(bits) {
+ case 1:
+ attr->StopBits = ONESTOPBIT;
+ break;
+ case 2:
+ attr->StopBits = TWOSTOPBITS;
+ break;
+ default:
+ return error(errStopbitsInvalid);
+ }
+
+ SetCommState(dev, attr);
+#else
+ struct termios *attr = (struct termios *)current;
+ attr->c_cflag &= ~CSTOPB;
+
+ switch(bits) {
+ case 1:
+ break;
+ case 2:
+ attr->c_cflag |= CSTOPB;
+ break;
+ default:
+ return error(errStopbitsInvalid);
+ }
+ tcsetattr(dev, TCSANOW, attr);
+#endif
+ return errSuccess;
+ }
+
+Serial::Error Serial::setCharBits(int bits)
+{
+#ifdef WIN32
+
+ DCB * attr = (DCB *)current;
+ switch(bits) {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ attr->ByteSize = bits;
+ break;
+ default:
+ return error(errCharsizeInvalid);
+ }
+ SetCommState(dev, attr);
+#else
+ struct termios *attr = (struct termios *)current;
+ attr->c_cflag &= ~CSIZE;
+
+ switch(bits) {
+ case 5:
+ attr->c_cflag |= CS5;
+ break;
+ case 6:
+ attr->c_cflag |= CS6;
+ break;
+ case 7:
+ attr->c_cflag |= CS7;
+ break;
+ case 8:
+ attr->c_cflag |= CS8;
+ break;
+ default:
+ return error(errCharsizeInvalid);
+ }
+ tcsetattr(dev, TCSANOW, attr);
+#endif
+ return errSuccess;
+ }
+
+Serial::Error Serial::setParity(Parity parity)
+{
+#ifdef WIN32
+
+ DCB * attr = (DCB *)current;
+ switch(parity) {
+ case parityEven:
+ attr->Parity = EVENPARITY;
+ break;
+ case parityOdd:
+ attr->Parity = ODDPARITY;
+ break;
+ case parityNone:
+ attr->Parity = NOPARITY;
+ break;
+ default:
+ return error(errParityInvalid);
+ }
+ SetCommState(dev, attr);
+#else
+ struct termios *attr = (struct termios *)current;
+ attr->c_cflag &= ~(PARENB | PARODD);
+
+ switch(parity) {
+ case parityEven:
+ attr->c_cflag |= PARENB;
+ break;
+ case parityOdd:
+ attr->c_cflag |= (PARENB | PARODD);
+ break;
+ case parityNone:
+ break;
+ default:
+ return error(errParityInvalid);
+ }
+ tcsetattr(dev, TCSANOW, attr);
+#endif
+ return errSuccess;
+ }
+
+void Serial::sendBreak(void)
+{
+#ifdef WIN32
+ SetCommBreak(dev);
+ Thread::sleep(100L);
+ ClearCommBreak(dev);
+#else
+ tcsendbreak(dev, 0);
+#endif
+ }
+
+void Serial::toggleDTR(timeout_t millisec)
+{
+#ifdef WIN32
+ EscapeCommFunction(dev, CLRDTR);
+ if(millisec) {
+ Thread::sleep(millisec);
+ EscapeCommFunction(dev, SETDTR);
+ }
+
+#else
+ struct termios tty, old;
+ tcgetattr(dev, &tty);
+ tcgetattr(dev, &old);
+ cfsetospeed(&tty, B0);
+ cfsetispeed(&tty, B0);
+ tcsetattr(dev, TCSANOW, &tty);
+
+ if(millisec) {
+ Thread::sleep(millisec);
+ tcsetattr(dev, TCSANOW, &old);
+ }
+#endif
+ }
+
+bool Serial::isPending(Pending pending, timeout_t timeout)
+{
+#ifdef WIN32
+ unsigned long dwError;
+ COMSTAT cs;
+
+ ClearCommError(dev, &dwError, &cs);
+
+ if(timeout == 0 || ((pending == pendingInput) && (0 != cs.cbInQue)) ||
+ ((pending == pendingOutput) && (0 != cs.cbOutQue)) || (pending == pendingError))
+ {
+ switch(pending) {
+ case pendingInput:
+ return (0 != cs.cbInQue);
+ case pendingOutput:
+ return (0 != cs.cbOutQue);
+ case pendingError:
+ return false;
+ }
+ }
+ else {
+ Thread::Cancel save = Thread::enterCancel();
+ OVERLAPPED ol;
+ DWORD dwMask;
+ DWORD dwEvents = 0;
+ BOOL suc;
+
+ memset(&ol, 0, sizeof(OVERLAPPED));
+ ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if(pending == pendingInput)
+ dwMask = EV_RXCHAR;
+ else if(pending == pendingOutput)
+ dwMask = EV_TXEMPTY;
+ else // on error
+ dwMask = EV_ERR;
+
+ SetCommMask(dev, dwMask);
+ // let's wait for event or timeout
+ if((suc = WaitCommEvent(dev, &dwEvents, &ol)) == FALSE) {
+ if(GetLastError() == ERROR_IO_PENDING) {
+ DWORD transferred;
+
+ dwError = WaitForSingleObject(ol.hEvent, timeout);
+ if (dwError != WAIT_OBJECT_0)
+ SetCommMask(dev, 0);
+
+ suc = GetOverlappedResult(dev, &ol, &transferred, TRUE);
+ if (suc)
+ suc = (dwEvents & dwMask) ? TRUE : FALSE;
+ }
+ else
+ ClearCommError(dev, &dwError, &cs);
+ }
+
+ if(ol.hEvent != INVALID_HANDLE_VALUE)
+ CloseHandle(ol.hEvent);
+
+ Thread::exitCancel(save);
+ if(suc == FALSE)
+ return false;
+ return true;
+ }
+#else
+
+
+ int status;
+#ifdef HAVE_POLL
+ struct pollfd pfd;
+
+ pfd.fd = dev;
+ pfd.revents = 0;
+ switch(pending) {
+ case pendingInput:
+ pfd.events = POLLIN;
+ break;
+ case pendingOutput:
+ pfd.events = POLLOUT;
+ break;
+ case pendingError:
+ pfd.events = POLLERR | POLLHUP;
+ break;
+ }
+
+ status = 0;
+ while(status < 1) {
+ if(timeout == TIMEOUT_INF)
+ status = poll(&pfd, 1, -1);
+ else
+ status = poll(&pfd, 1, timeout);
+
+ if(status < 1) {
+ if(status == -1 && errno == EINTR)
+ continue;
+ return false;
+ }
+ }
+
+ if(pfd.revents & pfd.events)
+ return true;
+
+#else
+ struct timeval tv;
+ fd_set grp;
+ struct timeval *tvp = &tv;
+
+ if(timeout == TIMEOUT_INF)
+ tvp = NULL;
+ else {
+ tv.tv_usec = (timeout % 1000) * 1000;
+ tv.tv_sec = timeout / 1000;
+ }
+
+ FD_ZERO(&grp);
+ FD_SET(dev, &grp);
+ switch(pending) {
+ case pendingInput:
+ status = select(dev + 1, &grp, NULL, NULL, tvp);
+ break;
+ case pendingOutput:
+ status = select(dev + 1, NULL, &grp, NULL, tvp);
+ break;
+ case pendingError:
+ status = select(dev + 1, NULL, NULL, &grp, tvp);
+ break;
+ }
+ if(status < 1)
+ return false;
+
+ if(FD_ISSET(dev, &grp))
+ return true;
+
+#endif
+
+#endif // WIN32
+
+ return false;
+ }
+
+
+
+
+
+TTYStream::TTYStream(const char *filename, timeout_t to)
+ : streambuf(),
+ Serial(filename),
+#ifdef HAVE_OLD_IOSTREAM
+ iostream()
+#else
+ iostream((streambuf *)this)
+#endif
+{
+#ifdef HAVE_OLD_IOSTREAM
+ init((streambuf *)this);
+#endif
+ gbuf = pbuf = NULL;
+ timeout = to;
+
+ if(INVALID_HANDLE_VALUE != dev)
+ allocate();
+ }
+
+TTYStream::TTYStream()
+ : streambuf(),
+ Serial(),
+#ifdef HAVE_OLD_IOSTREAM
+ iostream()
+#else
+ iostream((streambuf *)this)
+#endif
+{
+#ifdef HAVE_OLD_IOSTREAM
+ init((streambuf *)this);
+#endif
+ timeout = 0;
+ gbuf = pbuf = NULL;
+ }
+
+TTYStream::~TTYStream()
+{
+ endStream();
+ endSerial();
+ }
+
+void TTYStream::endStream(void)
+{
+ if(bufsize)
+ sync();
+
+ if(gbuf) {
+ delete[] gbuf;
+ gbuf = NULL;
+ }
+ if(pbuf) {
+ delete[] pbuf;
+ pbuf = NULL;
+ }
+ bufsize = 0;
+ clear();
+ }
+
+void TTYStream::allocate(void)
+{
+ if(INVALID_HANDLE_VALUE == dev)
+ return;
+
+#ifdef _PC_MAX_INPUT
+ bufsize = fpathconf(dev, _PC_MAX_INPUT);
+#else
+ bufsize = MAX_INPUT;
+#endif
+
+ gbuf = new char[bufsize];
+ pbuf = new char[bufsize];
+
+ if(!pbuf || !gbuf) {
+ error(errResourceFailure);
+ return;
+ }
+
+ clear();
+
+#if !(defined(STLPORT) || defined(__KCC))
+ setg(gbuf, gbuf + bufsize, 0);
+#endif
+
+ setg(gbuf, gbuf + bufsize, gbuf + bufsize);
+ setp(pbuf, pbuf + bufsize);
+ }
+
+int TTYStream::doallocate()
+{
+ if(bufsize)
+ return 0;
+
+ allocate();
+ return 1;
+ }
+
+void TTYStream::interactive(bool iflag)
+{
+#ifdef WIN32
+ if(dev == INVALID_HANDLE_VALUE)
+#else
+ if(dev < 0)
+#endif
+ return;
+
+ if(bufsize >= 1)
+ endStream();
+
+ if(iflag) {
+ // setting to unbuffered mode
+
+ bufsize = 1;
+ gbuf = new char[bufsize];
+
+#if !(defined(STLPORT) || defined(__KCC))
+#if defined(__GNUC__) && (__GNUC__ < 3)
+ setb(0,0);
+#endif
+#endif
+ setg(gbuf, gbuf+bufsize, gbuf+bufsize);
+ setp(pbuf, pbuf);
+ return;
+ }
+
+ if(bufsize < 2)
+ allocate();
+ }
+
+int TTYStream::uflow(void)
+{
+ int rlen;
+ unsigned char ch;
+
+ if(bufsize < 2) {
+ if(timeout) {
+ if(Serial::isPending(pendingInput, timeout))
+ rlen = aRead((char *)&ch, 1);
+ else
+ rlen = -1;
+ }
+ else
+ rlen = aRead((char *)&ch, 1);
+ if(rlen < 1) {
+ if(rlen < 0)
+ clear(ios::failbit | rdstate());
+ return EOF;
+ }
+ return ch;
+ }
+ else {
+ ch = underflow();
+ gbump(1);
+ return ch;
+ }
+ }
+
+int TTYStream::underflow(void)
+{
+ ssize_t rlen = 1;
+
+ if(!gptr())
+ return EOF;
+
+ if(gptr() < egptr())
+ return (unsigned char)*gptr();
+
+ rlen = (ssize_t)((gbuf + bufsize) - eback());
+ if(timeout && !Serial::isPending(pendingInput, timeout))
+ rlen = -1;
+ else
+ rlen = aRead((char *)eback(), rlen);
+
+ if(rlen < 1) {
+ if(rlen < 0) {
+ clear(ios::failbit | rdstate());
+ error(errInput);
+ }
+ return EOF;
+ }
+
+ setg(eback(), eback(), eback() + rlen);
+ return (unsigned char) *gptr();
+ }
+
+int TTYStream::sync(void)
+{
+ if(bufsize > 1 && pbase() && ((pptr() - pbase()) > 0)) {
+ overflow(0);
+ waitOutput();
+ setp(pbuf, pbuf + bufsize);
+ }
+ setg(gbuf, gbuf + bufsize, gbuf + bufsize);
+ return 0;
+ }
+
+int TTYStream::overflow(int c)
+{
+ unsigned char ch;
+ ssize_t rlen, req;
+
+ if(bufsize < 2) {
+ if(c == EOF)
+ return 0;
+
+ ch = (unsigned char)(c);
+ rlen = aWrite((char *)&ch, 1);
+ if(rlen < 1) {
+ if(rlen < 0)
+ clear(ios::failbit | rdstate());
+ return EOF;
+ }
+ else
+ return c;
+ }
+
+ if(!pbase())
+ return EOF;
+
+ req = (ssize_t)(pptr() - pbase());
+ if(req) {
+ rlen = aWrite((char *)pbase(), req);
+ if(rlen < 1) {
+ if(rlen < 0)
+ clear(ios::failbit | rdstate());
+ return EOF;
+ }
+ req -= rlen;
+ }
+
+ if(req)
+// memmove(pptr(), pptr() + rlen, req);
+ memmove(pbuf, pbuf + rlen, req);
+ setp(pbuf + req, pbuf + bufsize);
+
+ if(c != EOF) {
+ *pptr() = (unsigned char)c;
+ pbump(1);
+ }
+ return c;
+ }
+
+bool TTYStream::isPending(Pending pending, timeout_t timer)
+{
+// if(pending == pendingInput && in_avail())
+// return true;
+// else if(pending == pendingOutput)
+// flush();
+
+ return Serial::isPending(pending, timer);
+ }
+
+
+
+
+
+
+ttystream::ttystream() :
+TTYStream()
+{
+ setError(false);
+ }
+
+ttystream::ttystream(const char *name) :
+TTYStream()
+{
+ setError(false);
+ open(name);
+ }
+
+void ttystream::close(void)
+{
+#ifdef WIN32
+ if (INVALID_HANDLE_VALUE == dev)
+#else
+ if(dev < 0)
+#endif
+ return;
+
+ endStream();
+ restore();
+ TTYStream::close();
+ }
+
+void ttystream::open(const char *name)
+{
+ const char *cpp;
+ char *cp;
+ char pathname[256];
+ size_t namelen;
+ long opt;
+
+ if (INVALID_HANDLE_VALUE != dev) {
+ restore();
+ close();
+ }
+
+ cpp = strrchr(name, ':');
+ if(cpp)
+ namelen = cpp - name;
+ else
+ namelen = strlen(name);
+
+ cp = pathname;
+
+#ifndef WIN32
+ if(*name != '/') {
+ strcpy(pathname, "/dev/");
+ cp += 5;
+ }
+
+ if((cp - pathname) + namelen > 255) {
+ error(errResourceFailure);
+ return;
+ }
+#endif
+ setString(cp, pathname - cp + sizeof(pathname), name);
+ cp += namelen;
+#ifdef WIN32
+ *cp++ = ':';
+#endif
+ *cp = 0;
+
+ Serial::open(pathname);
+
+ if(INVALID_HANDLE_VALUE == dev) {
+ error(errOpenFailed);
+ return;
+ }
+
+ allocate();
+
+ setString(pathname, sizeof(pathname), name + namelen);
+ cp = pathname + 1;
+
+ if(*pathname == ':')
+ cp = strtok(cp, ",");
+ else
+ cp = NULL;
+
+ while(cp) {
+ switch(*cp) {
+ case 'h':
+ case 'H':
+ setFlowControl(flowHard);
+ break;
+ case 's':
+ case 'S':
+ setFlowControl(flowSoft);
+ break;
+ case 'b':
+ case 'B':
+ setFlowControl(flowBoth);
+ break;
+ case 'n':
+ case 'N':
+ setParity(parityNone);
+ break;
+ case 'O':
+ case 'o':
+ setParity(parityOdd);
+ break;
+ case 'e':
+ case 'E':
+ setParity(parityEven);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ opt = atol(cp);
+ if(opt == 1 || opt == 2) {
+ setStopBits((int)opt);
+ break;
+ }
+ if(opt > 4 && opt < 9) {
+ setCharBits((int)opt);
+ break;
+ }
+ setSpeed(opt);
+ break;
+ default:
+ error(errOptionInvalid);
+ }
+ cp = strtok(NULL, ",");
+ }
+ }
+
+TTYSession::TTYSession(const char *filename, int pri, int stack) :
+Thread(pri, stack), TTYStream(filename)
+{
+ setError(false);
+ }
+
+
+TTYSession::~TTYSession()
+{
+ terminate();
+ endSerial();
+ }
+
+#ifndef WIN32
+// Not supporting this right now........
+//
+
+SerialPort::SerialPort(SerialService *svc, const char *name) :
+Serial(name),
+detect_pending(true),
+detect_output(false),
+detect_disconnect(true)
+{
+ next = prev = NULL;
+ service = NULL;
+
+#ifdef WIN32
+ if(INVALID_HANDLE_VALUE != dev)
+#else
+ if(dev > -1)
+#endif
+ {
+ setError(false);
+ service = svc;
+ svc->attach(this);
+ }
+ }
+
+SerialPort::~SerialPort()
+{
+ if(service)
+ service->detach(this);
+
+ endSerial();
+ }
+
+void SerialPort::expired(void)
+{
+ }
+
+void SerialPort::pending(void)
+{
+ }
+
+void SerialPort::disconnect(void)
+{
+ }
+
+void SerialPort::output(void)
+{
+ }
+
+void SerialPort::setTimer(timeout_t ptimer)
+{
+ TimerPort::setTimer(ptimer);
+ service->update();
+ }
+
+void SerialPort::incTimer(timeout_t ptimer)
+{
+ TimerPort::incTimer(ptimer);
+ service->update();
+ }
+
+
+void SerialPort::setDetectPending( bool val )
+{
+ if ( detect_pending != val ) {
+ detect_pending = val;
+#ifdef USE_POLL
+ if ( ufd ) {
+ if ( val ) {
+ ufd->events |= POLLIN;
+ } else {
+ ufd->events &= ~POLLIN;
+ }
+ }
+#endif
+ service->update();
+ }
+ }
+
+void SerialPort::setDetectOutput( bool val )
+{
+ if ( detect_output != val ) {
+ detect_output = val;
+#ifdef USE_POLL
+ if ( ufd ) {
+ if ( val ) {
+ ufd->events |= POLLOUT;
+ } else {
+ ufd->events &= ~POLLOUT;
+ }
+ }
+#endif
+ service->update();
+ }
+ }
+
+
+SerialService::SerialService(int pri, size_t stack, const char *id) :
+Thread(pri, stack), Mutex(id)
+{
+ long opt;
+
+ first = last = NULL;
+ count = 0;
+ FD_ZERO(&connect);
+ if(::pipe(iosync)) {
+#ifdef CCXX_EXCEPTIONS
+ switch(Thread::getException()) {
+ case throwObject:
+ throw(this);
+ return;
+#ifdef COMMON_STD_EXCEPTION
+ case throwException:
+ throw(ThrException("no service pipe"));
+ return;
+#endif
+ default:
+ return;
+ }
+#else
+ return;
+#endif
+ }
+ hiwater = iosync[0] + 1;
+ FD_SET(iosync[0], &connect);
+
+ opt = fcntl(iosync[0], F_GETFL);
+ fcntl(iosync[0], F_SETFL, opt | O_NDELAY);
+ }
+
+SerialService::~SerialService()
+{
+ update(0);
+ terminate();
+
+ while(first)
+ delete first;
+ }
+
+void SerialService::onUpdate(unsigned char flag)
+{
+ }
+
+void SerialService::onEvent(void)
+{
+ }
+
+void SerialService::onCallback(SerialPort *port)
+{
+ }
+
+void SerialService::attach(SerialPort *port)
+{
+ enterMutex();
+#ifdef USE_POLL
+ port->ufd = 0;
+#endif
+ if(last)
+ last->next = port;
+
+ port->prev = last;
+ last = port;
+ FD_SET(port->dev, &connect);
+ if(port->dev >= hiwater)
+ hiwater = port->dev + 1;
+
+ if(!first) {
+ first = port;
+ leaveMutex();
+ ++count;
+ start();
+ }
+ else {
+ leaveMutex();
+ update();
+ ++count;
+ }
+ }
+
+void SerialService::detach(SerialPort *port)
+{
+ enterMutex();
+
+#ifndef USE_POLL
+ FD_CLR(port->dev, &connect);
+#endif
+
+ if(port->prev)
+ port->prev->next = port->next;
+ else
+ first = port->next;
+
+ if(port->next)
+ port->next->prev = port->prev;
+ else
+ last = port->prev;
+
+ --count;
+ leaveMutex();
+ update();
+ }
+
+void SerialService::update(unsigned char flag)
+{
+ if(::write(iosync[1], (char *)&flag, 1) < 1) {
+
+#ifdef CCXX_EXCEPTIONS
+ switch(Thread::getException()) {
+ case throwObject:
+ throw(this);
+ return;
+#ifdef COMMON_STD_EXCEPTION
+ case throwException:
+ throw(ThrException("update failed"));
+ return;
+#endif
+ default:
+ return;
+ }
+#else
+ return;
+#endif
+ }
+ }
+
+
+void SerialService::run(void)
+{
+ timeout_t timer, expires;
+ SerialPort *port;
+ unsigned char buf;
+
+#ifdef USE_POLL
+
+ Poller mfd;
+ pollfd *p_ufd;
+ int lastcount = 0;
+
+ // initialize ufd in all attached ports :
+ // probably don't need this but it can't hurt.
+ enterMutex();
+ port = first;
+ while(port) {
+ port->ufd = 0;
+ port = port->next;
+ }
+ leaveMutex();
+
+#else
+ struct timeval timeout, *tvp;
+ fd_set inp, out, err;
+ int dev;
+ FD_ZERO(&inp);
+ FD_ZERO(&out);
+ FD_ZERO(&err);
+#endif
+
+ setCancel(cancelDeferred);
+ for(;;) {
+ timer = TIMEOUT_INF;
+ while(1 == ::read(iosync[0], (char *)&buf, 1)) {
+ if(buf) {
+ onUpdate(buf);
+ continue;
+ }
+
+ setCancel(cancelImmediate);
+ sleep(TIMEOUT_INF);
+ exit();
+ }
+
+#ifdef USE_POLL
+
+ bool reallocate = false;
+
+ enterMutex();
+ onEvent();
+ port = first;
+ while(port) {
+ onCallback(port);
+ if ( ( p_ufd = port->ufd ) ) {
+
+ if ( ( POLLHUP | POLLNVAL ) & p_ufd->revents ) {
+ // Avoid infinite loop from disconnected sockets
+ port->detect_disconnect = false;
+ p_ufd->events &= ~POLLHUP;
+ port->disconnect();
+ }
+
+ if ( ( POLLIN | POLLPRI ) & p_ufd->revents )
+ port->pending();
+
+ if ( POLLOUT & p_ufd->revents )
+ port->output();
+
+ } else {
+ reallocate = true;
+ }
+
+retry:
+ expires = port->getTimer();
+ if(expires > 0)
+ if(expires < timer)
+ timer = expires;
+
+ if(!expires) {
+ port->endTimer();
+ port->expired();
+ goto retry;
+ }
+
+ port = port->next;
+ }
+
+ //
+ // reallocate things if we saw a ServerPort without
+ // ufd set !
+ if ( reallocate || ( ( count + 1 ) != lastcount ) ) {
+ lastcount = count + 1;
+ p_ufd = mfd.getList( count + 1 );
+
+ // Set up iosync polling
+ p_ufd->fd = iosync[0];
+ p_ufd->events = POLLIN | POLLHUP;
+ p_ufd ++;
+
+ port = first;
+ while(port) {
+ p_ufd->fd = port->dev;
+ p_ufd->events =
+ ( port->detect_disconnect ? POLLHUP : 0 )
+ | ( port->detect_output ? POLLOUT : 0 )
+ | ( port->detect_pending ? POLLIN : 0 )
+ ;
+ port->ufd = p_ufd;
+ p_ufd ++;
+ port = port->next;
+ }
+ }
+ leaveMutex();
+
+ poll( mfd.getList(), count + 1, timer );
+
+#else
+ enterMutex();
+ onEvent();
+ port = first;
+
+ while(port) {
+ onCallback(port);
+ dev = port->dev;
+ if(FD_ISSET(dev, &err)) {
+ port->detect_disconnect = false;
+ port->disconnect();
+ }
+
+ if(FD_ISSET(dev, &inp))
+ port->pending();
+
+ if(FD_ISSET(dev, &out))
+ port->output();
+
+retry:
+ expires = port->getTimer();
+ if(expires > 0)
+ if(expires < timer)
+ timer = expires;
+
+ if(!expires) {
+ port->endTimer();
+ port->expired();
+ goto retry;
+ }
+
+ port = port->next;
+ }
+
+ FD_ZERO(&inp);
+ FD_ZERO(&out);
+ FD_ZERO(&err);
+ int so;
+ port = first;
+ while(port) {
+ so = port->dev;
+
+ if(port->detect_pending)
+ FD_SET(so, &inp);
+
+ if(port->detect_output)
+ FD_SET(so, &out);
+
+ if(port->detect_disconnect)
+ FD_SET(so, &err);
+
+ port = port->next;
+ }
+
+ leaveMutex();
+ if(timer == TIMEOUT_INF)
+ tvp = NULL;
+ else {
+ tvp = &timeout;
+ timeout.tv_sec = timer / 1000;
+ timeout.tv_usec = (timer % 1000) * 1000;
+ }
+ select(hiwater, &inp, &out, &err, tvp);
+#endif
+ }
+ }
+
+#endif
+
+#ifdef CCXX_NAMESPACES
+ }
+#endif
+
+/** EMACS **
+ * Local variables:
+ * mode: c++
+ * c-basic-offset: 8
+ * End:
+ */