macssh/GUSI/include/GUSIFileSpec.h

600 lines
28 KiB
C++
Executable File

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % Project : GUSI - Grand Unified Socket Interface
// % File : GUSIFileSpec.nw - File specifications
// % Author : Matthias Neeracher
// % Language : C++
// %
// % $Log$
// % Revision 1.19 2001/04/16 01:50:02 neeri
// % Fix GUSIFSpGetCatInfo (MacPerl bug #232702); Fix parsing of absolute paths with embedded aliases.
// %
// % Revision 1.18 2001/04/01 07:40:15 neeri
// % Fix :::paths (MacPerl Bug #409940)
// %
// % Revision 1.17 2001/03/20 02:34:22 neeri
// % Commented out false friends
// %
// % Revision 1.16 2001/03/09 09:20:53 neeri
// % Fixed major bugs in relative path generation
// %
// % Revision 1.15 2001/01/17 08:46:45 neeri
// % Get rid of excess directory seperators when name is empty
// %
// % Revision 1.14 2000/12/23 06:10:17 neeri
// % Add GUSIFSpTouchFolder, GUSIFSpGetCatInfo; copy error in copy constructor
// %
// % Revision 1.13 2000/10/16 03:59:36 neeri
// % Make FSSpec a member of GUSIFileSpec instead of a base class
// %
// % Revision 1.12 2000/06/12 04:20:58 neeri
// % Introduce GUSI_*printf
// %
// % Revision 1.11 2000/05/23 07:00:00 neeri
// % Improve formatting
// %
// % Revision 1.10 2000/03/15 07:16:08 neeri
// % Fix path construction, temp name bugs
// %
// % Revision 1.9 2000/03/06 06:34:11 neeri
// % Added C FSSpec convenience calls
// %
// % Revision 1.8 1999/08/26 05:45:02 neeri
// % Fixes for literate edition of source code
// %
// % Revision 1.7 1999/07/19 06:21:02 neeri
// % Add mkdir/rmdir, fix various file manager related bugs
// %
// % Revision 1.6 1999/06/28 06:00:53 neeri
// % add support for generating temp names from basename
// %
// % Revision 1.5 1999/05/30 02:16:53 neeri
// % Cleaner definition of GUSICatInfo
// %
// % Revision 1.4 1999/03/17 09:05:07 neeri
// % Added GUSITimer, expanded docs
// %
// % Revision 1.3 1998/10/11 16:45:15 neeri
// % Ready to release 2.0a2
// %
// % Revision 1.2 1998/08/01 21:32:04 neeri
// % About ready for 2.0a1
// %
// % Revision 1.1 1998/01/25 21:02:46 neeri
// % Engine implemented, except for signals & scheduling
// %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// \chapter{File specifications}
//
// While the Macintosh toolbox has a convenient type [[FSSpec]] to pass to
// file system routines, it lacks manipulation functions. We provide them here.
// This module has been known under a different name and with different data types
// in GUSI 1.
//
// <GUSIFileSpec.h>=
#ifndef _GUSIFileSpec_
#define _GUSIFileSpec_
#include <MacTypes.h>
#include <Files.h>
#include <Folders.h>
#include <string.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
// \section{C Interfaces to [[GUSIFileSpec]]}
//
// Many of the API routines defined here are useful to C code and not too hard to make accessible,
// even though I prefer the C++ versions. As opposed to GUSI 1, we stick to our namespace now.
//
// <Plain C interfaces to [[GUSIFileSpec]]>=
/*
* Construct a FSSpec from...
*/
/* ... the refNum of an open file. */
OSErr GUSIFRefNum2FSp(short fRefNum, FSSpec * desc);
/* ... a working directory & file name. */
OSErr GUSIWD2FSp(short wd, ConstStr31Param name, FSSpec * desc);
/* ... a path name. */
OSErr GUSIPath2FSp(const char * path, FSSpec * desc);
/* ... a special object. */
OSErr GUSISpecial2FSp(OSType object, short vol, FSSpec * desc);
/* ... a temporary file path. */
OSErr GUSIMakeTempFSp(short vol, long dirID, FSSpec * desc);
/*
* Convert a FSSpec into...
*/
/* ... a full path name. */
char * GUSIFSp2FullPath(const FSSpec * desc);
/* ... a relative path name if possible, full if not */
char * GUSIFSp2RelPath(const FSSpec * desc);
/* ... a path relative to the specified directory */
char * GUSIFSp2DirRelPath(const FSSpec * desc, const FSSpec * dir);
/* ... an GUSI-=specific ASCII encoding. */
char * GUSIFSp2Encoding(const FSSpec * desc);
/*
* Construct FSSpec of...
*/
/* ... (vRefNum, parID) */
OSErr GUSIFSpUp(FSSpec * desc);
/* ... file (name) in directory denoted by desc */
OSErr GUSIFSpDown(FSSpec * desc, ConstStr31Param name);
/* ... of nth file in directory denoted by (vRefNum, parID) */
OSErr GUSIFSpIndex(FSSpec * desc, short n);
/* Resolve if alias file */
OSErr GUSIFSpResolve(FSSpec * spec);
/* Touch folder containing the object */
OSErr GUSIFSpTouchFolder(const FSSpec * spec);
/* Get catalog information (after resolving leaf aliases) */
OSErr GUSIFSpGetCatInfo(FSSpec * spec, CInfoPBRec * info);
__END_DECLS
#ifdef GUSI_SOURCE
#include "GUSIBasics.h"
#include "GUSIContext.h"
#include <ConditionalMacros.h>
#if PRAGMA_STRUCT_ALIGN
#pragma options align=native
#endif
// \section{Definition of [[GUSICatInfo]]}
//
// [[GUSICatInfo]] is a wrapper for [[CInfoPBRec]]. Since the latter is a [[union]], we cannot
// inherit from it.
//
// <Definition of class [[GUSICatInfo]]>=
class GUSICatInfo {
CInfoPBRec fInfo;
public:
bool IsFile() const;
bool IsAlias() const;
bool DirIsExported() const;
bool DirIsMounted() const;
bool DirIsShared() const;
bool HasRdPerm() const;
bool HasWrPerm() const;
bool Locked() const;
CInfoPBRec & Info() { return fInfo; }
operator CInfoPBRec &() { return fInfo; }
struct HFileInfo & FileInfo() { return fInfo.hFileInfo; }
struct DirInfo & DirInfo() { return fInfo.dirInfo; }
struct FInfo & FInfo() { return fInfo.hFileInfo.ioFlFndrInfo; }
struct FXInfo & FXInfo() { return fInfo.hFileInfo.ioFlXFndrInfo; }
const CInfoPBRec & Info() const { return fInfo; }
operator const CInfoPBRec &() const { return fInfo; }
const struct HFileInfo & FileInfo() const{ return fInfo.hFileInfo; }
const struct DirInfo & DirInfo() const { return fInfo.dirInfo; }
const struct FInfo & FInfo() const { return fInfo.hFileInfo.ioFlFndrInfo; }
const struct FXInfo & FXInfo() const { return fInfo.hFileInfo.ioFlXFndrInfo; }
};
// \section{Definition of [[GUSIFileSpec]]}
//
// A [[GUSIFileSpec]] is a manipulation friendly version of an [[FSSpec]]. Due to
// conversion operators, a [[GUSIFileSpec]] can be used whereever a [[const FSSpec]]
// is specified. For any long term storage, [[FSSpec]] should be used rather than the
// much bulkier [[GUSIFileSpec]].
//
// <Definition of class [[GUSIFileSpec]]>=
class GUSIFileSpec {
public:
// A [[GUSIFileSpec]] can be constructed in lots of different ways. Most of the
// constructors have a final argument [[useAlias]] that, when [[true]], suppresses
// resolution of leaf aliases.
//
// First, two trivial cases: The default constructor and the copy constructor, which
// do exactly what you'd expect them to do.
//
// <Constructors for [[GUSIFileSpec]]>=
GUSIFileSpec() {}
GUSIFileSpec(const GUSIFileSpec & spec);
// The [[FSSpec]] conversion is almost a copy constructor, but by default resolves
// leaf aliases.
//
// <Constructors for [[GUSIFileSpec]]>=
GUSIFileSpec(const FSSpec & spec, bool useAlias = false);
// A number of convenience constructors let you construct a [[GUSIFileSpec]] from
// various components.
//
// <Constructors for [[GUSIFileSpec]]>=
// Construct from volume reference number, directory ID & file name
GUSIFileSpec(short vRefNum, long parID, ConstStr31Param name, bool useAlias = false);
// Construct from working directory & file name
GUSIFileSpec(short wd, ConstStr31Param name, bool useAlias = false);
// Construct from full or relative path
GUSIFileSpec(const char * path, bool useAlias = false);
// Construct from open file reference number
explicit GUSIFileSpec(short fRefNum);
// Finally, there is a constructor for constructing a [[GUSIFileSpec]] for one of
// the folders obtainable with [[FindFolder]].
//
// <Constructors for [[GUSIFileSpec]]>=
GUSIFileSpec(OSType object, short vol = kOnSystemDisk);
// All [[GUSIFileSpecs]] have an error variable from which the last error
// is available.
//
// <Error handling in [[GUSIFileSpec]]>=
OSErr Error() const;
operator void*() const;
bool operator!() const;
// While earlier versions of GUSI maintained a notion of "current directory" that
// was independent of the HFS default directory, there is no such distinction anymore
// in GUSI 2. [[SetDefaultDirectory]] sets the default directory to [[vRefNum, dirID]].
// [[GetDefaultDirectory]] sets [[vRefNum,dirID]] to the default directory. Neither
// routine affects the [[name]]. [[GetVolume]] gets the named or indexed volume.
//
// <Default directory handling in [[GUSIFileSpec]]>=
OSErr SetDefaultDirectory();
OSErr GetDefaultDirectory();
OSErr GetVolume(short index = -1);
// Conversions of [[GUSIFileSpec]] to [[FSSpec]] values and references is, of course,
// simple. Pointers are a bit trickier; we define an custom [[operator&]] and two
// smart pointer classes. [[operator->]], however, is simpler.
//
// <Converting a [[GUSIFileSpec]] to a [[FSSpec]]>=
operator const FSSpec &() const;
class pointer {
public:
pointer(GUSIFileSpec * ptr);
operator GUSIFileSpec *() const;
operator const FSSpec *() const;
private:
GUSIFileSpec * ptr;
};
pointer operator&();
class const_pointer {
public:
const_pointer(const GUSIFileSpec * ptr);
operator const GUSIFileSpec *() const;
operator const FSSpec *() const;
private:
const GUSIFileSpec * ptr;
};
const_pointer operator&() const;
const FSSpec * operator->() const;
friend class pointer;
friend class const_pointer;
// Each [[GUSIFileSpec]] has an implicit [[GUSICatInfo]] record associated with it.
// [[CatInfo]] calls [[GetCatInfo]] if the record is not already valid and
// returns a pointer to the record. [[DirInfo]] replaces the [[GUSIFileSpec]] by
// the specification of its parent directory and returns a pointer to information
// about the parent. Both calls return a [[nil]] pointer if the object does not exist.
// If all you are interested in is whether the object exists, call [[Exists]].
//
// <Getting the [[GUSICatInfo]] for a [[GUSIFileSpec]]>=
const GUSICatInfo * CatInfo(short index);
const GUSICatInfo * DirInfo();
const GUSICatInfo * CatInfo() const;
bool Exists() const;
// In many POSIXish contexts, it's necessary to specify path names with C string.
// Although this practice is dangerous on a Mac, we of course have to conform to
// it. The basic operations are getting a full path and getting a path relative to
// a directory (the current directory by default).
//
// <Getting path names from a [[GUSIFileSpec]]>=
char * FullPath() const;
char * RelativePath() const;
char * RelativePath(const FSSpec & dir) const;
// If the path ultimately is going to flow back into a GUSI routine again, it is
// possible to simply encode the [[GUSIFileSpec]] in ASCII. This is fast and reliable,
// but you should of course not employ it with any non-GUSI destination routine and
// should never store such a part across a reboot. The standard [[GUSIFileSpec]]
// constructors for paths will accept encoded paths.
//
// The encoding is defined as:
//
// \begin{itemize}
// \item 1 byte: DC1 (ASCII 0x11)
// \item 4 bytes: Volume reference number in zero-padded hex
// \item 8 bytes: Directory ID in zero-padded hex
// \item n bytes: Partial pathname, starting with ':'
// \end{itemize}
//
// <Getting path names from a [[GUSIFileSpec]]>=
char * EncodedPath() const;
// Most aliases are resolved implicitly, but occasionally you may want to do
// explicit resolution. [[Resolve]] resolves an alias. If [[gently]] is set,
// nonexistent target files of aliases don't cause an error to be returned.
//
// <Alias resolution for a [[GUSIFileSpec]]>=
OSErr Resolve(bool gently = true);
// [[AliasPath]] returns the path an alias points to without mounting any volumes.
//
// <Alias resolution for a [[GUSIFileSpec]]>=
char * AliasPath() const;
// A major feature of the [[GUSIFileSpec]] class is the set of operators for
// manipulating a file specification.
//
// [[operator--]] replaces a file specification with the directory specification of
// its parent.
//
// <Manipulating a [[GUSIFileSpec]]>=
GUSIFileSpec & operator--();
// [[operator++]] sets the [[dirID]] of a directory specification to the ID of the
// directory.
//
// <Manipulating a [[GUSIFileSpec]]>=
GUSIFileSpec & operator++();
// The two versions of [[operator+=]], which internally both call [[AddPathComponent]],
// replace a directory specification with the specification
//
// <Manipulating a [[GUSIFileSpec]]>=
GUSIFileSpec & AddPathComponent(const char * name, int length, bool fullSpec);
GUSIFileSpec & operator+=(ConstStr31Param name);
GUSIFileSpec & operator+=(const char * name);
// [[operator+]] provides a non-destructive variant of [[operator+=]].
//
// <Manipulating a [[GUSIFileSpec]]>=
//
// These don't need access to the GUSIFileSpec internals
//
// friend GUSIFileSpec operator+(const FSSpec & spec, ConstStr31Param name);
// friend GUSIFileSpec operator+(const FSSpec & spec, const char * name);
// Array access replaces the file specification with the [[index]]th object in the
// {\em parent directory} of the specification (allowing the same specification to
// be reused for reading a directory).
//
// <Manipulating a [[GUSIFileSpec]]>=
GUSIFileSpec & operator[](short index);
// To manipulate the fields of the file specification directly, there is a procedural
// interface.
//
// <Manipulating a [[GUSIFileSpec]]>=
void SetVRef(short vref);
void SetParID(long parid);
void SetName(ConstStr63Param nam);
void SetName(const char * nam);
// For some modifications to propagate quickly, the surrounding folder needs to
// have its date modified.
//
// <Manipulating a [[GUSIFileSpec]]>=
OSErr TouchFolder();
// Two [[GUSIFileSpecs]] can be compared for (in)equality.
//
// <Comparing two [[GUSIFileSpec]] objects>=
friend bool operator==(const GUSIFileSpec & one, const GUSIFileSpec & other);
// [[IsParentOf]] determines whether the object specifies a parent directory of
// [[other]].
//
// <Comparing two [[GUSIFileSpec]] objects>=
bool IsParentOf(const GUSIFileSpec & other) const;
protected:
// \section{Implementation of [[GUSIFileSpec]]}
//
// [[sError]] contains the error code for failed calls. [[Error]] returns the value.
//
// <Privatissima of [[GUSIFileSpec]]>=
mutable OSErr fError;
// For path name constructions, a sometimes large scratch area is needed which is
// maintained in [[sScratch]]. A scratch request preserves a preexisting scratch area
// at the {\em end} of the new area if [[extend]] is nonzero.
//
// <Privatissima of [[GUSIFileSpec]]>=
static char * sScratch;
static long sScratchSize;
static char * CScratch(bool extend = false);
static StringPtr PScratch();
// A [[GUSIFileSpec]] has a [[GUSICatInfo]] embedded and a flag [[fValidInfo]] indicating
// that it is valid.
//
// <Privatissima of [[GUSIFileSpec]]>=
FSSpec fSpec;
GUSIIOPBWrapper<GUSICatInfo> fInfo;
bool fValidInfo;
// For convenience, we define a fictivous parent directory of all volumes.
//
// <Privatissima of [[GUSIFileSpec]]>=
enum { ROOT_MAGIC_COOKIE = 666 };
// Each accumulation step is preformed in [[PrependPathComponent]].
//
// <Privatissima of [[GUSIFileSpec]]>=
OSErr PrependPathComponent(char *&path, ConstStr63Param component, bool colon) const;
};
// A [[GUSITempFileSpec]] is just a [[GUSIFileSpec]] with constructors to construct
// filenames for temporary files.
//
// <Definition of class [[GUSIFileSpec]]>=
class GUSITempFileSpec : public GUSIFileSpec {
public:
GUSITempFileSpec(short vRefNum = kOnSystemDisk);
GUSITempFileSpec(short vRefNum, long parID);
GUSITempFileSpec(ConstStr31Param basename);
GUSITempFileSpec(short vRefNum, ConstStr31Param basename);
GUSITempFileSpec(short vRefNum, long parID, ConstStr31Param basename);
private:
void TempName();
void TempName(ConstStr31Param basename);
static int sID;
};
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#endif
// \section{Implementation of [[GUSICatInfo]]}
//
// All of the member functions for [[GUSICatInfo]] are inline.
//
// <Inline member functions for class [[GUSICatInfo]]>=
inline bool GUSICatInfo::IsFile() const
{
return !(DirInfo().ioFlAttrib & 0x10);
}
inline bool GUSICatInfo::IsAlias() const
{
return
!(FileInfo().ioFlAttrib & 0x10) &&
(FInfo().fdFlags & (1 << 15));
}
inline bool GUSICatInfo::DirIsExported() const
{
return (FileInfo().ioFlAttrib & 0x20) != 0;
}
inline bool GUSICatInfo::DirIsMounted() const
{
return (FileInfo().ioFlAttrib & 0x08) != 0;
}
inline bool GUSICatInfo::DirIsShared() const
{
return (FileInfo().ioFlAttrib & 0x04) != 0;
}
inline bool GUSICatInfo::HasRdPerm() const
{
return !(DirInfo().ioACUser & 0x02) != 0;
}
inline bool GUSICatInfo::HasWrPerm() const
{
return !(DirInfo().ioACUser & 0x04) != 0;
}
inline bool GUSICatInfo::Locked() const
{
return (FileInfo().ioFlAttrib & 0x11) == 0x01;
}
// <Inline member functions for class [[GUSIFileSpec]]>=
inline OSErr GUSIFileSpec::Error() const
{
return fError;
}
inline GUSIFileSpec::operator void*() const
{
return (void *)!fError;
}
inline bool GUSIFileSpec::operator!() const
{
return fError!=0;
}
// <Inline member functions for class [[GUSIFileSpec]]>=
inline StringPtr GUSIFileSpec::PScratch()
{
return (StringPtr) CScratch();
}
// <Inline member functions for class [[GUSIFileSpec]]>=
inline OSErr GUSIFileSpec::SetDefaultDirectory()
{
return fError = HSetVol(nil, fSpec.vRefNum, fSpec.parID);
}
inline OSErr GUSIFileSpec::GetDefaultDirectory()
{
fSpec.name[0] = 0;
fValidInfo = false;
return fError = HGetVol(nil, &fSpec.vRefNum, &fSpec.parID);
}
// [[operator+=]] and [[operator+]] are merely wrappers around [[AddPathComponent]].
//
// <Inline member functions for class [[GUSIFileSpec]]>=
inline GUSIFileSpec & GUSIFileSpec::operator+=(ConstStr31Param name)
{
return AddPathComponent((char *) name+1, name[0], true);
}
inline GUSIFileSpec & GUSIFileSpec::operator+=(const char * name)
{
return AddPathComponent(name, strlen(name), true);
}
// The other variations of the call are simple.
//
// <Inline member functions for class [[GUSIFileSpec]]>=
inline const GUSICatInfo * GUSIFileSpec::CatInfo() const
{
return const_cast<GUSIFileSpec *>(this)->CatInfo(0);
}
inline const GUSICatInfo * GUSIFileSpec::DirInfo()
{
if (CatInfo(-1)) {
fSpec.parID = fInfo->DirInfo().ioDrParID;
fValidInfo = true;
return &fInfo.fPB;
} else
return nil;
}
inline bool GUSIFileSpec::Exists() const
{
return CatInfo() != nil;
}
// Reference conversion is straightforward, as is [[operator->]].
//
// <Inline member functions for class [[GUSIFileSpec]]>=
inline GUSIFileSpec::operator const FSSpec &() const
{
return fSpec;
}
inline const FSSpec * GUSIFileSpec::operator->() const
{
return &fSpec;
}
// Pointers, however, are a trickier issue, as they might be used either as a
// [[GUSIFileSpec *]] or as an [[FSSpec *]].
//
// <Inline member functions for class [[GUSIFileSpec]]>=
inline GUSIFileSpec::const_pointer::const_pointer(const GUSIFileSpec * ptr)
: ptr(ptr)
{
}
inline GUSIFileSpec::const_pointer::operator const GUSIFileSpec *() const
{
return ptr;
}
inline GUSIFileSpec::const_pointer::operator const FSSpec *() const
{
return &ptr->fSpec;
}
inline GUSIFileSpec::const_pointer GUSIFileSpec::operator&() const
{
return const_pointer(this);
}
// [[GUSIFileSpec::pointer]] is the non-constant equivalent to [[GUSIFileSpec::const_pointer]].
//
// <Inline member functions for class [[GUSIFileSpec]]>=
inline GUSIFileSpec::pointer::pointer(GUSIFileSpec * ptr)
: ptr(ptr)
{
}
inline GUSIFileSpec::pointer::operator GUSIFileSpec *() const
{
return ptr;
}
inline GUSIFileSpec::pointer::operator const FSSpec *() const
{
return &ptr->fSpec;
}
inline GUSIFileSpec::pointer GUSIFileSpec::operator&()
{
return pointer(this);
}
#endif /* GUSI_SOURCE */
#endif /* GUSIFileSpec */