macssh/GUSI/include/GUSIDevice.h

382 lines
20 KiB
C++
Executable File

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % Project : GUSI - Grand Unified Socket Interface
// % File : GUSIDevice.nw - Devices
// % Author : Matthias Neeracher
// % Language : C++
// %
// % $Log$
// % Revision 1.1.1.1 2001/03/03 21:50:10 chombier
// % Initial import
// %
// % Revision 1.13 2000/06/12 04:22:30 neeri
// % Return values, not references
// %
// % Revision 1.12 2000/05/23 06:58:03 neeri
// % Improve formatting
// %
// % Revision 1.11 2000/03/15 07:22:06 neeri
// % Enforce alignment choices
// %
// % Revision 1.10 2000/03/06 06:30:30 neeri
// % Check for nonexistent device
// %
// % Revision 1.9 1999/08/26 05:45:01 neeri
// % Fixes for literate edition of source code
// %
// % Revision 1.8 1999/07/19 06:21:02 neeri
// % Add mkdir/rmdir, fix various file manager related bugs
// %
// % Revision 1.7 1999/05/29 06:26:42 neeri
// % Fixed header guards
// %
// % Revision 1.6 1999/03/17 09:05:07 neeri
// % Added GUSITimer, expanded docs
// %
// % Revision 1.5 1998/11/22 23:06:52 neeri
// % Releasing 2.0a4 in a hurry
// %
// % Revision 1.4 1998/10/25 11:37:38 neeri
// % More configuration hooks
// %
// % Revision 1.3 1998/10/11 16:45:13 neeri
// % Ready to release 2.0a2
// %
// % Revision 1.2 1998/08/01 21:28:57 neeri
// % Add directory operations
// %
// % Revision 1.1 1998/01/25 21:02:45 neeri
// % Engine implemented, except for signals & scheduling
// %
// % Revision 1.1 1996/12/16 02:12:40 neeri
// % TCP Sockets sort of work
// %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// \chapter{Devices}
//
// Similar to the creation of sockets, operations on files like opening or
// renaming them need to be dispatched to a variety of special cases (Most of
// them of the form "Dev:" preceded by a device name). Analogous to the
// [[GUSISocketFactory]] subclasses registered in a [[GUSISocketDomainRegistry]],
// we therefore have subclasses of [[GUSIDevice]] registered in a
// [[GUSIDeviceRegistry]], although the details of the two registries are
// quite different.
//
// During resolution of a file name, the name and information about it is passed
// around in a [[GUSIFileToken]].
//
// <GUSIDevice.h>=
#ifndef _GUSIDevice_
#define _GUSIDevice_
#ifdef GUSI_SOURCE
#include "GUSISocket.h"
#include "GUSIFileSpec.h"
#include <dirent.h>
#include <utime.h>
#include <ConditionalMacros.h>
#if PRAGMA_STRUCT_ALIGN
#pragma options align=native
#endif
// \section{Definition of [[GUSIFileToken]]}
//
// A [[GUSIFileToken]] consists of a pointer to the name as a C string, of a pointer
// to the [[GUSIDevice]] the token resolves to, and, if the token refers to a
// file name rather than a device name, a pointer to a [[GUSIFileSpec]]. Since
// depending on the call, different [[GUSIDevice]] subclasses may handle it, a
// request code has to be passed to the constructor, too.
//
// <Definition of class [[GUSIFileToken]]>=
class GUSIDevice;
class GUSIFileToken : public GUSIFileSpec {
public:
enum Request {
// \section{Operations on Devices}
//
// The [[open]] operation creates a new socket for the specified path or file
// specification.
//
// <Requests for [[GUSIFileToken]]>=
kWillOpen,
// [[remove]] deletes a path or file specification.
//
// <Requests for [[GUSIFileToken]]>=
kWillRemove,
// [[rename]] renames a path or file specification.
//
// <Requests for [[GUSIFileToken]]>=
kWillRename,
// [[stat]] gathers statistical data about a file or directory.
//
// <Requests for [[GUSIFileToken]]>=
kWillStat,
// [[chmod]] changes file modes, to the extent that this is meaningful on MacOS.
//
// <Requests for [[GUSIFileToken]]>=
kWillChmod,
// [[utime]] bumps a file's modification time.
//
// <Requests for [[GUSIFileToken]]>=
kWillUtime,
// [[access]] checks access permissions for a file.
//
// <Requests for [[GUSIFileToken]]>=
kWillAccess,
// [[mkdir]] creates a directory.
//
// <Requests for [[GUSIFileToken]]>=
kWillMkdir,
// [[rmdir]] deletes a directory.
//
// <Requests for [[GUSIFileToken]]>=
kWillRmdir,
// [[opendir]] opens a directory handle on the given directory.
//
// <Requests for [[GUSIFileToken]]>=
kWillOpendir,
// [[symlink]] creates a symbolic link to a file.
//
// <Requests for [[GUSIFileToken]]>=
kWillSymlink,
// [[readlink]] reads the contents of a symbolic link.
//
// <Requests for [[GUSIFileToken]]>=
kWillReadlink,
// [[fgetfileinfo]] and [[fsetfileinfo]] reads and set the type and creator
// code of a file.
//
// <Requests for [[GUSIFileToken]]>=
kWillGetfileinfo,
kWillSetfileinfo,
// [[faccess]] manipulates MPW properties of files.
//
// <Requests for [[GUSIFileToken]]>=
kWillFaccess,
kNoRequest
};
GUSIFileToken(const char * path, Request request, bool useAlias = false);
GUSIFileToken(const GUSIFileSpec & spec, Request request);
GUSIFileToken(short fRefNum, Request request);
bool IsFile() const { return fIsFile; }
bool IsDevice() const { return !fIsFile; }
Request WhichRequest() const { return fRequest; }
GUSIDevice * Device() const { return fDevice; }
const char * Path() const { return fPath; }
static bool StrFragEqual(const char * name, const char * frag);
enum StdStream {
kStdin,
kStdout,
kStderr,
kConsole,
kNoStdStream = -2
};
static StdStream StrStdStream(const char * name);
private:
GUSIDevice * fDevice;
const char * fPath;
bool fIsFile;
Request fRequest;
};
// \section{Definition of [[GUSIDirectory]]}
//
// [[GUSIDirectory]] is a directory handle to iterate over all entries in a
// directory.
//
// <Definition of class [[GUSIDirectory]]>=
class GUSIDirectory {
public:
virtual ~GUSIDirectory() {}
virtual dirent * readdir() = 0;
virtual long telldir() = 0;
virtual void seekdir(long pos) = 0;
virtual void rewinddir() = 0;
protected:
friend class GUSIDevice;
GUSIDirectory() {}
};
// \section{Definition of [[GUSIDeviceRegistry]]}
//
// The [[GUSIDeviceRegistry]] is a singleton class registering all socket
// domains.
//
// <Definition of class [[GUSIDeviceRegistry]]>=
class GUSIDeviceRegistry {
public:
// The only instance of [[GUSIDeviceRegistry]] is, as usual, obtained by calling
// [[Instance]].
//
// <Creation of [[GUSIDeviceRegistry]]>=
static GUSIDeviceRegistry * Instance();
// <Operations for [[GUSIDeviceRegistry]]>=
GUSISocket * open(const char * path, int flags);
// <Operations for [[GUSIDeviceRegistry]]>=
int remove(const char * path);
// <Operations for [[GUSIDeviceRegistry]]>=
int rename(const char * oldname, const char * newname);
// <Operations for [[GUSIDeviceRegistry]]>=
int stat(const char * path, struct stat * buf, bool useAlias);
// <Operations for [[GUSIDeviceRegistry]]>=
int chmod(const char * path, mode_t mode);
// <Operations for [[GUSIDeviceRegistry]]>=
int utime(const char * path, const utimbuf * times);
// <Operations for [[GUSIDeviceRegistry]]>=
int access(const char * path, int mode);
// <Operations for [[GUSIDeviceRegistry]]>=
int mkdir(const char * path);
// <Operations for [[GUSIDeviceRegistry]]>=
int rmdir(const char * path);
// <Operations for [[GUSIDeviceRegistry]]>=
GUSIDirectory * opendir(const char * path);
// <Operations for [[GUSIDeviceRegistry]]>=
int symlink(const char * target, const char * newlink);
// <Operations for [[GUSIDeviceRegistry]]>=
int readlink(const char * path, char * buf, int bufsize);
// <Operations for [[GUSIDeviceRegistry]]>=
int fgetfileinfo(const char * path, OSType * creator, OSType * type);
int fsetfileinfo(const char * path, OSType creator, OSType type);
// <Operations for [[GUSIDeviceRegistry]]>=
int faccess(const char * path, unsigned * cmd, void * arg);
// [[AddDevice]] and [[RemoveDevice]] add and remove a [[GUSIDevice]].
//
// <Registration interface of [[GUSIDeviceRegistry]]>=
void AddDevice(GUSIDevice * device);
void RemoveDevice(GUSIDevice * device);
// It is convenient to define iterators to iterate across all devices.
//
// <Iterator on [[GUSIDeviceRegistry]]>=
class iterator;
iterator begin();
iterator end();
protected:
// On construction, a [[GUSIFileToken]] looks up the appropriate device in the
// [[GUSIDeviceRegistry]].
//
// <Looking up a device in the [[GUSIDeviceRegistry]]>=
friend class GUSIFileToken;
GUSIDevice * Lookup(GUSIFileToken & file);
private:
// <Privatissima of [[GUSIDeviceRegistry]]>=
static GUSIDeviceRegistry * sInstance;
// Devices are stored in a linked list. On creation of the registry, it immediately
// registers the instance for plain Macintosh file sockets, to which pretty much all
// operations default. This device will never refuse any request.
//
// <Privatissima of [[GUSIDeviceRegistry]]>=
GUSIDevice * fFirstDevice;
GUSIDeviceRegistry();
};
// \section{Definition of [[GUSIDevice]]}
//
// [[GUSIDevice]] consists of a few maintenance functions and the
// device operations. The request dispatcher first calls [[Want]] for
// each candidate device and as soon as it's successful, calls the specific
// operation. Devices are kept in a linked list by the [[GUSIDeviceRegistry]].
//
// <Definition of class [[GUSIDevice]]>=
class GUSIDevice {
public:
virtual bool Want(GUSIFileToken & file);
// <Operations for [[GUSIDevice]]>=
virtual GUSISocket * open(GUSIFileToken & file, int flags);
// <Operations for [[GUSIDevice]]>=
virtual int remove(GUSIFileToken & file);
// <Operations for [[GUSIDevice]]>=
virtual int rename(GUSIFileToken & from, const char * newname);
// <Operations for [[GUSIDevice]]>=
virtual int stat(GUSIFileToken & file, struct stat * buf);
// <Operations for [[GUSIDevice]]>=
virtual int chmod(GUSIFileToken & file, mode_t mode);
// <Operations for [[GUSIDevice]]>=
virtual int utime(GUSIFileToken & file, const utimbuf * times);
// <Operations for [[GUSIDevice]]>=
virtual int access(GUSIFileToken & file, int mode);
// <Operations for [[GUSIDevice]]>=
virtual int mkdir(GUSIFileToken & file);
// <Operations for [[GUSIDevice]]>=
virtual int rmdir(GUSIFileToken & file);
// <Operations for [[GUSIDevice]]>=
virtual GUSIDirectory * opendir(GUSIFileToken & file);
// <Operations for [[GUSIDevice]]>=
virtual int symlink(GUSIFileToken & to, const char * newlink);
// <Operations for [[GUSIDevice]]>=
virtual int readlink(GUSIFileToken & link, char * buf, int bufsize);
// <Operations for [[GUSIDevice]]>=
virtual int fgetfileinfo(GUSIFileToken & file, OSType * creator, OSType * type);
virtual int fsetfileinfo(GUSIFileToken & file, OSType creator, OSType type);
// <Operations for [[GUSIDevice]]>=
virtual int faccess(GUSIFileToken & file, unsigned * cmd, void * arg);
protected:
friend class GUSIDeviceRegistry;
friend class GUSIDeviceRegistry::iterator;
GUSIDevice() : fNextDevice(nil) {}
virtual ~GUSIDevice() {}
GUSIDevice * fNextDevice;
};
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#endif
// \section{Implementation of [[GUSIDeviceRegistry]]}
//
//
// <Definition of [[GUSISetupDevices]] hook>=
extern "C" void GUSISetupDevices();
// <Inline member functions for class [[GUSIDeviceRegistry]]>=
inline GUSIDeviceRegistry * GUSIDeviceRegistry::Instance()
{
if (!sInstance) {
sInstance = new GUSIDeviceRegistry();
GUSISetupDevices();
}
return sInstance;
}
// The [[GUSIDeviceRegistry]] forward iterator is simple.
//
// <Inline member functions for class [[GUSIDeviceRegistry]]>=
class GUSIDeviceRegistry::iterator {
public:
iterator(GUSIDevice * device = 0) : fDevice(device) {}
GUSIDeviceRegistry::iterator & operator++()
{ fDevice = fDevice->fNextDevice; return *this; }
GUSIDeviceRegistry::iterator operator++(int)
{ GUSIDeviceRegistry::iterator old(*this); fDevice = fDevice->fNextDevice; return old; }
bool operator==(const GUSIDeviceRegistry::iterator other) const
{ return fDevice==other.fDevice; }
GUSIDevice & operator*() { return *fDevice; }
GUSIDevice * operator->() { return fDevice; }
private:
GUSIDevice * fDevice;
};
inline GUSIDeviceRegistry::iterator GUSIDeviceRegistry::begin()
{
return GUSIDeviceRegistry::iterator(fFirstDevice);
}
inline GUSIDeviceRegistry::iterator GUSIDeviceRegistry::end()
{
return GUSIDeviceRegistry::iterator();
}
#endif /* GUSI_SOURCE */
#endif /* _GUSIDevice_ */