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; Error: ERROR ~ CODE; 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] ~ { 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] ~ { 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. ΚPFSCanonicalNamesImpl.mesa Carl Hauser, August 23, 1989 11:40:59 am PDT Chauser, May 24, 1990 10:25 am PDT Canonical parsing. A convenient way to read a whitespace-delimited name from a stream; Κε– "cedar" style•NewlineDelimiter ˜šœ™Icode™,K™"—J™šΟk ˜ Kš œœœœœ˜Kšœœ˜+Kšœœœ˜+Kšœ ˜ Kšœ ˜ K˜Kšœœ'˜1K˜—K˜KšΟnœœ˜$Kšœ)˜0Kšœ˜Kšœ ˜Kšœœœ ˜K˜šžœœœ˜K˜—šœ œœ˜Kšœœ˜Kšœ˜K˜—š ž œ œœœ œ˜CKšœ œ ˜Kšœœœ˜šœ˜Kšœ8˜8šœœ˜Kšœœœ'˜BKšœœ˜Kšœ˜—Kšœ˜—šœ˜šœœœ˜Kšœœ&˜>K˜—šœ˜Kšœœœœ˜5Kšœ˜—Kšœ˜—Kšœ˜šœ œœ˜3Kšœœ˜Kšœœ˜—Kšœ œœœ˜:K˜K˜K˜—š žœœœœœ˜GKšœu˜{K˜K˜—š žœœœœœ˜Ašœœ˜(Kšœœ˜ K˜K˜K˜ Kšœ@˜@Kšœ˜K˜—K˜K˜—š ž œœœœœœ˜BK™Kšœœœ˜"Kšœœœ œ˜Kšœœœœ˜$K˜Kšœ œ œ˜:K˜+˜Kšœ}˜ƒKš œœœ œœ˜%Kšœœ˜Kšœœœ˜šž œœœ˜%J˜Jšœ,˜,šœœœ œ˜)Kšœœ˜šœ œœAœ˜YKšœ œ˜Kšœ(˜(K˜—Kšœ˜—Kšœœ˜Kšœœ˜K˜—šž œœœ˜%Kšœœœ ˜)K˜ Kšœ˜—š˜Kšœœœ˜Kšœ˜Kšœœœ˜K˜Kšœ˜—K˜—KšœUœ œ˜…Kšœ˜K˜—šžœœœœ œœ#œ˜kKšœœ˜Kšœ ˜ š˜šœœ˜Kšœ+˜+Kšœ˜K˜—šœ˜šœ˜Kšœ+˜+Kšœ5˜5Kšœ˜Kšœ˜—šœ˜Kšœ+˜+Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—K˜K˜—šž œœœœ œœ#œ˜fKšœœ˜Kšœ ˜ Kšœ œœ˜Kšœœœ ˜'Kšœ˜Kšœ œœ˜šœœ˜šœ˜Kšœœ˜K˜Kš œœœ œœ˜JKšœH˜NKšœ˜—Kšœ œ˜&Kšœ œ˜&Kšœ œ˜$Kšœœ˜Kšœœ˜—Kšœ ˜K˜K˜—š žœ œ œœœ˜HK™Dšž œœ˜Kšœ˜ šœœ˜Kšœœœ ˜Kšœ ˜—K˜—Kšœ œœ%˜8Kšœ˜Kšœ˜—K˜Kšœ˜—…—δ“