SystemInterfaceImpl.mesa
Copyright Ó 1990 by Xerox Corporation. All rights reserved.
Theimer, October 14, 1989 10:34:24 pm PDT
Last changed by Theimer on October 31, 1989 8:17:54 pm PST
Last changed by Sturgis: February 20, 1990 1:15 pm PST
Last tweaked by Mike Spreitzer on December 23, 1991 9:55 am PST
Last changed by Coolidge, July 27, 1990 4:40 pm PDT
Laurie Horton, October 11, 1990 3:02 pm PDT
Philip James, December 26, 1991 11:51 am PST
DIRECTORY
Atom,
BasicTime USING[FromNSTime, GMT, nullGMT, OutOfRange, ToNSTime],
CCTypes USING[CCError, CCErrorCase],
CirioDeltaFace USING[DebuggeeNameToDebuggerName, TargetVersionMapName],
Commander,
Convert,
MobDefs,
MorePfsNames,
IO,
PFS,
PFSNames,
Rope,
RopeParts,
RopeSequence,
SimpleFeedback USING[PutF],
SourceFileOpsExtras USING [FullOpenSource, Position],
SymTab,
SystemInterface USING[CirioFileInfo],
VersionMap,
VersionMap2,
VersionMap2Binding;
SystemInterfaceImpl: CEDAR MONITOR LOCKS set USING set: FileSet
IMPORTS Atom, BasicTime, CCTypes, CirioDeltaFace, Commander, Convert, IO, MorePfsNames, PFS, PFSNames, Rope, RopeParts, RopeSequence, SimpleFeedback, SourceFileOpsExtras, SymTab, VersionMap2, VersionMap2Binding
EXPORTS SystemInterface
= BEGIN OPEN IO, MPN:MorePfsNames, Rope, RP:RopeParts, RS:RopeSequence, VM2:VersionMap2, VM2Binding:VersionMap2Binding;
CCE: ERROR[case: CCTypes.CCErrorCase, msg: Rope.ROPENIL] ← CCTypes.CCError;
Useful shorthand
PATH: TYPE ~ PFSNames.PATH;
PathList: TYPE ~ LIST OF PATH;
Component: TYPE ~ PFSNames.Component;
ROPE: TYPE ~ Rope.ROPE;
RopeList: TYPE ~ LIST OF ROPE;
RopePart: TYPE ~ RP.RopePart;
RopeSeq: TYPE ~ RS.RopeSeq;
Useful, since many packages wish to deliver reports
ShowReport: PUBLIC SIGNAL[msgText: ROPE, priority: ATOM] = CODE;
priority is one of the following:
$urgent, $normal, $debug
Routines that abstract the FileViewerOps interface.
ShowSource: PUBLIC PROC [desc: ROPE, pos: SourceFileOpsExtras.Position, feedBack: STREAMNIL] =
BEGIN
SourceFileOpsExtras.FullOpenSource[desc, pos, feedBack];
END;
Routines that hide the details of file naming conventions.
All routines that know about the differences between D-machine and PCedar file names are (supposedly) here.
Currently we assume the old Cedar environment name format: [machine]<root-dir>sub-dir>...>rootName.
DirectorySeparatorChar: CHAR = '>;
DirectorySeparatorString: ROPE = ">";
MesaFileNameToCFileName: PUBLIC PROC [mesaFileName: ROPE, reports: IO.STREAM] RETURNS [ROPE] =
BEGIN
first we pick apart the given mesa file name
mns: MesaNameStuff ← GetMesaNameStuff[mesaFileName];
server: ROPE ← Rope.Substr[mns.fullMesaName, mns.positionsInMesaName.server.start, mns.positionsInMesaName.server.length];
dir: ROPE ← Rope.Substr[mns.fullMesaName, mns.positionsInMesaName.dir.start, mns.positionsInMesaName.dir.length];
subDirs: ROPE ← Rope.Substr[mns.fullMesaName, mns.positionsInMesaName.subDirs.start, mns.positionsInMesaName.subDirs.length];
base: ROPE ← Rope.Substr[mns.fullMesaName, mns.positionsInMesaName.base.start, mns.positionsInMesaName.base.length];
ext: ROPE ← Rope.Substr[mns.fullMesaName, mns.positionsInMesaName.ext.start, mns.positionsInMesaName.ext.length];
ver: ROPE ← Rope.Substr[mns.fullMesaName, mns.positionsInMesaName.ver.start, mns.positionsInMesaName.ver.length];
IF NOT Rope.Equal[ext, "mesa"] THEN
{IO.PutF[reports, "selection not in a mesa file, aborting\N"]; RETURN[NIL]};
see if we can find a corresponding C file
(THIS should really be in symbol finding)
(we don't check any version stamps, we should)
BEGIN
preamble1: ROPE ← Rope.Cat[
Rope.Cat["[", server, "]<", dir, DirectorySeparatorString]];
preamble: ROPE ← IF Rope.IsEmpty[subDirs] THEN preamble1 ELSE Rope.Cat[preamble1, subDirs, DirectorySeparatorString];
cFileName: ROPE ← Rope.Cat[preamble, base, ".c"];
cFileStream: IO.STREAM;
cFileStream ← FS.StreamOpen[cFileName
! FS.Error => CONTINUE];
IF cFileStream = NIL THEN {
cFileName ← Rope.Cat[preamble, base, ".c2c.c"];
cFileStream ← FS.StreamOpen[cFileName
! FS.Error => CONTINUE]};
IF cFileStream = NIL THEN
{IO.PutF[reports, "coresponding c file must be in same directory as selected mesa file, aborting\N"]; RETURN[NIL]};
IO.Close[cFileStream];
now, lets give the client what he wants
IO.PutF[reports, "corresponding C file is %g\N", IO.rope[cFileName]];
IO.PutF[reports, "\Twarning: consistency unchecked!!!\N"];
RETURN[cFileName];
END;
END;
MesaNameStuff: TYPE = RECORD[
fullMesaName: ROPE,
positionsInMesaName: FS.ComponentPositions,
dirOmittedInMesaName: BOOLEAN];
GetMesaNameStuff: PROC[mesaFileName: ROPE] RETURNS[MesaNameStuff] =
BEGIN
fullMesaName: ROPE;
positionsInMesaName: FS.ComponentPositions;
dirOmittedInMesaName: BOOLEAN;
[fullMesaName, positionsInMesaName, dirOmittedInMesaName] ← FS.ExpandName[mesaFileName];
RETURN[[fullMesaName, positionsInMesaName, dirOmittedInMesaName]];
END;
CFileNameToDotOFileName: PUBLIC PROC [cFileName: ROPE, reports: IO.STREAM] RETURNS [ROPE] =
BEGIN
dotOFileName, dotOFileName1: ROPE;
loc: INT;
Change the .c file extension to a .o file extension. Note: We can't assume the .c is at the end of the string. We DO assume that the last ".c" in the name is indeed the .c file extension.
loc ← Rope.FindBackward[cFileName, ".c"];
dotOFileName ← Rope.Replace[cFileName, loc+1, 1, "o"];
Try finding the constructed file name in a machine-dependent subdirectory first, since that is the common case.
dotOFileName1 ← AddMachineDependentSubdirectory[dotOFileName, MachineDependentSubdirectoryName[]];
IF FileExists[dotOFileName1, reports] # NIL THEN RETURN [dotOFileName1];
Check if the original constructed file name exists.
IF FileExists[dotOFileName, reports] # NIL THEN RETURN [dotOFileName];
We couldn't find the desired dotO file. Report an error.
CCE[cirioError];
END;
DotOFileNameToCFileName: PUBLIC PROC [dotOFileName: ROPE, searchDirectories: LIST OF ROPE, reports: IO.STREAM] RETURNS [dotCFileName: ROPE] =
BEGIN
dirPath: ROPE;
rootName: ROPE;
ext: ROPE;
Disassemble the file name into useful (and understandable) components.
[dirPath, rootName, ext] ← DisassembleUnixFileName[dotOFileName];
If dirPath represents a machine-dependent subdirectory then strip that subdirectory from it.
dirPath ← StripMachineDependentSubdirectory[dirPath, MachineDependentSubdirectoryName[]];
Check each legal C file name extension (using the appropriate view on the file system) to see which might have been used.
dotCFileName ← SearchThroughPossibleCExtensions[dirPath, rootName, reports];
IF dotCFileName # NIL THEN RETURN [dotCFileName];
Check if the file is in one of the connections' search directories.
FOR paths: LIST OF ROPE ← searchDirectories, paths.rest WHILE paths # NIL DO
dotCFileName ← SearchThroughPossibleCExtensions[paths.first, rootName, reports];
IF dotCFileName # NIL THEN RETURN [dotCFileName];
ENDLOOP;
We couldn't find a C file for this .o file. Report and error and return NIL.
IO.PutF[reports, "ERROR: Couldn't find a .c file corresponding to %g\n", IO.rope[dotOFileName]];
RETURN [NIL];
END;
DisassembleUnixFileName: PROC [unixFileName: ROPE] RETURNS [dirPath, rootName, ext: ROPE] =
BEGIN
extStartLoc, rootNameStartLoc: INT;
unixFileName ← ConvertFileNameToLocalFormat[unixFileName];
Search backwards for the end of the directory pathname. We assume that the first directory separator encountered, looking backwards, denotes the end of the directory pathname and the beginning of the root name.
rootNameStartLoc ← Rope.FindBackward[unixFileName, DirectorySeparatorString] + 1;
Search forwards from the start of the rootName for the beginning of the extension. We assume that the first "." encountered represents the separator for the extension.
extStartLoc ← Rope.Find[unixFileName, ".", rootNameStartLoc];
IF extStartLoc = -1 THEN
RETURN [NIL, NIL, NIL]
ELSE
extStartLoc ← extStartLoc + 1;
Return the relevant substrings.
dirPath ← Rope.Substr[unixFileName, 0, rootNameStartLoc];
rootName ← Rope.Substr[unixFileName, rootNameStartLoc, extStartLoc-rootNameStartLoc-1];
ext ← Rope.Substr[unixFileName, extStartLoc, Rope.Length[unixFileName]-extStartLoc];
RETURN [dirPath, rootName, ext];
END;
DetermineFileSystemView: PUBLIC PROC [fileName: ROPE, reports: IO.STREAM] RETURNS [ROPE] =
BEGIN
Note: We assume that dirName is already in the form [name-*]<...
Also, we assume that if the file name ends in ~*~ then it is an -nfs view file and we strip off the ~*~.
nfsFileName, uxFileName: ROPE;
locStart, locEnd, locTwiddle: INT;
lenFileName: INT;
locStart ← Rope.Find[fileName, "-"] + 1;
locEnd ← Rope.Find[fileName, "]"];
Try the -nfs file view first because that's the most common case.
Check for a trailing ~*~ and strip it off.
lenFileName ← Length[fileName];
IF Rope.Fetch[fileName, lenFileName-1] = '~ THEN
BEGIN
locTwiddle ← Rope.FindBackward[fileName, "~", lenFileName-2] - 1;
nfsFileName ← Rope.Replace[fileName, locTwiddle, lenFileName-locTwiddle, NIL];
END
ELSE nfsFileName ← fileName;
nfsFileName ← Rope.Replace[nfsFileName, locStart, locEnd-locStart, "nfs"];
IF FileExists[nfsFileName, reports] # NIL THEN RETURN [nfsFileName];
Try the -ux file view if we failed to find the file in the -nfs view.
locEnd ← Rope.Find[fileName, "]"];
uxFileName ← Rope.Replace[fileName, locStart, locEnd-locStart, "ux"];
IF FileExists[uxFileName, reports] # NIL THEN RETURN [uxFileName];
RETURN [NIL];
END;
CExtensionsList: LIST OF ROPELIST[".c2c.c", ".c"];
SearchThroughPossibleCExtensions: PROC [candidateDirPath, rootName: ROPE, reports: IO.STREAM] RETURNS [dotCFileName: ROPE] =
BEGIN
FOR cExtension: LIST OF ROPE ← CExtensionsList, cExtension.rest DO
IF cExtension = NIL THEN RETURN [NIL];
dotCFileName ← Rope.Cat[candidateDirPath, rootName, cExtension.first];
IF FileExists[dotCFileName, reports] # NIL THEN RETURN [dotCFileName];
ENDLOOP;
END;
FileExists: PROC [candidateFileName: ROPE, reports: IO.STREAM] RETURNS [fullFName: ROPE] =
BEGIN
IO.PutF[reports, "FileExists[%g]?\n", IO.rope[candidateFileName]];
[fullFName] ← FS.FileInfo[candidateFileName!
FS.Error => IF error.group = user THEN
BEGIN
IO.PutF[reports, "\tERROR: FS.FileInfo[%g] reported: %g\n", IO.rope[candidateFileName], IO.rope[error.explanation]];
fullFName ← NIL;
CONTINUE;
END];
RETURN [fullFName];
END;
CFileNameToMesaFileName: PUBLIC PROC [dotCFileName: ROPE, reports: IO.STREAM] RETURNS [ROPE] =
BEGIN
dotMesaFileName: ROPE;
loc: INT;
fullFName: ROPE ← NIL;
Note: We assume that the C file name is already in proper local format.
Change the .c file extension to a .mesa file extension. Note: We can't assume the .c is at the end of the string. We DO assume that the last ".c" in the name is indeed the .c file extension.
loc ← Rope.FindBackward[dotCFileName, ".c"];
dotMesaFileName ← Rope.Replace[dotCFileName, loc+1, 1, "mesa"];
Check for a c2c extension and remove it.
loc ← Rope.Find[dotMesaFileName, ".c2c.mesa"];
IF loc # -1 THEN dotMesaFileName ← Rope.Replace[dotMesaFileName, loc, 4];
Check to see if the file name we have constructed actually exists.
fullFName ← FileExists[dotMesaFileName, reports];
IF fullFName = NIL THEN
IO.PutF[reports, "ERROR: Couldn't find a .mesa file corresponding to %g\n", IO.rope[dotCFileName]];
RETURN [dotMesaFileName];
END;
ConvertFileNameToLocalFormat: PUBLIC PROC [fileName: ROPE, defaultDirectory: ROPE ← NIL] RETURNS [ROPE] =
BEGIN
loc: INT;
fileViewChange: BOOLEANTRUE;
factoredFileName: FactoredUnixFileName;
If the filename is already in old cedar format then simply return it.
IF Rope.Fetch[fileName, 0] = '[ THEN RETURN [fileName];
If the filename is not a fullName then return it as is.
IF Rope.Fetch[fileName, 0] # '/ THEN RETURN [fileName];
To convert the filename:
Make sure the fileserver name part is of the form [name-ux]<.
factoredFileName ← FactorUnixFileName[fileName];
fileName ← Rope.Cat[factoredFileName.cirioPrefix, factoredFileName.cirioRemainingFileName];
Change /'s to [,],>'s.
loc ← Rope.Find[fileName, "/"];
fileName ← Rope.Replace[fileName, loc, 1, "["];
loc ← Rope.Find[fileName, "/"];
fileName ← Rope.Replace[fileName, loc, 1, "]<"];
loc ← Rope.Find[fileName, "/"];
WHILE loc # -1 DO
fileName ← Rope.Replace[fileName, loc, 1, DirectorySeparatorString];
loc ← Rope.Find[fileName, "/"];
ENDLOOP;
RETURN [fileName];
END;
ConvertToDirectoryFormat: PUBLIC PROC [dirName: ROPE] RETURNS [ROPE] =
BEGIN
loc: INT ← Rope.Length[dirName];
c: CHAR ← Rope.Fetch[dirName, loc-1];
dirName ← CirioDeltaFace.ConvertFileNameToLocalFormat[dirName];
IF c # DirectorySeparatorChar THEN
BEGIN
User didn't terminate the directory name with a pathname separator. Add it on.
dirName ← Rope.Concat[dirName, DirectorySeparatorString];
END;
RETURN [dirName];
END;
ReplaceDirectory: PUBLIC PROC [fileName: ROPE, newDirectoryName: ROPE ← NIL] RETURNS [ROPE] =
BEGIN
loc: INT ← Rope.FindBackward[fileName, DirectorySeparatorString];
fileName ← Rope.Replace[fileName, 0, loc+1, newDirectoryName];
RETURN [fileName];
END;
MachineDependentSubdirectoryName: PUBLIC PROC [] RETURNS [ROPE] =
BEGIN
Eventually we will want to put code in here that check what machine the target world is running on and return the appropriate value as a function of that information.
RETURN ["sun4"];
END;
StripMachineDependentSubdirectory: PROC [fileName: ROPE, subDirectoryName: ROPE] RETURNS [ROPE] =
BEGIN
lenFileName, lenSubDirectoryName: INT;
lastChar: CHAR;
subDirStartLoc: INT;
endOfFileName: ROPE;
Check for the subdirectory name at the end of the filename and strip it off if found.
lenFileName ← Rope.Length[fileName];
lenSubDirectoryName ← Rope.Length[subDirectoryName];
IF (lenSubDirectoryName+1) > lenFileName THEN RETURN [fileName];
lastChar ← Rope.Fetch[fileName, lenFileName-1];
IF lastChar = DirectorySeparatorChar THEN
subDirStartLoc ← lenFileName - lenSubDirectoryName - 2
ELSE
subDirStartLoc ← lenFileName - lenSubDirectoryName - 1;
endOfFileName ← Rope.Substr[fileName, subDirStartLoc, lenSubDirectoryName+1];
IF Rope.Equal[endOfFileName, Rope.Cat[DirectorySeparatorString, subDirectoryName]] THEN RETURN [Rope.Replace[fileName, subDirStartLoc+1]]
ELSE RETURN [fileName];
END;
AddMachineDependentSubdirectory: PROC [fileName: ROPE, subDirectoryName: ROPE] RETURNS [ROPE] =
BEGIN
loc: INT ← Rope.FindBackward[fileName, DirectorySeparatorString];
separatedSubDirectoryName: ROPE ← Rope.Cat[DirectorySeparatorString, subDirectoryName, DirectorySeparatorString];
RETURN [Rope.Replace[fileName, loc, 1, separatedSubDirectoryName]];
END;
File sets
FileSet: TYPE = REF FileSetBody;
FileSetBody: PUBLIC TYPE = MONITORED RECORD[
files: LIST OF CirioFile,
nOpenStreams: CARD,
openInactiveStreams: LIST OF REF FileStreamInfo,
lastOpenInactiveStream: LIST OF REF FileStreamInfo];
CirioFile: TYPE = REF CirioFileBody;
Removed asAtom 7/17/90 --JLC.
CirioFileBody: PUBLIC TYPE = RECORD[
fileSet: FileSet,
name: PATH,
infoValid: BOOLEAN,
info: SystemInterface.CirioFileInfo,
streams: LIST OF REF FileStreamInfo];
FileStreamInfo: TYPE = RECORD[
file: CirioFile,
active: BOOLEAN,
stream: IO.STREAM,
openFile: PFS.OpenFile,
fileInfo: SystemInterface.CirioFileInfo];
The stream field will be NIL if the stream has been closed. This field may be subsequently used if a new stream is opened on the same file.
fileInfo represents the state of the file when the stream was opened. This may differ from the current state of the file.
NominalMaxOpenStreams: CARD ← 10;
This implementation uses linear search. We assume the number of streams etc that we have to search is relatively small.
CreateFileSet: PUBLIC PROC RETURNS[FileSet] =
{RETURN[NEW[FileSetBody←[ , NIL, 0, NIL, NIL]]]};
CloseFileSet: PUBLIC ENTRY PROC[set: FileSet] =
BEGIN
ENABLE UNWIND => NULL;
FOR fl: LIST OF CirioFile ← set.files, fl.rest WHILE fl # NIL DO
FOR sl: LIST OF REF FileStreamInfo ← fl.first.streams, sl.rest WHILE sl # NIL DO
IF sl.first.stream # NIL THEN
{IO.Close[sl.first.stream]; sl.first.stream ← NIL};
ENDLOOP;
ENDLOOP;
END;
GetCirioFileFromDebuggee: PUBLIC PROC[set: FileSet, name: PATH, debuggee: Rope.ROPE, uniqueID: PFS.UniqueID ← PFS.nullUniqueID] RETURNS[cf: CirioFile] ~ {
localName: PATH ~ DebuggeeUnixNameToDebuggerCedarName[name, debuggee];
cf ← GetCirioFile[set, localName, uniqueID];
IF cf=NIL THEN ShowReport[IO.PutFR["Local name (%g) for remote name (%g, on %g, created %g) doesn't work", [rope[PFS.RopeFromPath[localName]]], [rope[PFS.RopeFromPath[name]]], [rope[IF debuggee#NIL THEN debuggee ELSE "self"]], [rope[FmtUniqueID[uniqueID]]] ], $urgent];
RETURN};
we treat capitalization as significant
GetCirioFile: PUBLIC ENTRY PROC[set: FileSet, name: PATH, uniqueID: PFS.UniqueID ← PFS.nullUniqueID] RETURNS[CirioFile] = {
ENABLE UNWIND => NULL;
RETURN InnerGetCirioFile[set, name, uniqueID]};
InnerGetCirioFile: INTERNAL PROC[set: FileSet, name: PATH, uniqueID: PFS.UniqueID] RETURNS[CirioFile] = {
noDate: BOOL ~ uniqueID=PFS.nullUniqueID;
FOR fl: LIST OF CirioFile ← set.files, fl.rest WHILE fl # NIL DO
IF name.Equal[fl.first.name] AND (noDate OR uniqueID=fl.first.info.uniqueID) THEN RETURN[fl.first];
ENDLOOP;
didn't find it, so we have to make one
{
fileToOpen: PATH ← name;
file: CirioFile ← NEW[CirioFileBody←[set, fileToOpen, TRUE, [uniqueID, 0, fileToOpen], NIL]];
[bytes: file.info.bytes, uniqueID: file.info.uniqueID] ← PFS.FileInfo[file.name, uniqueID !PFS.Error => {
asRope: ROPE ~ PFS.RopeFromPath[file.name];
GOTO DontExist}];
set.files ← CONS[file, set.files];
RETURN[file];
EXITS DontExist => RETURN [NIL];
};
};
need not be an entry procedure because it makes no direct ref to the internals of a set.
can not be an entry procedure because it calls GetCirioFile, as do the implementors of for.
GenCirioFilesForInfo: PUBLIC PROC[set: FileSet, pattern: PATH, for: PROC[SystemInterface.CirioFileInfo] RETURNS[thisIsIt: BOOLEAN]] RETURNS[found: CirioFile] =
BEGIN
ENABLE UNWIND => NULL;
result: CirioFile ← NIL;
local: PROC [
fullFName, attachedTo: PATH,
uniqueID: PFS.UniqueID,
bytes: INT,
mutability: PFS.Mutability,
fileType: PFS.FileType
] RETURNS [continue: BOOL] =
(a PFS.InfoProc)
BEGIN
stop: BOOLEAN ← for[[uniqueID, bytes, fullFName]];
IF stop THEN result ← GetCirioFile[set, fullFName, uniqueID];
RETURN[NOT stop];
END;
PFS.EnumerateForInfo[pattern, local
! PFS.Error => CONTINUE];
RETURN[result];
END;
warnOnVmPfsErr: BOOLFALSE;
verboseGenVm: BOOLFALSE;
need not be an entry procedure because it makes no direct ref to the internals of a set.
can not be an entry procedure because it calls GetCirioFile, as do the implementors of for.
GenVersionMapFilesForInfo: PUBLIC PROC [set: FileSet, map: ATOM, versionStamp: MobDefs.VersionStamp, stems: PathList, suffixes: RopeList, testName: BOOL, for: PROC[SystemInterface.CirioFileInfo] RETURNS[thisIsIt: BOOLEAN], wantedUniqueID: PFS.UniqueID ← PFS.nullUniqueID] RETURNS[found: CirioFile ← NIL] = {
targetMap: ATOM ~ CirioDeltaFace.TargetVersionMapName[map];
vmap: VM2.Map ~ VM2Binding.GetMap[Atom.GetPName[targetMap]];
noDate: BOOL ← wantedUniqueID=PFS.nullUniqueID;
GenerateNames: PROC [Test: PROC [ROPE] RETURNS [BOOL]] RETURNS [BOOL] ~ {
FOR steml: PathList ← stems, steml.rest WHILE steml#NIL DO
FOR sufl: RopeList ← suffixes, sufl.rest WHILE sufl#NIL DO
IF Test[ Rope.Concat[ PFS.RopeFromPath[steml.first],
sufl.first] ] THEN RETURN [TRUE];
ENDLOOP;
ENDLOOP;
RETURN [FALSE]};
TryName: PROC [short: ROPE] RETURNS [BOOL] ~ {
triedDirs: SymTab.Ref ~ SymTab.Create[case: TRUE];
TryTuple: PROC [t: VM2.VersionTuple] RETURNS [BOOL] ~ {
fullRope: ROPE ~ PFS.RopeFromPath[t.name];
foundStamp: VersionMap.VersionStamp ~ t.stamp;
uniqueID: PFS.UniqueID ← t.created;
full: PFSNames.PATH ← t.name;
directory, verless: PFSNames.PATH;
IF verboseGenVm THEN ShowReport[IO.PutFR["\tvm: %g\n", [rope[fullRope]] ], $urgent];
directory ← full.Directory;
verless ← full.StripVersionNumber;
IF noDate THEN {
{bytes: INT;
[fullFName: full, bytes: bytes, uniqueID: uniqueID] ← PFS.FileInfo[full, wantedUniqueID !PFS.Error => {
IF warnOnVmPfsErr THEN ShowReport[IO.PutFR["PFS.FileInfo[%g (from version map)] => PFS.Error[%g, %g]", [rope[fullRope]], [atom[error.code]], [rope[error.explanation]] ], $urgent];
uniqueID ← PFS.nullUniqueID;
GOTO NotMe}];
IF for[[uniqueID, bytes, full]] THEN found ← GetCirioFile[set, full, uniqueID];
EXITS NotMe => found ← NIL};
IF found=NIL AND triedDirs.Insert[PFS.RopeFromPath[directory], $T] THEN {
highestFull: PATH;
highestCreated: PFS.UniqueID;
FilteringFor: PROC[cfi: SystemInterface.CirioFileInfo] RETURNS[thisIsIt: BOOLEAN] ~ {
IF cfi.uniqueID=highestCreated AND cfi.fullName.Equal[highestFull, FALSE] THEN RETURN [FALSE];
RETURN for[cfi]};
{bytes: INT;
[fullFName: highestFull, bytes: bytes, uniqueID: highestCreated] ← PFS.FileInfo[verless !PFS.Error => {
IF warnOnVmPfsErr THEN ShowReport[IO.PutFR["PFS.FileInfo[%g (from version map)] => PFS.Error[%g, %g]", [rope[PFS.RopeFromPath[verless]]], [atom[error.code]], [rope[error.explanation]] ], $urgent];
GOTO NotHere}];
IF highestCreated#uniqueID AND for[[highestCreated, bytes, highestFull]] THEN found ← GetCirioFile[set, highestFull, highestCreated];
EXITS NotHere => short ← short};
IF found=NIL THEN found ← GenCirioFilesForInfo[set, verless.SetVersionNumber[[PFSNames.VersionKind.all, 0]], FilteringFor]};
}
ELSE {bytes: INT;
[fullFName: full, bytes: bytes, uniqueID: uniqueID] ← PFS.FileInfo[verless, wantedUniqueID !PFS.Error => {
IF warnOnVmPfsErr THEN ShowReport[IO.PutFR["PFS.FileInfo[%g (from version map), %g] => PFS.Error[%g, %g]", [rope[PFS.RopeFromPath[verless]]], IF wantedUniqueID.egmt.time=BasicTime.nullGMT THEN [rope["nullGMT"]] ELSE [time[wantedUniqueID.egmt.time]], [atom[error.code]], [rope[error.explanation]] ], $urgent];
GOTO None}];
IF for[[uniqueID, bytes, full]] THEN found ← GetCirioFile[set, full, uniqueID];
EXITS None => NULL};
RETURN [found#NIL]};
pat: VM2.VersionTuple ~ [VM2.ShortNameToPattern[short].name, wantedUniqueID, versionStamp];
RETURN [VM2.ScanMatches[vmap, TryTuple, FALSE, pat].found]};
IF testName THEN [] ← GenerateNames[TryName] ELSE [] ← TryName["*"];
RETURN};
GetNameOfFile: PUBLIC PROC[file: CirioFile] RETURNS[PATH] =
BEGIN
inner: ENTRY PROC[set: FileSet] RETURNS[PATH] =
{ENABLE UNWIND => NULL; RETURN[file.name]};
IF file = NIL THEN RETURN[NIL] ELSE RETURN[inner[file.fileSet]];
END;
GetFileInfo: PUBLIC PROC[file: CirioFile] RETURNS[SystemInterface.CirioFileInfo] =
BEGIN
inner: ENTRY PROC[set: FileSet] RETURNS[SystemInterface.CirioFileInfo] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT file.infoValid THEN ERROR<<made always valid when made immutable
BEGIN
[bytes: file.info.bytes, uniqueID: file.info.uniqueID] ← PFS.FileInfo[file.name];
file.infoValid ← TRUE;
END>>;
RETURN[file.info];
END;
RETURN[inner[file.fileSet]]
END;
GetStreamForFile: PUBLIC PROC[file: CirioFile] RETURNS[IO.STREAM] = {
IF file=NIL
THEN RETURN[NIL]
ELSE RETURN EnterGetStreamForFile[file.fileSet, file]};
EnterGetStreamForFile: ENTRY PROC[set: FileSet, file: CirioFile] RETURNS[IO.STREAM] = {
ENABLE UNWIND => NULL;
RETURN InnerGetStreamForFile[set, file]};
InnerGetStreamForFile: INTERNAL PROC[set: FileSet, file: CirioFile] RETURNS[IO.STREAM] = {
IF NOT file.infoValid THEN ERROR<<immutable>>;
FOR sl: LIST OF REF FileStreamInfo ← file.streams, sl.rest WHILE sl # NIL DO
IF NOT sl.first.active THEN
BEGIN
sl.first.active ← TRUE;
IF sl.first.stream # NIL THEN
BEGIN -- remove from the open but inactive list, and reset position
set: FileSet ← file.fileSet;
stream: REF FileStreamInfo ← sl.first;
removed: BOOLEANFALSE;
info: SystemInterface.CirioFileInfo;
IF set.openInactiveStreams = NIL THEN ERROR;
IF set.openInactiveStreams.first = stream THEN
{set.openInactiveStreams ← set.openInactiveStreams.rest; removed ← TRUE}
ELSE
FOR sl: LIST OF REF FileStreamInfo ← set.openInactiveStreams, sl.rest WHILE sl.rest # NIL DO
IF sl.rest.first = stream THEN
{sl.rest ← sl.rest.rest; removed ← TRUE; EXIT};
ENDLOOP;
IF NOT removed THEN ERROR;
IF set.lastOpenInactiveStream.first = stream THEN
BEGIN -- ugh ... fixup
IF set.openInactiveStreams = NIL THEN set.lastOpenInactiveStream ← NIL
ELSE
FOR sl: LIST OF REF FileStreamInfo ← set.openInactiveStreams, sl.rest DO
IF sl.rest = NIL THEN
{set.lastOpenInactiveStream ← sl; EXIT};
ENDLOOP;
END;
lets check to be sure file hasn't changed
[bytes: info.bytes, uniqueID: info.uniqueID] ← PFS.GetInfo[stream.openFile];
IF info.bytes # stream.fileInfo.bytes OR info.uniqueID # stream.fileInfo.uniqueID THEN -- file has changed
ERROR PFS.Error[[environment, $ImmutableFileChanged, IO.PutFR["The CirioFile for %g of %g len %g has changed", [rope[PFS.RopeFromPath[file.name]]], [rope[FmtUniqueID[file.info.uniqueID]]], [integer[file.info.bytes]] ]]]
<<BEGIN
IO.Close[stream.stream];
stream.stream ← PFS.StreamOpen[file.name];
stream.fileInfo ← info;
file.info ← info; -- update the file info also
RETURN[sl.first.stream];
END>>;
IO.SetIndex[stream.stream, 0];
RETURN[stream.stream];
END
ELSE -- we have to open the stream
BEGIN
sl.first.stream ← PFS.StreamOpen[file.name, read, file.info.uniqueID];
sl.first.openFile ← PFS.OpenFileFromStream[sl.first.stream];
sl.first.fileInfo ← file.info;
<<[bytes: sl.first.fileInfo.bytes, uniqueID: sl.first.fileInfo.uniqueID] ← PFS.GetInfo[sl.first.openFile];
file.info ← sl.first.fileInfo; -- update the file info>>
file.fileSet.nOpenStreams ← file.fileSet.nOpenStreams+1;
CheckNOpenStreamsP[file.fileSet];
RETURN[sl.first.stream];
END;
END;
ENDLOOP;
we didn't find an inactive slot, so we have to make one
BEGIN
stream: IO.STREAMPFS.StreamOpen[file.name, read, file.info.uniqueID];
openFile: PFS.OpenFile ← PFS.OpenFileFromStream[stream];
streamInfo: REF FileStreamInfo ← NEW[FileStreamInfo←[
file,
TRUE,
stream,
openFile,
file.info]];
<<[bytes: streamInfo.fileInfo.bytes, uniqueID: streamInfo.fileInfo.uniqueID] ← PFS.GetInfo[openFile];
file.info ← streamInfo.fileInfo; -- update the file info>>
file.streams ← CONS[streamInfo, file.streams];
file.fileSet.nOpenStreams ← file.fileSet.nOpenStreams+1;
CheckNOpenStreamsP[file.fileSet];
RETURN[file.streams.first.stream];
END;
};
ReleaseStreamForFile: PUBLIC PROC[file: CirioFile, stream: IO.STREAM] =
BEGIN
inner: ENTRY PROC[set: FileSet] =
BEGIN
ENABLE UNWIND => NULL;
FOR sl: LIST OF REF FileStreamInfo ← file.streams, sl.rest WHILE sl # NIL DO
IF sl.first.stream = stream THEN
BEGIN
cell: LIST OF REF FileStreamInfo ← LIST[sl.first];
cell.first.active ← FALSE;
IF file.fileSet.openInactiveStreams = NIL THEN file.fileSet.openInactiveStreams ← cell ELSE file.fileSet.lastOpenInactiveStream.rest ← cell;
file.fileSet.lastOpenInactiveStream ← cell;
CheckNOpenStreamsP[file.fileSet];
RETURN;
END;
ENDLOOP;
ERROR;
END;
inner[file.fileSet];
END;
CheckNOpenStreamsP: PROC[set: FileSet] =
BEGIN
IF set.nOpenStreams > NominalMaxOpenStreams AND set.openInactiveStreams # NIL THEN
BEGIN
toClose: REF FileStreamInfo ← set.openInactiveStreams.first;
set.openInactiveStreams ← set.openInactiveStreams.rest;
IO.Close[toClose.stream];
toClose.stream ← NIL;
IF toClose.active THEN ERROR;
set.nOpenStreams ← set.nOpenStreams - 1;
END;
END;
factored file names
BadDotONameSyntax: ERROR = CODE;
A UnixFileName is:
volume name1 "/" remainingPath machineDependentSubdir stem secondaryExtension extension version
where
volume is either "/", "/volume/", or "/net/" or "/tmp←mnt/net/".
name1 contains no "/" and represents the pathname element that is relative to the volume, if there is one. (Thus name1 may be "".)
machineDependentSubdir is either "/" or "/target-machine-subdir/".
stem is the "root name" of the file and contains no "/".
secondaryExtension is either empty or something like ".c2c"; that is, it includes a ".".
extension is empty or something like ".o"; that is, it includes a ".".
version is empty or "~" someNumber "~" or "!" someNumber.
<<FactoredUnixFileName: TYPE = RECORD[
cirioPrefix: PATH, -- This rope is the Cirio-tool local equivalent of the volume,name1 part of a Unix file name (plus ending "/"). The concatenation [cirioPrefix cirioRemainingFileName] is the local equivalent of the original Unix file name and can be looked up from inside the Cirio tool.
cirioRemainingFileName: PATH, -- This rope is basically the concatenation of [remainingPath, ..., version].
volume: PATH,
name1: PATH,
remainingPath: PATH,
machineDependentSubdir: PATH,
stem: RopePart,
secondaryExtension: RopePart,
extension: RopePart,
version: PFSNames.Version
];
FactorUnixFileName: PUBLIC PROC[unixFileName: PATH] RETURNS[FactoredUnixFileName] =
BEGIN
ENABLE RuntimeError.BoundsFault => GOTO fails;
cirioPrefix: PATH ← PFSNames.EmptyPath;
cirioRemainingFileName: PATH ← PFSNames.EmptyPath;
volume: PATH ← PFSNames.EmptyPath;
name1: PATH ← PFSNames.EmptyPath;
remainingPath: PATH ← PFSNames.EmptyPath;
machineDependentSubdir: PATH ← PFSNames.EmptyPath;
stem: RopePart;
secondaryExtension: RopePart;
extension: RopePart;
version: PFSNames.Version;
fileNameSeq: RopeSeq ← NIL;
tentativeRemainingPath: LIST OF Component ← NIL;
exit: BOOLEANFALSE;
First, figure out volume
curPart: Component ← unixFileName.Fetch[1];
curPartRope: ROPE ← curPart.ComponentRope;
nextPart: INT ← 1;
IF Rope.Equal[curPartRope, "tmp←mnt", FALSE] THEN {
curPart ← unixFileName.Fetch[2];
curPartRope ← curPart.ComponentRope;
IF Rope.Equal[curPartRope, "net", FALSE] THEN {
nextPart ← 3;
volume ← PFSNames.ConstructName[LIST[unixFileName.Fetch[0], unixFileName.Fetch[1], curPart], FALSE, TRUE];
};
}
ELSE IF curPartRope.Equal["volume", FALSE] OR curPartRope.Equal["net", FALSE] OR curPartRope.Equal["pseudo", FALSE] THEN {
nextPart ← 2;
volume ← PFSNames.ConstructName[LIST[unixFileName.Fetch[0], curPart], FALSE, TRUE];
};
IF volume = PFSNames.EmptyPath THEN volume ← PFSNames.ConstructName[LIST[unixFileName.Fetch[0]], FALSE, TRUE];
IF PFS.RopeFromPath[volume].Substr[0,3].Equal["-ux"] THEN volume ← PFS.PathFromRope[PFS.RopeFromPath[volume].Replace[0, 3, ""]];
OK, that's done. Now for name1.
IF unixFileName.ComponentCount # 3 OR volume.ComponentCount = 1 THEN {
curPart ← unixFileName.Fetch[nextPart];
name1 ← PFSNames.ConstructName[LIST[curPart], FALSE, TRUE]}
ELSE nextPart ← nextPart - 1;
That was easy. Now to tackle remainingPath
nextPart ← nextPart + 1;
FOR part: INT ← nextPart, part+1 UNTIL part = unixFileName.ComponentCount-1 OR exit = TRUE DO
nextPart ← part+1;
curPart ← unixFileName.Fetch[part];
IF CirioDeltaFace.IsMachineDependentSubdirectory[curPart] THEN {
exit ← TRUE;
machineDependentSubdir ← PFSNames.ConstructName[LIST[curPart], FALSE, TRUE]}
ELSE
tentativeRemainingPath ← CONS[curPart, tentativeRemainingPath];
ENDLOOP;
remainingPath ← PFSNames.ConstructName[tentativeRemainingPath, FALSE, TRUE, TRUE];
We're still going. Time to disassemble the filename.
curPart ← unixFileName.Fetch[nextPart];
version ← curPart.version;
curPartRope ← curPart.ComponentRope;
{
fnPart: RopePart ← RS.PartFromRope[curPartRope];
fileNameSeq ← RS.ParsePartToSeq[fnPart, '.];
SELECT fileNameSeq.ComponentCount FROM
1 => stem ← fileNameSeq.Fetch[0];
2 => {
stem ← fileNameSeq.Fetch[0];
extension ← fileNameSeq.Fetch[1];
};
3 => {
stem ← fileNameSeq.Fetch[0];
secondaryExtension ← fileNameSeq.Fetch[1];
extension ← fileNameSeq.Fetch[2];
};
ENDCASE => {
stem ← fileNameSeq.Fetch[0];
secondaryExtension ← fileNameSeq.Fetch[1];
extension ← fileNameSeq.Fetch[2];
};
};
cirioPrefix ← volume.Cat[name1];
cirioRemainingFileName ← PFSNames.EmptyPath;
cirioRemainingFileName ← remainingPath.Cat[machineDependentSubdir];
cirioRemainingFileName ← cirioRemainingFileName.Cat[
PFS.PathFromRope[RS.ConstructSeq[LIST[stem,secondaryExtension,extension]].UnparseSeqToPart['.].RopeFromPart]];
Deal with PFS' occasional trailing :
curPartRope ← PFS.RopeFromPath[cirioRemainingFileName];
IF curPartRope.Fetch[curPartRope.Length-1] = ': THEN cirioRemainingFileName ← PFS.PathFromRope[curPartRope.Substr[0, curPartRope.Length - 1]];
cirioRemainingFileName ← cirioRemainingFileName.SetVersionNumber[version];
RETURN[[
cirioPrefix: cirioPrefix,
cirioRemainingFileName: cirioRemainingFileName,
volume: volume,
name1: name1,
remainingPath: remainingPath,
machineDependentSubdir: machineDependentSubdir,
stem: stem,
secondaryExtension: secondaryExtension,
extension: extension,
version: version
]];
EXITS
fails => BadDotONameSyntax[];
END;
GetLocalFileName: PROC [fileName: PFSNames.PATH, serverName: ROPENIL] RETURNS [PFSNames.PATH] ~ {
unixName: FactoredUnixFileName ← [];
ourPrefix: PATH;
If we're supposed to be local, act local.
IF serverName = NIL OR serverName.Equal["sameWorld"] THEN RETURN [fileName];
Parse the given filename into bits
unixName ← FactorUnixFileName[fileName ! BadDotONameSyntax => BEGIN
ShowReport[Rope.Cat["Unable to factor unix name ", PFS.RopeFromPath[fileName]], $urgent];
unixName.cirioPrefix ← NIL;
CONTINUE;
END;];
IF debugNaming THEN SimpleFeedback.PutF[$CirioSystemInterface, oneLiner, $Naming, "Factor[%g, %g] => %g", [rope[PFS.RopeFromPath[fileName]]], [rope[IF serverName=NIL THEN "NIL" ELSE Convert.RopeFromRope[serverName]]], [rope[FmtFufn[unixName]]] ];
IF unixName.cirioPrefix = NIL THEN RETURN[fileName];
Get the "right" volume prefix to get to the relative path
ourPrefix ← CirioDeltaFace.GetOurPrefixForUnixPrefix[unixName.volume, unixName.name1, serverName, "ux"];
IF debugNaming THEN SimpleFeedback.PutF[$CirioSystemInterface, oneLiner, $Naming, "GetOur... => %g", [rope[PFS.RopeFromPath[ourPrefix]]] ];
RETURN[ourPrefix.Cat[unixName.cirioRemainingFileName]];
};
FmtFufn: PROC [fufn: FactoredUnixFileName] RETURNS [ROPE] ~ {
buf: IO.STREAM ~ IO.ROS[];
buf.PutF["[cp:%g, crfn:%g; vol:%g, n1:%g, ", [rope[PFS.RopeFromPath[fufn.cirioPrefix]]], [rope[PFS.RopeFromPath[fufn.cirioRemainingFileName]]], [rope[PFS.RopeFromPath[fufn.volume]]], [rope[PFS.RopeFromPath[fufn.name1]]] ];
buf.PutF["rp:%g, mds:%g, stem:%g, se:%g, e:%g, v:", [rope[PFS.RopeFromPath[fufn.remainingPath]]], [rope[PFS.RopeFromPath[fufn.machineDependentSubdir]]], [rope[RS.RopeFromPart[fufn.stem]]], [rope[RS.RopeFromPart[fufn.secondaryExtension]]], [rope[RS.RopeFromPart[fufn.extension]]] ];
SELECT fufn.version.versionKind FROM
numeric => buf.Put[ [cardinal[fufn.version.version]] ];
none => buf.PutRope["none"];
lowest => buf.PutRope["lowest"];
highest => buf.PutRope["highest"];
next => buf.PutRope["next"];
all => buf.PutRope["all"];
ENDCASE => buf.PutRope["?"];
buf.PutChar[']];
RETURN buf.RopeFromROS[]};
>>
DebuggeeUnixNameToDebuggerCedarName: PUBLIC PROC [unixName: PATH, debuggee: ROPE] RETURNS [cedar: PATH] ~ {
lc: Component;
c0: ROPE ← "-ux";
cedar ← unixName;
lc ← cedar.ShortName[];
IF NOT cedar.Fetch[0].ComponentRope.IsEmpty THEN ShowReport[IO.PutFR["Given unix name (%g) on debuggee (%g) with non-empty 0'th component", [rope[PFS.RopeFromPath[unixName]]], [rope[debuggee]] ], $normal]
ELSE IF lc.version.versionKind # none THEN ShowReport[IO.PutFR["Given unix name (%g) on debuggee (%g) with non-empty version", [rope[PFS.RopeFromPath[unixName]]], [rope[debuggee]] ], $normal]
ELSE {
rs: RopeSeq ~ RS.ParsePartToSeq[MPN.ComponentName[lc], '.];
n: NAT ~ RS.Length[rs];
le: RopePart ~ rs.Fetch[n-1];
lel: INT ~ le.Length[];
IF lel>2 AND le.Fetch[0]='~ AND le.Fetch[lel-1]='~ THEN {
ver: CARD ~ Convert.CardFromRope[le.Substr[start: 1, len: lel-2].ToRope !Convert.Error => GOTO NotNum];
IF ver > CARDINAL.LAST THEN ShowReport[IO.PutFR["Can't naturalize huge version of unix name (%g) from debuggee (%g) - left in -ux: syntax", [rope[PFS.RopeFromPath[unixName]]], [rope[debuggee]] ], $urgent]
ELSE {
lc.name.len ← lc.name.len - (lel+1);
lc.version ← [numeric, ver];
cedar ← cedar.ReplaceShortName[lc];
c0 ← "-vux"};
EXITS NotNum => c0 ← c0};
cedar ← cedar.ReplaceComponent[0, [name: [c0, 0, c0.Length]]];
};
IF debuggee#NIL AND NOT debuggee.Equal["SameWorld", FALSE] THEN {
Warn: PROC [r: ROPE] ~ {ShowReport[r, $urgent]};
local: PATH ~ CirioDeltaFace.DebuggeeNameToDebuggerName[cedar, debuggee, Warn];
IF debugNaming THEN SimpleFeedback.PutF[$SystemInterfaceImpl, oneLiner, $Naming, "Localize[%g, %g] => %g", [rope[debuggee]], [rope[PFS.RopeFromPath[cedar]]], [rope[PFS.RopeFromPath[local]]] ];
cedar ← local};
RETURN};
debugNaming: BOOLFALSE;
FmtUniqueID: PUBLIC PROC [uid: PFS.UniqueID] RETURNS [ROPE] ~ {
time: ROPE ← "error formatting time";
IF uid.egmt.time = BasicTime.nullGMT THEN time ← "unspecified" ELSE time ← Convert.RopeFromTime[uid.egmt.time, years, seconds, FALSE, FALSE !Convert.Error => CONTINUE];
IF uid.egmt.usecs#0 THEN time ← IO.PutFR["%g plus %gus", [rope[time]], [cardinal[uid.egmt.usecs]] ];
IF uid.host # [0, 0] THEN time ← IO.PutFR["%g host [%xH, %xH]", [rope[time]], [cardinal[uid.host.a]], [cardinal[uid.host.b]] ];
RETURN [time]};
VersionStampToUniqueID: PUBLIC PROC [vs: MobDefs.VersionStamp] RETURNS [uid: PFS.UniqueID] ~ {
IF vs[1]#0 THEN CCE[cirioError, IO.PutFR["MobDefs.VersionStamp[%xH, %xH] can't be converted to a create date", [cardinal[vs[0]]], [cardinal[vs[1]]] ]];
uid ← [egmt: [time: BasicTime.FromNSTime[vs[0] !
BasicTime.OutOfRange => CCE[cirioError, IO.PutFR["MobDefs.VersionStamp[%xH, 0] doesn't encode a valid create date", [cardinal[vs[0]]] ]] ]]];
RETURN};
UniqueIDToVersionStamp: PUBLIC PROC [uid: PFS.UniqueID] RETURNS [vs: MobDefs.VersionStamp] ~ {
IF uid.egmt.usecs # 0 OR uid.host # [0, 0] THEN ERROR CCE[cirioError, IO.PutFR["PFS.UniqueID %g can't be converted to a MobDefs.VersionStamp", [rope[FmtUniqueID[uid]]] ]];
vs ← [BasicTime.ToNSTime[uid.egmt.time], 0];
RETURN};
this computation of vs is designed to be equivalent to MobAccessImpl.ComputeSourceVersionStamp.
Verbosify: Commander.CommandProc = {warnOnVmPfsErr ← verboseGenVm ← TRUE};
Tersify: Commander.CommandProc = {warnOnVmPfsErr ← verboseGenVm ← FALSE};
Commander.Register["Cirio.verboseScanVersionMaps", Verbosify];
Commander.Register["Cirio.terseScanVersionMaps", Tersify];
END.