<> <> DIRECTORY BasicTime, Booting, Commander, CommandTool, FS, IO, List, Process, PutGet, Random, Rope, RopeFile, TextNode, UserProfile; Cookie: CEDAR MONITOR IMPORTS BasicTime, Booting, Commander, CommandTool, FS, IO, List, Process, PutGet, Random, Rope, RopeFile, TextNode, UserProfile = BEGIN ROPE: TYPE = Rope.ROPE; RopeList: TYPE = LIST OF ROPE; WeightedIndexSet: TYPE = REF WeightedIndexSetRep; WeightedIndexSetRep: TYPE = RECORD [ length: CARDINAL, totalWeight: REAL, indices: SEQUENCE size: CARDINAL OF Index]; Index: TYPE = REF IndexRep; IndexRep: TYPE = RECORD [ incrWeight, sumWeight: REAL, alles: ROPE, entries: SEQUENCE size: CARDINAL OF IndexEntry]; IndexEntry: TYPE = RECORD [pos: INT]; rs: Random.RandomStream _ NIL; cookies: WeightedIndexSet _ NewWeightedIndexSet[10]; files: RopeList _ NIL; hold: RopeList _ NIL; installedDirectory: ROPE _ CommandTool.CurrentWorkingDirectory[]; defaultProbability: REAL _ 0.1; defaultInitial: INT _ 1; enabled: BOOL _ FALSE; AddFile: PROC [to: WeightedIndexSet, weight: REAL, fileName: ROPE] RETURNS [new: WeightedIndexSet, count: INT] = BEGIN indexStream: IO.STREAM _ NIL; ok: BOOLEAN _ TRUE; alles, dataName, indexName: ROPE _ NIL; dWeight: REAL; count _ 0; IF to.length = to.size THEN { new _ NEW [WeightedIndexSetRep[to.size*2]]; new.length _ to.length; new.totalWeight _ to.totalWeight; FOR i: CARDINAL IN [0 .. to.length) DO new[i] _ to[i]; ENDLOOP; } ELSE new _ to; dataName _ FS.ExpandName[fileName, installedDirectory].fullFName; indexName _ FS.ExpandName[fileName.Concat[".Index"], installedDirectory].fullFName; indexStream _ FS.StreamOpen[indexName !FS.Error => CONTINUE]; IF indexStream # NIL THEN { indexTime: BasicTime.GMT _ indexStream.GetTime[]; dataTime: BasicTime.GMT _ BasicTime.nullGMT; dataTime _ FS.FileInfo[dataName !FS.Error => CONTINUE].created; IF dataTime = BasicTime.nullGMT THEN RETURN; ok _ dataTime = indexTime; } ELSE ok _ FALSE; IF NOT ok THEN { messages _ messages.Cat["Had to remake index for ", fileName, " --- tell maintainer\n"]; MakeIndex[fileName, installedDirectory]; indexStream _ NIL; indexStream _ FS.StreamOpen[indexName !FS.Error => CONTINUE]; IF indexStream = NIL THEN RETURN; }; indexStream.SetIndex[indexStream.GetLength[] - 10]; count _ indexStream.GetCard[]; alles _ RopeFile.Create[name: dataName, raw: FALSE !FS.Error => CONTINUE]; IF alles = NIL THEN RETURN; hold _ CONS[alles, hold]; indexStream.SetIndex[0]; [] _ indexStream.GetTime[]; dWeight _ weight * count; new[new.length] _ NEW [IndexRep[count]]; new[new.length].incrWeight _ dWeight; new.totalWeight _ new[new.length].sumWeight _ dWeight + (IF new.length > 0 THEN new[new.length-1].sumWeight ELSE 0); new[new.length].alles _ alles; FOR i: INT IN [0 .. count) DO new[new.length][i].pos _ indexStream.GetInt[]; ENDLOOP; indexStream.Close[]; new.length _ new.length + 1; END; NewWeightedIndexSet: PROC [size: CARDINAL] RETURNS [new: WeightedIndexSet] = {new _ NEW [WeightedIndexSetRep[size]]; new.totalWeight _ new.length _ 0}; Allocate: ENTRY PROC = {workers _ workers + 1}; Release: ENTRY PROC = {workers _ workers - 1; BROADCAST done}; workers: INTEGER _ 0; done: CONDITION; NoticeChanges: UserProfile.ProfileChangedProc --PROC [reason: ProfileChangeReason]-- = BEGIN newFiles: RopeList _ UserProfile.ListOfTokens[ key: "Cookie.Sources", default: LIST [ "Almond.Cookies", "Legal.Cookies", "Doit.LOTS", "Snippets", "Dans.Cookies"]]; temp1, temp2: RopeList; defaultProbability _ IO.RIS[UserProfile.Token[key: "Cookie.Probability", default: "0.1"]].GetReal[! IO.Error => CONTINUE]; defaultInitial _ UserProfile.Number[key: "Cookie.Initial", default: 1]; enabled _ UserProfile.Boolean[key: "Cookie.Enabled", default: FALSE]; IF NOT enabled THEN RETURN; Allocate[]; BEGIN ENABLE UNWIND => Release[]; IF workers > 1 THEN ERROR; temp1 _ files; temp2 _ newFiles; WHILE (temp1 # NIL) AND (temp2 # NIL) DO IF NOT temp1.first.Equal[temp2.first] THEN EXIT; temp1 _ temp1.rest; temp2 _ temp2.rest; ENDLOOP; IF (temp1 # NIL) OR (temp2 # NIL) THEN BEGIN weight: REAL _ 1.0; files _ newFiles; fileSizes _ NIL; cookies.totalWeight _ cookies.length _ 0; FOR newFiles _ newFiles, newFiles.rest WHILE newFiles # NIL DO r: ROPE _ newFiles.first; IF r.Length[] = 0 THEN LOOP; IF r.Fetch[0] IN ['0 .. '9] THEN BEGIN in: IO.STREAM _ IO.RIS[r]; weight _ in.GetReal[!IO.Error => CONTINUE]; END ELSE BEGIN count: INT; [cookies, count] _ AddFile[cookies, weight, r]; <> fileSizes _ CONS[[r, count], fileSizes]; END ENDLOOP; END; END; Release[]; END; MakeIndex: PROC [rootName, wdir: ROPE] = BEGIN in, index: IO.STREAM _ NIL; created: BasicTime.GMT; count: CARDINAL _ 0; inName: ROPE _ FS.ExpandName[rootName, wdir].fullFName; outName: ROPE _ FS.ExpandName[rootName.Concat[".Index"], wdir].fullFName; in _ FS.StreamOpen[inName !FS.Error => CONTINUE]; index _ FS.StreamOpen[outName, create !FS.Error => CONTINUE]; IF in = NIL OR index = NIL THEN RETURN; [created: created] _ FS.FileInfo[inName]; index.PutF["%g\n", IO.time[created]]; DO where: INT _ in.GetIndex[]; [] _ in.SkipWhitespace[]; IF in.EndOf[] THEN EXIT; index.PutF[" %g", IO.card[where]]; [] _ in.GetRopeLiteral[]; count _ count + 1; ENDLOOP; index.PutF["\n %8g\n", IO.card[count]]; index.Close[]; END; FileSize: TYPE = RECORD [ name: ROPE, count: INT]; fileSizes: LIST OF FileSize _ NIL; ConvertVanilla: PROC [fromName, toName: ROPE] = BEGIN from, to: IO.STREAM; from _ FS.StreamOpen[fromName]; to _ FS.StreamOpen[toName, create]; WHILE NOT from.EndOf[] DO fortune: ROPE; fortune _ from.GetLineRope[]; to.PutF["\"%q\"\n", IO.rope[fortune]]; ENDLOOP; from.Close[]; to.Close[]; END; ConvertHacker: PROC [fromName, toName: ROPE] = BEGIN from, to: IO.STREAM; author, msg: ROPE _ NIL; tabs: INT _ 0; from _ FS.StreamOpen[fromName]; to _ FS.StreamOpen[toName, create]; WHILE NOT from.EndOf[] DO toke: ROPE _ from.GetTokenRope[CreditBreak].token; IF toke.Equal["\n"] THEN BEGIN IF msg.Length[] > 0 THEN {IF author.Length[] > 0 THEN to.PutF["\"%q -- %g\"\n", IO.rope[msg], IO.rope[author]] ELSE to.PutF["\"%q\"\n", IO.rope[msg]]}; author _ msg _ NIL; tabs _ 0; END ELSE IF toke.Equal["\t"] THEN {IF (tabs _ tabs+1) > 2 THEN ERROR} ELSE SELECT tabs FROM 0 => IF toke.Length[] # 1 THEN ERROR; 1 => author _ toke; 2 => msg _ toke; ENDCASE => ERROR; ENDLOOP; from.Close[]; to.Close[]; END; CreditBreak: IO.BreakProc --PROC [char: CHAR] RETURNS [CharClass]-- = {RETURN [SELECT char FROM '\n, '\t => break, ENDCASE => other]}; ConvertParagraphs: PROC [fromName, toName: ROPE] = BEGIN from, to: IO.STREAM; msg: ROPE _ NIL; from _ FS.StreamOpen[fromName]; to _ FS.StreamOpen[toName, create]; WHILE NOT from.EndOf[] DO toke: ROPE _ from.GetLineRope[]; IF toke.Length[] = 0 THEN BEGIN to.PutF["\"%q\"\n", IO.rope[msg]]; msg _ NIL; END ELSE BEGIN IF msg = NIL THEN BEGIN first: CHAR _ toke.Fetch[0]; IF first IN [0C .. IO.SP] THEN ERROR; msg _ toke; END ELSE msg _ msg.Cat["\n", toke]; END; ENDLOOP; from.Close[]; to.Close[]; END; ConvertStringy: PROC [fromName, toName, sep: ROPE] = BEGIN from: ROPE _ RopeFile.Create[name: fromName]; fromLength: INT = from.Length[]; to: IO.STREAM = FS.StreamOpen[toName, create]; sepLen: INT = sep.Length[]; start: INT _ 0; WHILE start < fromLength DO sepStart: INT _ Rope.Index[s1: from, s2: sep, case: TRUE, pos1: start]; to.PutF["\"%q\"\n", IO.rope[from.Substr[start: start, len: sepStart - start]]]; start _ sepStart + sepLen; ENDLOOP; to.Close[]; END; ConvertNodes: PROC [fromName, toName: ROPE] = BEGIN fromRoot: TextNode.Ref _ PutGet.FromFile[fromName]; cur: TextNode.Ref _ fromRoot; to: IO.STREAM _ FS.StreamOpen[toName, create]; WHILE (cur _ TextNode.StepForward[cur]) # NIL DO tn: TextNode.RefTextNode _ TextNode.NarrowToTextNode[cur]; r: ROPE _ NIL; IF tn # NIL THEN r _ TextNode.NodeRope[tn]; IF r.Length[] > 0 THEN to.PutF["\"%q\"\n", IO.rope[r]]; ENDLOOP; to.Close[]; END; CookieControl: TYPE = REF CookieControlRep; CookieControlRep: TYPE = RECORD [ probability: REAL _ 0.1, ferSure: INT _ 1 --because I can italicize in an After proc, but not a directly invoked one! ]; giveCookieHandle: Commander.CommandProcHandle _ NEW [Commander.CommandProcObject _ [proc: GiveCookie]]; NumArgs: INT = 2; names: ARRAY [0 .. NumArgs) OF ROPE = ["Probability", "Initial"]; Cookit: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = BEGIN cmdLine: IO.STREAM _ IO.RIS[cmd.commandLine]; index: CARDINAL _ 0; cc: CookieControl; cc _ NARROW[List.Assoc[aList: cmd.propertyList, key: $CookieControl]]; IF cc = NIL THEN BEGIN cmd.propertyList _ List.PutAssoc[ aList: cmd.propertyList, key: $CookieControl, val: cc _ NEW [CookieControlRep _ []]]; cmd.propertyList _ CommandTool.AddProcToList[aList: cmd.propertyList, listKey: $After, proc: giveCookieHandle]; END; cc^ _ [defaultProbability, defaultInitial]; [] _ cmdLine.SkipWhitespace[]; WHILE NOT cmdLine.EndOf[] DO name, value, temp: ROPE _ NIL; kind: IO.TokenKind; [kind, value, ] _ cmdLine.GetCedarTokenRope[]; SELECT kind FROM tokenDECIMAL, tokenREAL => IF index < NumArgs THEN name _ names[index] ELSE {result _ $Failure; msg _ syntaxErrMsg}; tokenID => BEGIN name _ value; [kind, value, ] _ cmdLine.GetCedarTokenRope[!IO.Error, IO.EndOfStream => {result _ $Failure; msg _ syntaxErrMsg; CONTINUE}]; IF result = $Failure THEN RETURN; IF value.Equal["="] OR value.Equal["_"] OR value.Equal["~"] OR value.Equal[":"] THEN [kind, value, ] _ cmdLine.GetCedarTokenRope[!IO.Error, IO.EndOfStream => {result _ $Failure; msg _ syntaxErrMsg; CONTINUE}]; END; ENDCASE => {result _ $Failure; msg _ syntaxErrMsg}; IF result = $Failure THEN RETURN; IF name.Equal["probability", FALSE] THEN cc.probability _ IO.RIS[value].GetReal[!IO.Error, IO.EndOfStream => {result _ $Failure; msg _ syntaxErrMsg; CONTINUE}] ELSE IF name.Equal["initial", FALSE] THEN cc.ferSure _ IO.RIS[value].GetInt[!IO.Error, IO.EndOfStream => {result _ $Failure; msg _ syntaxErrMsg; CONTINUE}] ELSE {result _ $Failure; msg _ IO.PutFR["No such keyword as \"%q\"", IO.rope[name]]}; IF result = $Failure THEN RETURN; [] _ cmdLine.SkipWhitespace[]; index _ index + 1; ENDLOOP; IF (NOT enabled) AND (cc.probability > 0 OR cc.ferSure > 0) THEN messages _ messages.Cat["You won't get your cookies because your profile doesn't enable Cookie.\n"]; END; syntaxErrMsg: ROPE = "Syntax Error. Correct Usage: Cookie [[Probability [=|_|~|:]] [[Initial [=|_|~|:]] ]]"; messages: ROPE _ NIL; GiveCookie: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = BEGIN toDo: INT _ rs.ChooseInt[min: 0, max: 999999999]; cc: CookieControl _ NARROW[List.Assoc[key: $CookieControl, aList: cmd.propertyList]]; IF messages # NIL THEN { cmd.out.PutF["%l%g%l", IO.rope["bi"], IO.rope[messages], IO.rope["BI"]]; messages _ NIL}; IF enabled THEN { WHILE cc.ferSure > 0 DO cc.ferSure _ cc.ferSure - 1; [] _ ReallyGiveCookie[cmd]; ENDLOOP; IF cc.probability*1E9 > toDo THEN [] _ ReallyGiveCookie[cmd]; }; END; ReallyGiveCookie: PROC [cmd: Commander.Handle] = BEGIN Enter: ENTRY PROC = {WHILE workers > 0 DO WAIT done ENDLOOP}; Enter[]; IF cookies.length = 0 THEN cmd.out.PutF["%lNo cookies to give! (your UserProfile entry \"Cookie.Sources\" lists only bad files%l\n", IO.rope["bi"], IO.rope["BI"]] ELSE BEGIN spot: REAL _ cookies.totalWeight*rs.ChooseInt[min: 0, max: 1000000000]/1000000000.0; file, entry: CARDINAL; is: IO.STREAM; rope: ROPE; FOR file _ 0, file+1 DO IF cookies[file].sumWeight >= spot THEN EXIT; ENDLOOP; entry _ rs.ChooseInt[min: 0, max: cookies[file].size-1]; is _ IO.RIS[cookies[file].alles.Substr[start: cookies[file][entry].pos]]; rope _ is.GetRopeLiteral[]; cmd.out.PutF["%l%s%l\n", IO.rope["i"], IO.rope[rope], IO.rope["I"]]; is.Close[]; END; END; Randomize: Booting.RollbackProc --PROC[clientData: REF ANY]-- = {rs _ Random.Create[seed: -1]}; TRUSTED {Process.InitializeCondition[@done, Process.SecondsToTicks[1]]}; Randomize[NIL]; Booting.RegisterProcs[r: Randomize]; UserProfile.CallWhenProfileChanges[NoticeChanges]; Commander.Register[key: "Cookie", proc: Cookit, doc: "sets up fortune cookery"]; END.