From 7fb94ed43a814788cda019c1e77314abc1626339 Mon Sep 17 00:00:00 2001 From: Fritz Elfert Date: Mon, 31 Jul 2000 03:12:38 +0000 Subject: Applied mjg-0.6 patch. Started adding kdoc compliant documentation comments. Added PsiTime --- lib/Makefile.am | 6 +- lib/bufferarray.cc | 22 +-- lib/bufferarray.h | 137 +++++++++++++---- lib/bufferstore.cc | 46 +++--- lib/bufferstore.h | 223 +++++++++++++++++++++++---- lib/ppsocket.cc | 4 + lib/psitime.cc | 117 +++++++++++++++ lib/psitime.h | 198 ++++++++++++++++++++++++ lib/rfsv.h | 64 +++++++- lib/rfsv16.cc | 432 +++++++++++++++++++++++++++++++++++++---------------- lib/rfsv16.h | 3 + lib/rfsv32.cc | 102 ++++++++++++- lib/rfsv32.h | 3 + lib/rpcs.cc | 138 ++++++++++------- lib/rpcs.h | 306 ++++++++++++++++++++++++++++++++++--- lib/rpcs16.cc | 2 +- lib/rpcs16.h | 2 +- lib/rpcs32.cc | 125 ++++++++++++++-- lib/rpcs32.h | 8 +- lib/rpcsfactory.cc | 9 +- lib/rpcsfactory.h | 28 +++- 21 files changed, 1640 insertions(+), 335 deletions(-) create mode 100644 lib/psitime.cc create mode 100644 lib/psitime.h (limited to 'lib') diff --git a/lib/Makefile.am b/lib/Makefile.am index 08df502..ec7a840 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,9 +1,9 @@ lib_LTLIBRARIES = libplp.la -libplp_la_LDFLAGS = --debug -version-info 1:0:0 +libplp_la_LDFLAGS = --debug -version-info 1:1:0 libplp_la_SOURCES = bufferarray.cc bufferstore.cc iowatch.cc ppsocket.cc \ rfsv16.cc rfsv32.cc rfsvfactory.cc log.cc rfsv.cc rpcs32.cc rpcs16.cc \ - rpcs.cc rpcsfactory.cc + rpcs.cc rpcsfactory.cc psitime.cc EXTRA_DIST = bool.h bufferarray.h bufferstore.h iowatch.h ppsocket.h \ rfsv.h rfsv16.h rfsv32.h rfsvfactory.h log.h rpcs32.h rpcs16.h rpcs.h \ - rpcsfactory.h + rpcsfactory.h psitime.h diff --git a/lib/bufferarray.cc b/lib/bufferarray.cc index 4e922ed..77a08c6 100644 --- a/lib/bufferarray.cc +++ b/lib/bufferarray.cc @@ -45,11 +45,11 @@ bufferArray::bufferArray(const bufferArray & a) bufferArray::~bufferArray() { - delete[]buff; + delete []buff; } bufferStore bufferArray:: -popBuffer() +pop() { bufferStore ret; if (len > 0) { @@ -63,7 +63,7 @@ popBuffer() } void bufferArray:: -pushBuffer(const bufferStore & b) +append(const bufferStore & b) { if (len == lenAllocd) { lenAllocd += ALLOC_MIN; @@ -71,7 +71,7 @@ pushBuffer(const bufferStore & b) for (long i = 0; i < len; i++) { nb[i] = buff[i]; } - delete[]buff; + delete []buff; buff = nb; } buff[len++] = b; @@ -84,7 +84,7 @@ push(const bufferStore & b) lenAllocd += ALLOC_MIN; bufferStore *nb = new bufferStore[lenAllocd]; for (long i = len; i > 0; i--) { - nb[i] = buff[i-1]; + nb[i] = buff[i - 1]; } nb[0] = b; delete[]buff; @@ -92,18 +92,6 @@ push(const bufferStore & b) len++; } -bufferStore bufferArray:: -pop() -{ - return popBuffer(); -} - -void bufferArray:: -append(const bufferStore & b) -{ - pushBuffer(b); -} - long bufferArray:: length(void) { diff --git a/lib/bufferarray.h b/lib/bufferarray.h index fbf26f1..7389ab6 100644 --- a/lib/bufferarray.h +++ b/lib/bufferarray.h @@ -4,35 +4,120 @@ #include "bool.h" class bufferStore; +/** + * An array of bufferStores + */ class bufferArray { - public: - bufferArray(); - bufferArray(const bufferArray &a); - ~bufferArray(); - bufferArray &operator =(const bufferArray &a); - bool empty() const; - - // this is NOT a real push as with a FIFO but - // appends the bufferStore. - void pushBuffer(const bufferStore& b); - bufferStore popBuffer(void); - - // new API (push() now behaves like a FIFO, more operators - bufferStore &operator [](const unsigned long index); - bufferArray &operator +(const bufferStore &); // append - bufferArray &operator +(const bufferArray &); // append - bufferArray &operator +=(const bufferStore &b); // append - bufferStore pop(void); - void push(const bufferStore& b); - void append(const bufferStore& b); - long length(void); - void clear(void); +public: + /** + * constructs a new bufferArray. + * A minimum of @ref ALLOC_MIN + * elements is allocated. + */ + bufferArray(); + + /** + * Constructs a new bufferArray. + * + * @param a The initial contents for this array. + */ + bufferArray(const bufferArray &a); + + /** + * Destroys the bufferArray. + */ + ~bufferArray(); + + /** + * Copys the bufferArray. + */ + bufferArray &operator =(const bufferArray &a); + + /** + * Checks if this bufferArray is empty. + * + * @return true if the bufferArray is empty. + */ + bool empty() const; + + /** + * Retrieves the bufferStore at given index. + * + * @return The bufferStore at index. + */ + bufferStore &operator [](const unsigned long index); + + /** + * Appends a bufferStore. + */ + bufferArray &operator +(const bufferStore &); + + /** + * Concatenates two bufferArrays. + */ + bufferArray &operator +(const bufferArray &); + + /** + * Appends a bufferStore. + */ + bufferArray &operator +=(const bufferStore &b); + + /** + * Removes the first bufferStore. + * + * @return The removed bufferStore. + */ + bufferStore pop(void); + + /** + * Inserts a bufferStore at index 0. + * + * @param b The bufferStore to be inserted. + */ + void push(const bufferStore& b); + + /** + * Appends a bufferStore. + * + * @param b The bufferStore to be appended. + */ + void append(const bufferStore& b); + + /** + * Evaluates the current length. + * + * @return The current number of bufferStores + */ + long length(void); + + /** + * Empties the bufferArray. + */ + void clear(void); private: - static const long ALLOC_MIN = 5; - long len; - long lenAllocd; - bufferStore* buff; + /** + * Minimum number of bufferStores to + * allocate. + */ + static const long ALLOC_MIN = 5; + + /** + * The current number of bufferStores in + * this bufferArray. + */ + long len; + + /** + * The current number of bufferStores + * allocated. + */ + long lenAllocd; + + /** + * The content. + */ + bufferStore* buff; }; inline bool bufferArray::empty() const { return len == 0; } diff --git a/lib/bufferstore.cc b/lib/bufferstore.cc index 842e666..7f2b49d 100644 --- a/lib/bufferstore.cc +++ b/lib/bufferstore.cc @@ -22,28 +22,30 @@ #include // That should be iostream.h, but it won't build on Sun WorkShop C++ 5.0 #include +#include #include +#include #include "bufferstore.h" bufferStore::bufferStore() { lenAllocd = 0; - buff = NULL; + buff = 0L; len = 0; start = 0; } bufferStore::bufferStore(const bufferStore &a) { lenAllocd = (a.getLen() > MIN_LEN) ? a.getLen() : MIN_LEN; - buff = new unsigned char [lenAllocd]; + buff = (unsigned char *)malloc(lenAllocd); len = a.getLen(); memcpy(buff, a.getString(0), len); start = 0; } -bufferStore::bufferStore(const unsigned char*_buff, long _len) { +bufferStore::bufferStore(const unsigned char *_buff, long _len) { lenAllocd = (_len > MIN_LEN) ? _len : MIN_LEN; - buff = new unsigned char [lenAllocd]; + buff = (unsigned char *)malloc(lenAllocd); len = _len; memcpy(buff, _buff, len); start = 0; @@ -54,6 +56,7 @@ bufferStore &bufferStore::operator =(const bufferStore &a) { len = a.getLen(); memcpy(buff, a.getString(0), len); start = 0; + return *this; } void bufferStore::init() { @@ -69,7 +72,8 @@ void bufferStore::init(const unsigned char*_buff, long _len) { } bufferStore::~bufferStore() { - delete [] buff; + if (buff != 0L) + free(buff); } unsigned long bufferStore::getLen() const { @@ -92,23 +96,26 @@ unsigned int bufferStore::getDWord(long pos) const { } const char* bufferStore::getString(long pos) const { - return (const char*)buff+pos+start; + return (const char*)buff + pos + start; } ostream &operator<<(ostream &s, const bufferStore &m) { - { - for (int i = m.start; i < m.len; i++) + // save stream flags + ostream::fmtflags old = s.flags(); + + for (int i = m.start; i < m.len; i++) s << hex << setw(2) << setfill('0') << (int)m.buff[i] << " "; - } + + // restore stream flags + s.flags(old); s << "("; - { - for (int i = m.start; i < m.len; i++) { - unsigned char c = m.buff[i]; - if (c>=' ' && c <= 'z') s << c; - } + + for (int i = m.start; i < m.len; i++) { + unsigned char c = m.buff[i]; + s << (unsigned char)(isprint(c) ? c : '.'); } - s<< ")" << dec << setw(0); - return s; + + return s << ")"; } void bufferStore::discardFirstBytes(int n) { @@ -119,12 +126,9 @@ void bufferStore::discardFirstBytes(int n) { void bufferStore::checkAllocd(long newLen) { if (newLen >= lenAllocd) { do { - lenAllocd = (lenAllocd < MIN_LEN)?MIN_LEN:(lenAllocd*2); + lenAllocd = (lenAllocd < MIN_LEN) ? MIN_LEN : (lenAllocd * 2); } while (newLen >= lenAllocd); - unsigned char* newBuff = new unsigned char [lenAllocd]; - memcpy(&newBuff[start], &buff[start], len - start); - delete [] buff; - buff = newBuff; + buff = (unsigned char *)realloc(buff, lenAllocd); } } diff --git a/lib/bufferstore.h b/lib/bufferstore.h index df216c0..cad32fa 100644 --- a/lib/bufferstore.h +++ b/lib/bufferstore.h @@ -4,48 +4,205 @@ #include "bool.h" class ostream; +/** + * A generic container for an array of bytes. + * + * bufferStore provides an array of bytes which + * can be accessed using various types. + */ class bufferStore { public: - bufferStore(); - bufferStore(const unsigned char *, long); - ~bufferStore(); - bufferStore(const bufferStore &); - bufferStore &operator =(const bufferStore &); - - // Reading Utils - unsigned long getLen() const; - unsigned char getByte(long pos) const; - unsigned int getWord(long pos) const; - unsigned int getDWord(long pos) const; - const char* getString(long pos=0) const; - void discardFirstBytes(int); - friend ostream &operator<<(ostream &, const bufferStore &); - bool empty() const; - - // Writing utils - void init(); - void init(const unsigned char*, long); - void addByte(unsigned char); - void addWord(int); - void addDWord(long); - void addString(const char*); - void addStringT(const char*); - void addBytes(const unsigned char*, int); - void addBuff(const bufferStore &, long maxLen=-1); + /** + * Constructs a new bufferStore. + */ + bufferStore(); + + /** + * Constructs a new bufferStore and + * initializes its content. + * + * @param buf Pointer to data for initialization. + * @param len Length of data for initialization. + */ + bufferStore(const unsigned char *, long); + + /** + * Destroys a bufferStore instance. + */ + ~bufferStore(); + + /** + * Constructs a new bufferStore and + * initializes its content. + * + * @param b A bufferStore, whose content is + * used for initialization. + */ + bufferStore(const bufferStore &); + + /** + * Copies a bufferStore. + */ + bufferStore &operator =(const bufferStore &); + + /** + * Retrieves the length of a bufferStore. + * + * @returns The current length of the contents + * in bytes. + */ + unsigned long getLen() const; + + /** + * Retrieves the byte at index pos. + * + * @param pos The index of the byte to retrieve. + * + * @returns The value of the byte at index pos + */ + unsigned char getByte(long pos) const; + + /** + * Retrieves the word at index pos. + * + * @param pos The index of the word to retrieve. + * + * @returns The value of the word at index pos + */ + unsigned int getWord(long pos) const; + + /** + * Retrieves the dword at index pos. + * + * @param pos The index of the dword to retrieve. + * + * @returns The value of the dword at index pos + */ + unsigned int getDWord(long pos) const; + + /** + * Retrieves the characters at index pos. + * + * @param pos The index of the characters to retrieve. + * + * @returns A pointer to characters at index pos + */ + const char* getString(long pos=0) const; + + /** + * Removes bytes from the start of the buffer. + * + * @param len Number of bytes to remove. + */ + void discardFirstBytes(int); + + /** + * Prints a dump of the content. + * + * Mainly used for debugging purposes. + * + * @param s The stream to write to. + * @param b The bufferStore do be dumped. + * + * @returns The stream. + */ + friend ostream &operator<<(ostream &, const bufferStore &); + + /** + * Tests if the bufferStore is empty. + * + * @returns true, if the bufferStore is empty. + * false, if it contains data. + */ + bool empty() const; + + /** + * Initializes the bufferStore. + * + * All data is removed, the length is + * reset to 0. + */ + void init(); + + /** + * Initializes the bufferStore with + * a given data. + * + * @param buf Pointer to data to initialize from. + * @param len Length of data. + */ + void init(const unsigned char*, long); + + /** + * Appends a byte to the content of this instance. + * + * @param c The byte to append. + */ + void addByte(unsigned char); + + /** + * Appends a word to the content of this instance. + * + * @param w The word to append. + */ + void addWord(int); + + /** + * Appends a dword to the content of this instance. + * + * @param dw The dword to append. + */ + void addDWord(long); + + /** + * Appends a string to the content of this instance. + * + * The trailing zero byte is not copied + * to the content. + * + * @param s The string to append. + */ + void addString(const char*); + + /** + * Appends a string to the content of this instance. + * + * The trailing zero byte is copied + * to the content. + * + * @param s The string to append. + */ + void addStringT(const char*); + + /** + * Appends data to the content of this instance. + * + * @param buf The data to append. + * @param len Length of data. + */ + void addBytes(const unsigned char*, int); + + /** + * Appends data to the content of this instance. + * + * @param b The bufferStore whose content to append. + * @param maxLen Length of content. + */ + void addBuff(const bufferStore &, long maxLen=-1); private: - void checkAllocd(long newLen); + void checkAllocd(long newLen); - long len; - long lenAllocd; - long start; - unsigned char* buff; + long len; + long lenAllocd; + long start; + unsigned char* buff; - enum c { MIN_LEN = 300 }; + enum c { MIN_LEN = 300 }; }; inline bool bufferStore::empty() const { - return (len-start) == 0; + return (len-start) == 0; } #endif diff --git a/lib/ppsocket.cc b/lib/ppsocket.cc index cb04919..1249f18 100644 --- a/lib/ppsocket.cc +++ b/lib/ppsocket.cc @@ -181,7 +181,11 @@ listen(char *Host, int Port) ppsocket *ppsocket:: accept(char *Peer, int MaxLen) { +#ifdef sun + int len; +#else socklen_t len; +#endif ppsocket *accepted; char *peer; diff --git a/lib/psitime.cc b/lib/psitime.cc new file mode 100644 index 0000000..95ba6da --- /dev/null +++ b/lib/psitime.cc @@ -0,0 +1,117 @@ + +#include "psitime.h" +#include + +PsiTime::PsiTime(psi_timeval *_ptv, psi_timezone *_ptz) { + if (_ptv != 0L) + ptv = *_ptv; + if (_ptz != 0L) { + ptz = *_ptz; + ptzValid = true; + } else + ptzValid = false; + /* get our own timezone */ + gettimeofday(NULL, &utz); + psi2unix(); +} + +PsiTime::~PsiTime() { +} + +void PsiTime::setUnixTime(struct timeval *_utv) { + if (_utv != 0L) + utv = *_utv; + unix2psi(); +} + +void PsiTime::setUnixNow(void) { + gettimeofday(&utv, &utz); + unix2psi(); +} + + +void PsiTime::setPsiTime(psi_timeval *_ptv) { + if (_ptv != 0L) + ptv = *_ptv; + psi2unix(); +} + +void PsiTime::setPsiZone(psi_timezone *_ptz) { + if (_ptz != 0L) { + ptz = *_ptz; + ptzValid = true; + } + psi2unix(); +} + +struct timeval &PsiTime::getTimeval(void) { + return utv; +} + +time_t PsiTime::getTime(void) { + return utv.tv_sec; +} + +psi_timeval &PsiTime::getPsiTimeval(void) { + return ptv; +} + +ostream &operator<<(ostream &s, const PsiTime &t) { + const char *fmt = "%c"; + char buf[100]; + strftime(buf, sizeof(buf), fmt, localtime(&t.utv.tv_sec)); + s << buf; + return s; +} + +/** + * The difference between + * EPOC epoch (01.01.0001 00:00:00) + * and Unix epoch (01.01.1970 00:00:00) + * in microseconds. + */ +#define EPOCH_DIFF 0x00dcddb30f2f8000ULL + +static unsigned long long +evalOffset(psi_timezone ptz, bool valid) { + unsigned long long offset = 0; + + if (valid) { + offset = ptz.utc_offset; + if ((ptz.dst_zones & 0x40000000) || (ptz.dst_zones & ptz.home_zone)) + offset += 3600; + } else { + const char *offstr = getenv("PSI_TZ"); + if (offstr != 0L) { + char *err = 0L; + offset = strtoul(offstr, &err, 0); + if (err != 0L) + offset = 0; + } + } + offset *= 1000000; + return offset; +} + +void PsiTime::psi2unix(void) { + unsigned long long micro = ptv.tv_high; + micro = (micro << 32) | ptv.tv_low; + + /* Substract Psion's idea of UTC offset */ + micro -= evalOffset(ptz, ptzValid); + micro -= EPOCH_DIFF; + + utv.tv_sec = micro / 1000000; + utv.tv_usec = micro % 1000000; +} + +void PsiTime::unix2psi(void) { + unsigned long long micro = utv.tv_sec * 1000000 + utv.tv_usec; + + /* Add Psion's idea of UTC offset */ + micro += EPOCH_DIFF; + micro += evalOffset(ptz, ptzValid); + + ptv.tv_low = micro & 0x0ffffffff; + ptv.tv_high = (micro >> 32) & 0x0ffffffff; +} diff --git a/lib/psitime.h b/lib/psitime.h new file mode 100644 index 0000000..6946fc1 --- /dev/null +++ b/lib/psitime.h @@ -0,0 +1,198 @@ +#ifndef _PSITIME_H_ +#define _PSITIME_H_ + +#include +#include + +#include + +/** + * Holds a Psion time value. + * Psion time values are 64 bit + * integers describing the time + * since 01.01.0001 in microseconds. + */ +typedef struct psi_timeval_t { + /** + * Prints a psi_timeval in human readable format. + */ + friend ostream &operator<<(ostream &o, const psi_timeval_t &ptv) { + ostream::fmtflags old = o.flags(); + unsigned long long micro = ptv.tv_high; + micro = (micro << 32) | ptv.tv_low; + micro /= 1000000; + int s = micro % 60; + micro /= 60; + int m = micro % 60; + micro /= 60; + int h = micro % 24; + micro /= 24; + int d = micro % 365; + micro /= 365; + int y = micro; + o << dec; + if (y > 0) + o << y << " year" << ((y > 1) ? "s " : " "); + if (d > 0) + o << d << " day" << ((d > 1) ? "s " : " "); + if (h > 0) + o << h << " hour" << ((h != 1) ? "s " : " "); + if (m > 0) + o << m << " minute" << ((m != 1) ? "s " : " "); + o << s << " second" << ((s != 1) ? "s" : ""); + o.flags(old); + return o; + } + /** + * The lower 32 bits + */ + unsigned long tv_low; + /** + * The upper 32 bits + */ + unsigned long tv_high; +} psi_timeval; + +/** + * holds a Psion time zone description. + */ +typedef struct psi_timezone_t { + friend ostream &operator<<(ostream &s, const psi_timezone_t &ptz) { + ostream::fmtflags old = s.flags(); + int h = ptz.utc_offset / 3600; + int m = ptz.utc_offset % 3600; + s << "offs: " << dec << h << "h"; + if (m != 0) + s << ", " << m << "m"; + s.flags(old); + return s; + } + unsigned long utc_offset; + unsigned long dst_zones; + unsigned long home_zone; +} psi_timezone; + +/** + * Psion time related utility class. + * + * PsiTime provides easy access to the time format, used + * when communicating with a Psion. Internally, the time + * is always normalized to GMT. The time value can be set + * and retrieved in both Unix and Psion formats. This + * allows easy conversion between both formats. + * NOTE: For proper conversion, the current timezone of + * the Psion has to be set. For EPOC devices, the + * timezone can be evaluated using + * @ref rpcs::getMachineInfo. For SIBO devices, + * unfortunately there is no known method of retrieving + * this information. Therefore, if the timezone is + * not set, a fallback using the environment + * variable PSI_TZ is provided. Users should + * set this variable to the offset of their time zone + * in seconds. + */ +class PsiTime { +public: + /** + * Contructs a new instance. + * + * @param _ptv A Psion time value for initialization. + * @param _ptz A Psion timezone for initialization. + */ + PsiTime(psi_timeval *_ptv = 0L, psi_timezone *_ptz = 0L); + + /** + * Constructs a new instance. + * + * @param _utv A Unix time value for initialization. + * @param _utz A Unix timezone for initialization. + */ + PsiTime(struct timeval *_utv = 0L, struct timezone *_utz = 0L); + + /** + * Destroys the instance. + */ + ~PsiTime(); + + /** + * Modifies the value of this instance. + * + * @param _ptv The new Psion time representation. + */ + void setPsiTime(psi_timeval *_ptv); + + /** + * Sets the Psion time zone of this instance. + * + * @param _ptz The new Psion time zone. + */ + void setPsiZone(psi_timezone *_ptz); + + /** + * Sets the value of this instance. + * + * @param _utv The new Unix time representation. + */ + void setUnixTime(struct timeval *_utv); + + /** + * Sets the value of this instance to the + * current time of the Unix machine. + */ + void setUnixNow(void); + + /** + * Retrieves the instance's current value + * in Unix time format. + * + * @returns The instance's current time as Unix struct timeval. + */ + struct timeval &getTimeval(void); + + /** + * Retrieves the instance's current value + * in Unix time format. + * + * @returns The instance's current time as Unix time_t. + */ + time_t getTime(void); + + /** + * Retrieves the instance's current value + * in Psion time format. + * + * @returns The instance's current time a Psion struct psi_timeval_t. + */ + psi_timeval &getPsiTimeval(void); + + /** + * Prints the instance's value in human readable format. + * This function uses the current locale setting for + * formatting the time. + * + * @param s The stream to be written. + * @param t The instance whose value should be displayed. + * + * @returns The stream. + */ + friend ostream &operator<<(ostream &s, const PsiTime &t); + + enum zone { + PSI_TZ_NONE = 0, + PSI_TZ_EUROPEAN = 1, + PSI_TZ_NORTHERN = 2, + PSI_TZ_SOUTHERN = 4, + PSI_TZ_HOME = 0x40000000, + }; + +private: + void psi2unix(void); + void unix2psi(void); + + psi_timeval ptv; + psi_timezone ptz; + struct timeval utv; + struct timezone utz; + bool ptzValid; +}; +#endif diff --git a/lib/rfsv.h b/lib/rfsv.h index e6006ef..888db8a 100644 --- a/lib/rfsv.h +++ b/lib/rfsv.h @@ -5,12 +5,29 @@ class ppsocket; class bufferStore; class bufferArray; -#define RFSV_SENDLEN 2000 +const unsigned long RFSV_SENDLEN = 2000; +/** + * Defines the callback procedure for + * progress indication of copy operations. + */ typedef int (*cpCallback_t)(long); // Abstract base class of RFSV ; 16-bit and 32-bit variants implement this // interface +/** + * Access remote file services of a Psion. + * + * rfsv provides an API for accessing file services + * of a Psion connected via ncpd. This class defines the + * interface and a small amount of common constants and + * methods. The majority of implementation is provided + * by @ref rfsv32 and @ref rfsv16, which implement the + * variations of the protocol for EPOC and SIBO respectively. + * Usually, the class @ref rfsvfactory is used to instantiate + * the correct variant depending on the remote machine, + * currently connected. + */ class rfsv { public: virtual ~rfsv() {} @@ -46,20 +63,32 @@ class rfsv { virtual long rename(const char *, const char *) = 0; virtual long remove(const char *) = 0; + virtual long attr2std(long) = 0; + virtual long std2attr(long) = 0; + char *opErr(long); + /** + * The kown modes for seek. + */ enum seek_mode { PSI_SEEK_SET = 1, PSI_SEEK_CUR = 2, PSI_SEEK_END = 3 }; + /** + * The known modes for file open. + */ enum open_flags { PSI_O_RDONLY = 00, PSI_O_WRONLY = 01, PSI_O_RDWR = 02, }; + /** + * The known modes for file creation. + */ enum open_mode { PSI_O_CREAT = 0100, PSI_O_EXCL = 0200, @@ -67,6 +96,9 @@ class rfsv { PSI_O_APPEND = 02000, }; + /** + * The known error codes. + */ enum errs { E_PSI_GEN_NONE = 0, E_PSI_GEN_FAIL = -1, @@ -145,6 +177,36 @@ class rfsv { // Special error code for "Operation not permitted in RFSV16" E_PSI_NOT_SIBO = -200 }; + + /** + * The known file attributes + */ + enum file_attribs { + /** + * Attributes, valid on both EPOC and SIBO. + */ + PSI_A_RDONLY = 0x0001, + PSI_A_HIDDEN = 0x0002, + PSI_A_SYSTEM = 0x0004, + PSI_A_DIR = 0x0008, + PSI_A_ARCHIVE = 0x0010, + PSI_A_VOLUME = 0x0020, + + /** + * Attributes, valid on EPOC only. + */ + PSI_A_NORMAL = 0x0040, + PSI_A_TEMP = 0x0080, + PSI_A_COMPRESSED = 0x0100, + + /** + * Attributes, valid on SIBO only. + */ + PSI_A_READ = 0x0200, + PSI_A_EXEC = 0x0400, + PSI_A_STREAM = 0x0800, + PSI_A_TEXT = 0x1000 + }; }; #endif diff --git a/lib/rfsv16.cc b/lib/rfsv16.cc index 9ee24f2..f71be9f 100644 --- a/lib/rfsv16.cc +++ b/lib/rfsv16.cc @@ -37,6 +37,8 @@ #include "ppsocket.h" #include "bufferarray.h" +#define RFSV16_MAXDATALEN 852 // 640 + rfsv16::rfsv16(ppsocket *_skt) : serNum(0) { skt = _skt; @@ -57,7 +59,6 @@ rfsv16::~rfsv16() void rfsv16:: reconnect() { -cerr << "rfsv16::reconnect" << endl; skt->closeSocket(); skt->reconnect(); serNum = 0; @@ -68,7 +69,6 @@ cerr << "rfsv16::reconnect" << endl; void rfsv16:: reset() { -cerr << "rfsv16::reset" << endl; bufferStore a; status = E_PSI_FILE_DISC; a.addStringT(getConnectName()); @@ -91,7 +91,7 @@ getStatus() const char *rfsv16:: getConnectName() { - return "SYS$RFSV.*"; + return "SYS$RFSV"; } int rfsv16:: @@ -148,14 +148,10 @@ fopen(long attr, const char *name, long &handle) return E_PSI_FILE_DISC; long res = getResponse(a); - // cerr << "fopen, getword 0 is " << hex << setw(2) << a.getWord(0) << endl; - // cerr << "fopen, getlen is " << hex << setw(2) << a.getLen() << endl; - // cerr << "fopen, res is " << hex << setw(2) << res << endl; - if (!res && a.getLen() == 4 && a.getWord(0) == 0) { - handle = (long)a.getWord(2); + if (res == 0) { + handle = (long)a.getWord(0); return 0; } - // cerr << "fopen: Unknown response (" << attr << "," << name << ") " << a < 16) { int version = a.getWord(0); if (version != 2) { @@ -242,7 +248,7 @@ dir(const char *dirName, bufferArray * files) temp.addDWord(size); temp.addDWord((long)status); temp.addStringT(s); - files->pushBuffer(temp); + files->append(temp); } } if ((short int)res == E_PSI_FILE_EOF) @@ -300,15 +306,13 @@ cerr << "rfsv16::fgetmtime" << endl; return E_PSI_FILE_DISC; long res = getResponse(a); - if (res != 0) + if (res != 0) { + cerr << "fgetmtime: Error " << res << " on file " << name << endl; return res; - if (a.getLen() == 2) { - cerr << "fgetmtime: Error " << a.getWord(0) << " on file " << name << endl; - return 1; } - else if (a.getLen() == 18 && a.getWord(0) == 0) { - *mtime = a.getDWord(10); - return a.getWord(0); + else if (a.getLen() == 16) { + *mtime = a.getDWord(8); + return res; } cerr << "fgetmtime: Unknown response (" << name << ") " << a < 16) { int version = a.getWord(0); if (version != 2) { @@ -475,7 +475,7 @@ devlist(long *devbits) // Find the drive to FOPEN char name[4] = { 'x', ':', '\\', '\0' } ; - a.discardFirstBytes(8); // Result, fsys, dev, path, file, file, ending, flag + a.discardFirstBytes(6); // Result, fsys, dev, path, file, file, ending, flag /* This leaves R E M : : M : \ */ name[0] = (char) a.getByte(5); // the M long status = fopen(P_FDEVICE, name, fileHandle); @@ -491,7 +491,6 @@ devlist(long *devbits) res = getResponse(a); if (res) break; - a.discardFirstBytes(2); // Result int version = a.getWord(0); if (version != 2) { cerr << "devlist: not version 2" << endl; @@ -551,7 +550,6 @@ devinfo(int devnum, long *vfree, long *vtotal, long *vattr, // cerr << "devinfo STATUSDEVICE res is " << dec << (signed short int) res << endl; return NULL; } - a.discardFirstBytes(2); // Result int type = a.getWord(2); int changeable = a.getWord(4); long size = a.getDWord(6); @@ -571,14 +569,24 @@ devinfo(int devnum, long *vfree, long *vtotal, long *vattr, bool rfsv16:: sendCommand(enum commands cc, bufferStore & data) { + if (status == E_PSI_FILE_DISC) { + reconnect(); + if (status == E_PSI_FILE_DISC) + return FALSE; + } + bool result; bufferStore a; a.addWord(cc); a.addWord(data.getLen()); a.addBuff(data); result = skt->sendBufferStore(a); + if (!result) { + reconnect(); + result = skt->sendBufferStore(a); if (!result) status = E_PSI_FILE_DISC; + } return result; } @@ -589,17 +597,22 @@ getResponse(bufferStore & data) // getWord(2) is the size field // which is the body of the response not counting the command (002a) and // the size word. - if (skt->getBufferStore(data) == 1 && - data.getWord(0) == 0x2a && + if (skt->getBufferStore(data) != 1) { + cerr << "rfsv16::getResponse: duff response. " + "getBufferStore failed." << endl; + } else if (data.getWord(0) == 0x2a && data.getWord(2) == data.getLen()-4) { - data.discardFirstBytes(4); - long ret = data.getWord(0); + long ret = (short)data.getWord(4); + data.discardFirstBytes(6); return ret; - } else - status = E_PSI_FILE_DISC; + } else { cerr << "rfsv16::getResponse: duff response. Size field:" << -data.getWord(2) << " Frame size:" << data.getLen()-4 << " Result field:" << -data.getWord(4) << endl; + data.getWord(2) << " Frame size:" << + data.getLen()-4 << " Result field:" << + data.getWord(4) << endl; + } + + status = E_PSI_FILE_DISC; return status; } @@ -614,61 +627,88 @@ cerr << "rfsv16::opErr 0x" << hex << setfill('0') << setw(4) long rfsv16:: fread(long handle, unsigned char *buf, long len) { -cerr << "rfsv16::fread ***" << endl; + long res; + long count = 0; + + while (count < len) { bufferStore a; - long remaining = len; - // Read in blocks of 291 bytes; the maximum payload for an RFSV frame. - // ( As seen in traces ) - this isn't optimal: RFSV can handle - // fragmentation of frames, where only the first FREAD RESPONSE frame - // has a RESPONSE (00 2A), SIZE and RESULT field. Every subsequent frame + + // Read in blocks of 291 bytes; the maximum payload for + // an RFSV frame. ( As seen in traces ) - this isn't optimal: + // RFSV can handle fragmentation of frames, where only the + // first FREAD RESPONSE frame has a RESPONSE (00 2A), SIZE + // and RESULT field. Every subsequent frame // just has data, 297 bytes (or less) of it. - const int maxblock = 291; - long readsofar = 0; - while (remaining) { - long thisblock = (remaining > maxblock) ? maxblock : remaining; -cerr << "fread: " << dec << remaining << " bytes remain. This block is " << thisblock -<< " bytes." << endl; - a.init(); a.addWord(handle); - a.addWord(thisblock); + a.addWord((len - count) > RFSV16_MAXDATALEN + ? RFSV16_MAXDATALEN + : (len - count)); sendCommand(FREAD, a); - long res = getResponse(a); - remaining -= a.getLen(); -// copy the data to buf + res = getResponse(a); -cerr << "fread getResponse returned " << dec<< (signed short int) res << " data: " << a << dec < RFSV16_MAXDATALEN + ? RFSV16_MAXDATALEN + : (len - count); + a.addWord(handle); + a.addBytes(buf, nbytes); + sendCommand(FWRITE, a); + res = getResponse(a); + if (res != 0) + return res; + + count += nbytes; + buf += nbytes; + } + return count; } long rfsv16:: copyFromPsion(const char *from, const char *to, cpCallback_t cb) { -cerr << "rfsv16::copyFromPsion" << endl; long handle; long res; long len; if ((res = fopen(P_FSHARE | P_FSTREAM, from, handle)) != 0) return res; -cerr << "fopen response is " << dec << (signed short int)res << endl; ofstream op(to); if (!op) { fclose(handle); return -1; } do { - unsigned char buf[2000]; + unsigned char buf[RFSV_SENDLEN]; if ((len = fread(handle, buf, sizeof(buf))) > 0) op.write(buf, len); if (cb) { @@ -681,13 +721,12 @@ cerr << "fopen response is " << dec << (signed short int)res << endl; fclose(handle); op.close(); - return len; + return len == E_PSI_FILE_EOF ? 0 : len; } long rfsv16:: copyToPsion(const char *from, const char *to, cpCallback_t cb) { -cerr << "rfsv16::copyToPsion" << endl; long handle; long res; @@ -701,50 +740,30 @@ cerr << "rfsv16::copyToPsion" << endl; return res; } unsigned char *buff = new unsigned char[RFSV_SENDLEN]; - int total = 0; - while (ip && !ip.eof()) { + while (res >= 0 && ip && !ip.eof()) { ip.read(buff, RFSV_SENDLEN); - bufferStore tmp(buff, ip.gcount()); - int len = tmp.getLen(); - total += len; - if (len == 0) - break; - bufferStore a; - a.addDWord(handle); - a.addBuff(tmp); - if (!sendCommand(FWRITE, a)) { // FIXME: need to check params - fclose(handle); - ip.close(); - delete[]buff; - return E_PSI_FILE_DISC; + res = fwrite(handle, buff, ip.gcount()); + if (cb) + if (!cb(res)) { + res = E_PSI_FILE_CANCEL; } - res = getResponse(a); - if (res) { - fclose(handle); - ip.close(); - delete[]buff; - return res; } - if (cb) { - if (!cb(len)) { - fclose(handle); - ip.close(); + delete[]buff; - return E_PSI_FILE_CANCEL; - } - } - } fclose(handle); ip.close(); - delete[]buff; return 0; } long rfsv16:: fsetsize(long handle, long size) { -cerr << "rfsv16::fsetsize ***" << endl; - return 0; + bufferStore a; + a.addWord(handle); + a.addDWord(size); + if (!sendCommand(FSETEOF, a)) + return E_PSI_FILE_DISC; + return getResponse(a); } /* @@ -755,8 +774,92 @@ cerr << "rfsv16::fsetsize ***" << endl; long rfsv16:: fseek(long handle, long pos, long mode) { -cerr << "rfsv16::fseek ***" << endl; - return 0; + bufferStore a; + long res; + long savpos = 0; + long realpos; + long calcpos = 0; + +/* + seek-parameter for psion: + dword position + dword handle + dword mode + 1 = from start + 2 = from current pos + 3 = from end + ??no more?? 4 = sense recpos + ??no more?? 5 = set recpos + ??no more?? 6 = text-rewind + */ + + if ((mode < PSI_SEEK_SET) || (mode > PSI_SEEK_END)) + return E_PSI_GEN_ARG; + + if ((mode == PSI_SEEK_CUR) && (pos >= 0)) { + /* get and save current position */ + a.init(); + a.addWord(handle); + a.addDWord(0); + a.addWord(PSI_SEEK_CUR); + if (!sendCommand(FSEEK, a)) + return E_PSI_FILE_DISC; + if ((res = getResponse(a)) != 0) + return res; + savpos = a.getDWord(0); + if (pos == 0) + return savpos; + } + if ((mode == PSI_SEEK_END) && (pos >= 0)) { + /* get and save end position */ + a.init(); + a.addWord(handle); + a.addDWord(0); + a.addWord(PSI_SEEK_END); + if (!sendCommand(FSEEK, a)) + return E_PSI_FILE_DISC; + if ((res = getResponse(a)) != 0) + return res; + savpos = a.getDWord(0); + if (pos == 0) + return savpos; + } + /* Now the real seek */ + a.addWord(handle); + a.addDWord(pos); + a.addWord(mode); + if (!sendCommand(FSEEK, a)) + return E_PSI_FILE_DISC; + if ((res = getResponse(a)) != 0) + return res; + realpos = a.getDWord(0); + switch (mode) { + case PSI_SEEK_SET: + calcpos = pos; + break; + case PSI_SEEK_CUR: + calcpos = savpos + pos; + break; + case PSI_SEEK_END: + return realpos; + break; + } + if (calcpos > realpos) { + /* Beyond end of file */ + res = fsetsize(handle, calcpos); + if (res != 0) + return res; + a.init(); + a.addWord(handle); + a.addDWord(calcpos); + a.addWord(PSI_SEEK_SET); + if (!sendCommand(FSEEK, a)) + return E_PSI_FILE_DISC; + if ((res = getResponse(a)) != 0) + return res; + realpos = a.getDWord(0); + } + return realpos; } long rfsv16:: @@ -771,11 +874,11 @@ mkdir(const char* dirName) // and this needs sending in the length word. sendCommand(MKDIR, a); long res = getResponse(a); - if (!res && a.getLen() == 2) { + if (!res) { // Correct response - return a.getWord(0); + return res; } - cerr << "Unknown response from mkdir "<< a <tm_isdst) micro -= (60*60); /* Adjust for DST */ + } + return (long) micro; } @@ -216,8 +225,17 @@ time2micro(unsigned long time, unsigned long µHi, unsigned long µLo) pes <<= 8; micro += pes; micro -= EPOCH_DIFF_SECS; - micro += EPOCH_2H; - micro -= 3600; /* 1 hour PJC */ + + /* Adjust for timezone and daylight saving time */ + { + struct tm *t; + long date=time; + + t = localtime(&date); + micro -= timezone; /* Adjust for timezone */ + if (t->tm_isdst) micro += (60*60); /* Adjust for DST */ + } + micro *= (unsigned long long)1000000; microLo = (micro & (unsigned long long)0x0FFFFFFFF); micro >>= 32; @@ -263,7 +281,7 @@ dir(const char *name, bufferArray * files) s.addByte(0); while (d % 4) d++; - files->pushBuffer(s); + files->append(s); d += shortLen; while (d % 4) d++; @@ -495,8 +513,12 @@ sendCommand(enum commands cc, bufferStore & data) serNum = 0; a.addBuff(data); result = skt->sendBufferStore(a); + if (!result) { + reconnect(); + result = skt->sendBufferStore(a); if (!result) status = E_PSI_FILE_DISC; + } return result; } @@ -888,3 +910,73 @@ err2psierr(long status) } return e2psi[status - E_EPOC_DIR_FULL]; } + + +/* + * Translate EPOC attributes to standard attributes. + */ +long rfsv32:: +attr2std(long attr) +{ + long res = 0; + + // Common attributes + if (attr & EPOC_ATTR_RONLY) + res |= PSI_A_RDONLY; + if (attr & EPOC_ATTR_HIDDEN) + res |= PSI_A_HIDDEN; + if (attr & EPOC_ATTR_SYSTEM) + res |= PSI_A_SYSTEM; + if (attr & EPOC_ATTR_DIRECTORY) + res |= PSI_A_DIR; + if (attr & EPOC_ATTR_ARCHIVE) + res |= PSI_A_ARCHIVE; + if (attr & EPOC_ATTR_VOLUME) + res |= PSI_A_VOLUME; + + // EPOC-specific + if (attr & EPOC_ATTR_NORMAL) + res |= PSI_A_NORMAL; + if (attr & EPOC_ATTR_TEMPORARY) + res |= PSI_A_TEMP; + if (attr & EPOC_ATTR_COMPRESSED) + res |= PSI_A_COMPRESSED; + + // Do what we can for SIBO + res |= PSI_A_READ; + + return res; +} + +/* + * Translate standard attributes to EPOC attributes. + */ +long rfsv32:: +std2attr(long attr) +{ + long res = 0; + + // Common attributes + if (!(attr & PSI_A_RDONLY)) + res |= EPOC_ATTR_RONLY; + if (attr & PSI_A_HIDDEN) + res |= EPOC_ATTR_HIDDEN; + if (attr & PSI_A_SYSTEM) + res |= EPOC_ATTR_SYSTEM; + if (attr & PSI_A_DIR) + res |= EPOC_ATTR_DIRECTORY; + if (attr & PSI_A_ARCHIVE) + res |= EPOC_ATTR_ARCHIVE; + if (attr & PSI_A_VOLUME) + res |= EPOC_ATTR_VOLUME; + + // EPOC-specific + if (attr & PSI_A_NORMAL) + res |= EPOC_ATTR_NORMAL; + if (attr & PSI_A_TEMP) + res |= EPOC_ATTR_TEMPORARY; + if (attr & PSI_A_COMPRESSED) + res |= EPOC_ATTR_COMPRESSED; + + return res; +} diff --git a/lib/rfsv32.h b/lib/rfsv32.h index 8383e72..2aac5b9 100644 --- a/lib/rfsv32.h +++ b/lib/rfsv32.h @@ -44,6 +44,9 @@ class rfsv32 : public rfsv { char *opAttr(long); long opMode(long); + long attr2std(long); + long std2attr(long); + private: enum file_attrib { diff --git a/lib/rpcs.cc b/lib/rpcs.cc index 5863c5b..79630cf 100644 --- a/lib/rpcs.cc +++ b/lib/rpcs.cc @@ -28,11 +28,21 @@ #include "ppsocket.h" #include "bufferarray.h" +static const char * const langstrings[] = { + "Test", "English", "French", "German", "Spanish", "Italian", "Swedish", + "Danish", "Norwegian", "Finnish", "American", "Swiss French", + "Swiss German", "Portugese", "Turkish", "Icelandic", "Russian", + "Hungarian", "Dutch", "Belgian Flemish", "Australian", + "Belgish French", "Austrian", "New Zealand", + "International French", "Czech", "Slovak", "Polish", "Slovenian", + 0L +}; + // // public common API // void rpcs:: -reconnect() +reconnect(void) { skt->closeSocket(); skt->reconnect(); @@ -40,7 +50,7 @@ reconnect() } void rpcs:: -reset() +reset(void) { bufferStore a; status = E_PSI_FILE_DISC; @@ -54,13 +64,13 @@ reset() } long rpcs:: -getStatus() +getStatus(void) { return status; } const char *rpcs:: -getConnectName() +getConnectName(void) { return "SYS$RPCS"; } @@ -68,16 +78,6 @@ getConnectName() // // protected internals // -char *rpcs:: -convertSlash(const char *name) -{ - char *n = strdup(name); - for (char *p = n; *p; p++) - if (*p == '/') - *p = '\\'; - return n; -} - bool rpcs:: sendCommand(enum commands cc, bufferStore & data) { @@ -91,16 +91,21 @@ sendCommand(enum commands cc, bufferStore & data) a.addByte(cc); a.addBuff(data); result = skt->sendBufferStore(a); - if (!result) - status = E_PSI_FILE_DISC; + if (!result) { + reconnect(); + result = skt->sendBufferStore(a); + if (!result) + status = E_PSI_FILE_DISC; + } return result; } -long rpcs:: +int rpcs:: getResponse(bufferStore & data) { if (skt->getBufferStore(data) == 1) { - long ret = data.getByte(0); + char ret = data.getByte(0); + data.discardFirstBytes(1); return ret; } else status = E_PSI_FILE_DISC; @@ -113,12 +118,12 @@ getResponse(bufferStore & data) int rpcs:: getNCPversion(int &major, int &minor) { + int res; bufferStore a; + if (!sendCommand(QUERY_NCP, a)) return E_PSI_FILE_DISC; - long res = getResponse(a); - a.discardFirstBytes(1); - if (res) + if ((res = getResponse(a))) return res; if (a.getLen() != 2) return E_PSI_GEN_FAIL; @@ -131,6 +136,7 @@ int rpcs:: execProgram(const char *program, const char *args) { bufferStore a; + a.addStringT(program); int l = strlen(program); for (int i = 127; i > l; i--) @@ -139,15 +145,14 @@ execProgram(const char *program, const char *args) a.addStringT(args); if (!sendCommand(EXEC_PROG, a)) return E_PSI_FILE_DISC; - long res = getResponse(a); -cout << res << " " << a << endl; - return res; + return getResponse(a); } int rpcs:: stopProgram(const char *program) { bufferStore a; + a.addStringT(program); if (!sendCommand(STOP_PROG, a)) return E_PSI_FILE_DISC; @@ -158,6 +163,7 @@ int rpcs:: queryProgram(const char *program) { bufferStore a; + a.addStringT(program); if (!sendCommand(QUERY_PROG, a)) return E_PSI_FILE_DISC; @@ -167,13 +173,13 @@ queryProgram(const char *program) int rpcs:: formatOpen(const char *drive, int &handle, int &count) { + int res; bufferStore a; + a.addStringT(drive); if (!sendCommand(FORMAT_OPEN, a)) return E_PSI_FILE_DISC; - long res = getResponse(a); - a.discardFirstBytes(1); - if (res) + if ((res = getResponse(a))) return res; if (a.getLen() != 4) return E_PSI_GEN_FAIL; @@ -186,6 +192,7 @@ int rpcs:: formatRead(int handle) { bufferStore a; + a.addWord(handle); if (!sendCommand(FORMAT_READ, a)) return E_PSI_FILE_DISC; @@ -195,13 +202,13 @@ formatRead(int handle) int rpcs:: getUniqueID(const char *device, long &id) { + int res; bufferStore a; + a.addStringT(device); if (!sendCommand(GET_UNIQUEID, a)) return E_PSI_FILE_DISC; - long res = getResponse(a); - a.discardFirstBytes(1); - if (res) + if ((res = getResponse(a))) return res; if (a.getLen() != 4) return E_PSI_GEN_FAIL; @@ -210,14 +217,14 @@ getUniqueID(const char *device, long &id) } int rpcs:: -getOwnerInfo(bufferArray &ret) +getOwnerInfo(bufferArray &owner) { + int res; bufferStore a; + if (!sendCommand(GET_OWNERINFO, a)) return E_PSI_FILE_DISC; - long res = getResponse(a); - a.discardFirstBytes(1); - if (res) + if ((res = getResponse(a))) return res; a.addByte(0); int l = a.getLen(); @@ -225,13 +232,18 @@ getOwnerInfo(bufferArray &ret) for (int i = 0; i < l; i++) if (s[i] == 6) s[i] = 0; - ret.clear(); + owner.clear(); while (l > 0) { - bufferStore b; - b.addStringT(s); - ret += b; - l -= (strlen(s) + 1); - s += (strlen(s) + 1); + if (*s != '\0') { + bufferStore b; + b.addStringT(s); + owner += b; + l -= (strlen(s) + 1); + s += (strlen(s) + 1); + } else { + l--; + s++; + } } return res; } @@ -239,12 +251,12 @@ getOwnerInfo(bufferArray &ret) int rpcs:: getMachineType(int &type) { + int res; bufferStore a; + if (!sendCommand(GET_MACHINETYPE, a)) return E_PSI_FILE_DISC; - long res = getResponse(a); - a.discardFirstBytes(1); - if (res) + if ((res = getResponse(a))) return res; if (a.getLen() != 2) return E_PSI_GEN_FAIL; @@ -253,27 +265,25 @@ getMachineType(int &type) } int rpcs:: -fuser(const char *name, char *buf, int bufsize) +fuser(const char *name, char *buf, int maxlen) { + int res; bufferStore a; + char *p; + a.addStringT(name); if (!sendCommand(FUSER, a)) return E_PSI_FILE_DISC; - long res = getResponse(a); - a.discardFirstBytes(1); - if (res) + if ((res = getResponse(a))) return res; - int len = ((int)a.getLen() > bufsize) ? bufsize - 1 : a.getLen(); - strncpy(buf, a.getString(0), len); - buf[len] = '\0'; - char *p; + strncpy(buf, a.getString(0), maxlen - 1); while ((p = strchr(buf, 6))) - *p = '\n'; + *p = '\0'; return res; } int rpcs:: -quitServer() +quitServer(void) { bufferStore a; if (!sendCommand(QUIT_SERVER, a)) @@ -281,3 +291,25 @@ quitServer() return getResponse(a); } +const char *rpcs:: +languageString(int code) { + for (int i = 0; i <= code; i++) + if (langstrings[i] == 0L) + return "Unknown"; + return langstrings[code]; +} + +const char *rpcs:: +batteryStatusString(int code) { + switch (code) { + case PSI_BATT_DEAD: + return "Empty"; + case PSI_BATT_VERYLOW: + return "Very low"; + case PSI_BATT_LOW: + return "Low"; + case PSI_BATT_GOOD: + return "Good"; + } + return "Unknown"; +} diff --git a/lib/rpcs.h b/lib/rpcs.h index cb71c88..3c569bb 100644 --- a/lib/rpcs.h +++ b/lib/rpcs.h @@ -1,37 +1,252 @@ #ifndef _rpcs_h_ #define _rpcs_h_ +#include "psitime.h" + class ppsocket; class bufferStore; class bufferArray; +/** + * This struct holds the data returned + * by @ref rpcs::getMachineInfo. + */ +typedef struct machineInfo_t { + unsigned long machineType; + char machineName[17]; + unsigned long long machineUID; + unsigned long countryCode; + char uiLanguage[32]; + + unsigned short romMajor; + unsigned short romMinor; + unsigned short romBuild; + unsigned long romSize; + bool romProgrammable; + + unsigned long ramSize; + unsigned long ramFree; + unsigned long ramMaxFree; + unsigned long ramDiskSize; + + unsigned long registrySize; + unsigned long displayWidth; + unsigned long displayHeight; + + psi_timeval time; + psi_timezone tz; + + psi_timeval mainBatteryInsertionTime; + unsigned long mainBatteryStatus; + psi_timeval mainBatteryUsedTime; + unsigned long mainBatteryCurrent; + unsigned long mainBatteryUsedPower; + unsigned long mainBatteryVoltage; + unsigned long mainBatteryMaxVoltage; + + unsigned long backupBatteryStatus; + unsigned long backupBatteryVoltage; + unsigned long backupBatteryMaxVoltage; + psi_timeval backupBatteryUsedTime; + + bool externalPower; +} machineInfo; + +/** + * Remote procedure call services via PLP + * + * rpcs provides an interface for communicating + * with the Psion's remote procedure call + * service. The generic facilities for both, + * EPOC and SIBO are implemented here, while + * the facilities, unique to each of those + * variants are implemented in + * @ref rpcs32 or @ref rpcs16 respectively. + * These normally are instantiated by using + * @ref rpcsfactory. + */ class rpcs { public: + /** + * Provides a virtual destructor. + */ virtual ~rpcs() {}; + + /** + * Initializes a connection to the remote + * machine. + */ void reset(); + + /** + * Attempts to re-establish a remote + * connection by first closing the socket, + * then connecting again to the ncpd daemon + * and finally calling @ref reset. + */ void reconnect(); + + /** + * Retrieves the current status of the + * connection. + * + * @returns The connection status. + */ long getStatus(); - // API idendical on SIBO and EPOC - int getNCPversion(int &, int &); - int execProgram(const char *, const char *); + /** + * Retrieves the version of the NCP protocol + * on the remote side. + * + * This function is working with both SIBO and EPOC + * devices. + * + * @param major The major part of the NCP version. + * Valid only if returned with no error. + * @param minor The minor part of the NCP version. + * Valid only if returned with no error. + * + * @returns A psion error code. 0 = Ok. + */ + int getNCPversion(int &major, int &minor); + + /** + * Starts execution of a program on the remote machine. + * + * This function is working with both SIBO and EPOC + * devices. + * + * @param program The full path of the executable + * on the remote machine + * @param args The arguments for this program, separated + * by space. + * + * @returns A psion error code. 0 = Ok. + */ + int execProgram(const char *program, const char *args); + + /** + * Requests termination of a program running on the + * remote machine. + * + * This function is working with both SIBO and EPOC + * devices. + * + * @param program + * + * @returns A psion error code. 0 = Ok. + */ int stopProgram(const char *); int queryProgram(const char *); int formatOpen(const char *, int &, int &); int formatRead(int); int getUniqueID(const char *, long &); + + /** + * Retrieve owner information of the remote machine. + * + * This function is working with both SIBO and EPOC + * devices. + * + * @param owner A bufferArray, containing the lines + * of the owner info upon return. + * + * @returns A psion error code. 0 = Ok. + */ int getOwnerInfo(bufferArray &); - int getMachineType(int &); - int fuser(const char *, char *, int); + + /** + * Retrieves the type of machine on the remote side + * as defined in @ref #machs. + * + * This function is working with both SIBO and EPOC + * devices + * + * @param type The code describing the type of machine + * on the remote side is stored here on return. + * + * @returns A psion error code. 0 = Ok. + */ + int getMachineType(int &type); + + /** + * Retrieves the name of a process, having a + * given file opened on the remote side. + * + * This function is working with both SIBO and EPOC + * devices + * + * @param name The full path of a file to be checked + * for beeing used by a program. + * @param buf A buffer which gets filled with the + * program's name. + * @param maxlen The maximum capacity of the buffer. + */ + int fuser(const char *name, char *buf, int maxlen); + + /** + * Requests the remote server to terminate. + * + * This function is working with both SIBO and EPOC + * devices. There is usually no need to call this + * function, because the remote server is automatically + * stopped on disconnect. + * + * @returns A psion error code. 0 = Ok. + */ int quitServer(void); + /** + * Maps a language code to a human readable language name. + * + * @param code The language code to map. + * + * @returns The name of the language, represented by code, or + * "Unknown", if the code does not match one of the known + * languages. + */ + const char *languageString(const int code); + + /** + * Maps a battery status code to a human readable description. + * + * @param code The battary status code to map. + * + * @returns A descriptive text for the battery status, or + * "Unknown", if the code does not match a known + * battery status. + */ + const char *batteryStatusString(const int code); + + // API different on SIBO and EPOC virtual int queryDrive(const char, bufferArray &) = 0; - virtual int getCmdLine(const char *, char *, int) = 0; + /** + * Retrieves the command line of a running process. + * + * This function works with EPOC only. Using it with SIBO + * machines, returns always an error code E_PSI_NOT_SIBO. + * + * @param process Name of process. Format: processname.$pid + * @param ret The program name and arguments are returned here. + * + * @return Psion error code. 0 = Ok. + */ + virtual int getCmdLine(const char *process, bufferStore &ret) = 0; // API only existent on EPOC // default-methods for SIBO here. - virtual int getMachineInfo(void) { return E_PSI_NOT_SIBO;} + /** + * Retrieve general Information about the connected + * machine. + * + * This function works with EPOC only. Using it with SIBO + * machines, returns always an error code E_PSI_NOT_SIBO. + * + * @param machineInfo The struct holding all information on return. + * @return Psion error code. 0 = Ok. + */ + virtual int getMachineInfo(machineInfo &) { return E_PSI_NOT_SIBO;} virtual int closeHandle(int) { return E_PSI_NOT_SIBO;} virtual int regOpenIter(void) { return E_PSI_NOT_SIBO;} virtual int regReadIter(void) { return E_PSI_NOT_SIBO;} @@ -46,13 +261,16 @@ class rpcs { virtual int queryRead(void) { return E_PSI_NOT_SIBO;} enum errs { - E_PSI_GEN_NONE = 0, + E_PSI_GEN_NONE = 0, E_PSI_GEN_FAIL = -1, - E_PSI_FILE_DISC = -50, + E_PSI_FILE_DISC = -50, // Special error code for "Operation not permitted in RPCS16" - E_PSI_NOT_SIBO = -200 + E_PSI_NOT_SIBO = -200 }; + /** + * The known machine types. + */ enum machs { PSI_MACH_UNKNOWN = 0, PSI_MACH_PC = 1, @@ -65,15 +283,44 @@ class rpcs { PSI_MACH_S3C = 8, PSI_MACH_S5 = 32, PSI_MACH_WINC = 33, -//TODO: Code for 5mx + // TODO: Code for 5mx }; - protected: + /** + * The known interface languages. + */ + enum languagecodes { + PSI_LANG_TEST = 0, + PSI_LANG_ENGLISH = 1, + PSI_LANG_FRENCH = 2, + PSI_LANG_GERMAN = 3, + }; + + /** + * The known battery states. + */ + enum batterystates { + PSI_BATT_DEAD = 0, + PSI_BATT_VERYLOW = 1, + PSI_BATT_LOW = 2, + PSI_BATT_GOOD = 3, + }; - // Vars + protected: + /** + * The socket, used for communication + * with ncpd. + */ ppsocket *skt; + + /** + * The current status of the connection. + */ long status; + /** + * The possible commands. + */ enum commands { QUERY_NCP = 0x00, EXEC_PROG = 0x01, @@ -87,17 +334,38 @@ class rpcs { GET_MACHINETYPE = 0x09, GET_CMDLINE = 0x0a, FUSER = 0x0b, - CONFIG_OPEN = 0x66, + GET_MACHINE_INFO = 0x64, + REG_OPEN_ITER = 0x66, + REG_READ_ITER = 0x67, + REG_WRITE = 0x68, + REG_READ = 0x69, + REG_DELETE = 0x6a, + SET_TIME = 0x6b, + CONFIG_OPEN = 0x6c, CONFIG_READ = 0x6d, + CONFIG_WRITE = 0x6e, + QUERY_OPEN = 0x6f, + QUERY_READ = 0x70, QUIT_SERVER = 0xff }; - // Communication - bool sendCommand(enum commands, bufferStore &); - long getResponse(bufferStore &); + /** + * Sends a command to the remote side. + * + * If communication fails, a reconnect is triggered + * and a second attempt to transmit the request + * is attempted. If that second attempt fails, + * the function returns an error an sets rpcs::status + * to E_PSI_FILE_DISC. + * + * @param cc The command to execute on the remote side. + * @param data Additional data for this command. + * + * @returns true on success, false on failure. + */ + bool sendCommand(enum commands cc, bufferStore &data); + int getResponse(bufferStore &); const char *getConnectName(); - - char *convertSlash(const char *); }; #endif diff --git a/lib/rpcs16.cc b/lib/rpcs16.cc index 2786ee8..a3af320 100644 --- a/lib/rpcs16.cc +++ b/lib/rpcs16.cc @@ -57,7 +57,7 @@ cout << dec << "qd: " << res << " " << a.getLen() << " a="<< a << endl; } int rpcs16:: -getCmdLine(const char *process, char *buf, int bufsize) +getCmdLine(const char *process, bufferStore &ret) { return 0; } diff --git a/lib/rpcs16.h b/lib/rpcs16.h index 14809ae..c803fff 100644 --- a/lib/rpcs16.h +++ b/lib/rpcs16.h @@ -12,7 +12,7 @@ class rpcs16 : public rpcs { ~rpcs16(); int queryDrive(const char, bufferArray &); - int getCmdLine(const char *, char *, int); + int getCmdLine(const char *, bufferStore &); }; #endif diff --git a/lib/rpcs32.cc b/lib/rpcs32.cc index d710b9c..74d5ca4 100644 --- a/lib/rpcs32.cc +++ b/lib/rpcs32.cc @@ -48,14 +48,16 @@ int rpcs32:: queryDrive(char drive, bufferArray &ret) { bufferStore a; + int res; + a.addByte(drive); if (!sendCommand(rpcs::QUERY_DRIVE, a)) return rpcs::E_PSI_FILE_DISC; - getResponse(a); + if ((res = getResponse(a))) + return res; int l = a.getLen(); ret.clear(); -//cout << dec << "qd: " << a.getLen() << " a="<< a << endl; - while (l > 1) { + while (l > 0) { bufferStore b, c; const char *s; char *p; @@ -66,8 +68,7 @@ queryDrive(char drive, bufferArray &ret) sl = strlen(s) + 1; l -= sl; a.discardFirstBytes(sl); - p = strstr(s, ".$"); - if (p) { + if ((p = strstr(s, ".$"))) { *p = '\0'; p += 2; sscanf(p, "%d", &pid); } else @@ -82,33 +83,125 @@ queryDrive(char drive, bufferArray &ret) ret.push(c); ret.push(b); } - return 0; + return res; } int rpcs32:: -getCmdLine(const char *process, char *buf, int bufsize) +getCmdLine(const char *process, bufferStore &ret) { - return 0; + bufferStore a; + int res; + + a.addStringT(process); + if (!sendCommand(rpcs::GET_CMDLINE, a)) + return rpcs::E_PSI_FILE_DISC; + res = getResponse(a); + ret = a; + return res; } +int rpcs32:: +getMachineInfo(machineInfo &mi) +{ + bufferStore a; + int res; + + if (!sendCommand(rpcs::GET_MACHINE_INFO, a)) + return rpcs::E_PSI_FILE_DISC; + if ((res = getResponse(a))) + return res; + if (a.getLen() != 256) + return E_PSI_GEN_FAIL; + mi.machineType = a.getDWord(0); + strncpy(mi.machineName, a.getString(16), 16); + mi.machineName[16] = '\0'; + mi.machineUID = a.getDWord(44); + mi.machineUID <<= 32; + mi.machineUID |= a.getDWord(40); + mi.countryCode = a.getDWord(56); + strcpy(mi.uiLanguage, languageString(a.getDWord(164))); + + mi.romMajor = a.getByte(4); + mi.romMinor = a.getByte(5); + mi.romBuild = a.getWord(6); + mi.romSize = a.getDWord(140); + + mi.ramSize = a.getDWord(136); + mi.ramMaxFree = a.getDWord(144); + mi.ramFree = a.getDWord(148); + mi.ramDiskSize = a.getDWord(152); + + mi.registrySize = a.getDWord(156); + mi.romProgrammable = (a.getDWord(160) != 0); + + mi.displayWidth = a.getDWord(32); + mi.displayHeight = a.getDWord(36); + + mi.time.tv_low = a.getDWord(48); + mi.time.tv_high = a.getDWord(52); + + mi.tz.utc_offset = a.getDWord(60); + mi.tz.dst_zones = a.getDWord(64); + mi.tz.home_zone = a.getDWord(68); + + mi.mainBatteryInsertionTime.tv_low = a.getDWord(72); + mi.mainBatteryInsertionTime.tv_high = a.getDWord(76); + mi.mainBatteryStatus = a.getDWord(80); + mi.mainBatteryUsedTime.tv_low = a.getDWord(84); + mi.mainBatteryUsedTime.tv_high = a.getDWord(88); + mi.mainBatteryCurrent = a.getDWord(92); + mi.mainBatteryUsedPower = a.getDWord(96); + mi.mainBatteryVoltage = a.getDWord(100); + mi.mainBatteryMaxVoltage = a.getDWord(104); + + mi.backupBatteryStatus = a.getDWord(108); + mi.backupBatteryVoltage = a.getDWord(112); + mi.backupBatteryMaxVoltage = a.getDWord(116); + mi.backupBatteryUsedTime.tv_low = a.getDWord(124); + mi.backupBatteryUsedTime.tv_high = a.getDWord(128); + + mi.externalPower = (a.getDWord(120) != 0); + + return res; +} + +static unsigned long hhh; + int rpcs32:: configOpen(void) { bufferStore a; -cout << "configOpen:" << endl; + int res; + if (!sendCommand(rpcs::CONFIG_OPEN, a)) return rpcs::E_PSI_FILE_DISC; - getResponse(a); -cout << a << endl; + res = getResponse(a); +cout << "co: r=" << res << " a=" << a << endl; + hhh = a.getDWord(0); + return 0; } int rpcs32:: configRead(void) { bufferStore a; -cout << "configRead:" << endl; - if (!sendCommand(rpcs::CONFIG_READ, a)) - return rpcs::E_PSI_FILE_DISC; - getResponse(a); -cout << a << endl; + int res; + int l; + FILE *f; + + f = fopen("blah", "w"); + do { + a.init(); + a.addDWord(hhh); + if (!sendCommand(rpcs::CONFIG_READ, a)) + return rpcs::E_PSI_FILE_DISC; + if ((res = getResponse(a))) + return res; + l = a.getLen(); + cout << "cr: " << l << endl; + fwrite(a.getString(0), 1, l, f); + } while (l > 0); + fclose(f); +//cout << "cr: r=" << res << " a=" << a << endl; + return 0; } diff --git a/lib/rpcs32.h b/lib/rpcs32.h index e33ef10..6c333b4 100644 --- a/lib/rpcs32.h +++ b/lib/rpcs32.h @@ -11,9 +11,11 @@ class rpcs32 : public rpcs { ~rpcs32(); int queryDrive(const char, bufferArray &); - int getCmdLine(const char *, char *, int); + int getCmdLine(const char *, bufferStore &); + int getMachineInfo(machineInfo &); + int configOpen(void); + int configRead(void); #if 0 - int getMachineInfo(void); int closeHandle(int); int regOpenIter(void); int regReadIter(void); @@ -21,10 +23,8 @@ class rpcs32 : public rpcs { int regRead(void); int regDelete(void); int setTime(void); -#endif int configOpen(void); int configRead(void); -#if 0 int configWrite(void); int queryOpen(void); int queryRead(void); diff --git a/lib/rpcsfactory.cc b/lib/rpcsfactory.cc index 7358e56..4fe0bff 100644 --- a/lib/rpcsfactory.cc +++ b/lib/rpcsfactory.cc @@ -34,7 +34,7 @@ #include "bufferstore.h" #include "ppsocket.h" -rpcsfactory::rpcsfactory(ppsocket *_skt) : serNum(0) +rpcsfactory::rpcsfactory(ppsocket *_skt) //: serNum(0) { skt = _skt; } @@ -52,9 +52,10 @@ rpcs * rpcsfactory::create(bool reconnect) a.addStringT("NCP$INFO"); if (!skt->sendBufferStore(a)) { if (!reconnect) - cerr << "rpcsfactory::create couldn't send version request" << endl; else { + cerr << "rpcsfactory::create couldn't send version request" << endl; + else { skt->closeSocket(); - serNum = 0; + // serNum = 0; skt->reconnect(); } return NULL; @@ -68,7 +69,7 @@ rpcs * rpcsfactory::create(bool reconnect) } if ((a.getLen() > 8) && !strncmp(a.getString(), "No Psion", 8)) { skt->closeSocket(); - serNum = 0; + // serNum = 0; skt->reconnect(); return NULL; } diff --git a/lib/rpcsfactory.h b/lib/rpcsfactory.h index 17a2719..feffaee 100644 --- a/lib/rpcsfactory.h +++ b/lib/rpcsfactory.h @@ -5,15 +5,37 @@ class ppsocket; +/** + * A factory for automatically instantiating the correct protocol + * variant depending on the connected Psion. + */ class rpcsfactory { public: + /** + * Constructs a rpcsfactory. + * + * @param skt The socket to be used for connecting + * to the ncpd daemon. + */ rpcsfactory(ppsocket * skt); - virtual rpcs * create(bool); + + /** + * Creates a new rpsc instance. + * + * @param reconnect Set to true, if automatic reconnect + * should be performed on failure. + * + * @returns A pointer to a newly created rpcs instance or + * NULL on failure. + */ + virtual rpcs * create(bool reconnect); private: - // Vars + /** + * The socket to be used for connecting to the + * ncpd daemon. + */ ppsocket *skt; - int serNum; }; #endif -- cgit v1.2.3