PFSUXImpl.mesa
Copyright Ó 1987, 1988, 1989, 1990, 1991, 1992, 1993 by Xerox Corporation. All rights reserved.
Demers, March 16, 1988 11:04:54 am PST
Bill Jackson (bj) May 31, 1989 10:03:04 pm PDT
Carl Hauser, July 26, 1989 10:20:51 am PDT
Doug Wyatt, February 5, 1990 1:40:41 pm PST
Chauser, February 3, 1993 3:11 pm PST
Willie-s, February 20, 1992 4:12 pm PST
Wade, May 3, 1991 5:27 pm PDT
Michael Plass, January 29, 1992 10:56 am PST
Christian Jacobi, March 31, 1993 3:29 pm PST
DIRECTORY
Arpa USING [Address, MyAddress],
Basics16,
BasicTime USING [GetClockPulses, GMT, Now, nullGMT, Pack, Period, TimeNotKnown],
Convert USING [RopeFromCard, CardFromRope],
CStrings USING [CString],
EnvironmentVariables USING [Get],
HostTime USING [ExtendedGMTFromHostTime],
IO,
PFS USING [ Error, FileType, GetClientProperty, Mutability, NameProc, nullUniqueID, InfoProc, OpenFileFromStream, RopeFromPath, PathFromRope, PFSNameToUnixName, tDirectory, tUnspecified, UniqueID],
PFSBackdoor,
PFSNames,
PFSClass,
PFSPrefixMap USING [Insert],
PFSPrivate USING [],
RefText,
Rope USING [Cat, Concat, Equal, IsEmpty, Length, Match, ROPE],
UnixErrno,
UnixDirectory,
UnixFSPrivate USING [ReportFailure],
UnixSysCalls,
UnixSysCallExtensions USING [SetGetBlocking],
UnixTypes,
UXStrings
;
PFSUXImpl: CEDAR MONITOR LOCKS lock.lock USING lock: REF UXData
IMPORTS Arpa, Basics16, BasicTime, Convert, EnvironmentVariables, HostTime, IO, PFS, PFSBackdoor, PFSClass,
PFSNames, PFSPrefixMap, RefText, Rope, UnixDirectory, UnixErrno, UnixFSPrivate, UnixSysCalls, UnixSysCallExtensions, UXStrings
EXPORTS PFS, PFSPrivate
~ {
Types
ROPE: TYPE ~ Rope.ROPE;
PATH: TYPE ~ PFSNames.PATH;
RES: TYPE ~ UnixTypes.RES;
CString: TYPE ~ CStrings.CString;
OpenFileObject: PUBLIC TYPE = PFSClass.OpenFileObject; -- necessary to establish a type identity for the compiler
Parameters
myFlavor: ATOM ¬ $UX;
myReadOnlyFlavor: ATOM ¬ $UXRO;
versionOfAllFiles: PFSNames.Version ¬ [versionKind: none];
defaultCreateMode: CARD ¬ 0644B;
Constants
uxRoot: PATH ~ PFS.PathFromRope["-ux:/"];
ux0Component: PFSNames.Component ~ PFSNames.Fetch[uxRoot, 0];
Export to PFSPrivate
GetDefaultWDirRope: PUBLIC PROC RETURNS [ wDirRope: ROPE ] ~ {
value: ROPE ~ EnvironmentVariables.Get["PWD"];
wDirRope ¬ value.Concat["/"];
};
Registered with PFSClass
FSData: TYPE ~ REF FSDataObject;
FSDataObject: TYPE ~ RECORD [
fs: ROPE,
readOnly: BOOL
];
myMaintenanceProcs: PFSClass.MaintenanceProcs ¬ NEW[PFSClass.MaintenanceProcsObject ¬ [
sweep~Sweep,
validate~Validate]];
Sweep: PFSClass.SweepProc ~ { NULL };
GetHandle: PFSClass.GetHandleProc -- [fs: ROPE, flavorSpecified: BOOL] RETURNS [h: FSHandle, downMsg: ROPE] -- ~ {
IF flavorSpecified
THEN [h, downMsg] ¬ GetHandleInner[fs, FALSE]
ELSE { h ¬ NIL; downMsg ¬ NIL };
};
GetROHandle: PFSClass.GetHandleProc -- [fs: FS, flavorSpecified: BOOL] RETURNS [h: FSHandle, downMsg: ROPE ¬ NIL] -- ~ {
IF flavorSpecified
THEN [h, downMsg] ¬ GetHandleInner[fs, TRUE]
ELSE { h ¬ NIL; downMsg ¬ NIL };
};
GetHandleInner: PROC [fs: ROPE, readOnly: BOOL] RETURNS [h: PFSClass.FSHandle, downMsg: ROPE ¬ NIL] ~ {
Returns [NIL, NIL] if fs can't be located.
data: FSData ¬ NEW[FSDataObject ¬ [fs, readOnly]];
IF NOT Rope.IsEmpty[fs] THEN RETURN[NIL, NIL];
at the moment, we only support UX views through the local Unix name space.
h ¬ NEW[PFSClass.FSObject ¬ [flavor~myFlavor, name~fs, maintenanceProcs~myMaintenanceProcs, procs~unixProcs, data~data]];
};
Validate: PFSClass.ValidateProc -- [h: FSHandle] RETURNS [obsolete: BOOL, downMsg: ROPE] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
RETURN[ FALSE, NIL ]; -- the local file system is always available
};
Procs to provide unix view
unixProcs: PFSClass.FileManipulationProcs ¬ NEW[PFSClass.FileManipulationProcsObject ¬ [
delete~UXDelete,
enumerateForInfo~UXEnumerateForInfo,
enumerateForNames~UXEnumerateForNames,
lookupName: UXLookupName,
copy: UXCopy,
setAttributes: UXSetAttributes,
setByteCountAndUniqueID: UXSetByteCountAndUniqueID,
setClientProperty: UXSetClientProperty,
getClientProperty: UXGetClientProperty,
enumerateClientProperties: UXEnumerateClientProperties,
fileInfo~UXFileInfo,
rename~UXRename,
read~UXRead,
write~UXWrite,
open~UXOpen,
close~UXClose,
store~UXStore,
retrieve~UXRetrieve,
attach~UXAttach,
getInfo~UXGetInfo,
pfsNameToUnixName: UXPFSNameToUnixName,
caseSensitive: UXCaseSensitive
]];
UXDelete: PFSClass.DeleteProc -- [h, file, wantedUniqueID, proc] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
selfName: ROPE ~ "UXDelete";
FailIfReadOnly[h, file];
file ¬ FixPathForUnix[file]; -- PFS.Error if it's a pattern
IF (proc = NIL) OR proc[file, UXGetInfoInner[h, file, wantedUniqueID].uniqueID] THEN {
unixFile: ROPE ~ PFS.RopeFromPath[file];
cFile: CString ~ UXO[unixFile];
res: RES ~ UnixSysCalls.Unlink[cFile];
UXR[cFile];
IF res#success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF errno = EPERM OR errno = EINVAL THEN PFSBackdoor.ProduceError[accessDenied, MakeMsg[selfName, unixFile, " not deleted: can't delete a directory."]]
ELSE {
message: ROPE ~ MakeMsg[selfName, "Delete Failed for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
};
};
};
PFSFileTypeFromUnixType: PROC [ uft: UnixTypes.Fmt ]
RETURNS [ pfs: PFS.FileType ] ~ {
pfs ¬ SELECT uft FROM
dir => PFS.tDirectory,
ENDCASE => PFS.tUnspecified;
};
UXEnumerateForInfo: PFSClass.EnumerateForInfoProc -- [h: FSHandle, pattern: NAME, proc: InfoProc] -- ~ TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check
dirPath, unixDirPath: PATH;
listOfNames: LIST OF PATH ¬ NIL;
selfName: ROPE ~ "UXEnumerateForInfo";
[dirPath, unixDirPath, listOfNames] ¬ EnumerateInner[h, pattern];
FOR each: LIST OF PATH ¬ listOfNames, each.rest WHILE each # NIL DO
fileType: PFS.FileType ¬ PFS.tUnspecified;
attachedTo: PATH ¬ NIL;
uniqueID: PFS.UniqueID;
mutability: PFS.Mutability ¬ mutable;
unixFile: ROPE ¬ PFS.RopeFromPath[each.first];
cFile: CString ~ UXO[unixFile];
buf: UnixTypes.Stat;
bytes: INT;
res: RES ~ UnixSysCalls.LStat[cFile, @buf];
IF res # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
SELECT errno FROM
EPERM, ENOENT, ENXIO, EACCES, ENODEV, ENOTDIR, EISDIR => NULL; -- ignore things we can't look at
ENDCASE => {
message: ROPE ~ MakeMsg[selfName, "LStat failed (during enumeration) for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
};
IF buf.mode.fmt=lnk THEN {
res: RES ~ UnixSysCalls.Stat[cFile, @buf];
IF res # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
SELECT errno FROM
EPERM, ENOENT, ENXIO, EACCES, ENODEV, ENOTDIR, EISDIR => bytes ¬ -1; -- ignore things we can't look at
ENDCASE => {
message: ROPE ~ MakeMsg[selfName, "Stat failed (during enumeration) for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
}
ELSE bytes ¬ buf.size;
attachedTo ¬ GetAttachment[cFile];
attachedTo ¬ ResolveAttachment[each.first, attachedTo];
}
ELSE bytes ¬ buf.size;
fileType ¬ PFSFileTypeFromUnixType[buf.mode.fmt];
uniqueID ¬ UIDFromMTime[buf.mtime];
UXR[cFile]; 
IF NOT proc[each.first, attachedTo, uniqueID, bytes, mutability, fileType] THEN EXIT;
ENDLOOP;
};
UXEnumerateForNames: PFSClass.EnumerateForNamesProc -- [h: FSHandle, pattern: NAME, proc: NameProc] -- ~ {
dirPath: PATH;
listOfNames: LIST OF PATH ¬ NIL;
[dirPath~dirPath, listOfNames~listOfNames] ¬ EnumerateInner[h, pattern];
FOR each: LIST OF PATH ¬ listOfNames, each.rest WHILE each # NIL DO
IF NOT proc[each.first] THEN EXIT;
ENDLOOP;
};
EnumerateInner: PROC [h: PFSClass.FSHandle, pattern: PATH]
RETURNS [dirPath, unixDirPath: PATH ¬ NIL, listOfNames: LIST OF PATH ¬ NIL] ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
selfName: ROPE ~ "EnumerateInner";
patternPart: PATH;
[dirPath, patternPart] ¬ ParsePattern[pattern];
unixDirPath ¬ FixPathForUnix[dirPath];
{
dirRope: ROPE ~ PFS.RopeFromPath[unixDirPath];
dirString: CString ~ UXO[dirRope];
patternRope: ROPE ~ PFS.RopeFromPath[patternPart];
dirp: UnixDirectory.DirPtr ~ UnixDirectory.OpenDir[dirString];
errno: UnixErrno.Errno;
UXR[dirString];
IF dirp=NIL AND (errno ← UnixErrno.GetErrno[]) = $ENOENT THEN RETURN;
IF dirp=NIL THEN UnixFSPrivate.ReportFailure[errno, MakeMsg["EnumerateInner", "Directory open failed in EnumerateInner for ", dirRope]];
TRUSTED {
entryp: UnixDirectory.DirEntPtr ¬ UnixDirectory.ReadDir[dirp];
WHILE entryp # NIL DO
name: ROPE ¬ UnixDirectory.NameFromDirEntP[entryp];
IF NOT Rope.Equal[name, ".", TRUE] AND NOT Rope.Equal[name, "..", TRUE] THEN {
IF Rope.Match[patternRope, name, TRUE] THEN {
fullName: PATH ~ PFSNames.Cat[dirPath, PFS.PathFromRope[name]];
listOfNames ¬ CONS[fullName, listOfNames];
};
};
entryp ¬ UnixDirectory.ReadDir[dirp];
ENDLOOP;
};
[] ¬ UnixDirectory.CloseDir[dirp];
};
listOfNames ¬ SortNameList[listOfNames];
};
UXFileInfo: PFSClass.FileInfoProc -- [h: FSHandle, file: PATH, wantedUniqueID: UniqueID] RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: PFS.Mutability, fileType: PFS.FileType] -- ~ {
RETURN UXGetInfoInner[h, FixPathForUnix[file], wantedUniqueID];
};
UXGetInfoInner: PFSClass.FileInfoProc -- [h: FSHandle, file: PATH, wantedUniqueID: UniqueID] RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: PFS.Mutability, fileType: PFS.FileType] -- ~ TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check;
unixFile: ROPE ¬ PFS.RopeFromPath[file];
selfName: ROPE ~ "UXGetInfoInner";
buf: UnixTypes.Stat;
cFile: CString ~ UXO[unixFile];
res: RES ~ UnixSysCalls.LStat[cFile, @buf];
IF res # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ Rope.Concat["Stat failed (in UXGetInfoInner) for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
IF buf.mode.fmt=lnk THEN {
res: RES ~ UnixSysCalls.Stat[cFile, @buf];
IF res # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
SELECT errno FROM
EPERM, ENOENT, ENXIO, EACCES, ENODEV, ENOTDIR, EISDIR => bytes ¬ -1; -- ignore things we can't look at
ENDCASE => {
message: ROPE ~ MakeMsg[selfName, "Stat failed for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
}
ELSE bytes ¬ buf.size;
attachedTo ¬ GetAttachment[cFile];
attachedTo ¬ ResolveAttachment[file, attachedTo];
}
ELSE {
bytes ¬ buf.size;
attachedTo ¬ NIL;
};
UXR[cFile];
fileType ¬ PFSFileTypeFromUnixType[buf.mode.fmt];
uniqueID ¬ UIDFromMTime[buf.mtime];
IF ( wantedUniqueID # PFS.nullUniqueID )
AND ( uniqueID # wantedUniqueID )
THEN PFSBackdoor.ProduceError[unknownUniqueID, "UniqueID wrong"];
mutability ¬ mutable;
version ¬ versionOfAllFiles;
fullFName ¬ file; -- for unix view, this is the fullFName
};
UXGetInfo: PFSClass.GetInfoProc -- [h: FSHandle, file: OpenFile] RETURNS [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: PFS.Mutability, fileType: PFS.FileType] -- ~ TRUSTED {
data: REF UXData ~ NARROW[file.data];
fd: UnixTypes.FD ¬ data.fd;
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.FStat[fd, @buf];
IF res # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXGetInfo", "Stat failed for ", file.fullFName];
UnixFSPrivate.ReportFailure[errno, message];
};
RETURN[file.fullFName, file.attachedTo, file.uniqueID, buf.size, file.mutability, PFSFileTypeFromUnixType[buf.mode.fmt]];
};
UXRename: PFSClass.RenameProc -- [h: FSHandle, fromFile: NAME, wantedUniqueID: UniqueID, toFile: NAME, keep: CARDINAL, proc: PFS.NameConfirmProc] RETURNS [done: BOOL ¬ FALSE] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check
fullName: PATH; uniqueID: PFS.UniqueID;
FailIfReadOnly[h, fromFile];
fromFile ¬ FixPathForUnix[fromFile]; -- PFS.Error if it's a pattern
toFile ¬ FixPathForUnix[toFile];
[uniqueID~uniqueID] ¬ UXGetInfoInner[h, fromFile, wantedUniqueID]; -- PFS.Error if wantedUniqueID not available
IF (proc = NIL) OR proc[fromFile, uniqueID] THEN { -- fromFile or toFile?
unixFromFile: ROPE ¬ PFS.RopeFromPath[fromFile];
unixToFile: ROPE ¬ PFS.RopeFromPath[toFile];
cFromFile: CString ~ UXO[unixFromFile];
cToFile: CString ~ UXO[unixToFile];
res: RES ¬ UnixSysCalls.Rename[cFromFile, cToFile];
UXR[cFromFile];
UXR[cToFile];
RETURN[res=success];
};
};
bufferBytes: CARDINAL ¬ 8192;
UXStore: PUBLIC PFSClass.StoreProc -- [h: FSHandle, file: NAME, wantedUniqueID: UniqueID, str: IO.STREAM, proc: PFS.StoreConfirmProc, createOptions: PFS.CreateOptions] -- ~ {
selfName: ROPE ~ "UXStore";
DoStore: PROC [fd: UnixTypes.FD] ~ TRUSTED {
buffer: REF TEXT;
TRUSTED {
ENABLE UNWIND => {
IF buffer # NIL THEN RefText.ReleaseScratch[buffer];
};
buffer ¬ RefText.ObtainScratch[bufferBytes];
DO
bytesWritten: INT;
ptr: UnixTypes.CHARPtr ~ LOOPHOLE[LOOPHOLE[buffer, UnixTypes.CHARPtr] +SIZE[TEXT[0]]];
buffer.length ¬ IO.GetBlock[str, buffer, 0, buffer.maxLength];
IF buffer.length = 0 THEN EXIT;
bytesWritten ¬ UnixSysCalls.Write[fd, ptr, buffer.length];
IF bytesWritten # buffer.length THEN {
IF bytesWritten < 0 THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Write failed for", file];
UnixFSPrivate.ReportFailure[errno, message];
}
ELSE PFSBackdoor.ProduceError[inconsistent, "Invalid assumption about UnixSysCalls.Write in Store"];
};
ENDLOOP;
{
res: RES ~ UnixSysCalls.FSync[fd];
errno: UnixErrno.Errno;
IF res#success AND (errno ← UnixErrno.GetErrno[]) # EBADF THEN UnixFSPrivate.ReportFailure[errno, MakeMsg[selfName, "File system full? FSync failed for", file]];
};
RefText.ReleaseScratch[buffer]; buffer ¬ NIL;
};
};
openFile: PFSClass.OpenFile ~ UXOpen[h, file, wantedUniqueID, create, FALSE, PFS.tUnspecified, createOptions];
data: REF UXData ~ NARROW[openFile.data];
fd: UnixTypes.FD ~ data.fd;
IF proc # NIL THEN [] ¬ proc[openFile.fullFName];
DoStore[fd];
{
Preserve "Execute" bits in Unix-based views
This is not pleasant...
ENABLE PFS.Error => CONTINUE;
oldMode: ROPE ¬ PFS.GetClientProperty[PFS.OpenFileFromStream[str], "UnixMode"];
newMode: ROPE ¬ UXGetClientProperty[h, openFile, "UnixMode"];
IF oldMode # NIL AND newMode # NIL THEN {
oldModeBits: CARD16 ~ Convert.CardFromRope[oldMode];
newModeBits: CARD16 ~ Convert.CardFromRope[newMode];
newerModeBits: CARD16 ~
Basics16.BITOR[newModeBits, Basics16.BITAND[oldModeBits, 111B]];
newerMode: ROPE ~ Convert.RopeFromCard[newerModeBits, 8];
UXSetClientProperty[h, openFile, "UnixMode", newerMode];
};
};
UXClose[h, openFile, FALSE];
};
UXRetrieve: PFSClass.RetrieveProc ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
PFSBackdoor.ProduceError[notImplemented, "Retrieve not yet implemented for -ux view"];
};
NotYetImpl: ERROR ~ CODE;
UXLookupName: PFSClass.LookupNameProc -- [h: FSHandle, file: NAME] RETURNS[NAME] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
success: BOOL ¬ TRUE;
[] ¬ UXGetInfoInner[h, FixPathForUnix[file], PFS.nullUniqueID ! PFS.Error => IF error.code = $unknownFile THEN {success ¬ FALSE; CONTINUE} ELSE REJECT ];
IF success THEN RETURN[file] ELSE RETURN[NIL];
};
UXCopy: PFSClass.CopyProc -- [h: FSHandle, fromFile: NAME, wantedUniqueID: UniqueID, toFile: NAME, keep: CARDINAL, proc: PFS.NameConfirmProc] RETURNS [done: BOOL ¬ FALSE] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
done ¬ FALSE; -- let the higher level do the copying
};
UXSetAttributes: PFSClass.SetAttributesProc -- [h: FSHandle, file: OpenFile, attributes: PFS.CreateOptions] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
PFSBackdoor.ProduceError[notImplemented, "SetAttributes not implemented in UX view"];
};
UXSetByteCountAndUniqueID: PFSClass.SetByteCountAndUniqueIDProc -- [h: FSHandle, file: OpenFile, bytes: INT, uniqueID: PFS.UniqueID] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
IF bytes>=0 THEN {
res: RES ~ UnixSysCalls.FTruncate[fd, bytes];
IF res#success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXSetByteCountAndUniqueID", "failed to change byteCount of", file];
UnixFSPrivate.ReportFailure[errno, message]
};
};
IF wantedUniqueID.egmt.time # BasicTime.nullGMT THEN TRUSTED {
bah! there's no string name available for the UTimes.
mTime: MTime ~ MTimeFromGMT[wantedUniqueID.egmt.time];
modTimeVal: UnixSysCalls.AccModTimes ¬
[accessTime: [sec: mTime, usec: 0], modTime: [sec: mTime, usec: 0]];
res: RES ~ UnixSysCalls.UTimes[stringName, @modTimeVal];
};
};
UXGetClientProperty: PFSClass.GetClientPropertyProc -- [h: FSHandle, file: OpenFile, propertyName: ROPE] RETURNS [propertyValue: ROPE] -- ~ TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.FStat[fd, @buf];
IF res#success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXGetClientProperty", "Stat failed for", file];
UnixFSPrivate.ReportFailure[errno, message];
};
SELECT TRUE FROM
Rope.Equal[propertyName, "UnixMode"] => {
RETURN[Convert.RopeFromCard[LOOPHOLE[buf.mode, CARD16], 8]];
};
Rope.Equal[propertyName, "UnixOwner"] => {
RETURN[Convert.RopeFromCard[buf.uid]];
};
Rope.Equal[propertyName, "UnixGroup"] => {
RETURN[Convert.RopeFromCard[buf.gid]];
};
ENDCASE => RETURN[NIL];
};
UXSetClientProperty: PUBLIC PFSClass.SetClientPropertyProc -- [h: FSHandle, file: OpenFile, propertyName: ROPE, propertyValue: ROPE] -- = {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
SELECT TRUE FROM
Rope.Equal[propertyName, "UnixMode"] => {
cardMode: CARD16 ~ Convert.CardFromRope[propertyValue, 8];
res: RES ~ UnixSysCalls.FChMod[fd, LOOPHOLE[cardMode]];
};
Rope.Equal[propertyName, "UnixOwner"] => {
PFSBackdoor.ProduceError[notImplemented, "The UnixOwner cannot be changed in the UX view"];
};
Rope.Equal[propertyName, "UnixGroup"] => {
PFSBackdoor.ProduceError[notImplemented, "The UnixGroup cannot be changed in the UX view"];
};
ENDCASE => PFSBackdoor.ProduceError[notImplemented, Rope.Concat[propertyName, " is not an implemented property in the UX view"]];
};
UXEnumerateClientProperties: PFSClass.EnumerateClientPropertiesProc -- [h: FSHandle, file: OpenFile, proc: PFS.PropProc] -- ~ TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.FStat[fd, @buf];
IF NOT proc["UnixGroup", Convert.RopeFromCard[buf.gid]] THEN RETURN;
IF NOT proc["UnixMode", Convert.RopeFromCard[LOOPHOLE[buf.mode, CARD16], 8]] THEN RETURN;
[] ¬ proc["UnixOwner", Convert.RopeFromCard[buf.uid]];
};
UXRead: UNSAFE PROC [h: PFSClass.FSHandle, file: PFSClass.OpenFile, filePosition, nBytes: CARD, toPtr: LONG POINTER, toStart: CARD] RETURNS [bytesRead: INT] ~ {
fs: FSData ~ NARROW[h.data]; -- sanity check
data: REF UXData ~ NARROW[file.data];
fd: UnixTypes.FD ¬ data.fd;
failure: ROPE;
errno: UnixErrno.Errno;
selfName: ROPE ~ "UXRead";
DoRead: ENTRY PROC [lock: REF UXData] RETURNS [failure: ROPE ¬ NIL, errno: UnixErrno.Errno ¬ ok] ~ TRUSTED {
ENABLE UNWIND => NULL;
fdIndex: PFSBackdoor.FDIndex ~ data.fdIndex;
data.fdIndex ¬ PFSBackdoor.unreliableIndex;
IF filePosition # fdIndex THEN {
res: INT ~ UnixSysCalls.LSeek[fd, filePosition, set];
IF res < 0 THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "LSeek failed for", file];
RETURN[failure, errno];
};
};
bytesRead ¬ UnixSysCalls.Read[d~fd, buf~LOOPHOLE[toPtr+toStart], nBytes~nBytes];
IF bytesRead < 0 THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "Read failed for", file];
RETURN[failure, errno];
};
IF fdIndex # PFSBackdoor.unreliableIndex THEN data.fdIndex ¬ filePosition+bytesRead;
};
bytesRead ¬ 0;
[failure, errno] ¬ DoRead[data];
IF failure # NIL THEN UnixFSPrivate.ReportFailure[errno, failure];
};
UXWrite: PROC [h: PFSClass.FSHandle, file: PFSClass.OpenFile, filePosition, nBytes: CARD, fromPtr: LONG POINTER, fromStart: CARD] RETURNS [bytesWritten: INT] ~ {
fs: FSData ¬ NARROW[h.data]; -- sanity check
data: REF UXData ~ NARROW[file.data];
fd: UnixTypes.FD ~ data.fd;
failure: ROPE;
errno: UnixErrno.Errno;
selfName: ROPE ~ "UXWrite";
DoWrite: ENTRY PROC [lock: REF UXData] RETURNS [failure: ROPE ¬ NIL, errno: UnixErrno.Errno ¬ ok] ~ {
ENABLE UNWIND => NULL;
fdIndex: PFSBackdoor.FDIndex ~ data.fdIndex;
data.fdIndex ¬ PFSBackdoor.unreliableIndex;
IF filePosition # fdIndex THEN {
res: INT ~ UnixSysCalls.LSeek[fd, filePosition, set];
IF res < 0 THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "LSeek failed for", file];
RETURN[failure, errno];
};
};
bytesWritten ¬ UnixSysCalls.Write[d~fd, buf~LOOPHOLE[fromPtr+fromStart], nBytes~nBytes];
IF bytesWritten < 0 THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "Write failed for", file];
RETURN[failure, errno];
};
IF fdIndex # PFSBackdoor.unreliableIndex THEN data.fdIndex ¬ filePosition+bytesWritten;
};
bytesWritten ¬ 0;
[failure, errno] ¬ DoWrite[data];
IF failure # NIL THEN UnixFSPrivate.ReportFailure[errno, failure];
};
mutable: UnixTypes.Permission ~ [read: true, write: true, exec: false];
immutable: UnixTypes.Permission ~ [read: true, write: false, exec: false];
mutableMode: UnixTypes.Mode ~ [owner: mutable, group: mutable, others: mutable];
immutableMode: UnixTypes.Mode ~ [owner: immutable, group: immutable, others: immutable];
immutableMode: UnixTypes.Mode ~ [owner: mutable, group: mutable, others: mutable]; -- because, temporarily, the DF software doesn't make the necessary files mutable, and the compiler can't deal with immutable files
UXData: TYPE ~ PFSBackdoor.UXData;
maxAttachmentLength: CARD ~ 1000;
GetAttachment: PROC [fHandle: CString] RETURNS [attachedTo: PATH] ~ TRUSTED {
buf: CString ~ UXStrings.ObtainScratch[1000];
linkSize: INT ~ UnixSysCalls.ReadLink[fHandle, buf, maxAttachmentLength];
IF linkSize<0 THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["GetAttachment", "ReadLink failed for", UXStrings.ToRope[fHandle]];
UnixFSPrivate.ReportFailure[errno, message];
};
attachedTo ¬ PFS.PathFromRope[UXStrings.ToRope[buf, linkSize]];
UXR[buf];
};
ResolveAttachment: PROC [attachment: PATH, attachedTo: PATH] RETURNS [PATH] ~ {
{
absolute: PATH ~ IF PFSNames.IsAbsolute[attachedTo] THEN attachedTo ELSE PFSNames.Cat[PFSNames.Directory[attachment], attachedTo];
RETURN[ PFSNames.ReplaceComponent[absolute, 0, ux0Component] ];
};
};
UXOpen: PFSClass.OpenProc -- PROCEDURE [h: FSHandle, file: NAME, wantedUniqueID: UniqueID, access: PFS.AccessOptions, checkFileType: BOOL, fileType: PFS.FileType, createOptions: PFS.CreateOptions] RETURNS [OpenFile] -- ~ {
openFile: PFSClass.OpenFile ¬ NEW[PFSClass.OpenFileObject ¬
[fs~h, uniqueID~PFS.nullUniqueID, access~access, state~open, bytes: 0, mutability: mutable, fileType: PFS.tUnspecified]];
fileName: PATH ¬ FixPathForUnix[file];
unixFileName: ROPE ~ PFS.RopeFromPath[fileName];
fHandle: CString ¬ UXS[unixFileName];
fd: UnixTypes.FD;
mode: UnixTypes.Mode;
selfName: ROPE ~ "UXOpen";
SELECT access FROM
read => TRUSTED {
buf: UnixTypes.Stat;
fd ¬ UnixSysCalls.Open[path~fHandle,
flags~[access~RDONLY],
mode~immutableMode
];
IF fd=error THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXOpen", "Open failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
openFile.data ¬ NEW[UXData ¬ [fd~fd, fdIndex~0]];
};
write, append => TRUSTED {
buf: UnixTypes.Stat;
res: RES ¬ UnixSysCalls.LStat[fHandle, @buf];
IF res=success THEN {
IF buf.mode.fmt=lnk THEN {
PFSBackdoor.ProduceError[accessDenied, MakeMsg[selfName, "Can't open an attachment for writing or appending", unixFileName]];
};
};
fd ¬ UnixSysCalls.Open[path~fHandle,
flags~[excl~false, trunc~false, creat~true, access~RDWR],
mode~IF createOptions.mutability=mutable THEN mutableMode ELSE immutableMode
];
IF fd=error THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Open failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
IF access=write THEN openFile.data ¬ NEW[UXData ¬ [fd~fd, fdIndex~0]] ELSE openFile.data ¬ NEW[UXData ¬ [fd]];
};
create => TRUSTED {
shortName: ROPE ~ PFSNames.ComponentRope[PFSNames.ShortName[fileName]];
backupLastComp: PFSNames.Component ~ [name: [base: shortName.Concat["~"], start: 0, len: shortName.Length[]+1]];
backupName: PATH ~ PFSNames.ReplaceShortName[fileName, backupLastComp];
backupUnixName: ROPE ~ PFS.RopeFromPath[backupName];
tempLastCompName: ROPE ~ CreateTempName[shortName];
tempLastComp: PFSNames.Component ~ [name: [base: tempLastCompName, start: 0, len: tempLastCompName.Length[]]];
tempName: PATH ~ PFSNames.ReplaceShortName[fileName, tempLastComp];
tempUnixName: ROPE ~ PFS.RopeFromPath[tempName];
tempFHandle: CString ~ UXS[tempUnixName];
cBackupName: CString ~ UXO[backupUnixName];
buf: UnixTypes.Stat;
res: RES ¬ UnixSysCalls.LStat[fHandle, @buf];
IF res#success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF errno # ENOENT THEN {
message: ROPE ~ MakeMsg[selfName, "Stat failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
};
IF buf.mode.fmt=dir THEN {
message: ROPE ~ Rope.Cat["Creating a file with the same name (", unixFileName, " ) as an existing directory is not allowd."];
PFSBackdoor.ProduceError[accessDenied, message];
};
BEGIN
ok: BOOL ¬ FALSE;
fd: UnixTypes.FD ¬ UnixSysCalls.Open[path~fHandle,
flags~[access~RDONLY],
mode~immutableMode
];
IF fd#error THEN {
res ¬ UnixSysCalls.FStat[fd, @buf];
IF res#success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "FStat failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
res ¬ UnixSysCalls.Close[fd];
ok ¬ TRUE;
};
IF ok AND (createOptions.setMutability=inherit) THEN mode ¬ buf.mode
ELSE {
SELECT TRUE FROM
createOptions.mutability=mutable => mode ¬ mutableMode;
ENDCASE => mode ¬ immutableMode;
IF ok THEN {  -- fold in execute bits from buf.mode
mode.owner.exec ¬ buf.mode.owner.exec;
mode.group.exec ¬ buf.mode.group.exec;
mode.others.exec ¬ buf.mode.others.exec;
};
};
END;
IF createOptions.keep # 1 THEN {
res ¬ UnixSysCalls.Rename[fHandle, cBackupName];
UXR[cBackupName];
IF res#success THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err # ENOENT THEN {
message: ROPE ~ MakeMsg[selfName, Rope.Cat["Rename failed for ", unixFileName, " to"], backupUnixName];
UnixFSPrivate.ReportFailure[err, message];
};
};
}
ELSE {
UXR[cBackupName];
res ¬ UnixSysCalls.Unlink[fHandle];
IF res#success THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err # ENOENT THEN {
message: ROPE ~ MakeMsg[selfName, "Unlink failed for", unixFileName];
UnixFSPrivate.ReportFailure[err, message];
};
};
};
fd ¬ UnixSysCalls.Open[path~tempFHandle,
flags~[excl~true, creat~true, access~WRONLY],
mode~mode
];
IF fd=error THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Open failed for", tempUnixName];
UnixFSPrivate.ReportFailure[errno, message];
};
openFile.data ¬ NEW[UXData ¬ [fd, tempFHandle, wantedUniqueID, fHandle, 0]];
fHandle ¬ tempFHandle;
};
ENDCASE => ERROR;
TRUSTED {
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.Stat[fHandle, @buf];
IF res # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Stat failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
openFile.bytes ¬ buf.size;
IF openFile.uniqueID = PFS.nullUniqueID THEN openFile.uniqueID ¬ UIDFromMTime[buf.mtime];
};
{
res: RES ~ UnixSysCallExtensions.SetGetBlocking[fd, allData];
IF res # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "SetGetBlocking failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
};
openFile.fullFName ¬ fileName;
RETURN [openFile];
};
UXAttach: PFSClass.AttachProc -- PROCEDURE [h: FSHandle, file: PATH, to: PATH, keep: CARDINAL, wantedUniqueID: UniqueID, remoteCheck: BOOL ¬ TRUE] RETURNS [toFName: PATH] -- ~ TRUSTED {
selfName: ROPE ~ "UXAttach";
fileName: PATH ¬ FixPathForUnix[file];
unixFileName: ROPE ~ PFS.RopeFromPath[fileName];
fHandle: CString ¬ UXS[unixFileName];
shortName: ROPE ~ PFSNames.ComponentRope[PFSNames.ShortName[fileName]];
backupLastComp: PFSNames.Component ~ [name: [base: shortName.Concat["~"], start: 0, len: shortName.Length[]+1]];
backupName: PATH ~ PFSNames.ReplaceShortName[fileName, backupLastComp];
backupUnixName: ROPE ~ PFS.RopeFromPath[backupName];
cBackupName: CString ~ UXO[backupUnixName];
buf: UnixTypes.Stat;
res: RES ¬ UnixSysCalls.LStat[fHandle, @buf];
IF res#success THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err # ENOENT THEN {
message: ROPE ~ MakeMsg[selfName, "Stat failed for", unixFileName];
UnixFSPrivate.ReportFailure[err, message];
};
};
IF buf.mode.fmt=dir THEN {
message: ROPE ~ Rope.Cat["Creating a file with the same name (", unixFileName, " ) as an existing directory is not allowed."];
PFSBackdoor.ProduceError[accessDenied, message];
};
res ¬ UnixSysCalls.Rename[fHandle, cBackupName];
UXR[cBackupName];
IF res#success THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err # ENOENT THEN {
message: ROPE ~ MakeMsg[selfName, Rope.Cat["Rename failed for ", unixFileName, " to"], backupUnixName];
UnixFSPrivate.ReportFailure[err, message];
};
};
{
toFileName: ROPE ~ PFS.PFSNameToUnixName[to];
toFHandle: CString ~ UXO[toFileName];
IF toFileName=NIL THEN PFSBackdoor.ProduceError[unknownFile, Rope.Cat["Couldn't deduce a unix name for ", PFS.RopeFromPath[to], " in UXAttach."]];
res ¬ UnixSysCalls.SymLink[name1~toFHandle, name2~fHandle];
IF res#success THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, Rope.Cat["SymLink failed from ", unixFileName, " to"], toFileName];
UnixFSPrivate.ReportFailure[err, message];
};
FixUID[fHandle, wantedUniqueID]; Unix won't let me set the times on a symlink.
UXR[toFHandle];
};
RETURN[file];
};
UXS: PROC [rope: ROPE] RETURNS [CString] ~ INLINE {
RETURN [UXStrings.Create[rope]];
};
UXO: PROC[r: REF] RETURNS [CString] ~ INLINE{
RETURN[UXStrings.CreateScratch[r]];
};
UXR: PROC[s: CString] ~ INLINE{
UXStrings.ReleaseScratch[s];
};
UXClose: PFSClass.CloseProc -- PROCEDURE [h: FSHandle, file: OpenFile, abort: BOOL] -- ~ {
fsData: FSData ¬ NARROW[h.data]; -- sanity check
data: REF UXData ¬ NARROW[file.data];
fd: UnixTypes.FD ¬ data.fd;
res: RES;
failure: ROPE;
errno: UnixErrno.Errno;
selfName: ROPE ~ "UXClose";
DoClose: ENTRY PROC [lock: REF UXData] RETURNS [failure: ROPE ¬ NIL, errno: UnixErrno.Errno ¬ ok] ~ {
ENABLE UNWIND => NULL;
data.fdIndex ¬ PFSBackdoor.unreliableIndex;
IF data.setUniqueIDAtClose#NIL THEN FixUID[data.setUniqueIDAtClose, IF data.wantedUniqueID = PFS.nullUniqueID THEN file.uniqueID ELSE data.wantedUniqueID];
Sync the file, because otherwise writes to full NFS servers all appear to work, but the bits don't appear there.
IF file.access#read THEN {
res ¬ UnixSysCalls.FSync[fd];
IF res#success AND UnixErrno.GetErrno[] # EBADF THEN {
RETURN["FSync failed in Close; file system full?"];
};
};
res ¬ UnixSysCalls.Close[fd];
IF res#success THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "Close failed for", file];
RETURN[failure, errno];
};
IF data.setNameAtClose#NIL THEN {
This is a file being created
IF NOT abort THEN {
res: RES ~ UnixSysCalls.Rename[data.setUniqueIDAtClose, data.setNameAtClose];
IF res#success THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "Rename failed for", file];
RETURN[failure, errno];
}
}
ELSE {
res: RES ~ UnixSysCalls.Unlink[data.setUniqueIDAtClose];
IF res#success THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "Delete failed in aborted Close for", file];
RETURN[failure, errno];
}
};
};
};
[failure, errno] ¬ DoClose[data];
IF failure # NIL THEN UnixFSPrivate.ReportFailure[errno, failure];
};
UXPFSNameToUnixName: PUBLIC PFSClass.PFSNameToUnixNameProc -- [h: FSHandle, file: PATH] RETURNS [ ROPE ] -- = {
RETURN[PFS.RopeFromPath[file]];
};
UXCaseSensitive: PUBLIC PFSClass.CaseSensitiveProc -- [h: FSHandle, file: PATH] RETURNS [ BOOL ] -- = {
RETURN[TRUE];
};
MTime: TYPE ~ INT;
unixEpoch: BasicTime.GMT ¬ BasicTime.Pack[
[year~1970, month~January, day~1, hour~0, minute~0, second~0, zone~0, dst~no]
];
MTimeFromGMT: PUBLIC PROC [gmt: BasicTime.GMT]
RETURNS [unixTime: UnixTypes.Time] ~ {
RETURN [BasicTime.Period[from~unixEpoch, to~gmt]];
};
FixUID: PROC [stringName: CString, wantedUniqueID: PFS.UniqueID] ~ TRUSTED {
mTime: MTime ~ MTimeFromGMT[wantedUniqueID.egmt.gmt];
modTimeVal: UnixTypes.AccModTimes ¬
[accessTime: [sec: mTime, usec: 0], modTime: [sec: mTime, usec: 0]];
IF wantedUniqueID.egmt.gmt # BasicTime.nullGMT THEN IF UnixSysCalls.UTimes[stringName, @modTimeVal] # success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
UnixFSPrivate.ReportFailure[errno, MakeMsg["FixUID", "UTimes failed for", UXStrings.ToRope[stringName]]];
};
};
Utilities
UIDFromMTime: PROC [mTime: MTime] RETURNS [PFS.UniqueID] ~ {
the LOOPHOLE is to guard again bogus unix file times
RETURN[[egmt: [gmt: HostTime.ExtendedGMTFromHostTime[[a~LOOPHOLE[mTime]]].gmt, usecs: 0], host: [0, 0]]]
};
tempNamePrefix: ROPE ¬ NIL;
CreateTempName: PROC [base: ROPE] RETURNS [tempName: ROPE] ~ {
u1, u2: CARD;
IF tempNamePrefix = NIL THEN {
a: Arpa.Address ~ Arpa.MyAddress[];
tempNamePrefix ¬ IO.PutFLR["fs.%g.%g.%g.%g", LIST[[cardinal[a.a]], [cardinal[a.b]], [cardinal[a.c]], [cardinal[a.d]]]];
};
u1 ¬ u2 ¬ BasicTime.GetClockPulses[];
u1 ¬ LOOPHOLE[BasicTime.Now[! BasicTime.TimeNotKnown => CONTINUE]];
tempName ¬ IO.PutFR["%g.%g.%g", [rope[tempNamePrefix]], [cardinal[u1]], [cardinal[u2]] ];
};
FixPathForUnix: PROC [path: PATH] RETURNS [fixedPath: PATH] ~ {
fixedPath ¬ PFSNames.StripVersionNumber[path];
};
moduleName: ROPE ~ "PFSUXImpl."; -- for producing error messages
MakeMsg: PROC [procName: ROPE, constantText: ROPE, file: REF] RETURNS [ROPE] ~ {
fileRope: ROPE ~ WITH file SELECT FROM
r: ROPE => r,
p: PATH => PFS.RopeFromPath[p],
o: PFSClass.OpenFile => PFS.RopeFromPath[o.fullFName]
ENDCASE => NIL;
RETURN[ Rope.Cat[moduleName, procName, ": ", Rope.Cat[constantText, " ", fileRope]] ];
};
jack: PFSNames.Component ~ [["*", 0, 1], [none]];
wild: PATH ~ PFSNames.ConstructName[components: LIST[jack]];
ParsePattern: PROC [pattern: PATH] RETURNS [dirPath, patternPart: PATH] ~ {
IF PFSNames.IsADirectory[pattern]
THEN { dirPath ¬ pattern; patternPart ¬ wild }
ELSE {
dirPath ¬ PFSNames.Parent[pattern];
patternPart ¬ FixPathForUnix[pattern.SubName[pattern.ComponentCount[]-1, 1]];
};
};
SortNameList: PROC [list: LIST OF PATH] RETURNS [out: LIST OF PATH] ~ {
Destructive ...
left, right: LIST OF PATH;
IF (list = NIL) OR (list.rest = NIL) THEN RETURN[list];
[left, right] ¬ SplitNameList[list];
RETURN[MergeNameLists[SortNameList[left], SortNameList[right]]];
};
SplitNameList: PROC [list: LIST OF PATH] RETURNS [left, right: LIST OF PATH] ~ {
Destructive ...
len: CARDINAL ¬ 0;
FOR each: LIST OF PATH ¬ list, each.rest WHILE each # NIL DO
len ¬ len + 1;
ENDLOOP;
IF len = 0 THEN RETURN[NIL, NIL];
left ¬ list;
THROUGH [1 .. len/2) DO list ¬ list.rest ENDLOOP;
right ¬ list.rest;
list.rest ¬ NIL;
};
MergeNameLists: PROC [left, right: LIST OF PATH] RETURNS [LIST OF PATH] ~ {
Destructive ...
oFinger, out: LIST OF PATH;
out ¬ oFinger ¬ LIST[NIL];
DO
SELECT TRUE FROM
(left = NIL) => { oFinger.rest ¬ right; EXIT };
(right = NIL) => { oFinger.rest ¬ left; EXIT };
(PFSNames.Compare [left.first, right.first] = less) => {
oFinger.rest ¬ left; left ¬ left.rest;
};
ENDCASE => {
oFinger.rest ¬ right; right ¬ right.rest;
};
oFinger ¬ oFinger.rest;
oFinger.rest ¬ NIL;
ENDLOOP;
RETURN[out.rest];
};
Errors
FailIfReadOnly: PROC [h: PFSClass.FSHandle, file: PATH] ~ {
data: FSData ¬ NARROW[h.data];
r: ROPE;
IF NOT data.readOnly THEN RETURN;
r ¬ Rope.Cat[
"FS for [",
IF h # NIL THEN h.name ELSE "?",
"]",
PFS.RopeFromPath[file],
" is read-only."];
PFSBackdoor.ProduceError[accessDenied, r, NIL];
};
Dead code for debugging
DInfo: TYPE ~ RECORD [
fileName: PATH,
bytes: INT,
uniqueID: PFS.UniqueID
];
listOfInfo: LIST OF DInfo ¬ NIL;
DoEnumerateForInfo: PROC [h: PFSClass.FSHandle, pattern: PATH] ~ {
EachInfo: PFS.InfoProc -- [fullFName, attachedTo, uniqueID, bytes, mutability, fileType] RETURNS [continue: ¬ TRUE] -- ~ {
listOfInfo ¬ CONS[[fullFName, bytes, uniqueID], listOfInfo];
RETURN[TRUE];
};
listOfInfo ¬ NIL;
UXEnumerateForInfo[h, pattern, EachInfo, NIL, NIL];
};
listOfNames: LIST OF PATH ¬ NIL;
DoEnumerateForNames: PROC [h: PFSClass.FSHandle, pattern: PATH] ~ {
EachName: PFS.NameProc -- [name: PATH] RETURNS [continue: BOOL ¬ TRUE] -- ~ {
listOfNames ¬ CONS[name, listOfNames];
RETURN[TRUE];
};
listOfNames ¬ NIL;
UXEnumerateForNames[h, pattern, EachName, NIL, NIL];
};
Initialization
PFSClass.Register[myFlavor, GetHandle];
PFSClass.Register[myReadOnlyFlavor, GetROHandle];
[] ¬ PFSPrefixMap.Insert[ PFS.PathFromRope["/"], uxRoot ];
}.
Carl Hauser, January 12, 1989 3:05:27 pm PST
created from UXRemoteFileImpl