<> <> DIRECTORY IO, FS, Rope, UserProfileOps, UserProfile; UserProfileOpsImpl: CEDAR PROGRAM IMPORTS FS, UserProfile, IO EXPORTS UserProfileOps = BEGIN OPEN UserProfileOps; Expand: PUBLIC PROC [format: Rope.ROPE _ NIL, v1, v2, v3, v4, v5: IO.Value _ [null[]]] RETURNS [names: LIST OF Rope.ROPE _ NIL] ~ { KeyIndex: TYPE = [0..MaxKeys]; -- counts number of `%k' sequences encountered keyIndex: KeyIndex _ 0; -- counts number of %k sequences encountered kkFormat: Rope.ROPE; -- folds rope after %k to %%k expansion putfFormat: Rope.ROPE; -- holds rope after %g expansion kSubFormat: Rope.ROPE; -- holds format after %k substitution in, out: IO.STREAM; eos: BOOLEAN _ FALSE; keys, defaults: ARRAY [0..2] OF Rope.ROPE _ ALL[NIL]; -- parameters to MakeOptionList breakChar, c, k: CHAR; <<-- expand %k to %%k to preserve across PutFR call>> in _ IO.RIS[format]; out _ IO.ROS[]; c _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; WHILE ~eos DO SELECT c FROM '% => { k _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; IF eos THEN EXIT; IF k=KeyChar THEN out.PutF["%%%%k"] ELSE { out.PutChar[c]; out.PutChar[k] }; }; ENDCASE => out.PutChar[c]; c _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; ENDLOOP; kkFormat _ out.RopeFromROS[]; in.Close[]; <<-- now expand out the %g stuff using regular formatted IO stuff>> putfFormat _ IO.PutFR[kkFormat, v1, v2, v3, v4, v5]; <<-- now replace the %k stuff with %g and parameters>> eos _ FALSE; in _ IO.RIS[putfFormat]; out _ IO.ROS[]; keyIndex _ 0; -- start counting keys - first is 0 c _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; WHILE ~eos DO SELECT c FROM '% => { -- possible %k sequence k _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; IF eos THEN EXIT; IF k=KeyChar THEN { -- BINGO keyOut: IO.STREAM _ IO.ROS[]; -- stream to build Key defaultOut: IO.STREAM; -- stream to build Default <<-- set the break char>> breakChar _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; IF eos THEN EXIT; IF keyIndex=MaxKeys THEN ERROR; -- only 3 keys allowed MAX -- build key rope terminated by breakChar k _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; WHILE ~eos DO IF k=breakChar THEN EXIT; keyOut.PutChar[k]; k _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; ENDLOOP; keys[keyIndex] _ keyOut.RopeFromROS[]; IF eos THEN EXIT; <<-- build the default rope terminated by breakChar>> defaultOut _ IO.ROS[]; k _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; WHILE ~eos DO IF k=breakChar THEN EXIT; defaultOut.PutChar[k]; k _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; ENDLOOP; defaults[keyIndex] _ defaultOut.RopeFromROS[]; <<-- substitute %k with %g in format stream>> out.PutF["%%g"]; keyIndex _ keyIndex + 1; } ELSE { out.PutChar[c]; out.PutChar[k] }; }; ENDCASE => out.PutChar[c]; c _ in.GetChar[! IO.EndOfStream => {eos _ TRUE; CONTINUE}]; ENDLOOP; kSubFormat _ out.RopeFromROS[]; in.Close[]; <<-- now build the name list>> names _ MakeOptionList[kSubFormat, keys[0], defaults[0], keys[1], defaults[1], keys[2], defaults[2]]; }; MakeOptionList: PROC [ format:Rope.ROPE, k1, def1: Rope.ROPE _ NIL, k2, def2: Rope.ROPE _ NIL, k3, def3: Rope.ROPE _ NIL] RETURNS [names: LIST OF Rope.ROPE _ NIL] ~ { <> <> << >> << e.g. Given the user profile entries>> << AISseparationKeys.green: "-green -grn -g">> << AISextensions: "ais pic">> <<>> <> <> <> <> <<] >> <> <<("Image-grn.AIS", "Image-grn.pic", "Image-green.AIS", "Image-green.pic", "Image-g.AIS", "Image-g.pic")>> << If the key `kN' is not found then the default "defN" string is used. N.B. The first item in the list is guaranteed to be formed from the first element of each key list.>> <<>> Reverse: PROC [l1, l2: LIST OF Rope.ROPE _ NIL] RETURNS [LIST OF Rope.ROPE] ~ { IF l1=NIL THEN RETURN [l2] ELSE RETURN[Reverse[l1.rest, CONS[l1.first, l2]]]; }; up1, up2, up3: LIST OF Rope.ROPE; IF def1=NIL THEN def1_""; IF def2=NIL THEN def2_""; IF def3=NIL THEN def3_""; up1 _ UserProfile.ListOfTokens[k1, LIST[def1]]; up2 _ UserProfile.ListOfTokens[k2, LIST[def2]]; up3 _ UserProfile.ListOfTokens[k3, LIST[def3]]; FOR l1:LIST OF Rope.ROPE_up1, l1.rest WHILE l1#NIL DO FOR l2:LIST OF Rope.ROPE_up2, l2.rest WHILE l2#NIL DO FOR l3:LIST OF Rope.ROPE_up3, l3.rest WHILE l3#NIL DO newName: Rope.ROPE; SELECT TRUE FROM k1=NIL => newName _ IO.PutFR[format]; k2=NIL => newName _ IO.PutFR[format, IO.rope[l1.first]]; k3=NIL => newName _ IO.PutFR[format, IO.rope[l1.first], IO.rope[l2.first]]; ENDCASE => newName _ IO.PutFR[format, IO.rope[l1.first], IO.rope[l2.first], IO.rope[l3.first]]; names _ CONS[newName, names]; ENDLOOP; ENDLOOP; ENDLOOP; names _ Reverse[names, NIL]; }; Enumerate: PUBLIC PROC [ proc: EnumerateProc, format: Rope.ROPE _ NIL, v1, v2, v3, v4, v5: IO.Value _ [null[]] ] ~ { list: LIST OF Rope.ROPE _ Expand[format, v1, v2, v3, v4,v5]; WHILE list#NIL DO IF ~proc[list.first] THEN EXIT; list _ list.rest; ENDLOOP; }; StreamOpen: PUBLIC PROC [ format: Rope.ROPE _ NIL, v1, v2, v3, v4, v5: IO.Value _ [null[]], accessOptions: FS.AccessOptions _ $read, streamOptions: FS.StreamOptions _ FS.defaultStreamOptions, keep: CARDINAL _ 1, createByteCount: FS.ByteCount _ 2560, streamBufferParms: FS.StreamBufferParms _ FS.defaultStreamBufferParms, extendFileProc: FS.ExtendFileProc _ NIL ] RETURNS [stream: IO.STREAM _ NIL] ~ { TryAndOpen: EnumerateProc ~ { tryNext _ TRUE; stream _ FS.StreamOpen[ rope, accessOptions, streamOptions, keep, createByteCount, streamBufferParms, extendFileProc ! FS.Error => { tryNext _ FALSE; CONTINUE }]; }; Enumerate[TryAndOpen, format, v1, v2, v3, v4, v5]; }; END.