diff -urN --exclude-from=plptools.exclude plptools-0.4/Makefile.am plptools-0.4-mjg/Makefile.am
--- plptools-0.4/Makefile.am	Mon Jul  5 22:12:41 1999
+++ plptools-0.4-mjg/Makefile.am	Wed Oct 20 18:39:24 1999
@@ -1,7 +1,7 @@
 # $Id: Makefile.am,v 1.7 1999/07/05 22:12:41 felfert Exp $
 #
 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 -urN --exclude-from=plptools.exclude plptools-0.4/README.mjg plptools-0.4-mjg/README.mjg
--- plptools-0.4/README.mjg	Thu Jan  1 00:00:00 1970
+++ plptools-0.4-mjg/README.mjg	Thu Oct 21 18:27:37 1999
@@ -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<x>. 
+
+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 -urN --exclude-from=plptools.exclude plptools-0.4/configure.in plptools-0.4-mjg/configure.in
--- plptools-0.4/configure.in	Sat Aug  7 17:18:33 1999
+++ plptools-0.4-mjg/configure.in	Thu Oct 21 18:20:57 1999
@@ -6,7 +6,9 @@
 AM_PROG_LIBTOOL
 
 dnl Enable Mainatiner stuff
-AM_MAINTAINER_MODE
+dnl **** NOTE **** Matt had to comment the following line out to get it to
+dnl ************** build, and doesn't know why - YMMV.
+dnl AM_MAINTAINER_MODE
 
 dnl checks for programs
 AC_PROG_MAKE_SET
@@ -85,6 +87,26 @@
     ]
 )
 AC_SUBST(DPORT)
