// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // % 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. // // = #ifndef _GUSIFileSpec_ #define _GUSIFileSpec_ #include #include #include #include #include __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. // // = /* * 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 #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. // // = 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]]. // // = 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. // // = GUSIFileSpec() {} GUSIFileSpec(const GUSIFileSpec & spec); // The [[FSSpec]] conversion is almost a copy constructor, but by default resolves // leaf aliases. // // = GUSIFileSpec(const FSSpec & spec, bool useAlias = false); // A number of convenience constructors let you construct a [[GUSIFileSpec]] from // various components. // // = // 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]]. // // = GUSIFileSpec(OSType object, short vol = kOnSystemDisk); // All [[GUSIFileSpecs]] have an error variable from which the last error // is available. // // = 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. // // = 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. // // = 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]]. // // = 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). // // = 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} // // = 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. // // = OSErr Resolve(bool gently = true); // [[AliasPath]] returns the path an alias points to without mounting any volumes. // // = 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. // // = GUSIFileSpec & operator--(); // [[operator++]] sets the [[dirID]] of a directory specification to the ID of the // directory. // // = GUSIFileSpec & operator++(); // The two versions of [[operator+=]], which internally both call [[AddPathComponent]], // replace a directory specification with the specification // // = 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+=]]. // // = // // 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). // // = GUSIFileSpec & operator[](short index); // To manipulate the fields of the file specification directly, there is a procedural // interface. // // = 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. // // = OSErr TouchFolder(); // Two [[GUSIFileSpecs]] can be compared for (in)equality. // // = friend bool operator==(const GUSIFileSpec & one, const GUSIFileSpec & other); // [[IsParentOf]] determines whether the object specifies a parent directory of // [[other]]. // // = bool IsParentOf(const GUSIFileSpec & other) const; protected: // \section{Implementation of [[GUSIFileSpec]]} // // [[sError]] contains the error code for failed calls. [[Error]] returns the value. // // = 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. // // = 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. // // = FSSpec fSpec; GUSIIOPBWrapper fInfo; bool fValidInfo; // For convenience, we define a fictivous parent directory of all volumes. // // = enum { ROOT_MAGIC_COOKIE = 666 }; // Each accumulation step is preformed in [[PrependPathComponent]]. // // = OSErr PrependPathComponent(char *&path, ConstStr63Param component, bool colon) const; }; // A [[GUSITempFileSpec]] is just a [[GUSIFileSpec]] with constructors to construct // filenames for temporary files. // // = 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 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 OSErr GUSIFileSpec::Error() const { return fError; } inline GUSIFileSpec::operator void*() const { return (void *)!fError; } inline bool GUSIFileSpec::operator!() const { return fError!=0; } // = inline StringPtr GUSIFileSpec::PScratch() { return (StringPtr) CScratch(); } // = 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 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 const GUSICatInfo * GUSIFileSpec::CatInfo() const { return const_cast(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 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 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 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 */