PFSCanonicalNamesImpl.mesa
Carl Hauser, August 23, 1989 11:40:59 am PDT
Chauser, May 24, 1990 10:25 am PDT
DIRECTORY
Ascii USING [NUL, SP, TAB],
Convert USING [CardFromRope, RopeFromCard],
IO USING [BreakProc, GetTokenRope, STREAM],
PFSBackdoor,
PFSNames,
PFSCanonicalNames,
Rope USING [Cat, Substr, Fetch, IsEmpty, Length];
PFSCanonicalNamesImpl: CEDAR PROGRAM
IMPORTS Convert, IO, Rope, PFSBackdoor, PFSNames
EXPORTS PFSCanonicalNames
SHARES PFSNames
~ BEGIN OPEN PFSNames;
Unparsed:
TYPE ~
RECORD [
canonicalUnparsing: ROPE ← NIL
];
UnparseName:
PUBLIC PROC [name:
PATH]
RETURNS [fullFName:
ROPE] ~ {
unparsed: REF Unparsed;
serverComponent: BOOL ← TRUE;
cproc: ComponentProc ~ {
fullFName ← Rope.Cat[fullFName, UnparseComponent[comp]];
IF serverComponent
THEN {
IF NOT comp.name.len=0 THEN fullFName ← Rope.Cat[fullFName, ":"];
serverComponent ← FALSE;
}
};
sproc: SeparatorProc ~ {
IF
NOT serverComponent
THEN {
IF separatorPresent THEN fullFName ← Rope.Cat[fullFName, "/"];
}
ELSE {
IF NOT separatorPresent THEN serverComponent ← FALSE;
};
};
name ← PFSNames.NonNIL[name];
unparsed ←
WITH name.GetUnparsingHint[]
SELECT
FROM
unp: REF Unparsed => unp,
ENDCASE => NIL;
IF unparsed#NIL THEN RETURN [unparsed.canonicalUnparsing];
Map[name, cproc, sproc];
};
UnparseComponent:
PUBLIC
PROC [component: Component]
RETURNS [
ROPE] ~ {
RETURN[Rope.Cat[
component.name.base.Substr[component.name.start, component.name.len], UnparseVersion[component.version]]];
};
UnparseVersion:
PUBLIC
PROC [version: Version]
RETURNS [
ROPE] ~ {
RETURN [
SELECT version.versionKind
FROM
none => NIL,
lowest => "!L",
highest => "!H",
all => "!*",
numeric => Rope.Cat["!", Convert.RopeFromCard[version.version]],
ENDCASE => NIL
];
};
ParseName:
PUBLIC
PROC [name:
ROPE]
RETURNS [parsedName:
PATH] ~ {
Canonical parsing.
absolute, directory: BOOL ← FALSE;
comps: LIST OF Component ← NIL;
IF name.IsEmpty[] THEN RETURN [NIL];
absolute ← name.Fetch[0]='/;
IF absolute THEN comps ← CONS[ [name~["", 0, 0]], comps ];
directory ← name.Fetch[name.Length[]-1]='/;
{
ENABLE Error => PFSBackdoor.ProduceError[invalidNameSyntax, Rope.Cat[name, " is syntactically invalid as a Unix-style file name"]];
pos: NAT ← IF absolute THEN 1 ELSE 0;
limit: NAT ← name.Length[];
firstComponent: BOOL ← TRUE;
ScanComponent:
PROC []
RETURNS [] ~ {
nextComp: Component;
[nextComp, pos] ← ParseComponent[name, pos];
IF firstComponent
AND
NOT absolute
THEN {
len: INT ← nextComp.name.len;
IF len > 0
THEN
IF Rope.Fetch[nextComp.name.base, nextComp.name.start+len-1] = ':
THEN {
absolute ← TRUE;
nextComp.name.len ← nextComp.name.len-1;
};
};
firstComponent ← FALSE;
comps ← CONS[nextComp, comps];
};
ScanSeparator:
PROC []
RETURNS [] ~ {
IF name.Fetch[pos]#'/ THEN ERROR Error[];
pos ← pos+1;
};
DO
IF pos >= limit THEN EXIT;
ScanComponent[];
IF pos >= limit THEN EXIT;
ScanSeparator[];
ENDLOOP;
};
RETURN[ConstructName[components: comps, absolute: absolute, directory: directory, reverse: TRUE, unparsed: NEW[Unparsed ← [name]] ]];
};
ParseComponent:
PUBLIC
PROC [name:
ROPE, first:
NAT ← 0]
RETURNS [component: Component ← [], next:
NAT] ~ {
limit: NAT ← name.Length[];
next ← first;
DO
IF next >= limit
THEN
{
component.name ← [name, first, next-first];
EXIT;
};
SELECT name.Fetch[next]
FROM
'! => {
component.name ← [name, first, next-first];
[component.version, next] ← ParseVersion[name, next];
EXIT;
};
'/ => {
component.name ← [name, first, next-first];
EXIT;
};
ENDCASE => next ← next+1;
ENDLOOP;
};
ParseVersion:
PUBLIC
PROC [v:
ROPE, first:
NAT ← 0]
RETURNS [version: Version ← [none], next:
NAT] ~ {
limit: NAT ← v.Length[];
next ← first;
IF next>=limit THEN RETURN;
IF v.Fetch[next]#'! THEN ERROR Error[];
next ← next+1;
IF next>=limit THEN RETURN;
SELECT v.Fetch[next]
FROM
IN ['0..'9] => {
start: NAT ← next;
next ← next+1;
WHILE next < limit AND v.Fetch[next] IN ['0..'9] DO next ← next+1 ENDLOOP;
RETURN [ [numeric, Convert.CardFromRope[v.Substr[start, next-start]]], next ];
};
'H, 'h => RETURN[ [highest], next+1 ];
'L, 'l => RETURN[ [lowest], next+1 ];
'N, 'n => RETURN[ [next], next+1 ];
'* => RETURN[ [all], next+1 ];
ENDCASE => NULL;
ERROR Error[];
};
ParseNameFromStream:
PUBLIC PROC [s:
IO.STREAM]
RETURNS [name:
PATH] ~ {
A convenient way to read a whitespace-delimited name from a stream;
FileNameProc:
IO.BreakProc ~ {
OPEN Ascii;
RETURN [
SELECT char
FROM
NUL, SP, TAB => sepr,
ENDCASE => other ];
};
ropeName: ROPE ← IO.GetTokenRope[s, FileNameProc].token;
RETURN[ParseName[ropeName]];
};
END.