DIRECTORY Ascii USING [NUL, SP, TAB], Commander USING [CommandProc, Handle, Register], Convert USING [CardFromRope, Error, RopeFromCard], IO USING [BreakProc, CharClass, GetTokenRope, PutF, rope, STREAM], PFSBackdoor, PFSNames, ProcessProps USING [GetProp], RefText USING [Append, Fetch, Length, ObtainScratch, ReleaseScratch], Rope USING [Concat, Fetch, FromRefText, IsEmpty, Length, ROPE, Substr], RuntimeError USING [ ], XNSFilingNames USING [ ], XNSFilingOps USING [Op], XNSFilingPrivate USING [ ]; XNSFilingPathImpl: CEDAR MONITOR IMPORTS Commander, Convert, IO, PFSBackdoor, PFSNames, ProcessProps, RefText, Rope EXPORTS XNSFilingNames, XNSFilingPrivate SHARES PFSNames ~ { ROPE: TYPE ~ Rope.ROPE; DebugOut: PUBLIC PROC [msg: ROPE] ~ { IF ( debugging ) THEN { cmd: Commander.Handle ~ NARROW[ProcessProps.GetProp[$CommanderHandle]]; out: IO.STREAM ~ IF ( cmd # NIL ) THEN cmd.out ELSE NARROW[ProcessProps.GetProp[$StdOut]]; IF ( out = NIL ) THEN { --MessageWindow.Append[msg, TRUE];-- RETURN }; out.PutF["%g\n", IO.rope[msg] ]; }; }; debugging: BOOL ¬ FALSE; DebugCmd: Commander.CommandProc ~ { atom: ATOM ~ NARROW[cmd.procData.clientData]; SELECT atom FROM $true => { debugging ¬ TRUE }; $false => { debugging ¬ FALSE }; ENDCASE => { result ¬ $Failure }; }; Error: ERROR ~ CODE; Unparsed: TYPE ~ RECORD [ canonicalUnparsing: ROPE ¬ NIL ]; UnparseName: PUBLIC PROC [name: PFSNames.PATH] RETURNS [filingName: ROPE] ~ { serverComponent: BOOL ¬ TRUE; cproc: PFSNames.ComponentProc ~ { chunk: ROPE ~ UnparseComponent[comp]; filingName ¬ filingName.Concat[chunk]; IF ( serverComponent ) THEN { IF ( comp.name.len # 0 ) THEN filingName ¬ filingName.Concat[":"]; serverComponent ¬ FALSE; } }; sproc: PFSNames.SeparatorProc ~ { IF ( NOT serverComponent ) THEN { IF ( separatorPresent ) THEN filingName ¬ filingName.Concat["/"] } ELSE { IF ( NOT separatorPresent ) THEN serverComponent ¬ FALSE }; }; unparsed: REF Unparsed; name ¬ PFSNames.NonNIL[name]; unparsed ¬ WITH name.GetUnparsingHint[] SELECT FROM unp: REF Unparsed => unp, ENDCASE => NIL; IF ( unparsed # NIL ) THEN RETURN [unparsed.canonicalUnparsing]; name.Map[cproc, sproc]; }; UnparseComponent: PUBLIC PROC [component: PFSNames.Component] RETURNS [r: ROPE] ~ { chunk: ROPE ~ component.name.base.Substr[component.name.start, component.name.len]; vers: ROPE ~ UnparseVersion[component.version]; r ¬ Rope.Concat[chunk, vers]; }; UnparseVersion: PUBLIC PROC [version: PFSNames.Version] RETURNS [r: ROPE] ~ { HardCase: PROC RETURNS [v: ROPE] ~ INLINE { numb: ROPE ~ Convert.RopeFromCard[version.version]; v ¬ Rope.Concat["!", numb]; }; r ¬ SELECT version.versionKind FROM none => NIL, lowest => "!-", highest => "!+", all => "!*", numeric => HardCase[], ENDCASE => NIL; }; ParseName: PUBLIC PROC [name: ROPE] RETURNS [parsedName: PFSNames.PATH] ~ { absolute, directory: BOOL ¬ FALSE; comps: LIST OF PFSNames.Component ¬ NIL; IF ( name.IsEmpty[] ) THEN RETURN [NIL]; IF ( FALSE ) THEN DebugOut[name]; absolute ¬ ( name.Fetch[0] = '/ ); IF ( absolute ) THEN comps ¬ CONS[ [], comps ]; directory ¬ ( name.Fetch[name.Length[]-1] = '/ ); { ENABLE Error => { msg: ROPE ~ name.Concat[": syntactically invalid file name"]; PFSBackdoor.ProduceError[invalidNameSyntax, msg]; }; pos: NAT ¬ IF ( absolute ) THEN 1 ELSE 0; limit: NAT ¬ name.Length[]; firstComponent: BOOL ¬ TRUE; ScanComponent: PROC ~ { nextComp: PFSNames.Component; base: ROPE; start, len: INT; [nextComp, pos] ¬ ParseComponent[name, pos]; base ¬ nextComp.name.base; start ¬ nextComp.name.start; len ¬ nextComp.name.len; SELECT TRUE FROM ( NOT firstComponent ) => { NULL }; ( absolute ) => { NULL }; ( len <= 0 ) => { NULL }; ( base.Fetch[start+len.PRED] = ': ) => { absolute ¬ TRUE; nextComp.name.len ¬ len.PRED; }; ENDCASE => { NULL }; firstComponent ¬ FALSE; comps ¬ CONS[nextComp, comps]; }; ScanSeparator: PROC ~ { IF ( name.Fetch[pos] # '/ ) THEN ERROR Error[]; pos ¬ pos.SUCC; }; DO IF ( pos >= limit ) THEN EXIT; ScanComponent[]; IF ( pos >= limit ) THEN EXIT; ScanSeparator[]; ENDLOOP; }; { unparsed: REF Unparsed ~ NEW[Unparsed ¬ [name]]; parsedName ¬ PFSNames.ConstructName[components: comps, absolute: absolute, directory: directory, reverse: TRUE, unparsed: unparsed]; }; }; ParseComponent: PUBLIC PROC [name: ROPE, first: NAT ¬ 0] RETURNS [component: PFSNames.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.SUCC; ENDLOOP; }; ParseVersion: PUBLIC PROC [v: ROPE, first: NAT ¬ 0] RETURNS [version: PFSNames.Version ¬ [none], next: NAT] ~ { limit: NAT ¬ v.Length[]; next ¬ first; IF ( next >= limit ) THEN RETURN; IF ( v.Fetch[next]#'! ) THEN ERROR Error[]; next ¬ next.SUCC; SELECT v.Fetch[next] FROM IN ['0..'9] => { vers: ROPE; card: CARD32; start: NAT ¬ next; next ¬ next.SUCC; WHILE ( next < limit ) AND ( v.Fetch[next] IN ['0..'9] ) DO next ¬ next.SUCC ENDLOOP; vers ¬ v.Substr[start, next-start]; card ¬ Convert.CardFromRope[vers]; version ¬ [numeric, card]; -- next ¬ next; RETURN; }; 'H, 'h => RETURN[ [highest], next.SUCC ]; 'L, 'l => RETURN[ [lowest], next.SUCC ]; '+ => RETURN[ [highest], next.SUCC ]; '- => RETURN[ [lowest], next.SUCC ]; 'N, 'n => RETURN[ [next], next.SUCC ]; '* => RETURN[ [all], next.SUCC ]; ENDCASE => NULL; ERROR Error[]; }; ParseNameFromStream: PUBLIC PROC [s: IO.STREAM] RETURNS [name: PFSNames.PATH] ~ { FileNameProc: IO.BreakProc ~ { OPEN Ascii; class: IO.CharClass ~ SELECT char FROM NUL, SP, TAB => sepr, ENDCASE => other; RETURN [class]; }; ropeName: ROPE ¬ IO.GetTokenRope[s, FileNameProc].token; name ¬ ParseName[ropeName]; }; RopeForPath: PROC [path: PFSNames.PATH] RETURNS [rope: ROPE ¬ NIL] ~ { }; ParseFSName: PROC [file: ROPE] RETURNS [dir: LIST OF ROPE, name: ROPE, vers: PFSNames.Version] ~ { dirSeen: BOOL ¬ FALSE; versR: ROPE ¬ NIL; stopAt: INT ¬ file.Length[]; FOR i: INT DECREASING IN [0..file.Length[]) DO c: CHAR ~ file.Fetch[index: i]; SELECT c FROM '! => { versR ¬ file.Substr[start: i]; stopAt ¬ i.PRED }; '> => { IF ( dirSeen ) THEN dir ¬ CONS[file.Substr[start: i.SUCC, len: stopAt.PRED], dir] ELSE name ¬ file.Substr[start: i.SUCC, len: stopAt - i]; dirSeen ¬ TRUE; stopAt ¬ i-1; }; '< => { dir ¬ CONS[file.Substr[start: i.SUCC, len: stopAt-i], dir]; EXIT; -- this must be the last char }; ENDCASE => NULL; ENDLOOP; vers ¬ [$none]; IF ( versR.Length[] > 1 ) THEN { rope: ROPE ~ Rope.Substr[base: versR, start: 1]; card: CARD32 ¬ 0; card ¬ Convert.CardFromRope[rope ! Convert.Error => { CONTINUE }]; IF ( card > 0 ) THEN vers ¬ [$numeric, card]; }; }; ConvertFSNameToXNS: PUBLIC PROC [file: ROPE, op: XNSFilingOps.Op] RETURNS [name: ROPE ¬ NIL] ~ { text: REF TEXT ~ RefText.ObtainScratch[nChars: 200]; AddChar: PROC [c: CHAR] = { text[text.length] ¬ c; text.length ¬ text.length.SUCC }; versionR: ROPE ¬ NIL; text.length ¬ 0; FOR i: INT IN [0..file.Length[]) DO c: CHAR ~ file.Fetch[index: i]; SELECT c FROM '< => { LOOP }; -- throw the < away. '> => { AddChar['/] }; -- the XNS server uses / as a file separator '* => { AddChar[c]; AddChar[c] }; -- kludge to handle * '! => { versionR ¬ file.Substr[start: i]; EXIT }; -- the whole version including the ! ENDCASE => { AddChar[c] }; ENDLOOP; name ¬ Rope.FromRefText[s: text]; RefText.ReleaseScratch[t: text]; IF ( versionR # NIL ) THEN { SELECT versionR.Length[] FROM 0 => { versionR ¬ NIL }; -- no version specified 1 => { versionR ¬ SELECT op FROM delete => "!-", -- lowest enumerate => NIL, enumerateNames => NIL, rename => "!+", -- highest retrieve => "!+", -- highest store => "!+", -- is this right? ENDCASE => ERROR; -- can't happen }; 2 => { -- is it H, L or *? versionR ¬ SELECT versionR.Fetch[1] FROM 'h, 'H => "!+", 'l, 'L => "!-", '* => NIL, ENDCASE => versionR; }; ENDCASE => NULL; IF ( versionR # NIL ) THEN name ¬ name.Concat[versionR]; }; }; ConvertPathToFile: PROC [path: REF TEXT, isDir: BOOL ¬ FALSE] RETURNS [file: ROPE ¬ NIL] ~ { slashSeen: BOOL ¬ FALSE; state: {name, version} ¬ name; name: REF TEXT ¬ RefText.ObtainScratch[nChars: 200]; ver: REF TEXT ¬ RefText.ObtainScratch[nChars: 200]; AddChar: PROC [t: REF TEXT, c: CHAR] ~ { t[t.length] ¬ c; t.length ¬ t.length.SUCC }; name.length ¬ 0; ver.length ¬ 0; AddChar[name, '<]; -- since the path doesn't have this FOR i: INT IN [0..RefText.Length[path]) DO c: CHAR ~ RefText.Fetch[path, i]; SELECT c FROM '! => { ver.length ¬ 0; state ¬ version }; '/ => { slashSeen ¬ TRUE; ver.length ¬ 0; -- discard the version information since it is still a subdir state ¬ name; -- go back to collection characters AddChar[name, '>]; -- insert FS's notion of what a delineator is }; '> => { -- an illegal character RefText.ReleaseScratch[t: name]; RefText.ReleaseScratch[t: ver]; RETURN; }; ENDCASE => { SELECT state FROM name => AddChar[name, c]; version => AddChar[ver, c]; ENDCASE => ERROR; }; ENDLOOP; IF ( state = version ) THEN { -- this is the version from the item last on the path IF ( ( NOT slashSeen ) OR ( isDir ) ) THEN AddChar[name, '>]; AddChar[name, '!]; -- indicate the version name ¬ RefText.Append[to: name, from: ver]; -- and glue it on }; file ¬ Rope.FromRefText[s: name]; RefText.ReleaseScratch[t: name]; RefText.ReleaseScratch[t: ver]; }; Commander.Register["XNSFiling.DebugOn", DebugCmd, "start logging", $true]; Commander.Register["XNSFiling.DebugOff", DebugCmd, "stop logging", $false]; }. ” XNSFilingPathImpl.mesa Copyright Σ 1990, 1992 by Xerox Corporation. All rights reserved. Bill Jackson (bj), May 30, 1990 3:33 pm PDT MessageWindow USING [Append], Magic XNSFilingNames Canonical parsing. A convenient way to read a whitespace-delimited name from a stream; Filename Utils the version rope is just a ! so do the 'default' discard the version information since it is still a subdir ΚΞ–(cedarcode) style•NewlineDelimiter ™šœ™Jšœ Οeœ7™BJ™+Icode˜—šΟk ˜ Kš œžœžœžœžœ˜Kšœ žœ!˜0Kšœžœ%˜2Kšžœžœ2žœ˜BKšœžœ ™Kšœ ˜ Kšœ ˜ Kšœ žœ ˜Kšœžœ8˜EKšœžœ/žœ ˜GKšœ žœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜K˜—šΟnœžœž˜ Kšžœžœ4˜RKšžœ!˜(Kšžœ ˜Kšžœžœžœ˜head2šΟz™šŸœžœžœžœ˜%šžœžœ˜Kšœžœ)˜GKšœžœžœžœ žœžœ žœžœ ˜[Kš žœ žœžœΟc$œžœ˜FKšœžœ ˜ K˜—K˜K˜—Kšœ žœžœ˜K˜šŸœ˜#Kšœžœžœ˜-šžœž˜Kšœžœ˜Kšœžœ˜ Kšžœ˜!—K˜——š ™šŸœžœžœ˜K˜—š œ žœžœžœžœ˜;K˜—š Ÿ œžœžœžœžœžœ˜MKšœžœžœ˜šœ!˜!Kšœžœ˜%K˜&šžœžœ˜Kšžœžœ%˜BKšœžœ˜Kšœ˜—Kšœ˜—šœ!˜!šžœžœ˜Kšžœžœžœ&˜IKš žœžœžœžœžœ˜B—Kšœ˜—Kšœ žœ ˜K˜šœ žœžœž˜3Kšœžœ˜Kšžœžœ˜—Kšžœžœžœžœ˜@K˜K˜K˜—š Ÿœžœžœ!žœžœ˜SKšœžœH˜SKšœžœ%˜/K˜K˜K˜—š Ÿœžœžœžœžœ˜Mš Ÿœžœžœžœžœ˜+Kšœžœ)˜3K˜K˜—šœžœž˜#Kšœžœ˜ K˜K˜K˜ Kšœ˜Kšžœžœ˜—K˜K˜—š Ÿ œžœžœžœžœžœ˜KK™Kšœžœžœ˜"Kšœžœžœžœ˜(Kšžœžœžœžœ˜(Kšžœžœžœ˜!K˜"Kšžœžœ žœ˜/K˜1˜šžœ ˜Kšœžœ4˜=Kšœ1˜1K˜—Kš œžœžœžœžœ˜)Kšœžœ˜Kšœžœžœ˜šŸ œžœ˜Kšœ˜Kšœžœžœ˜K˜,K˜Pšžœžœž˜Kšœžœžœ˜#Kšœžœ˜Kšœžœ˜šœžœ ˜(Kšœ žœ˜Kšœžœ˜K˜—Kšžœžœ˜—Kšœžœ˜Kšœžœ˜K˜—šŸ œžœ˜Kšžœžœžœ ˜/Kšœ žœ˜Kšœ˜—šž˜Kšžœžœžœ˜Kšœ˜Kšžœžœžœ˜K˜Kšžœ˜—K˜—šœ˜Kšœ žœ žœ˜0Kšœjžœ˜„Kšœ˜—Kšœ˜K˜—šŸœžœžœžœ žœžœ,žœ˜tKšœžœ˜K˜ šž˜šžœžœ˜K˜+Kšžœ˜K˜—šžœž˜ šœ˜K˜+K˜5Kšžœ˜Kšœ˜—šœ˜K˜+Kšžœ˜Kšœ˜—Kšžœžœ˜—Kšžœ˜—K˜K˜—šŸ œžœžœžœ žœžœ,žœ˜oKšœžœ˜K˜ Kšžœžœžœ˜!Kšžœžœžœ ˜+Kšœ žœ˜šžœžœ˜šžœ˜Kšœžœžœ˜Kšœžœ˜Kšœ žœ˜Kš žœžœžœ žœ žœžœ˜UK˜#K˜"Kšœ‘œ‘˜*Kšžœ˜Kšœ˜—Kšœ žœžœ˜)Kšœ žœžœ˜)Kšœžœžœ˜%Kšœžœžœ˜%Kšœ žœžœ˜'Kšœžœžœ˜"Kšžœžœ˜—Kšžœ ˜K˜K˜—šŸœžœžœžœžœžœžœ˜QK™DšŸ œžœ˜Kšžœ˜ šœžœ žœž˜&Kšžœžœžœ ˜Kšžœ ˜—Kšžœ ˜Kšœ˜—Kšœ žœžœ%˜8K˜Kšœ˜—K˜—š ™š Ÿ œžœžœžœžœžœ˜FK˜K˜—šŸ œžœžœžœžœžœžœžœ˜bKšœ žœžœ˜Kšœžœžœ˜Kšœžœ˜š žœžœž œžœž˜.K•StartOfExpansion [base: ROPE, index: INT _ 0]šœžœ˜šžœž˜ Kšœ2žœ˜9šœ˜–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šžœ ˜Kšžœžœžœžœ˜BKšžœžœ˜8—Kšœ žœ˜K˜ Kšœ˜—šœ˜Kšœžœžœ˜;Kšžœ‘˜#K˜—Kšžœžœ˜—Kšžœ˜—K˜šžœžœ˜ Kšœžœ&˜0Kšœžœ˜Kšœ6žœ˜BKšžœžœ˜-K˜—K˜K˜—šŸœžœžœžœžœžœžœ˜`Kšœžœžœ&˜4K–[nChars: NAT]šŸœžœžœ7žœ˜TKšœ žœžœ˜K˜šžœžœžœž˜#K– [base: ROPE, index: INT _ 0]šœžœ˜šžœž˜ Kšœžœ‘˜$Kšœ‘,˜CKšœ"‘˜7Kšœ*žœ‘$˜VKšžœ˜—Kšžœ˜—K–([base: ROPE _ NIL, rest: ROPE _ NIL]˜!K–[t: REF TEXT]˜ šžœžœžœ˜šžœž˜Kšœžœ‘˜0šœ˜Kšœ0™0šœ žœž˜Kšœ‘ ˜Kšœ žœ˜Kšœžœ˜Kšœ‘ ˜Kšœ‘ ˜Kšœ‘˜ Kšžœžœ‘˜"—Kšœ˜—šœ‘˜šœ žœž˜(Kšœ˜Kšœ˜Kšœžœ˜ Kšžœ ˜—K˜—Kšžœžœ˜—Kšžœžœžœ˜8Kšœ˜—Kšœ˜—K˜šŸœžœžœžœ žœžœžœžœžœ˜\Kšœ žœžœ˜K˜Kšœžœžœ&˜4Kšœžœžœ&˜3K–[nChars: NAT]š Ÿœžœžœžœžœ+žœ˜UK˜K˜Kšœ‘#˜6šžœžœžœž˜*Kšœžœ˜!šžœž˜ ˜*Kšœ:™:—šœ˜Kšœ žœ˜Kšœ‘=˜NKšœ‘#˜1Kšœ‘-˜@Kšœ˜—šœ‘˜K–[t: REF TEXT]˜ K–[t: REF TEXT]˜Kšžœ˜K˜—šžœ˜ šžœž˜Kšœ˜Kšœ˜Kšžœžœ˜—Kšœ˜——Kšžœ˜—šžœžœ‘5˜SK–P[to: REF TEXT, from: RefText.PureText, start: NAT _ 0, len: NAT _ 32767]šžœžœ žœ žœ˜=Kšœ‘˜*Kšœ,‘˜=Kšœ˜—K–>[s: REF READONLY TEXT, start: NAT _ 0, len: NAT _ 32767]˜!K–[t: REF TEXT]˜ K–[t: REF TEXT]˜Kšœ˜K˜——KšœJ˜JKšœK˜KK˜K˜——…—%h6Κ