From ab18114bfd38d4632c66401b5bc079241e27fab3 Mon Sep 17 00:00:00 2001 From: Fritz Elfert Date: Mon, 17 Jan 2000 11:49:41 +0000 Subject: Release of plptools-0.5 --- .cvsignore | 1 - CHANGES | 13 + INSTALL | 2 +- Makefile.am | 2 +- Makefile.cvs | 4 +- README | 25 +- README.mjg | 120 ++++++++ configure.in | 24 +- include/.cvsignore | 1 + include/config.h.in | 11 +- include/defs.h.in | 2 + include/mp.h | 2 +- lib/Makefile.am | 8 +- lib/bufferarray.cc | 97 +++++- lib/bufferarray.h | 21 +- lib/bufferstore.cc | 13 +- lib/bufferstore.h | 25 +- lib/ppsocket.cc | 21 +- lib/rfsv.cc | 158 ++++++++++ lib/rfsv.h | 151 ++++++++++ lib/rfsv16.cc | 836 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/rfsv16.h | 127 ++++++++ lib/rfsv32.cc | 368 ++++++++++------------- lib/rfsv32.h | 199 ++++++------ lib/rfsvfactory.cc | 86 ++++++ lib/rfsvfactory.h | 20 ++ lib/rpcs.cc | 283 +++++++++++++++++ lib/rpcs.h | 103 +++++++ lib/rpcs16.cc | 63 ++++ lib/rpcs16.h | 18 ++ lib/rpcs32.cc | 114 +++++++ lib/rpcs32.h | 36 +++ lib/rpcsfactory.cc | 85 ++++++ lib/rpcsfactory.h | 20 ++ ncpd/channel.cc | 25 ++ ncpd/channel.h | 11 +- ncpd/linkchan.cc | 61 +++- ncpd/linkchan.h | 9 +- ncpd/main.cc | 12 +- ncpd/ncp.cc | 101 +++++-- ncpd/ncp.h | 15 +- ncpd/packet.cc | 11 +- ncpd/packet.h | 3 +- ncpd/socketchan.cc | 107 ++++++- ncpd/socketchan.h | 8 +- plpftp/ftp.cc | 568 +++++++++++++++++++++++++++------- plpftp/ftp.h | 22 +- plpftp/main.cc | 36 ++- plpnfsd/main.cc | 163 ++++++++-- plpnfsd/mp_main.c | 5 +- plpnfsd/mp_mount.c | 1 + plpnfsd/mp_pfs_ops.c | 45 ++- plpnfsd/rfsv_api.h | 5 +- 53 files changed, 3722 insertions(+), 545 deletions(-) create mode 100644 CHANGES create mode 100644 README.mjg create mode 100644 lib/rfsv.cc create mode 100644 lib/rfsv.h create mode 100644 lib/rfsv16.cc create mode 100644 lib/rfsv16.h create mode 100644 lib/rfsvfactory.cc create mode 100644 lib/rfsvfactory.h create mode 100644 lib/rpcs.cc create mode 100644 lib/rpcs.h create mode 100644 lib/rpcs16.cc create mode 100644 lib/rpcs16.h create mode 100644 lib/rpcs32.cc create mode 100644 lib/rpcs32.h create mode 100644 lib/rpcsfactory.cc create mode 100644 lib/rpcsfactory.h diff --git a/.cvsignore b/.cvsignore index 364e9f2..e36cc10 100644 --- a/.cvsignore +++ b/.cvsignore @@ -3,7 +3,6 @@ Makefile aclocal.m4 configure libtool -stamp-h.in config.status config.cache config.log diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..d1b0459 --- /dev/null +++ b/CHANGES @@ -0,0 +1,13 @@ +Changes from Version 0.4 to 0.5: + + - merged all stuff from Matt Gumbley's plptools-0.4-mjg5 + see his README.mjg + - Added a speedup-patch for plpnfsd from Rudol Koenig. This + should also solve Olaf Flebbe's problems as a side effect. + - Added command- and filename-completion to plpftp + - Changed plpftp's default-drive to "AUTO". This triggers + auto-detection of available drives and selecting the first one. + - fixed various bugs in plpftp + - added hash printing in plpftp. + - Added new commands "ps", "kill" and "run" to plpftp (these should + work at least for Series5. For Series3: INCOMPLETE) diff --git a/INSTALL b/INSTALL index ca5d962..cb328dd 100644 --- a/INSTALL +++ b/INSTALL @@ -2,7 +2,7 @@ Basic Installation ================== These are generic installation instructions. - For package-specific stuff, look into README. + >>>> For package-specific stuff, look into README. <<<< The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses diff --git a/Makefile.am b/Makefile.am index 94ecd0f..fc7f1fc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ # $Id$ # SUBDIRS = lib ncpd plpnfsd plpftp -EXTRA_DIST = COPYING README TODO INSTALL include/*.h* etc/*magic etc/*.in +EXTRA_DIST = COPYING README TODO INSTALL README.mjg include/*.h* etc/*magic etc/*.in DISTCLEANFILES = etc/psion AUTOMAKE_OPTIONS = foreign diff --git a/Makefile.cvs b/Makefile.cvs index b7e3170..72d73e0 100644 --- a/Makefile.cvs +++ b/Makefile.cvs @@ -6,14 +6,14 @@ devel: autoheader automake autoconf - touch stamp-h.in + touch include/stamp-h.in dist: aclocal autoheader automake --include-deps autoconf - touch stamp-h.in + touch include/stamp-h.in ./configure $(MAKE) dist diff --git a/README b/README index 91a1f59..2fd2b77 100644 --- a/README +++ b/README @@ -4,8 +4,7 @@ - p3nfsd-5.4 by Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) an nfs daemon for Psion series 3 and 5 - - plp_1_7 by Philip Proudman - (former email, current address unknown proudman@btinternet.com) + - plp_1_7 by Philip Proudman (phil@proudman51.freeserve.co.uk) I want to thank both authors for their nice packages and for making these available under GPL which makes it possible for me to @@ -25,6 +24,14 @@ because a) i can't test it and b) Sibo support is dropped by Psion. + Since version 0.5, there is also starting support for RPC-service + on the Psion. Note the new commands "ps", "kill" and "run" in + plpftp. This support was only possible because of Alexander Thoukydides' + excellent documentation at + http://www.btinternet.com/~thouky/software/psifs/plp.html + Thanks a lot for that. Also Psion-3 support is now added by Matt Gumbley + (matt@gumbley.demon.co.uk) + How to install: ./configure @@ -54,6 +61,20 @@ overrides the default port number, where ncpd listens and plpftp resp. plpnfsd connects to. + --with-drive=drivespec + + overrides the default drive for plpftp. The default "AUTO" + triggers a drive-scan on the psion and sets the drive to + the first drive found. If you don't want that, specify + C: for example. + + --with-basedir=dirspec + + overrides the default directory for plpftp. The default is \\ + which means the root-dir. Note: directory-separators have to + be specified as C-like backslashes and if you consider shell's + escape-mechanism, these in turn have to be escaped with backslashes ;-) + You will get 3 programs and a shared and static library. By default, the install-prefix is /usr/local, so you probably should add /usr/local/lib to your /etc/ld.so.conf and then run diff --git a/README.mjg b/README.mjg new file mode 100644 index 0000000..a557988 --- /dev/null +++ b/README.mjg @@ -0,0 +1,120 @@ +Retrofitting RFSV 16 to Fritz Elfert's plptools +=============================================== + +These notes updated 21/10/99 + + +I started modifying Fritz' plptools-0.3 with the intention of adding back the +RFSV16 support. Fritz then released 0.4, and so I re-patched 0.4, and did lots +more work, and merged in some patches from Pete Bentley. + +The result is this, plptools-0.4-mjg. + +When it's finished, hopefully Fritz will merge it into the next official +release. + +Release Dates: +plptools-0.4-mjg1 on 23/09/99 +plptools-0.4-mjg2 on 04/10/99 +plptools-0.4-mjg3 on 07/10/99 +plptools-0.4-mjg4 on 10/10/99 +plptools-0.4-mjg5 on 21/10/99 (+ patch against 0.4 original) + +The changes to plptools-0.4 are: +* Now detects which version of the NCP protocol is in use, and records this: + this is how we detect whether the Psion is a Series 3 or Series 5. +* Makefiles updated to include the rfsv16 stuff, the rfsv base class, and the + rfsvfactory object. +* Instantiates either an RFSV32 or RFSV16 object to handle the file server + requests, based on the protocol version returned by the INFO command above. + There is an rfsvfactory object to make this easier in client programs. + This is used in plpftp and plpnfsd. This will obtain the NCP version number + over the socket, by announcing itself as "NCP$INFO.*". The correct RFSV class + will further announce itself as SYS$RFSV.* - this is stored in connectName. + RFSV objects have an opAttr method to output a correct attributes flag for each + protocol. +* Added --with-drive and --with-basedir to the configure.in script, and + include/defs.h.in. These are used in RFSV16, and plpftp now uses these, + instead of the DEFAULT_DRIVE AND DEFAULT_BASE_DIRECTORY of ftp.h +* ncpd/packet.h: verbose is now a short int, to allow packet debugging. +* Pete Bentley's changes. Fixes bogus definition of usleep(); plpftp compiles in + the absence of libreadline and libhistory; accept 3rd parameter in ppsocket is + signed not unsigned; -v all debugging option; (sun) link detection changes; + new PKT_DEBUG_HANDSHAKE debug level added; correct termios.h include for + Solaris; Solaris non-blocking fixes; move the fcntl for O_NONBLOCK; remove + O_NONBLOCK where needed; set SO_REUSEADDR for ncpd. +* Peter Cherriman's "1 hour out" fix (doesn't work for me though... could be my + PC clock) + +Some things to do: +* Finish rfsv16.cc: + fread, fwrite, fseek, copyFromPsion, copyToPsion, fsetsize, mktemp, + fcreatefile, freplacefile, fopendir, fsetmtime + +* Test everything with plpftp and plpnfsd. +* Remove read, write fully? +* Remove unused convertName method in plpftp/ftp.cc,h + +Adding RFSV16 back is still a work in progress.... stay tuned! + + +Building plptools for use with a Psion 3 +======================================== +./configure --with-speed=19200 --with-serial=/dev/ttyS1 --with-drive=M: +# Or whatever port you use. I use ttyS1, since ttyS0 is my mouse. +# The default speed is 115200 bps, which is only for the Lucky S5 owners. +# The S3c can take speeds up to 57600, but my RS232 protocol analyser can't - +# your mileage may vary. +make +make install +plpftp +# NFS access not finished yet + + +Tip for use with a Psion 3 +========================== +Turn the Psion's remote link on first, THEN run ncpd. + +Why this seems to help matters +------------------------------ +This seems to ensure that the Psion will send a request for the LINK.* process, +which is what we must detect to allow other connections to the NCP daemon. It +seems there may be a strange bug when you start ncpd, THEN start the Psion link +- the link request/acknowledge messages are exchanged correctly, followed by +valid INFO/NCP Protocol Version messages - and then the Psion attempts to +connect to the PC's SYS$RFSV.* - IMHO, it should start by attempting to connect +to LINK.* + +Philip Proudman's plp_1.7 always receives the LINK.* connect request, as do +other protocol implementations. + +I haven't managed to work out why the Psion starts by sending a SYS$RFSV.* +connect request with plptools, and not with other implementations, but the above +work-around gets round it. + + +Troubleshooting +=============== +Q. I can't build it on SuSE Linux. +A. You need the gppshare and libgpp packages. (Thanks to der.hans) + + +Acknowledgements +================ +This couldn't have been written without pioneering investigations into the link +protocol by Olaf Flebbe, Michael Pieper, and others. Philip Proudman beat me to +producing a decent, working PLP stack (Thanks, Philip ;-) and Fritz Elfert did +some excellent merging of Rudolf Koenig's p3nfs. Also thanks to the Majordomo +maintainers @geekstuff.co.uk, who host the linux-psion list. + +Finally, the RFSV16 re-merge would still be in my In-tray if my wife Angela +hadn't been away for the week, leaving me to code, and fend for myself. + +Thanks to Pete Bentley for ideas on proxying, his patches, suggestions for +improvement, and a loan Series 5 for testing against. + + +Viola! +-- +Matt Gumbley +matt@gumbley.demon.co.uk diff --git a/configure.in b/configure.in index a5fd29b..03e631b 100644 --- a/configure.in +++ b/configure.in @@ -2,10 +2,12 @@ AC_REVISION($ Revision: 0.1 $)dnl revision of this configure.in script AC_INIT(acinclude.m4) AM_CONFIG_HEADER(include/config.h) -AM_INIT_AUTOMAKE(plptools, 0.4) +AM_INIT_AUTOMAKE(plptools, 0.5) AM_PROG_LIBTOOL dnl Enable Mainatiner stuff +dnl **** NOTE **** Matt had to comment the following line out to get it to +dnl ************** build, and doesn't know why - YMMV. AM_MAINTAINER_MODE dnl checks for programs @@ -86,6 +88,26 @@ AC_ARG_WITH(port, ) AC_SUBST(DPORT) +AC_ARG_WITH(drive, + [ --with-drive=DRIVE override default Psion drive [AUTO]], + [ DDRIVE="$withval" + AC_MSG_RESULT(Overriding drive: $DDRIVE) ], + [ DDRIVE='AUTO' + AC_MSG_RESULT(Using default Psion drive: $DDRIVE) + ] +) +AC_SUBST(DDRIVE) + +AC_ARG_WITH(basedir, + [ --with-basedir=DIR override default Psion directory [\\\\]], + [ DBASEDIR="$withval" + AC_MSG_RESULT(Overriding directory: $DBASEDIR) ], + [ DBASEDIR="\\\\" + AC_MSG_RESULT(Using default Psion directory: $DBASEDIR) + ] +) +AC_SUBST(DBASEDIR) + AC_OUTPUT( Makefile lib/Makefile diff --git a/include/.cvsignore b/include/.cvsignore index 123a642..bd3c1d5 100644 --- a/include/.cvsignore +++ b/include/.cvsignore @@ -1,3 +1,4 @@ +stamp-h.in stamp-h config.h defs.h diff --git a/include/config.h.in b/include/config.h.in index 4932d0a..a9d3daa 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -1,9 +1,5 @@ /* include/config.h.in. Generated automatically from configure.in by autoheader. */ -#undef VERSION - -#undef PACKAGE - /* Define if you have the header file. */ #undef HAVE_STDLIB_H @@ -33,3 +29,10 @@ /* Define if you have the socket library (-lsocket). */ #undef HAVE_LIBSOCKET + +/* Name of package */ +#undef PACKAGE + +/* Version number of package */ +#undef VERSION + diff --git a/include/defs.h.in b/include/defs.h.in index 3c6fbce..ce5f68d 100644 --- a/include/defs.h.in +++ b/include/defs.h.in @@ -36,6 +36,8 @@ #define DDEV "@DDEV@" #define DSPEED @DSPEED@ #define DPORT @DPORT@ +#define DDRIVE "@DDRIVE@" +#define DBASEDIR "@DBASEDIR@" /* Debugging */ diff --git a/include/mp.h b/include/mp.h index ae2fe19..125ad80 100644 --- a/include/mp.h +++ b/include/mp.h @@ -122,7 +122,7 @@ extern char *index(), *rindex(), *strdup(); #endif /* mp_main.c */ -#if defined(hpux) || defined(__SVR4) +#if defined(hpux) || defined(__SVR4) && !defined(sun) /* HPUX 10.20 declares int usleep( useconds_t useconds); */ # ifndef HPUX10 extern void usleep __P((int usec)); diff --git a/lib/Makefile.am b/lib/Makefile.am index 27d5ea4..08df502 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,9 @@ lib_LTLIBRARIES = libplp.la libplp_la_LDFLAGS = --debug -version-info 1:0:0 -libplp_la_SOURCES = bufferarray.cc bufferstore.cc iowatch.cc ppsocket.cc rfsv32.cc log.cc -EXTRA_DIST = bool.h bufferarray.h bufferstore.h iowatch.h ppsocket.h rfsv32.h log.h +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 +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 diff --git a/lib/bufferarray.cc b/lib/bufferarray.cc index 99b1811..4e922ed 100644 --- a/lib/bufferarray.cc +++ b/lib/bufferarray.cc @@ -2,6 +2,7 @@ // PLP - An implementation of the PSION link protocol // // Copyright (C) 1999 Philip Proudman +// extensions Copyright (C) 2000 Fritz Elfert // // 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 @@ -20,7 +21,8 @@ // e-mail philip.proudman@btinternet.com -#include +#include +#include #include "bufferstore.h" #include "bufferarray.h" @@ -28,7 +30,7 @@ bufferArray::bufferArray() { len = 0; - lenAllocd = 5; + lenAllocd = ALLOC_MIN; buff = new bufferStore[lenAllocd]; } @@ -64,7 +66,7 @@ void bufferArray:: pushBuffer(const bufferStore & b) { if (len == lenAllocd) { - lenAllocd += 5; + lenAllocd += ALLOC_MIN; bufferStore *nb = new bufferStore[lenAllocd]; for (long i = 0; i < len; i++) { nb[i] = buff[i]; @@ -74,3 +76,92 @@ pushBuffer(const bufferStore & b) } buff[len++] = b; } + +void bufferArray:: +push(const bufferStore & b) +{ + if (len == lenAllocd) + lenAllocd += ALLOC_MIN; + bufferStore *nb = new bufferStore[lenAllocd]; + for (long i = len; i > 0; i--) { + nb[i] = buff[i-1]; + } + nb[0] = b; + delete[]buff; + buff = nb; + len++; +} + +bufferStore bufferArray:: +pop() +{ + return popBuffer(); +} + +void bufferArray:: +append(const bufferStore & b) +{ + pushBuffer(b); +} + +long bufferArray:: +length(void) +{ + return len; +} + +void bufferArray:: +clear(void) +{ + len = 0; + lenAllocd = ALLOC_MIN; + delete []buff; + buff = new bufferStore[lenAllocd]; +} + +bufferArray &bufferArray:: +operator =(const bufferArray & a) +{ + delete []buff; + len = a.len; + lenAllocd = a.lenAllocd; + buff = new bufferStore[lenAllocd]; + for (int i = 0; i < len; i++) + buff[i] = a.buff[i]; + return *this; +} + +bufferStore &bufferArray:: +operator [](const unsigned long index) +{ + return buff[index]; +} + +bufferArray &bufferArray:: +operator +(const bufferStore &a) +{ + append(a); + return *this; +} + +bufferArray &bufferArray:: +operator +(const bufferArray &a) +{ + lenAllocd += a.lenAllocd; + bufferStore *nb = new bufferStore[lenAllocd]; + for (int i = 0; i < len; i++) + nb[len + i] = buff[i]; + for (int i = 0; i < a.len; i++) + nb[len + i] = a.buff[i]; + len += a.len; + delete []buff; + buff = nb; + return *this; +} + +bufferArray &bufferArray:: +operator +=(const bufferStore &a) +{ + append(a); + return *this; +} diff --git a/lib/bufferarray.h b/lib/bufferarray.h index 4cb948e..fbf26f1 100644 --- a/lib/bufferarray.h +++ b/lib/bufferarray.h @@ -9,12 +9,27 @@ class bufferArray { bufferArray(); bufferArray(const bufferArray &a); ~bufferArray(); - void operator =(const bufferArray &a); - + bufferArray &operator =(const bufferArray &a); bool empty() const; - bufferStore popBuffer(); + + // 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); + private: + static const long ALLOC_MIN = 5; long len; long lenAllocd; bufferStore* buff; diff --git a/lib/bufferstore.cc b/lib/bufferstore.cc index 886b952..842e666 100644 --- a/lib/bufferstore.cc +++ b/lib/bufferstore.cc @@ -19,7 +19,8 @@ // // e-mail philip.proudman@btinternet.com -#include +#include +// That should be iostream.h, but it won't build on Sun WorkShop C++ 5.0 #include #include @@ -48,7 +49,7 @@ bufferStore::bufferStore(const unsigned char*_buff, long _len) { start = 0; } -void bufferStore::operator =(const bufferStore &a) { +bufferStore &bufferStore::operator =(const bufferStore &a) { checkAllocd(a.getLen()); len = a.getLen(); memcpy(buff, a.getString(0), len); @@ -106,7 +107,7 @@ ostream &operator<<(ostream &s, const bufferStore &m) { if (c>=' ' && c <= 'z') s << c; } } - s<< ")"; + s<< ")" << dec << setw(0); return s; } @@ -144,6 +145,12 @@ void bufferStore::addStringT(const char* s) { addByte(0); } +void bufferStore::addBytes(const unsigned char* s, int l) { + checkAllocd(len + l); + memcpy(&buff[len], s, l); + len += l; +} + void bufferStore::addBuff(const bufferStore &s, long maxLen) { long l = s.getLen(); checkAllocd(len + l); diff --git a/lib/bufferstore.h b/lib/bufferstore.h index 4840689..df216c0 100644 --- a/lib/bufferstore.h +++ b/lib/bufferstore.h @@ -7,10 +7,10 @@ class ostream; class bufferStore { public: bufferStore(); - bufferStore(const unsigned char*buff, long len); + bufferStore(const unsigned char *, long); ~bufferStore(); - bufferStore(const bufferStore &a); - void operator =(const bufferStore &a); + bufferStore(const bufferStore &); + bufferStore &operator =(const bufferStore &); // Reading Utils unsigned long getLen() const; @@ -18,19 +18,20 @@ public: unsigned int getWord(long pos) const; unsigned int getDWord(long pos) const; const char* getString(long pos=0) const; - void discardFirstBytes(int n); - friend ostream &operator<<(ostream &s, const bufferStore &m); + void discardFirstBytes(int); + friend ostream &operator<<(ostream &, const bufferStore &); bool empty() const; // Writing utils void init(); - void init(const unsigned char*buff, long len); - void addByte(unsigned char c); - void addWord(int a); - void addDWord(long a); - void addString(const char* s); - void addStringT(const char* s); - void addBuff(const bufferStore &s, long maxLen=-1); + 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); private: void checkAllocd(long newLen); diff --git a/lib/ppsocket.cc b/lib/ppsocket.cc index c51c557..cb04919 100644 --- a/lib/ppsocket.cc +++ b/lib/ppsocket.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include "defs.h" #include "bool.h" @@ -126,7 +127,6 @@ printPeer() bool ppsocket:: connect(char *Peer, int PeerPort, char *Host, int HostPort) { - //**************************************************** //* If we aren't already bound set the host and bind * //**************************************************** @@ -172,13 +172,16 @@ listen(char *Host, int Port) m_LastError = lastErrorCode(); return (false); } + // Our accept member function relies on non-blocking accepts, + // so set the flag here (rather than every time around the loop) + fcntl(m_Socket, F_SETFL, O_NONBLOCK); return (true); } ppsocket *ppsocket:: accept(char *Peer, int MaxLen) { - unsigned int len; + socklen_t len; ppsocket *accepted; char *peer; @@ -197,7 +200,6 @@ accept(char *Peer, int MaxLen) //*********************** len = sizeof(struct sockaddr); - fcntl(m_Socket, F_SETFL, O_NONBLOCK); accepted->m_Socket =::accept(m_Socket, &accepted->m_PeerAddr, &len); if (accepted->m_Socket == INVALID_SOCKET) { @@ -209,6 +211,14 @@ accept(char *Peer, int MaxLen) //* Got a connection so fill in the other attributes * //**************************************************** + // Make sure the new socket hasn't inherited O_NONBLOCK + // from the accept socket + int flags = fcntl( accepted->m_Socket, F_GETFL, 0 ); + if( flags >= 0 ) { + flags &= ~O_NONBLOCK; + fcntl( accepted->m_Socket, F_SETFL, flags); + } + accepted->m_HostAddr = m_HostAddr; accepted->m_Bound = true; @@ -503,6 +513,11 @@ bindSocket(char *Host, int Port) return (false); } } + // Set SO_REUSEADDR + int one = 1; + if (setsockopt( m_Socket, SOL_SOCKET, SO_REUSEADDR, + (const char *) &one, sizeof(int)) < 0 ) + cerr << "Warning: Unable to set SO_REUSEADDR option\n"; // If a host name was supplied then use it if (!setHost(Host, Port)) { return (false); diff --git a/lib/rfsv.cc b/lib/rfsv.cc new file mode 100644 index 0000000..3b9dc6a --- /dev/null +++ b/lib/rfsv.cc @@ -0,0 +1,158 @@ +#include "rfsv.h" + +char *rfsv:: +opErr(long status) +{ + enum errs e = (enum errs) status; + switch (e) { + case E_PSI_GEN_NONE: + return "no error"; + case E_PSI_GEN_FAIL: + return "general"; + case E_PSI_GEN_ARG: + return "bad argument"; + case E_PSI_GEN_OS: + return "OS error"; + case E_PSI_GEN_NSUP: + return "not supported"; + case E_PSI_GEN_UNDER: + return "numeric underflow"; + case E_PSI_GEN_OVER: + return "numeric overflow"; + case E_PSI_GEN_RANGE: + return "numeric exception"; + case E_PSI_GEN_INUSE: + return "in use"; + case E_PSI_GEN_NOMEMORY: + return "out of memory"; + case E_PSI_GEN_NOSEGMENTS: + return "out of segments"; + case E_PSI_GEN_NOSEM: + return "out of semaphores"; + case E_PSI_GEN_NOPROC: + return "out of processes"; + case E_PSI_GEN_OPEN: + return "already open"; + case E_PSI_GEN_NOTOPEN: + return "not open"; + case E_PSI_GEN_IMAGE: + return "bad image"; + case E_PSI_GEN_RECEIVER: + return "receiver error"; + case E_PSI_GEN_DEVICE: + return "device error"; + case E_PSI_GEN_FSYS: + return "no filesystem"; + case E_PSI_GEN_START: + return "not ready"; + case E_PSI_GEN_NOFONT: + return "no font"; + case E_PSI_GEN_TOOWIDE: + return "too wide"; + case E_PSI_GEN_TOOMANY: + return "too many"; + case E_PSI_FILE_EXIST: + return "file already exists"; + case E_PSI_FILE_NXIST: + return "no such file"; + case E_PSI_FILE_WRITE: + return "write error"; + case E_PSI_FILE_READ: + return "read error"; + case E_PSI_FILE_EOF: + return "end of file"; + case E_PSI_FILE_FULL: + return "disk/serial read buffer full"; + case E_PSI_FILE_NAME: + return "invalid name"; + case E_PSI_FILE_ACCESS: + return "access denied"; + case E_PSI_FILE_LOCKED: + return "ressource locked"; + case E_PSI_FILE_DEVICE: + return "no such device"; + case E_PSI_FILE_DIR: + return "no such directory"; + case E_PSI_FILE_RECORD: + return "no such record"; + case E_PSI_FILE_RDONLY: + return "file is read-only"; + case E_PSI_FILE_INV: + return "invalid I/O operation"; + case E_PSI_FILE_PENDING: + return "I/O pending (not yet completed)"; + case E_PSI_FILE_VOLUME: + return "invalid volume name"; + case E_PSI_FILE_CANCEL: + return "cancelled"; + case E_PSI_FILE_ALLOC: + return "no memory for control block"; + case E_PSI_FILE_DISC: + return "unit disconnected"; + case E_PSI_FILE_CONNECT: + return "already connected"; + case E_PSI_FILE_RETRAN: + return "retransmission threshold exceeded"; + case E_PSI_FILE_LINE: + return "physical link failure"; + case E_PSI_FILE_INACT: + return "inactivity timer expired"; + case E_PSI_FILE_PARITY: + return "serial parity error"; + case E_PSI_FILE_FRAME: + return "serial framing error"; + case E_PSI_FILE_OVERRUN: + return "serial overrun error"; + case E_PSI_MDM_CONFAIL: + return "modem cannot connect to remote modem"; + case E_PSI_MDM_BUSY: + return "remote modem busy"; + case E_PSI_MDM_NOANS: + return "remote modem did not answer"; + case E_PSI_MDM_BLACKLIST: + return "number blacklisted by the modem"; + case E_PSI_FILE_NOTREADY: + return "drive not ready"; + case E_PSI_FILE_UNKNOWN: + return "unknown media"; + case E_PSI_FILE_DIRFULL: + return "directory full"; + case E_PSI_FILE_PROTECT: + return "write-protected"; + case E_PSI_FILE_CORRUPT: + return "media corrupt"; + case E_PSI_FILE_ABORT: + return "aborted operation"; + case E_PSI_FILE_ERASE: + return "failed to erase flash media"; + case E_PSI_FILE_INVALID: + return "invalid file for DBF system"; + case E_PSI_GEN_POWER: + return "power failure"; + case E_PSI_FILE_TOOBIG: + return "too big"; + case E_PSI_GEN_DESCR: + return "bad descriptor"; + case E_PSI_GEN_LIB: + return "bad entry point"; + case E_PSI_FILE_NDISC: + return "could not diconnect"; + case E_PSI_FILE_DRIVER: + return "bad driver"; + case E_PSI_FILE_COMPLETION: + return "operation not completed"; + case E_PSI_GEN_BUSY: + return "server busy"; + case E_PSI_GEN_TERMINATED: + return "terminated"; + case E_PSI_GEN_DIED: + return "died"; + case E_PSI_FILE_HANDLE: + return "bad handle"; + case E_PSI_NOT_SIBO: + return "invalid operation for RFSV16"; + default: + return "Unknown error"; + } +} + diff --git a/lib/rfsv.h b/lib/rfsv.h new file mode 100644 index 0000000..e6006ef --- /dev/null +++ b/lib/rfsv.h @@ -0,0 +1,151 @@ +#ifndef _rfsv_h_ +#define _rfsv_h_ + +class ppsocket; +class bufferStore; +class bufferArray; + +#define RFSV_SENDLEN 2000 + +typedef int (*cpCallback_t)(long); + +// Abstract base class of RFSV ; 16-bit and 32-bit variants implement this +// interface +class rfsv { + public: + virtual ~rfsv() {} + virtual void reset() = 0; + virtual void reconnect() = 0; + virtual long getStatus() = 0; + virtual const char *getConnectName() = 0; + virtual long fopen(long, const char *, long &) = 0; + virtual long mktemp(long *, char *) = 0; + virtual long fcreatefile(long, const char *, long &) = 0; + virtual long freplacefile(long, const char *, long &) = 0; + virtual long fopendir(long, const char *, long &) = 0; + virtual long fclose(long) = 0; + virtual long dir(const char *, bufferArray *) = 0; + virtual long fgetmtime(const char *, long *) = 0; + virtual long fsetmtime(const char *, long) = 0; + virtual long fgetattr(const char *, long *) = 0; + virtual long fgeteattr(const char *, long *, long *, long *) =0; + virtual long fsetattr(const char *, long, long) = 0; + virtual long dircount(const char *, long *) = 0; + virtual long devlist(long *) = 0; + virtual char *devinfo(int, long *, long *, long *, long *) = 0; + virtual char *opAttr(long) = 0; + virtual long opMode(long) = 0; + virtual long fread(long, unsigned char *, long) = 0; + virtual long fwrite(long, unsigned char *, long) = 0; + virtual long copyFromPsion(const char *, const char *, cpCallback_t) = 0; + virtual long copyToPsion(const char *, const char *, cpCallback_t) = 0; + virtual long fsetsize(long, long) = 0; + virtual long fseek(long, long, long) = 0; + virtual long mkdir(const char *) = 0; + virtual long rmdir(const char *) = 0; + virtual long rename(const char *, const char *) = 0; + virtual long remove(const char *) = 0; + + char *opErr(long); + + enum seek_mode { + PSI_SEEK_SET = 1, + PSI_SEEK_CUR = 2, + PSI_SEEK_END = 3 + }; + + enum open_flags { + PSI_O_RDONLY = 00, + PSI_O_WRONLY = 01, + PSI_O_RDWR = 02, + }; + + enum open_mode { + PSI_O_CREAT = 0100, + PSI_O_EXCL = 0200, + PSI_O_TRUNC = 01000, + PSI_O_APPEND = 02000, + }; + + enum errs { + E_PSI_GEN_NONE = 0, + E_PSI_GEN_FAIL = -1, + E_PSI_GEN_ARG = -2, + E_PSI_GEN_OS = -3, + E_PSI_GEN_NSUP = -4, + E_PSI_GEN_UNDER = -5, + E_PSI_GEN_OVER = -6, + E_PSI_GEN_RANGE = -7, + E_PSI_GEN_DIVIDE = -8, + E_PSI_GEN_INUSE = -9, + E_PSI_GEN_NOMEMORY = - 10, + E_PSI_GEN_NOSEGMENTS = -11, + E_PSI_GEN_NOSEM = -12, + E_PSI_GEN_NOPROC = -13, + E_PSI_GEN_OPEN = -14, + E_PSI_GEN_NOTOPEN = -15, + E_PSI_GEN_IMAGE = -16, + E_PSI_GEN_RECEIVER = -17, + E_PSI_GEN_DEVICE = -18, + E_PSI_GEN_FSYS = -19, + E_PSI_GEN_START = -20, + E_PSI_GEN_NOFONT = -21, + E_PSI_GEN_TOOWIDE = -22, + E_PSI_GEN_TOOMANY = -23, + E_PSI_FILE_EXIST = -32, + E_PSI_FILE_NXIST = -33, + E_PSI_FILE_WRITE = -34, + E_PSI_FILE_READ = -35, + E_PSI_FILE_EOF = -36, + E_PSI_FILE_FULL = -37, + E_PSI_FILE_NAME = -38, + E_PSI_FILE_ACCESS = -39, + E_PSI_FILE_LOCKED = -40, + E_PSI_FILE_DEVICE = -41, + E_PSI_FILE_DIR = -42, + E_PSI_FILE_RECORD = -43, + E_PSI_FILE_RDONLY = -44, + E_PSI_FILE_INV = -45, + E_PSI_FILE_PENDING = -46, + E_PSI_FILE_VOLUME = -47, + E_PSI_FILE_CANCEL = -48, + E_PSI_FILE_ALLOC = -49, + E_PSI_FILE_DISC = -50, + E_PSI_FILE_CONNECT = -51, + E_PSI_FILE_RETRAN = -52, + E_PSI_FILE_LINE = -53, + E_PSI_FILE_INACT = -54, + E_PSI_FILE_PARITY = -55, + E_PSI_FILE_FRAME = -56, + E_PSI_FILE_OVERRUN = -57, + E_PSI_MDM_CONFAIL = -58, + E_PSI_MDM_BUSY = -59, + E_PSI_MDM_NOANS = -60, + E_PSI_MDM_BLACKLIST = -61, + E_PSI_FILE_NOTREADY = -62, + E_PSI_FILE_UNKNOWN = -63, + E_PSI_FILE_DIRFULL = -64, + E_PSI_FILE_PROTECT = -65, + E_PSI_FILE_CORRUPT = -66, + E_PSI_FILE_ABORT = -67, + E_PSI_FILE_ERASE = -68, + E_PSI_FILE_INVALID = -69, + E_PSI_GEN_POWER = -100, + E_PSI_FILE_TOOBIG = -101, + E_PSI_GEN_DESCR = -102, + E_PSI_GEN_LIB = -103, + E_PSI_FILE_NDISC = -104, + E_PSI_FILE_DRIVER = -105, + E_PSI_FILE_COMPLETION = -106, + E_PSI_GEN_BUSY = -107, + E_PSI_GEN_TERMINATED = -108, + E_PSI_GEN_DIED = -109, + E_PSI_FILE_HANDLE = -110, + + // Special error code for "Operation not permitted in RFSV16" + E_PSI_NOT_SIBO = -200 + }; +}; + +#endif + diff --git a/lib/rfsv16.cc b/lib/rfsv16.cc new file mode 100644 index 0000000..9420b57 --- /dev/null +++ b/lib/rfsv16.cc @@ -0,0 +1,836 @@ +// +// RFSV16 - An implementation of the PSION SIBO RFSV Client protocol +// +// Copyright (C) 1999 Philip Proudman +// Modifications for plptools: +// Copyright (C) 1999 Matt J. Gumbley +// Sources: rfsv32.cc by Fritz Elfert, and rfsv16.cc by Philip Proudman +// Descriptions of the RFSV16 protocol by Michael Pieper, Olaf Flebbe & Me. +// +// 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 +// +// e-mail philip.proudman@btinternet.com + +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "bool.h" +#include "rfsv16.h" +#include "bufferstore.h" +#include "ppsocket.h" +#include "bufferarray.h" + +rfsv16::rfsv16(ppsocket *_skt) : serNum(0) +{ + skt = _skt; + reset(); +} + +// move to base class? +rfsv16::~rfsv16() +{ + bufferStore a; + a.addStringT("Close"); + if (status == E_PSI_GEN_NONE) + skt->sendBufferStore(a); + skt->closeSocket(); +} + +// move to base class? +void rfsv16:: +reconnect() +{ +cerr << "rfsv16::reconnect" << endl; + skt->closeSocket(); + skt->reconnect(); + serNum = 0; + reset(); +} + +// move to base class? +void rfsv16:: +reset() +{ +cerr << "rfsv16::reset" << endl; + bufferStore a; + status = E_PSI_FILE_DISC; + a.addStringT(getConnectName()); + if (skt->sendBufferStore(a)) { + if (skt->getBufferStore(a) == 1) { + if (!strcmp(a.getString(0), "Ok")) + status = E_PSI_GEN_NONE; + } + } +} + +// move to base class? +long rfsv16:: +getStatus() +{ + return status; +} + +// move to base class? +const char *rfsv16:: +getConnectName() +{ + return "SYS$RFSV.*"; +} + +int rfsv16:: +convertName(const char* orig, char *retVal) +{ + int len = strlen(orig); + char *temp = new char [len+1]; + + // FIXME: need to return 1 if OOM? + for (int i=0; i <= len; i++) { + if (orig[i] == '/') + temp[i] = '\\'; + else + temp[i] = orig[i]; + } + + if (len == 0 || temp[1] != ':') { + // We can automatically supply a drive letter + strcpy(retVal, DDRIVE); + + if (len == 0 || temp[0] != '\\') { + strcat(retVal, DBASEDIR); + } + else { + retVal[strlen(retVal)-1] = 0; + } + + strcat(retVal, temp); + } + else { + strcpy(retVal, temp); + } + + delete [] temp; + cout << retVal << endl; + return 0; +} + +long rfsv16:: +fopen(long attr, const char *name, long &handle) +{ + bufferStore a; + char realName[200]; + int rv = convertName(name, realName); + if (rv) return (long)rv; + + // FIXME: anything that calls fopen should NOT do the name + // conversion - it's just done here. + + a.addWord(attr & 0xFFFF); + a.addString(realName); + a.addByte(0x00); // Needs to be manually Null-Terminated. + if (!sendCommand(FOPEN, a)) + 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); + return 0; + } + // cerr << "fopen: Unknown response (" << attr << "," << name << ") " << a < 16) { + int version = a.getWord(0); + if (version != 2) { + cerr << "dir: not version 2" << endl; + return 1; + } + int status = a.getWord(2); + long size = a.getDWord(4); + long date = a.getDWord(8); + const char *s = a.getString(16); + a.discardFirstBytes(17+strlen(s)); + + bufferStore temp; + temp.addDWord(date); + temp.addDWord(size); + temp.addDWord((long)status); + temp.addStringT(s); + files->pushBuffer(temp); + } + } + if ((short int)res == E_PSI_FILE_EOF) + res = 0; + fclose(fileHandle); + return res; +} + +char * rfsv16:: +opAttr(long attr) +{ + static char buf[11]; + buf[0] = ((attr & rfsv16::P_FAWRITE) ? 'w' : '-'); + buf[1] = ((attr & rfsv16::P_FAHIDDEN) ? 'h' : '-'); + buf[2] = ((attr & rfsv16::P_FASYSTEM) ? 's' : '-'); + buf[3] = ((attr & rfsv16::P_FAVOLUME) ? 'v' : '-'); + buf[4] = ((attr & rfsv16::P_FADIR) ? 'd' : '-'); + buf[5] = ((attr & rfsv16::P_FAMOD) ? 'm' : '-'); + buf[6] = ((attr & rfsv16::P_FAREAD) ? 'r' : '-'); + buf[7] = ((attr & rfsv16::P_FAEXEC) ? 'x' : '-'); + buf[8] = ((attr & rfsv16::P_FASTREAM) ? 'b' : '-'); + buf[9] = ((attr & rfsv16::P_FATEXT) ? 't' : '-'); + buf[10] = '\0'; + return (char *) (&buf); +} + + +long rfsv16:: +opMode(long mode) +{ + long ret = 0; + + ret |= ((mode & 03) == PSI_O_RDONLY) ? 0 : P_FUPDATE; + ret |= (mode & PSI_O_TRUNC) ? P_FREPLACE : 0; + ret |= (mode & PSI_O_CREAT) ? P_FCREATE : 0; + ret |= (mode & PSI_O_APPEND) ? P_FAPPEND : 0; + ret |= (mode & PSI_O_EXCL) ? 0 : P_FSHARE; + return ret; +} + +long rfsv16:: +fgetmtime(const char *name, long *mtime) +{ +cerr << "rfsv16::fgetmtime" << endl; + // NB: fgetattr, fgeteattr is almost identical... + bufferStore a; + char realName[200]; + int rv = convertName(name, realName); + if (rv) return rv; + a.addString(realName); + a.addByte(0x00); // needs to be null-terminated, + // and this needs sending in the length word. + if (!sendCommand(FINFO, a)) + return E_PSI_FILE_DISC; + + long res = getResponse(a); + if (res != 0) + 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); + } + cerr << "fgetmtime: Unknown response (" << name << ") " << a < 16) { + int version = a.getWord(0); + if (version != 2) { + cerr << "dir: not version 2" << endl; + return 1; + } + // int status = a.getWord(2); + // long size = a.getDWord(4); + // long date = a.getDWord(8); + const char *s = a.getString(16); + a.discardFirstBytes(17+strlen(s)); + (*count)++; + } + } + if ((short int)res == E_PSI_FILE_EOF) + res = 0; + fclose(fileHandle); + return res; +} + +long rfsv16:: +devlist(long *devbits) +{ + long res; + long fileHandle; + *devbits = 0; + + // The following is taken from a trace between a Series 3c and PsiWin. + // Hope it works! We PARSE to find the correct node, then FOPEN + // (P_FDEVICE) this, FDEVICEREAD each entry, setting the appropriate + // drive-letter-bit in devbits, then FCLOSE. + + bufferStore a; + a.init(); + a.addByte(0x00); // no Name 1 + a.addByte(0x00); // no Name 2 + a.addByte(0x00); // no Name 3 + if (!sendCommand(PARSE, a)) + return E_PSI_FILE_DISC; + res = getResponse(a); + if (res) + return res; + + // Find the drive to FOPEN + char name[4] = { 'x', ':', '\\', '\0' } ; + a.discardFirstBytes(8); // 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); + if (status != 0) { + return status; + } + while (1) { + bufferStore a; + a.init(); + a.addWord(fileHandle & 0xFFFF); + if (!sendCommand(FDEVICEREAD, a)) + return E_PSI_FILE_DISC; + res = getResponse(a); + if (res) + break; + a.discardFirstBytes(2); // Result + int version = a.getWord(0); + if (version != 2) { + cerr << "devlist: not version 2" << endl; + return 1; // FIXME + } + char drive = a.getByte(64); + if (drive >= 'A' && drive <= 'Z') { + int shift = (drive - 'A'); + (*devbits) |= (long) ( 1 << shift ); + } + else { + cerr << "devlist: non-alphabetic drive letter (" + << drive << ")" << endl; + } + } + if ((short int)res == E_PSI_FILE_EOF) + res = 0; + fclose(fileHandle); + return res; +} + +char *rfsv16:: +devinfo(int devnum, long *vfree, long *vtotal, long *vattr, + long *vuniqueid) +{ + bufferStore a; + long res; + long fileHandle; + + // Again, this is taken from an excahnge between PsiWin and a 3c. + // For each drive, we PARSE with its drive letter to get a response + // (which we ignore), then do a STATUSDEVICE to get the info. + + a.init(); + a.addByte((char) (devnum + 'A')); // Name 1 + a.addByte(':'); + a.addByte(0x00); + a.addByte(0x00); // No name 2 + a.addByte(0x00); // No name 3 + if (!sendCommand(PARSE, a)) + return NULL; + res = getResponse(a); + if (res) { + // cerr << "devinfo PARSE res is " << dec << (signed short int) res << endl; + return NULL; + } + + a.init(); + a.addByte((char) (devnum + 'A')); // Name 1 + a.addByte(':'); + a.addByte('\\'); + a.addByte(0x00); + if (!sendCommand(STATUSDEVICE, a)) + return NULL; + res = getResponse(a); + if (res) { + // 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); + long free = a.getDWord(10); + const char *volume = a.getString(14); + int battery = a.getWord(30); + const char *devicename = a.getString(62); + *vfree = free; + *vtotal = size; + *vattr = type; + *vuniqueid = 0; + static char name[2] = { 'x', '\0' }; + name[0] = (char) (devnum + 'A'); + return strdup(name); +} + +bool rfsv16:: +sendCommand(enum commands cc, bufferStore & data) +{ + bool result; + bufferStore a; + a.addWord(cc); + a.addWord(data.getLen()); + a.addBuff(data); + result = skt->sendBufferStore(a); + if (!result) + status = E_PSI_FILE_DISC; + return result; +} + + +long rfsv16:: +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 && + data.getWord(2) == data.getLen()-4) { + data.discardFirstBytes(4); + long ret = data.getWord(0); + return ret; + } else + status = E_PSI_FILE_DISC; + cerr << "rfsv16::getResponse: duff response. Size field:" << +data.getWord(2) << " Frame size:" << data.getLen()-4 << " Result field:" << +data.getWord(4) << endl; + return status; +} + +char * rfsv16:: +opErr(long status) +{ +cerr << "rfsv16::opErr 0x" << hex << setfill('0') << setw(4) + << status << " (" << dec << (signed short int)status << ")" << endl; + return rfsv::opErr(status); +} + +long rfsv16:: +fread(long handle, unsigned char *buf, long len) +{ +cerr << "rfsv16::fread ***" << endl; + 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 + // 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); + sendCommand(FREAD, a); + long res = getResponse(a); + remaining -= a.getLen(); +// copy the data to buf + +cerr << "fread getResponse returned " << dec<< (signed short int) res << " data: " << a << dec < 0) + op.write(buf, len); + if (cb) { + if (!cb(len)) { + len = E_PSI_FILE_CANCEL; + break; + } + } + } while (len > 0); + + fclose(handle); + op.close(); + return len; +} + +long rfsv16:: +copyToPsion(const char *from, const char *to, cpCallback_t cb) +{ +cerr << "rfsv16::copyToPsion" << endl; + long handle; + long res; + + ifstream ip(from); + if (!ip) + return E_PSI_FILE_NXIST; + res = fcreatefile(P_FSTREAM | P_FUPDATE, to, handle); + if (res != 0) { + res = freplacefile(P_FSTREAM | P_FUPDATE, to, handle); + if (res != 0) + return res; + } + unsigned char *buff = new unsigned char[RFSV_SENDLEN]; + int total = 0; + while (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 = 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; +} + +/* + * Unix-like implementation off fseek with one + * exception: If seeking beyond eof, the gap + * contains garbage instead of zeroes. + */ +long rfsv16:: +fseek(long handle, long pos, long mode) +{ +cerr << "rfsv16::fseek ***" << endl; + return 0; +} + +long rfsv16:: +mkdir(const char* dirName) +{ + char realName[200]; + int rv = convertName(dirName, realName); + if (rv) return rv; + bufferStore a; + a.addString(realName); + a.addByte(0x00); // needs to be null-terminated, + // and this needs sending in the length word. + sendCommand(MKDIR, a); + long res = getResponse(a); + if (!res && a.getLen() == 2) { + // Correct response + return a.getWord(0); + } + cerr << "Unknown response from mkdir "<< a <