<> <> <> <<>> DIRECTORY Ascii USING [NUL, SP, TAB], Convert USING [CardFromRope, RopeFromCard], IO USING [BreakProc, GetTokenRope, STREAM], PFSNames, PFSBackdoor, PFSCFSNames, Rope USING [Cat, Substr, Fetch, IsEmpty, Length]; PFSCFSNamesImpl: CEDAR PROGRAM IMPORTS Convert, IO, Rope, PFSBackdoor, PFSNames EXPORTS PFSCFSNames SHARES PFSNames ~ BEGIN OPEN PFSNames; Unparsed: TYPE ~ RECORD [ cfsUnparsing: ROPE _ NIL ]; Error: ERROR = CODE; UnparseName: PUBLIC PROC [name: PATH] RETURNS [fullFName: ROPE] ~ { unparsed: REF Unparsed _ WITH name.GetUnparsingHint[] SELECT FROM unp: REF Unparsed => unp, ENDCASE => NIL; serverComponent: BOOL _ TRUE; state: {start, insideSquare, startingAnglePair, doingAnglePair, done} _ start; insideAngleComp: ROPE; cproc: ComponentProc ~ { IF serverComponent THEN {serverComponent _ FALSE; RETURN}; SELECT state FROM start => NULL; insideSquare => NULL; startingAnglePair => { insideAngleComp _ UnparseComponent[comp]; state _ doingAnglePair; RETURN; }; done => NULL; ENDCASE => ERROR; fullFName _ Rope.Cat[fullFName, UnparseComponent[comp]]; }; sproc: SeparatorProc ~ { IF serverComponent THEN { IF separatorPresent THEN RETURN ELSE serverComponent _ FALSE; }; SELECT state FROM start => { IF separatorPresent THEN { fullFName _ Rope.Cat[fullFName, "["]; state _ insideSquare } ELSE state _ done; }; insideSquare => { fullFName _ Rope.Cat[fullFName, "]"]; state _ startingAnglePair }; doingAnglePair => { IF separatorPresent THEN fullFName _ Rope.Cat[fullFName, "<", insideAngleComp, ">"] ELSE fullFName _ Rope.Cat[fullFName, insideAngleComp]; state _ done; }; done => IF separatorPresent THEN fullFName _ Rope.Cat[fullFName, ">"]; ENDCASE => ERROR; }; IF unparsed#NIL THEN RETURN [unparsed.cfsUnparsing]; 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] ~ { <> absolute, directory, pattern: 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]='] OR name.Fetch[name.Length[]-1]='>; { ENABLE Error => PFSBackdoor.ProduceError[invalidNameSyntax, Rope.Cat[name, " is syntactically invalid as a Cedar-style file name"]]; pos: NAT _ IF absolute THEN 1 ELSE 0; limit: NAT _ name.Length[] - (IF directory THEN 1 ELSE 0); initialComponent: BOOL _ absolute; -- current component must end with ] or ]<; otherwise must end with > ScanComponent: PROC [] RETURNS [] ~ { nextComp: Component; [nextComp, pos] _ ParseComponent[name, pos]; comps _ CONS[nextComp, comps]; }; ScanSeparator: PROC [] RETURNS [] ~ { IF initialComponent THEN { IF name.Fetch[pos] # '] THEN ERROR Error[]; pos _ pos+1; IF 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 ]; '* => RETURN[ [all], next+1 ]; ENDCASE => NULL; ERROR Error[]; }; ParseNameFromStream: PUBLIC PROC [s: IO.STREAM] RETURNS [name: PATH] ~ { <> 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.