+
+AC_ARG_WITH(drive,
+    [  --with-drive=DRIVE      override default Psion drive [M:]],
+    [ DDRIVE="$withval"
+      AC_MSG_RESULT(Overriding drive: $DDRIVE) ],
+    [ DDRIVE='M:'
+      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
diff -urN --exclude-from=plptools.exclude plptools-0.4/include/defs.h.in plptools-0.4-mjg/include/defs.h.in
--- plptools-0.4/include/defs.h.in	Sun Jul  4 11:59:41 1999
+++ plptools-0.4-mjg/include/defs.h.in	Wed Aug 11 21:16:47 1999
@@ -36,6 +36,8 @@
 #define DDEV		"@DDEV@"
 #define DSPEED		@DSPEED@
 #define DPORT		@DPORT@
+#define DDRIVE		"@DDRIVE@"
+#define DBASEDIR	"@DBASEDIR@"
 
 /* Debugging */
 
diff -urN --exclude-from=plptools.exclude plptools-0.4/include/mp.h plptools-0.4-mjg/include/mp.h
--- plptools-0.4/include/mp.h	Sat Aug  7 17:07:48 1999
+++ plptools-0.4-mjg/include/mp.h	Fri Oct  1 19:47:40 1999
@@ -122,7 +122,7 @@
 #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 -urN --exclude-from=plptools.exclude plptools-0.4/lib/Makefile.am plptools-0.4-mjg/lib/Makefile.am
--- plptools-0.4/lib/Makefile.am	Sun Jul  4 11:59:42 1999
+++ plptools-0.4-mjg/lib/Makefile.am	Wed Sep 15 21:05:01 1999
@@ -1,5 +1,5 @@
 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 
+EXTRA_DIST = bool.h bufferarray.h bufferstore.h iowatch.h ppsocket.h rfsv.h rfsv16.h rfsv32.h rfsvfactory.h log.h
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/bufferstore.cc plptools-0.4-mjg/lib/bufferstore.cc
--- plptools-0.4/lib/bufferstore.cc	Mon Jun 28 23:51:15 1999
+++ plptools-0.4-mjg/lib/bufferstore.cc	Thu Oct 21 18:17:17 1999
@@ -19,7 +19,8 @@
 //
 //  e-mail philip.proudman@btinternet.com
 
-#include <stream.h>
+#include <stream.h> 
+// That should be iostream.h, but it won't build on Sun WorkShop C++ 5.0
 #include <iomanip.h>
 #include <string.h>
 
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/ppsocket.cc plptools-0.4-mjg/lib/ppsocket.cc
--- plptools-0.4/lib/ppsocket.cc	Sun Jul  4 11:59:42 1999
+++ plptools-0.4-mjg/lib/ppsocket.cc	Sun Oct 10 09:26:09 1999
@@ -30,6 +30,7 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "defs.h"
 #include "bool.h"
@@ -126,7 +127,6 @@
 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 @@
 		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;
+	int len;
 	ppsocket *accepted;
 	char *peer;
 
@@ -197,7 +200,6 @@
 	//***********************
 
 	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 @@
 	//* 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 @@
 			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 -urN --exclude-from=plptools.exclude plptools-0.4/lib/rfsv.h plptools-0.4-mjg/lib/rfsv.h
--- plptools-0.4/lib/rfsv.h	Thu Jan  1 00:00:00 1970
+++ plptools-0.4-mjg/lib/rfsv.h	Thu Oct  7 20:41:44 1999
@@ -0,0 +1,49 @@
+#ifndef _rfsv_h_
+#define _rfsv_h_
+
+class ppsocket;
+class bufferStore;
+class bufferArray;
+
+#define RFSV_SENDLEN 230
+
+// 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 attr, const char *name, long &handle) = 0;
+  virtual long mktemp(long *handle, char *tmpname) = 0;
+  virtual long fcreatefile(long attr, const char *name, long &handle) = 0;
+  virtual long freplacefile(long attr, const char *name, long &handle) = 0;
+  virtual long fopendir(long attr, const char *name, long &handle) = 0;
+  virtual long fclose(long handle) = 0;
+  virtual long dir(const char* psionName, bufferArray *files) = 0;
+  virtual long fgetmtime(const char *name, long *mtime) = 0;
+  virtual long fsetmtime(const char *name, long mtime) = 0;
+  virtual long fgetattr(const char *name, long *attr) = 0;
+  virtual long fgeteattr(const char *name, long *attr, long *size, long *time) =0; 
+  virtual long fsetattr(const char *name, long seta, long unseta) = 0;
+  virtual long dircount(const char *name, long *count) = 0;
+  virtual long devlist(long *devbits) = 0;
+  virtual char *devinfo(int devnum, long *vfree, long *vtotal, long *vattr, long *vuiqueid) = 0;
+  virtual char *opErr(long status) = 0;
+  virtual char *opAttr(long status) = 0;
+  virtual long fread(long handle, char *buf, long len) = 0;
+  virtual long fwrite(long handle, char *buf, long len) = 0;
+  virtual long copyFromPsion(const char *from, const char *to) = 0;
+  virtual long copyToPsion(const char *from, const char *to) = 0;
+  virtual long fsetsize(long handle, long size) = 0;
+  virtual long fseek(long handle, long pos, long mode) = 0;
+  virtual long mkdir(const char* dirName) = 0;
+  virtual long rmdir(const char *name) = 0;
+  virtual long rename(const char *oldname, const char *newname) = 0;
+  virtual long remove(const char *name) = 0;
+};
+
+#endif
+
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/rfsv16.cc plptools-0.4-mjg/lib/rfsv16.cc
--- plptools-0.4/lib/rfsv16.cc	Thu Jan  1 00:00:00 1970
+++ plptools-0.4-mjg/lib/rfsv16.cc	Fri Oct 15 19:56:41 1999
@@ -0,0 +1,887 @@
+//
+//  RFSV16 - An implementation of the PSION SIBO RFSV Client protocol
+//
+//  Copyright (C) 1999  Philip Proudman
+//  Modifications for plptools:
+//    Copyright (C) 1999 Matt J. Gumbley <matt@gumbley.demon.co.uk>
+//    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 <stream.h>
+#include <stdlib.h>
+#include <fstream.h>
+#include <iomanip.h>
+#include <time.h>
+#include <string.h>
+
+#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 == NO_ERROR)
+		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_FILE_DISC;
+	a.addStringT(getConnectName());
+	if (skt->sendBufferStore(a)) {
+		if (skt->getBufferStore(a) == 1) {
+			if (!strcmp(a.getString(0), "Ok"))
+				status = NO_ERROR;
+		}
+	}
+}
+
+// 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_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 <<endl;
+	return res;
+}
+
+// internal
+long rfsv16::
+mktemp(long *handle, char *tmpname)
+{
+cerr << "rfsv16::mktemp ***" << endl;
+	return 0;
+}
+
+// internal and external
+long rfsv16::
+fcreatefile(long attr, const char *name, long &handle)
+{
+cerr << "rfsv16::fcreatefile ***" << endl;
+	return 0;
+}
+
+// this is internal - not used by plpnfsd, unlike fcreatefile
+long rfsv16::
+freplacefile(long attr, const char *name, long &handle)
+{
+cerr << "rfsv16::freplacefile ***" << endl;
+	return 0;
+}
+
+// internal
+long rfsv16::
+fopendir(long attr, const char *name, long &handle)
+{
+cerr << "rfsv16::fopendir ***" << endl;
+	return 0;
+}
+
+long rfsv16::
+fclose(long fileHandle)
+{
+	bufferStore a;
+	a.addWord(fileHandle & 0xFFFF);
+	if (!sendCommand(FCLOSE, a))
+		return E_FILE_DISC;
+	long res = getResponse(a);
+	if (!res && a.getLen() == 2)
+		return (long)a.getWord(0);
+	cerr << "fclose: Unknown response "<< a <<endl;
+	return 1;
+}
+
+long rfsv16::
+dir(const char *dirName, bufferArray * files)
+{
+	long fileHandle;
+	long res;
+
+	long status = fopen(P_FDIR, dirName, fileHandle);
+	if (status != 0) {
+		return status;
+	}
+
+	while (1) {
+		bufferStore a;
+		a.addWord(fileHandle & 0xFFFF);
+		if (!sendCommand(FDIRREAD, a))
+			return E_FILE_DISC;
+		res = getResponse(a);
+		if (res)
+			break;
+		a.discardFirstBytes(4); // Don't know what these mean!
+		while (a.getLen() > 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_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::
+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_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 <<endl;
+	return 1;
+}
+
+long rfsv16::
+fsetmtime(const char *name, long mtime)
+{
+cerr << "rfsv16::fsetmtime ***" << endl;
+	// I don't think there's a protocol frame that allows us to set the
+	// modification time. SFDATE allows setting of creation time...
+	return E_INVALID_OPERATION;
+}
+
+long rfsv16::
+fgetattr(const char *name, long *attr)
+{
+	// NB: fgetmtime, fgeteattr are 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_FILE_DISC;
+  
+	long res = getResponse(a);
+	if (res != 0)
+		return res;
+	if (a.getLen() == 2) {
+		cerr << "fgetattr: Error " << a.getWord(0) << " on file " << name << endl;
+		return 1;
+	}
+	else if (a.getLen() == 18 && a.getWord(0) == 0) {
+		*attr = (long)(a.getWord(4));
+		return a.getWord(0);
+	}
+	cerr << "fgetattr: Unknown response (" << name << ") " << a <<endl;
+	return 1;
+}
+
+long rfsv16::
+fgeteattr(const char *name, long *attr, long *size, long *time)
+{
+	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_FILE_DISC;
+  
+	long res = getResponse(a);
+	if (res != 0)
+		return res;
+	if (a.getLen() == 2) {
+		cerr << "fgeteattr: Error " << a.getWord(0) << " on file " << name << endl;
+		return 1;
+	}
+	else if (a.getLen() == 18 && a.getWord(0) == 0) {
+		*attr = (long)(a.getWord(4));
+		*size = a.getDWord(6);
+		*time = a.getDWord(10);
+		return a.getWord(0);
+	}
+	cerr << "fgeteattr: Unknown response (" << name << ") " << a <<endl;
+	return 1;
+}
+
+long rfsv16::
+fsetattr(const char *name, long seta, long unseta)
+{
+cerr << "rfsv16::fsetattr" << endl;
+	// seta are attributes to set; unseta are attributes to unset. Need to
+	// turn this into attributes to change state and a bit mask.
+	// 210000
+	// 008421
+	// a  shr
+	long statusword = seta & (~ unseta);
+	statusword ^= 0x0000001; // r bit is inverted
+	long bitmask = seta | unseta;
+	// cerr << "seta is   " << hex << setw(2) << setfill('0') << seta << endl;
+	// cerr << "unseta is " << hex << setw(2) << setfill('0') << unseta << endl;
+	// cerr << "statusword is  " << hex << setw(2) << setfill('0') << statusword << endl;
+	// cerr << "bitmask is     " << hex << setw(2) << setfill('0') << bitmask << endl;
+	bufferStore a;
+	a.addWord(statusword & 0xFFFF);
+	a.addWord(bitmask & 0xFFFF);
+	a.addString(name);
+	a.addByte(0x00); 	// needs to be null-terminated, 
+				// and this needs sending in the length word.
+	if (!sendCommand(SFSTAT, a))
+		return E_FILE_DISC;
+	return getResponse(a);
+}
+
+long rfsv16::
+dircount(const char *name, long *count)
+{
+	long fileHandle;
+	long res;
+	*count = 0;
+
+	long status = fopen(P_FDIR, name, fileHandle);
+	if (status != 0) {
+		return status;
+	}
+
+	while (1) {
+		bufferStore a;
+		a.addWord(fileHandle & 0xFFFF);
+		if (!sendCommand(FDIRREAD, a))
+			return E_FILE_DISC;
+		res = getResponse(a);
+		if (res)
+			break;
+		a.discardFirstBytes(4); // Don't know what these mean!
+		while (a.getLen() > 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_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_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_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_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_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_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;
+	signed short int statcode = (signed short int) status;
+	enum errs e = (enum errs) statcode;
+	switch (e) {
+		case NO_ERROR:
+			return "No error occurred";
+		case E_FILE_EXIST:
+			return "File already exists";
+		case E_FILE_NXIST:
+			return "File does not exist";
+		case E_FILE_WRITE:
+			return "Write to file failed";
+		case E_FILE_READ:
+			return "Read from file failed";
+		case E_FILE_EOF:
+			return "Read past end of file";
+		case E_FILE_FULL:
+			return "Disk/serial read buffer full";
+		case E_FILE_NAME:
+			return "Invalid file name";
+		case E_FILE_ACCESS:
+			return "Access denied";
+		case E_FILE_LOCKED:
+			return "Record or file locked";
+		case E_FILE_DEVICE:
+			return "Device does not exist";
+		case E_FILE_DIR:
+			return "Directory does not exist";
+		case E_FILE_RECORD:
+			return "Record too large";
+		case E_FILE_RDONLY:
+			return "File is read-only";
+		case E_FILE_INV:
+			return "Invalid I/O operation";
+		case E_FILE_PENDING:
+			return "I/O pending (not yet completed)";
+		case E_FILE_VOLUME:
+			return "Invalid volume";
+		case E_FILE_CANCEL:
+			return "Async operation was cancelled";
+		case E_FILE_ALLOC:
+			return "No memory for control block";
+		case E_FILE_DISC:
+			return "Link is disconnected";
+		case E_FILE_CONNECT:
+			return "Link already connected";
+		case E_FILE_RETRAN:
+			return "Retransmission threshold exceeded";
+		case E_FILE_LINE:
+			return "Physical link failure";
+		case E_FILE_INACT:
+			return "Inactivity timer expired";
+		case E_FILE_PARITY:
+			return "Serial parity error";
+		case E_FILE_FRAME:
+			return "Serial framing error";
+		case E_FILE_OVERRUN:
+			return "Serial overrun error";
+		case E_MDM_CONFAIL:
+			return "Modem cannot connect to remote modem";
+		case E_MDM_BUSY:
+			return "Remote modem busy";
+		case E_MDM_NOANS:
+			return "Remote modem did not answer";
+		case E_MDM_BLACKLIST:
+			return "Number blacklisted by the modem";
+		case E_FILE_NOTREADY:
+			return "Drive not ready";
+		case E_FILE_UNKNOWN:
+			return "Unknown media in drive";
+		case E_FILE_DIRFULL:
+			return "Root directory full";
+		case E_FILE_PROTECT:
+			return "Device is write-protected";
+		case E_FILE_CORRUPT:
+			return "Media is corrupt";
+		case E_FILE_ABORT:
+			return "User aborted operation";
+		case E_FILE_ERASE:
+			return "Failed to erase flash media";
+		case E_FILE_INVALID:
+			return "Invalid file for DBF system";
+		case E_INVALID_OPERATION:
+			return "Invalid operation for RFSV16";
+		default:
+			return "Unknown error";
+	}
+}
+
+long rfsv16::
+fread(long handle, 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 <<endl;
+		if (res) {
+			return res;
+		}
+	}
+	return len;
+}
+
+long rfsv16::
+fwrite(long handle, char *buf, long len)
+{
+cerr << "rfsv16::fwrite ***" << endl;
+	return 0;
+}
+
+long rfsv16::
+copyFromPsion(const char *from, const char *to)
+{
+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 {
+		char buf[2000];
+		if ((len = fread(handle, buf, sizeof(buf))) > 0)
+			op.write(buf, len);
+	} while (len > 0);
+
+	fclose(handle);
+	op.close();
+	return len;
+}
+
+long rfsv16::
+copyToPsion(const char *from, const char *to)
+{
+cerr << "rfsv16::copyToPsion" << endl;
+	long handle;
+	long res;
+
+	ifstream ip(from);
+	if (!ip)
+		return E_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());
+		total += tmp.getLen();
+		if (tmp.getLen() == 0)
+			break;
+		bufferStore a;
+		a.addDWord(handle);
+		a.addBuff(tmp);
+		if (!sendCommand(FWRITE, a)) // FIXME: need to check params
+			return E_FILE_DISC;
+		res = getResponse(a);
+		if (res) {
+			fclose(handle);
+			return res;
+		}
+	}
+	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 <<endl;
+	return 1;
+}
+
+long rfsv16::
+rmdir(const char *dirName)
+{
+	// There doesn't seem to be an RMDIR command, but DELETE works. We
+	// should probably check to see if the file is a directory first!
+	return remove(dirName);
+}
+
+long rfsv16::
+rename(const char *oldName, const char *newName)
+{
+cerr << "rfsv16::rename ***" << endl;
+	char realOldName[200];
+	int rv = convertName(oldName, realOldName);
+	if (rv) return rv;
+	char realNewName[200];
+	rv = convertName(newName, realNewName);
+	if (rv) return rv;
+	bufferStore a;
+	a.addString(realOldName);
+	a.addByte(0x00); 	// needs to be null-terminated, 
+				// and this needs sending in the length word.
+	a.addString(realNewName);
+	a.addByte(0x00); 	// needs to be null-terminated, 
+				// and this needs sending in the length word.
+	sendCommand(RENAME, a);
+	long res = getResponse(a);
+	if (!res && a.getLen() == 2) {
+		// Correct response
+		return a.getWord(0);
+	}
+	cerr << "Unknown response from rename "<< a <<endl;
+	return 1;
+}
+
+long rfsv16::
+remove(const char* psionName)
+{
+	char realName[200];
+	int rv = convertName(psionName, 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(DELETE, a);
+	long res = getResponse(a);
+	if (!res && a.getLen() == 2) {
+		// Correct response
+		return a.getWord(0);
+	}
+	cerr << "Unknown response from delete "<< a <<endl;
+	return 1;
+}
+
+
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/rfsv16.h plptools-0.4-mjg/lib/rfsv16.h
--- plptools-0.4/lib/rfsv16.h	Thu Jan  1 00:00:00 1970
+++ plptools-0.4-mjg/lib/rfsv16.h	Mon Oct 11 21:29:13 1999
@@ -0,0 +1,171 @@
+#ifndef _rfsv16_h_
+#define _rfsv16_h_
+
+#include "rfsv.h"
+
+class rfsv16 : public rfsv {
+	public:
+	rfsv16(ppsocket *skt);
+	~rfsv16();
+	void reset(); // these 2 added
+	void reconnect();
+
+	// these are the original publics
+	long dir(const char* dirName, bufferArray *files);
+	/*long read(const char* psionName, const char* localName);
+	long write(const char* localName, const char* psionName);*/
+	long mkdir(const char* dirName);
+
+	// these are FS' promotions
+	long dircount(const char *name, long *count);
+	long copyFromPsion(const char *from, const char *to);
+	long copyToPsion(const char *from, const char *to);
+	long rmdir(const char *name);
+	long remove(const char *name); // MJG: was this del?
+	long rename(const char *oldname, const char *newname);
+	long mktemp(long *handle, char *tmpname);
+	long fgeteattr(const char *name, long *attr, long *size, long *time);
+	long fgetattr(const char *name, long *attr);
+	long fsetattr(const char *name, long seta, long unseta);
+	long fgetmtime(const char *name, long *mtime);
+	long fsetmtime(const char *name, long mtime);
+	long fopendir(long attr, const char *name, long &handle);
+	long fopen(long attr, const char *name, long &handle);
+	long fcreatefile(long attr, const char *name, long &handle);
+	long freplacefile(long attr, const char *name, long &handle);
+	long fseek(long handle, long pos, long mode);
+	long fread(long handle, char *buf, long len);
+	long fwrite(long handle, char *buf, long len);
+	long fsetsize(long handle, long size);
+	long fclose(long handle);
+
+	long devlist(long *devbits);
+	char *devinfo(int devnum, long *vfree, long *vtotal, long *vattr, long *vuiqueid);
+	long getStatus();
+	char *opErr(long status);
+	char *opAttr(long status);
+
+	enum errs {
+		NO_ERROR = 0,
+		E_FILE_EXIST = -32,
+		E_FILE_NXIST = -33,
+		E_FILE_WRITE = -34,
+		E_FILE_READ = -35,
+		E_FILE_EOF = -36,
+		E_FILE_FULL = -37,
+		E_FILE_NAME = -38,
+		E_FILE_ACCESS = -39,
+		E_FILE_LOCKED = -40,
+		E_FILE_DEVICE = -41,
+		E_FILE_DIR = -42,
+		E_FILE_RECORD = -43,
+		E_FILE_RDONLY = -44,
+		E_FILE_INV = -45,
+		E_FILE_PENDING = -46,
+		E_FILE_VOLUME = -47,
+		E_FILE_CANCEL = -48,
+		E_FILE_ALLOC = -10,
+		E_FILE_DISC = -50,
+		E_FILE_CONNECT = -51,
+		E_FILE_RETRAN = -52,
+		E_FILE_LINE = -53,
+		E_FILE_INACT = -54,
+		E_FILE_PARITY = -55,
+		E_FILE_FRAME = -56,
+		E_FILE_OVERRUN = -57,
+		E_MDM_CONFAIL = -58,
+		E_MDM_BUSY = -59,
+		E_MDM_NOANS = -60,
+		E_MDM_BLACKLIST = -61,
+		E_FILE_NOTREADY = -62,
+		E_FILE_UNKNOWN = -63,
+		E_FILE_DIRFULL = -64,
+		E_FILE_PROTECT = -65,
+		E_FILE_CORRUPT = -66,
+		E_FILE_ABORT = -67,
+		E_FILE_ERASE = -68,
+		E_FILE_INVALID = -69,
+		// Special error code for "Operation not permitted in RFSV16"
+		E_INVALID_OPERATION = -100,
+	};
+
+ 
+private:
+  enum commands {
+    FOPEN = 0, // File Open
+    FCLOSE = 2, // File Close
+    FREAD = 4, // File Read
+    FDIRREAD = 6, // Read Directory entries
+    FDEVICEREAD = 8, // Device Information
+    FWRITE = 10, // File Write
+    FSEEK = 12, // File Seek
+    FFLUSH = 14, // Flush
+    FSETEOF = 16,
+    RENAME = 18,
+    DELETE = 20,
+    FINFO = 22,
+    SFSTAT = 24,
+    PARSE = 26,
+    MKDIR = 28,
+    OPENUNIQUE = 30,
+    STATUSDEVICE = 32,
+    PATHTEST = 34,
+    STATUSSYSTEM = 36,
+    CHANGEDIR = 38,
+    SFDATE = 40,
+    RESPONSE = 42
+  };
+  
+  enum fopen_attrib {
+    P_FOPEN = 0x0000, /* Open file */
+    P_FCREATE = 0x0001, /* Create file */
+    P_FREPLACE = 0x0002, /* Replace file */
+    P_FAPPEND = 0x0003, /* Append records */
+    P_FUNIQUE = 0x0004, /* Unique file open */
+    P_FSTREAM = 0x0000, /* Stream access to a binary file */
+    P_FSTREAM_TEXT = 0x0010, /* Stream access to a text file */
+    P_FTEXT = 0x0020, /* Record access to a text file */
+    P_FDIR = 0x0030, /* Record access to a directory file */
+    P_FFORMAT = 0x0040, /* Format a device */
+    P_FDEVICE = 0x0050, /* Record access to device name list */
+    P_FNODE = 0x0060, /* Record access to node name list */
+    P_FUPDATE = 0x0100, /* Read and write access */
+    P_FRANDOM = 0x0200, /* Random access */
+    P_FSHARE = 0x0400 /* File can be shared */
+  };
+
+  enum status_enum {
+    P_FAWRITE = 0x0001, /* can the file be written to? */    
+    P_FAHIDDEN = 0x0002, /* set if file is hidden */    
+    P_FASYSTEM = 0x0004, /* set if file is a system file */
+    P_FAVOLUME = 0x0008, /* set if the name is a volume name */    
+    P_FADIR = 0x0010, /* set if file is a directory file */    
+    P_FAMOD = 0x0020, /* has the file been modified? */    
+    P_FAREAD = 0x0100, /* can the file be read? */    
+    P_FAEXEC = 0x0200, /* is the file executable? */
+    P_FASTREAM = 0x0400, /* is the file a byte stream file? */    
+    P_FATEXT = 0x0800 /* is it a text file? */
+  };
+  
+  const char *getConnectName();
+
+  // File handlers
+  //long fopen(fopen_attrib a, const char* file, int &handle); // returns status 0=OK
+  //long fclose(int fileHandle);
+
+	// Miscellaneous
+	int convertName(const char* orig, char *retVal);
+
+	// Communication
+	bool sendCommand(enum commands c, bufferStore &data);
+	long getResponse(bufferStore &data);
+  
+	// Vars
+	ppsocket *skt;
+	// MJG: not sure what these are yet
+	int serNum;
+	long status;	// current connection status
+	int tDiff;	// don't think this is used anywhere
+};
+
+#endif
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/rfsv32.cc plptools-0.4-mjg/lib/rfsv32.cc
--- plptools-0.4/lib/rfsv32.cc	Sat Aug  7 16:18:06 1999
+++ plptools-0.4-mjg/lib/rfsv32.cc	Wed Oct 20 19:25:00 1999
@@ -33,8 +33,6 @@
 #include "ppsocket.h"
 #include "bufferarray.h"
 
-#define RFSV_SENDLEN 230
-
 rfsv32::rfsv32(ppsocket * _skt) : serNum(0)
 {
 	skt = _skt;
@@ -206,6 +204,7 @@
 	micro -= pes;
 	micro += EPOCH_DIFF_SECS;
 	micro -= EPOCH_2H;
+	micro += 3600; /* 1 hour PJC */
 	return (long) micro;
 }
 
@@ -218,6 +217,7 @@
 	micro += pes;
 	micro -= EPOCH_DIFF_SECS;
 	micro += EPOCH_2H;
+	micro -= 3600; /* 1 hour PJC */
 	micro *= (unsigned long long)1000000;
 	microLo = (micro & (unsigned long long)0x0FFFFFFFF);
 	micro >>= 32;
@@ -274,6 +274,24 @@
 		res = 0;
 	fclose(handle);
 	return res;
+}
+
+// beware this returns static data
+char * rfsv32::
+opAttr(long attr)
+{
+	static char buf[10];
+	buf[0] = ((attr & rfsv32::PSI_ATTR_DIRECTORY) ? 'd' : '-');
+	buf[1] = ((attr & rfsv32::PSI_ATTR_RONLY) ? '-' : 'w');
+	buf[2] = ((attr & rfsv32::PSI_ATTR_HIDDEN) ? 'h' : '-');
+	buf[3] = ((attr & rfsv32::PSI_ATTR_SYSTEM) ? 's' : '-');
+	buf[4] = ((attr & rfsv32::PSI_ATTR_ARCHIVE) ? 'a' : '-');
+	buf[5] = ((attr & rfsv32::PSI_ATTR_VOLUME) ? 'v' : '-');
+	buf[6] = ((attr & rfsv32::PSI_ATTR_NORMAL) ? 'n' : '-');
+	buf[7] = ((attr & rfsv32::PSI_ATTR_TEMPORARY) ? 't' : '-');
+	buf[8] = ((attr & rfsv32::PSI_ATTR_COMPRESSED) ? 'c' : '-');
+	buf[9] = '\0';
+	return (char *) (&buf);
 }
 
 long rfsv32::
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/rfsv32.h plptools-0.4-mjg/lib/rfsv32.h
--- plptools-0.4/lib/rfsv32.h	Sat Aug  7 12:39:24 1999
+++ plptools-0.4-mjg/lib/rfsv32.h	Thu Oct  7 20:41:28 1999
@@ -1,11 +1,13 @@
 #ifndef _rfsv32_h_
 #define _rfsv32_h_
 
+#include "rfsv.h"
+
 class ppsocket;
 class bufferStore;
 class bufferArray;
 
-class rfsv32 {
+class rfsv32 : public rfsv {
 	public:
 	rfsv32(ppsocket * skt);
 	~rfsv32();
@@ -40,6 +42,7 @@
 	char *devinfo(int devnum, long *vfree, long *vtotal, long *vattr, long *vuiqueid);
 	long getStatus();
 	char *opErr(long status);
+	char *opAttr(long status);
 
 	enum seek_mode {
 		PSI_SEEK_SET = 1,
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/rfsvfactory.cc plptools-0.4-mjg/lib/rfsvfactory.cc
--- plptools-0.4/lib/rfsvfactory.cc	Thu Jan  1 00:00:00 1970
+++ plptools-0.4-mjg/lib/rfsvfactory.cc	Fri Oct  1 20:20:05 1999
@@ -0,0 +1,75 @@
+//
+//  RFSVFACTORY - factory object that creates an appropriate RFSV object
+//  based on whatever the NCP daemon discovered in the INFO exchange.
+//
+//  Copyright (C) 1999 Matt J. Gumbley <matt@gumbley.demon.co.uk>
+//
+//  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
+
+#include <stream.h>
+#include <stdlib.h>
+#include <fstream.h>
+#include <iomanip.h>
+#include <time.h>
+#include <string.h>
+
+#include "defs.h"
+#include "bool.h"
+#include "rfsv.h"
+#include "rfsv16.h"
+#include "rfsv32.h"
+#include "rfsvfactory.h"
+#include "bufferstore.h"
+#include "ppsocket.h"
+#include "bufferarray.h"
+
+rfsvfactory::rfsvfactory(ppsocket *_skt) : serNum(0)
+{
+	skt = _skt;
+}
+
+rfsv * rfsvfactory::create()
+{
+	// skt is connected to the ncp daemon, which will have (hopefully) seen
+	// an INFO exchange, where the protocol version of the remote Psion was
+	// sent, and noted. We have to ask the ncp daemon which protocol it saw,
+	// so we can instantiate the correct RFSV protocol handler for the
+	// caller. We announce ourselves to the NCP daemon, and the relevant
+	// RFSV module will also announce itself.
+	bufferStore a;
+	a.init();
+	a.addStringT("NCP$INFO");
+	if (!skt->sendBufferStore(a)) {
+		cerr << "rfsvfactory::create couldn't send version request" << endl;
+		return NULL;
+	}
+	if (skt->getBufferStore(a) == 1) {
+		if (a.getLen() > 8 && !strncmp(a.getString(), "Series 3", 8)) {
+			return new rfsv16(skt);
+		}
+		else if (a.getLen() > 8 && !strncmp(a.getString(), "Series 5", 8)) {
+			return new rfsv32(skt);
+		}
+		// Invalid protocol version
+		cerr << "rfsvfactory::create received odd protocol version from
+ncpd! (" << a << ")" << endl;
+	} else {
+		cerr << "rfsvfactory::create sent, response not 1" << endl;
+	}
+
+	// No message returned.
+	return NULL;
+}
+
diff -urN --exclude-from=plptools.exclude plptools-0.4/lib/rfsvfactory.h plptools-0.4-mjg/lib/rfsvfactory.h
--- plptools-0.4/lib/rfsvfactory.h	Thu Jan  1 00:00:00 1970
+++ plptools-0.4-mjg/lib/rfsvfactory.h	Fri Sep 24 20:37:02 1999
@@ -0,0 +1,20 @@
+#ifndef _rfsvfactory_h_
+#define _rfsvfactory_h_
+
+#include "rfsv.h"
+
+class ppsocket;
+
+class rfsvfactory {
+	public:
+	rfsvfactory(ppsocket * skt);
+	virtual rfsv * create();
+
+	private:
+	// Vars
+	ppsocket *skt;
+	int serNum;
+};
+
+#endif
+
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/channel.cc plptools-0.4-mjg/ncpd/channel.cc
--- plptools-0.4/ncpd/channel.cc	Mon Jul  5 21:48:26 1999
+++ plptools-0.4-mjg/ncpd/channel.cc	Mon Sep 20 21:45:17 1999
@@ -66,6 +66,12 @@
 	ncpController->disconnect(ncpChannel);
 }
 
+short int channel::
+ncpProtocolVersion()
+{
+	return ncpController->getProtocolVersion();
+}
+
 void channel::
 setNcpChannel(int chan)
 {
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/channel.h plptools-0.4-mjg/ncpd/channel.h
--- plptools-0.4/ncpd/channel.h	Mon Jul  5 21:48:26 1999
+++ plptools-0.4-mjg/ncpd/channel.h	Thu Sep 16 20:20:42 1999
@@ -47,6 +47,7 @@
 		virtual void ncpConnectAck() = NULL;
 		virtual void ncpConnectTerminate() = NULL;
 		void ncpDisconnect();
+		short int ncpProtocolVersion();
 
 		// The following two calls are used for destructing an instance
 		virtual bool terminate(); // Mainloop will terminate this class if true
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/main.cc plptools-0.4-mjg/ncpd/main.cc
--- plptools-0.4/ncpd/main.cc	Mon Jul  5 21:48:26 1999
+++ plptools-0.4-mjg/ncpd/main.cc	Sat Oct  2 17:59:20 1999
@@ -52,7 +52,7 @@
 			cout << "New socket connection from " << peer << endl;
 		if ((numScp == 7) || (!a->gotLinkChannel())) {
 			bufferStore a;
-			a.addStringT("No psion");
+			a.addStringT("No Psion Connected\n");
 			next->sendBufferStore(a);
 			next->closeSocket();
 		} else
@@ -119,8 +119,16 @@
 				pverbose |= PKT_DEBUG_LOG;
 			if (!strcmp(argv[i], "pd"))
 				pverbose |= PKT_DEBUG_DUMP;
+			if (!strcmp(argv[i], "ph"))
+				lverbose |= PKT_DEBUG_HANDSHAKE;
 			if (!strcmp(argv[i], "m"))
 				verbose = true;
+			if (!strcmp(argv[i], "all")) {
+				nverbose = NCP_DEBUG_LOG | NCP_DEBUG_DUMP;
+				lverbose = LNK_DEBUG_LOG | LNK_DEBUG_DUMP;
+				pverbose = PKT_DEBUG_LOG | PKT_DEBUG_DUMP;
+				verbose = true;
+			}
 		} else if (!strcmp(argv[i], "-b") && i + 1 < argc) {
 			baudRate = atoi(argv[++i]);
 		} else if (!strcmp(argv[i], "-d")) {
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/ncp.cc plptools-0.4-mjg/ncpd/ncp.cc
--- plptools-0.4/ncpd/ncp.cc	Mon Jul  5 21:48:26 1999
+++ plptools-0.4-mjg/ncpd/ncp.cc	Tue Sep 21 19:16:09 1999
@@ -41,6 +41,7 @@
 	l = new link(fname, baud, iow);
 	failed = false;
 	verbose = 0;
+ 	protocolVersion = PV_SERIES_5; // until detected on receipt of INFO
 
 	// init channels
 	for (int i = 0; i < 8; i++)
@@ -100,6 +101,12 @@
 	l->setPktVerbose(_verbose);
 }
 
+short int ncp::
+getProtocolVersion()
+{
+	return protocolVersion;
+}
+
 void ncp::
 poll()
 {
@@ -153,6 +160,7 @@
 decodeControlMessage(bufferStore & buff)
 {
 	int remoteChan = buff.getByte(0);
+	short int ver;
 	interControllerMessageType imt = (interControllerMessageType) buff.getByte(1);
 	buff.discardFirstBytes(2);
 	if (verbose & NCP_DEBUG_LOG)
@@ -174,6 +182,11 @@
 				b.addByte(0x0);
 				controlChannel(localChan, NCON_MSG_CONNECT_RESPONSE, b);
 
+				// NOTE: we don't allow connections from the
+				// Psion to any local "processes" other than
+				// LINK.* - Matt might need to change this for
+				// his NCP<->TCP/IP bridge code...
+
 				if (!strcmp(buff.getString(0), "LINK.*")) {
 					if (gotLinkChan)
 						failed = true;
@@ -215,18 +228,33 @@
 			}
 			break;
 		case NCON_MSG_NCP_INFO:
-			if (buff.getByte(0) == 6) {
+			ver = buff.getByte(0);
+			// Series 3c returns '3', as does mclink. PsiWin 1.1
+			// returns version 2. We return whatever version we're
+			// sent, which is rather crude, but works for Series 3
+			// and probably 5. If Symbian have changed EPOC Connect
+			// for the Series 5mx/7, this may need to change.
+			// 
+			if (ver == PV_SERIES_5 || ver == PV_SERIES_3) {
 				bufferStore b;
+				protocolVersion = ver;
 				if (verbose & NCP_DEBUG_LOG) {
 					if (verbose & NCP_DEBUG_DUMP)
 						cout << " " << buff;
 					cout << endl;
 				}
-				b.addByte(6);
-				b.addDWord(0);
+				// Fake NCP version 2 for a Series 3 (behave like PsiWin 1.1)
+				if (ver == PV_SERIES_3)
+					ver = 2;
+				b.addByte(ver);
+				// Do we send a time of 0 or a real time?
+				// The Psion uses this to determine whether to
+				// restart. (See protocol docs for details)
+				// b.addDWord(0);
+				b.addDWord(time(NULL));
 				controlChannel(0, NCON_MSG_NCP_INFO, b);
 			} else
-				cout << "ALERT!!!! Protocol-Version is NOT 6!! (No Series 5?)!" << endl;
+				cout << "ALERT!!!! Unexpected Protocol Version!! (No Series 5/3?)!" << endl;
 			break;
 		case NCON_MSG_CHANNEL_DISCONNECT:
 			if (verbose & NCP_DEBUG_LOG)
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/ncp.h plptools-0.4-mjg/ncpd/ncp.h
--- plptools-0.4/ncpd/ncp.h	Mon Jul  5 21:48:27 1999
+++ plptools-0.4-mjg/ncpd/ncp.h	Sun Sep 12 16:24:05 1999
@@ -53,6 +53,7 @@
 		short int getLinkVerbose();
 		void setPktVerbose(short int);
 		short int getPktVerbose();
+		short int getProtocolVersion();
   
 	private:
 		enum c { MAX_LEN = 200, LAST_MESS = 1, NOT_LAST_MESS = 2 };
@@ -67,6 +68,7 @@
 			NCON_MSG_CHANNEL_DISCONNECT=7,
 			NCON_MSG_NCP_END=8
 		};
+		enum protocolVersionType { PV_SERIES_5 = 6, PV_SERIES_3 = 3 };
 		int getFirstUnusedChan();
 		void decodeControlMessage(bufferStore &buff);
 		void controlChannel(int chan, enum interControllerMessageType t, bufferStore &command);
@@ -79,6 +81,7 @@
 		int remoteChanList[8];
 		bool gotLinkChan;
 		bool failed;
+		short int protocolVersion;
 };
 
 #endif
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/packet.cc plptools-0.4-mjg/ncpd/packet.cc
--- plptools-0.4/ncpd/packet.cc	Mon Jul  5 21:48:27 1999
+++ plptools-0.4-mjg/ncpd/packet.cc	Thu Oct  7 19:32:59 1999
@@ -33,6 +33,7 @@
 #include <iomanip.h>
 #include <errno.h>
 #include <sys/ioctl.h>
+#include <termios.h>
 
 extern "C" {
 #include "mp_serial.h"
@@ -170,6 +171,10 @@
 		}
 		if (res < 0)
 			return false;
+		// XXX Solaris returns 0 on non blocking TTY lines
+		// even when VMIN > 0
+		if( res == 0 && inLen == 0 )
+			return false;
 		if (inLen >= BUFFERLEN) {
 			cerr << "packet: input buffer overflow!!!!" << endl;
 			inLen = 0;
@@ -256,13 +261,17 @@
 	int res = ioctl(fd, TIOCMGET, &arg);
 	if (res < 0)
 		failed = true;
-	if (verbose & PKT_DEBUG_DUMP)
+	if (verbose & PKT_DEBUG_HANDSHAKE)
 		cout << "packet: DTR:" << ((arg & TIOCM_DTR)?1:0)
 			<< " RTS:" << ((arg & TIOCM_RTS)?1:0)
 			<< " DCD:" << ((arg & TIOCM_CAR)?1:0)
 			<< " DSR:" << ((arg & TIOCM_DSR)?1:0)
 			<< " CTS:" << ((arg & TIOCM_CTS)?1:0) << endl;
+#ifdef sun
+	if ((arg & TIOCM_CTS) == 0)
+#else
 	if (((arg & TIOCM_DSR) == 0) || ((arg & TIOCM_CTS) == 0))
+#endif
 		failed = true;
 	if ((verbose & PKT_DEBUG_LOG) && failed)
 		cout << "packet: linkFAILED\n";
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/packet.h plptools-0.4-mjg/ncpd/packet.h
--- plptools-0.4/ncpd/packet.h	Mon Jul  5 21:48:27 1999
+++ plptools-0.4-mjg/ncpd/packet.h	Fri Oct  1 20:02:03 1999
@@ -34,6 +34,7 @@
 
 #define PKT_DEBUG_LOG  1
 #define PKT_DEBUG_DUMP 2
+#define PKT_DEBUG_HANDSHAKE 4
 
 class packet {
 	public:
@@ -63,7 +64,7 @@
 		int outLen;
 		int termLen;
 		int fd;
-		bool verbose;
+		short int verbose;
 		bool esc;
 		char *devname;
 		int baud;
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/socketchan.cc plptools-0.4-mjg/ncpd/socketchan.cc
--- plptools-0.4/ncpd/socketchan.cc	Mon Jul  5 21:48:27 1999
+++ plptools-0.4-mjg/ncpd/socketchan.cc	Fri Oct  1 20:35:44 1999
@@ -67,6 +67,57 @@
 	return connectName;
 }
 
+// NCP Command processing
+bool socketChan::
+ncpCommand(bufferStore & a)
+{
+cerr << "socketChan:: received NCP command (" << a << ")" << endl;
+	// str is guaranteed to begin with NCP$, and all NCP commands are
+	// greater than or equal to 8 characters in length.
+	const char *str = a.getString();
+	unsigned long len = a.getLen();
+	bool ok = false;
+
+	if (!strncmp(str+4, "INFO", 4)) {
+		// Send information discovered during the receipt of the
+		// NCON_MSG_NCP_INFO message.
+		a.init();
+		switch (ncpProtocolVersion()) {
+			case PV_SERIES_3:
+				a.addStringT("Series 3");
+				break;
+			case PV_SERIES_5:
+				a.addStringT("Series 5");
+				break;
+			default:
+				cerr << "ncpd: protocol version not known" << endl;
+				a.addStringT("Unknown!");
+				break;
+		}
+		skt->sendBufferStore(a);
+		ok = true;
+	} else if (!strncmp(str+4, "CONN", 4)) {
+		// Connect to a channel that was placed in 'pending' mode, by
+		// checking the channel table against the ID...
+		// DO ME LATER
+		ok = true;
+	} else if (!strncmp(str+4, "CHAL", 4)) {
+		// Challenge
+		// The idea is that the channel stays in 'secure' mode until a
+		// valid RESP is received
+		// DO ME LATER
+		ok = true;
+	} else if (!strncmp(str+4, "RESP", 4)) {
+		// Reponse - here is the plaintext as sent in the CHAL - the
+		// channel will only open up if this matches what ncpd has for
+		// the encrypted plaintext.
+		// DO ME LATER
+		ok = true;
+	}
+	return ok;
+}
+
+
 void socketChan::
 ncpConnectAck()
 {
@@ -91,6 +142,38 @@
 	if (connectName == 0) {
 		bufferStore a;
 		if (skt->getBufferStore(a, false) == 1) {
+			// A client has connected, and is announcing who it
+			// is...  e.g.  "SYS$RFSV.*"
+			//
+			// An NCP Channel can be in 'Control' or 'Data' mode.
+			// Initially, it is in 'Control' mode, and can accept
+			// certain commands.
+			//
+			// When a command is received that ncpd does not
+			// understand, this is assumed to be a request to
+			// connect to the remote service of that name, and enter
+			// 'data' mode.
+			//
+			// Later, there might be an explicit command to enter
+			// 'data' mode, and also a challenge-response protocol
+			// before any connection can be made.
+			//
+			// All commands begin with "NCP$".
+
+			// There is a magic process name called "NCP$INFO.*"
+			// which is announced by the rfsvfactory. This causes a
+			// response to be issued containing the NCP version
+			// number. The rfsvfactory will create the correct type
+			// of RFSV protocol handler, which will then announce
+			// itself. So, first time in here, we might get the
+			// NCP$INFO.*
+			if (a.getLen() > 8 && !strncmp(a.getString(), "NCP$", 4)) {
+				if (!ncpCommand(a))
+					cerr << "ncpd: command " << a << " unrecognised." << endl;
+				return;
+			}
+
+			// This isn't a command, it's a remote process. Connect.
 			connectName = strdup(a.getString());
 			ncpConnect();
 		}
diff -urN --exclude-from=plptools.exclude plptools-0.4/ncpd/socketchan.h plptools-0.4-mjg/ncpd/socketchan.h
--- plptools-0.4/ncpd/socketchan.h	Mon Jul  5 21:48:27 1999
+++ plptools-0.4-mjg/ncpd/socketchan.h	Fri Sep 24 20:34:30 1999
@@ -43,6 +43,8 @@
   bool isConnected() const;
   void socketPoll();
 private:
+  enum protocolVersionType { PV_SERIES_5 = 6, PV_SERIES_3 = 3 };
+  bool ncpCommand(bufferStore &a);
   ppsocket* skt;
   IOWatch &iow;
   char* connectName;
diff -urN --exclude-from=plptools.exclude plptools-0.4/plpftp/ftp.cc plptools-0.4-mjg/plpftp/ftp.cc
--- plptools-0.4/plpftp/ftp.cc	Mon Jul  5 21:40:18 1999
+++ plptools-0.4-mjg/plpftp/ftp.cc	Thu Oct 14 21:04:34 1999
@@ -59,8 +59,8 @@
 ftp::ftp()
 {
 	resetUnixPwd();
-	strcpy(psionDir, DEFAULT_DRIVE);
-	strcat(psionDir, DEFAULT_BASE_DIRECTORY);
+	strcpy(psionDir, DDRIVE);
+	strcat(psionDir, DBASEDIR);
 }
 
 ftp::~ftp()
@@ -147,7 +147,7 @@
 }
 
 int ftp::
-session(rfsv32 & a, int xargc, char **xargv)
+session(rfsv & a, int xargc, char **xargv)
 {
 	int argc;
 	char *argv[10];
@@ -199,6 +199,17 @@
 			strcat(f1, argv[1]);
 			if ((res = a.fgeteattr(f1, &attr, &size, &time)) != 0)
 				errprint(res, a);
+			else {
+				// never used to do this
+				char dateBuff[100];
+                               	struct tm *t;
+                               	t = localtime(&time);
+                               	strftime(dateBuff, 100, "%c", t);
+                               	cout << a.opAttr(attr);
+                               	cout << " " << dec << setw(10) << setfill(' ') << size;
+                               	cout << " " << dateBuff;
+				cout << " " << argv[1] << endl;
+			}
 			continue;
 		}
 		if (!strcmp(argv[0], "gattr") && (argc == 2)) {
@@ -207,8 +218,10 @@
 			strcat(f1, argv[1]);
 			if ((res = a.fgetattr(f1, &attr)) != 0)
 				errprint(res, a);
-			else
-				cout << hex << setw(4) << setfill('0') << attr << endl;
+			else {
+				cout << hex << setw(4) << setfill('0') << attr;
+				cout << " (" << a.opAttr(attr) << ")" << endl;
+			}
 			continue;
 		}
 		if (!strcmp(argv[0], "gtime")) {
@@ -287,7 +300,8 @@
 						if (vname != NULL) {
 							cout << (char) ('A' + i) << "     " <<
 							    hex << setw(4) << setfill('0') << vattr << " " <<
-							    setw(12) << setfill(' ') << setiosflags(ios::left) << vname << resetiosflags(ios::left) << dec << setw(9) <<
+							    setw(12) << setfill(' ') << setiosflags(ios::left) << 
+							    vname << resetiosflags(ios::left) << dec << setw(9) <<
 							    vtotal << setw(9) << vfree << setw(10) << vuniqueid << endl;
 							free(vname);
 						}
@@ -313,15 +327,7 @@
                                 	struct tm *t;
                                 	t = localtime(&date);
                                 	strftime(dateBuff, 100, "%c", t);
-                                	cout << ((attr & rfsv32::PSI_ATTR_DIRECTORY) ? "d" : "-");
-                                	cout << ((attr & rfsv32::PSI_ATTR_RONLY) ? "-" : "w");
-                                	cout << ((attr & rfsv32::PSI_ATTR_HIDDEN) ? "h" : "-");
-                                	cout << ((attr & rfsv32::PSI_ATTR_SYSTEM) ? "s" : "-");
-                                	cout << ((attr & rfsv32::PSI_ATTR_ARCHIVE) ? "a" : "-");
-                                	cout << ((attr & rfsv32::PSI_ATTR_VOLUME) ? "v" : "-");
-                                	cout << ((attr & rfsv32::PSI_ATTR_NORMAL) ? "n" : "-");
-                                	cout << ((attr & rfsv32::PSI_ATTR_TEMPORARY) ? "t" : "-");
-                                	cout << ((attr & rfsv32::PSI_ATTR_COMPRESSED) ? "c" : "-");
+                                	cout << a.opAttr(attr);
                                 	cout << " " << dec << setw(10) << setfill(' ') << size;
                                 	cout << " " << dateBuff;
 					cout << " " << s.getString(12) << endl;
@@ -342,8 +348,8 @@
 		}
 		if (!strcmp(argv[0], "cd")) {
 			if (argc == 1) {
-				strcpy(psionDir, DEFAULT_DRIVE);
-				strcat(psionDir, DEFAULT_BASE_DIRECTORY);
+				strcpy(psionDir, DDRIVE);
+				strcat(psionDir, DBASEDIR);
 			} else {
 				long tmp;
 				if (!strcmp(argv[1], "..")) {
@@ -371,8 +377,10 @@
 					strcat(f1,"\\");
 				if ((res = a.dircount(f1, &tmp)) == 0)
 					strcpy(psionDir, f1);
-				else
+				else {
+					errprint(res, a);
 					cerr << "Keeping original directory \"" << psionDir << "\"" << endl;
+				}
 			}
 			continue;
 		}
@@ -578,10 +586,11 @@
 }
 
 void ftp::
-errprint(long errcode, rfsv32 & a) {
+errprint(long errcode, rfsv & a) {
 	cerr << "Error: " << a.opErr(errcode) << endl;
 }
 
+// MJG: note, this isn't actually used anywhere
 int ftp::
 convertName(const char *orig, char *retVal)
 {
@@ -596,10 +605,10 @@
 	}
 	if (len == 0 || temp[1] != ':') {
 		// We can automatically supply a drive letter
-		strcpy(retVal, DEFAULT_DRIVE);
+		strcpy(retVal, DDRIVE);
 
 		if (len == 0 || temp[0] != '\\') {
-			strcat(retVal, DEFAULT_BASE_DIRECTORY);
+			strcat(retVal, DBASEDIR);
 		} else {
 			retVal[strlen(retVal) - 1] = 0;
 		}
@@ -616,21 +625,28 @@
 getCommand(int &argc, char **argv)
 {
 	int ws;
-	char *bp;
 
 	static char buf[1024];
 
 	buf[0] = 0; argc = 0;
 	while (!strlen(buf)) {
-		bp = readline("> ");
+#if HAVE_LIBREADLINE
+		char *bp = readline("> ");
 		if (!bp) {
 			strcpy(buf, "bye");
 			cout << buf << endl;
 		} else {
 			strcpy(buf, bp);
+#if HAVE_LIBHISTORY
 			add_history(buf);
+#endif
 			free(bp);
 		}
+#else
+		cout << "> ";
+		cout.flush();
+		cin.getline(buf, 1023);
+#endif
 	}
 	ws = 1;
 	for (char *p = buf; *p; p++)
diff -urN --exclude-from=plptools.exclude plptools-0.4/plpftp/ftp.h plptools-0.4-mjg/plpftp/ftp.h
--- plptools-0.4/plpftp/ftp.h	Mon Jul  5 21:40:18 1999
+++ plptools-0.4-mjg/plpftp/ftp.h	Tue Sep 21 21:03:49 1999
@@ -27,18 +27,15 @@
 
 #include "bool.h"
 
-class rfsv32;
+class rfsv;
 class bufferStore;
 class bufferArray;
 
-#define DEFAULT_DRIVE "C:"
-#define DEFAULT_BASE_DIRECTORY "\\"
-
 class ftp {
 	public:
 	ftp();
 	~ftp();
-	int session(rfsv32 & a, int xargc, char **xargv);
+	int session(rfsv & a, int xargc, char **xargv);
 
 	 private:
 	void getCommand(int &argc, char **argv);
@@ -48,8 +45,10 @@
 	void getUnixDir(bufferArray & files);
 	void resetUnixPwd();
 	void usage();
-	void errprint(long errcode, rfsv32 & a);
+	void errprint(long errcode, rfsv & a);
 	void cd(const char *source, const char *cdto, char *dest);
+
+	// MJG: note, this isn't actually used anywhere
 	int convertName(const char *orig, char *retVal);
 
 	char localDir[1024];
diff -urN --exclude-from=plptools.exclude plptools-0.4/plpftp/main.cc plptools-0.4-mjg/plpftp/main.cc
--- plptools-0.4/plpftp/main.cc	Mon Jul  5 21:40:18 1999
+++ plptools-0.4-mjg/plpftp/main.cc	Tue Oct 12 19:11:44 1999
@@ -31,6 +31,9 @@
 #include "defs.h"
 #include "bool.h"
 #include "ppsocket.h"
+#include "rfsv.h"
+#include "rfsvfactory.h"
+#include "rfsv16.h"
 #include "rfsv32.h"
 #include "ftp.h"
 #include "bufferstore.h"
@@ -48,6 +51,7 @@
 	cout << "PLPFTP Version " << VERSION;
 	cout << " Copyright (C) 1999  Philip Proudman" << endl;
 	cout << " Additions Copyright (C) 1999 Fritz Elfert <felfert@to.com>" << endl;
+	cout << "                   & (C) 1999 Matt Gumbley <matt@gumbley.demon.co.uk>" << endl;
 	cout << "PLP comes with ABSOLUTELY NO WARRANTY;" << endl;
 	cout << "This is free software, and you are welcome to redistribute it" << endl;
 	cout << "under GPL conditions; see the COPYING file in the distribution." << endl;
@@ -59,7 +63,7 @@
 main(int argc, char **argv)
 {
 	ppsocket *skt;
-	rfsv32 *a;
+	rfsv *a;
 	ftp f;
 	int status = 0;
 	sigset_t sigset;
@@ -80,10 +84,19 @@
 	if (argc < 2)
 		ftpHeader();
 	skt = new ppsocket();
-	skt->connect(NULL, sockNum);
-	a = new rfsv32(skt);
-	status = f.session(*a, argc, argv);
-
-	delete a;
+	if (!skt->connect(NULL, sockNum)) {
+		cout << "plpftp: could not connect to ncpd" << endl;
+		status = 1;
+	} else {
+		rfsvfactory *rf = new rfsvfactory(skt);
+		a = rf->create();
+		if (a != NULL) {
+			status = f.session(*a, argc, argv);
+			delete a;
+		} else {
+			cout << "plpftp: could not create rfsv object" << endl;
+			status = 1;
+		}
+	}
 	return status;
 }
diff -urN --exclude-from=plptools.exclude plptools-0.4/plpnfsd/main.cc plptools-0.4-mjg/plpnfsd/main.cc
--- plptools-0.4/plpnfsd/main.cc	Mon Jul  5 21:34:39 1999
+++ plptools-0.4-mjg/plpnfsd/main.cc	Tue Oct 19 20:34:48 1999
@@ -11,6 +11,9 @@
 
 #include "defs.h"
 #include "bool.h"
+#include "rfsv.h"
+#include "rfsvfactory.h"
+#include "rfsv16.h"
 #include "rfsv32.h"
 #include "bufferstore.h"
 #include "bufferarray.h"
@@ -19,7 +22,7 @@
 #include "rfsv_api.h"
 }
 
-static rfsv32 *a;
+static rfsv *a;
 
 long rfsv_isalive() {
 	return (a->getStatus() == 0);
@@ -82,6 +85,7 @@
 }
 
 long rfsv_read(char *buf, long offset, long len, long handle) {
+	// FIXME: this might break on RFSV16?
 	long ret = a->fseek(handle, offset, rfsv32::PSI_SEEK_SET);
 	if (ret >= 0)
 		ret = a->fread(handle, buf, len);
@@ -89,6 +93,7 @@
 }
 
 long rfsv_write(char *buf, long offset, long len, long handle) {
+	// FIXME: this might break on RFSV16?
 	long ret = a->fseek(handle, offset, rfsv32::PSI_SEEK_SET);
 	if (ret >= 0)
 		ret = a->fwrite(handle, buf, len);
@@ -101,6 +106,7 @@
 
 long rfsv_setsize(const char *name, long size) {
 	long ph;
+	// FIXME: this might break on RFSV16?
 	long ret = a->fopen(rfsv32::PSI_OMODE_READ_WRITE, name, ph);
 	if (!ret) {
 		ret = a->fsetsize(ph, size);
@@ -171,6 +177,7 @@
 	char *mdir = DDIR;
 	int sockNum = DPORT;
 	int verbose = 0;
+	int status = 0;
 
 	for (int i = 1; i < argc; i++) {
 		if (!strcmp(argv[i], "-p") && i + 1 < argc) {
@@ -187,10 +194,24 @@
 		} else
 			usage();
 	}
-
+cout << "hello from plpnfsd" << endl;
 	signal(SIGPIPE, SIG_IGN);
 	skt = new ppsocket();
-	skt->connect(NULL, sockNum);
-	a = new rfsv32(skt);
-	return mp_main(verbose, mdir, user);
+	if (!skt->connect(NULL, sockNum)) {
+		cout << "plpnfsd: could not connect to ncpd" << endl;
+		status = 1;
+	} else {
+		rfsvfactory *rf = new rfsvfactory(skt);
+		a = rf->create();
+		if (a != NULL) {
+cout << "seems ok so far" << endl;
+			status = mp_main(verbose, mdir, user);
+cout << "status is " << status << endl;
+			delete a;
+		} else {
+			cout << "plpnfsd: could not create rfsv object" << endl;
+			status = 1;
+		}
+	}
+	exit(status);
 }
diff -urN --exclude-from=plptools.exclude plptools-0.4/plpnfsd/mp_main.c plptools-0.4-mjg/plpnfsd/mp_main.c
--- plptools-0.4/plpnfsd/mp_main.c	Mon Jul  5 21:33:05 1999
+++ plptools-0.4-mjg/plpnfsd/mp_main.c	Fri Oct  1 19:53:19 1999
@@ -46,7 +46,8 @@
 	{0, 0}
 };
 
-#if defined(hpux) || defined(__SVR4) || defined(__sgi)
+#if defined(hpux) || defined(__SVR4) || defined(__sgi) 
+#ifndef sun  
 void
 usleep(usec)
 int usec;
@@ -57,7 +58,7 @@
 	t.tv_usec = (long) (usec % 1000000);
 	select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &t);
 }
-
+#endif
 #endif				/* hpux */
 
 int

