DIRECTORY Ascii USING [Lower, Upper], Atom USING [GetPName, MakeAtom], BasicTime USING [GMT, Period], ChoiceButtons USING [EnumTypeRef, BuildEnumTypeSelection, BuildTextPrompt, GetSelectedButton, UpdateChoiceButtons], Containers USING [ ChildXBound, ChildYBound, Container, Create ], Convert USING [Error, IntFromRope], FS USING [ComponentPositions, ExpandName, Position], Icons USING [ IconFlavor, NewIconFromFile ], IO USING [atom, BreakProc, EndOfStream, GetTokenRope, GetLineRope, TokenProc, GetChar, SkipWhitespace, PeekChar, GetRopeLiteral, Error, IDProc, STREAM, PutRope, PutF, PutFR, real, RIS, RopeFromROS, rope, ROS, time], Labels USING [Create, Label, Set], LoganBerryStub USING [AttributeType, AttributeValue, BuildIndices, Close, CompactLogs, DeleteEntry, Entry, EntryProc, Error, ErrorCode, Open, OpenDB, WriteEntry], LoganBerryStubExtras USING [Describe2, IndexInfo, SchemaInfo], LoganQuery USING [ComplexCursor, EndGenerate, Equal, FilterEntries, FilterProc, GenerateEntries, Prefix, NextEntry, RE, Soundex, Wildcard], Menus USING [AppendMenuEntry, ChangeNumberOfLines, ClickProc, CreateEntry, CreateMenu, GetNumberOfLines, InsertMenuEntry, Menu, MenuLine], MessageWindow USING [Append, Blink], Process USING [Abort, GetCurrent, InvalidProcess], RefTab USING [Ref, EachPairAction, Fetch, Pairs, Create, Store], Rope USING [Cat, Compare, Concat, Equal, Fetch, Find, FromChar, Index, Length, Replace, ROPE, Run, SkipTo, Substr, Translate, TranslatorType], Rules USING [ Create, Rule ], Tempus USING [Adjust, Parse, Precision, MakeRope, Unintelligible], TiogaButtons USING [CreateButton, CreateViewer, TiogaButtonProc], TypeScript USING [ Create, ChangeLooks, PutChar, PutRope ], ViewerBLT USING [ChangeNumberOfLines], ViewerClasses USING [ Viewer, ViewerClassRec, ViewerFlavor, ViewerRec ], ViewerEvents USING [ EventProc, RegisterEventProc ], ViewerOps USING [AddProp, CloseViewer, CreateViewer, MoveViewer, OpenIcon, PaintViewer], ViewerTools USING [GetContents, SetContents], LoganBerryBrowser; LoganBerryBrowserImpl: CEDAR PROGRAM IMPORTS Ascii, Atom, BasicTime, ChoiceButtons, Containers, Convert, FS, Icons, IO, Labels, LoganBerry: LoganBerryStub, LoganBerryExtras: LoganBerryStubExtras, LoganQuery, Menus, MessageWindow, Process, RefTab, Rope, Rules, Tempus, TiogaButtons, TypeScript, ViewerBLT, ViewerEvents, ViewerOps, ViewerTools EXPORTS LoganBerryBrowser = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Viewer: TYPE = ViewerClasses.Viewer; BrowserTool: TYPE = LoganBerryBrowser.Tool ; FormField: TYPE = REF FormFieldRec; FormFieldRec: TYPE = RECORD [ textViewer: Viewer, patternButton: ChoiceButtons.EnumTypeRef, feedbackArea: Labels.Label ]; HistoryData: TYPE = REF HistoryDataRec; HistoryDataRec: TYPE = RECORD [ tool: BrowserTool, cmd: ROPE, form: LoganBerryBrowser.AttributePatterns, other: ROPE ]; browserIcon: Icons.IconFlavor _ tool; itemHeight: CARDINAL = 14; interItemHeight: CARDINAL = 5; ruleHeight: CARDINAL = 1; patternButtonWidth: CARDINAL = 100; patternHSpace: CARDINAL = 5; feedbackHSpace: CARDINAL = 350; historyHeight: CARDINAL = 100; patternChoices: LIST OF ROPE = LIST["exact", "prefix", "wildcard", "reg. exp.", "soundex", "subrange", "numrange", "daterange", "date", "DWIM"]; defaultPattern: ROPE _ "DWIM"; StopProc: Menus.ClickProc = TRUSTED { tool: BrowserTool _ NARROW[clientData]; Process.Abort[tool.process ! Process.InvalidProcess => CONTINUE]; }; DetailsProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; IF tool.details.wx = 0 THEN HideViewer[tool.details] ELSE UnHideViewer[tool.details]; }; HistoryProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; IF tool.history.wx = 0 THEN { HideViewer[tool.history]; ViewerOps.MoveViewer[viewer: tool.inner, x: tool.inner.wx, y: tool.inner.wy - historyHeight, w: tool.inner.ww, h: tool.inner.wh]; tool.height _ tool.height - historyHeight; } ELSE { ViewerOps.MoveViewer[viewer: tool.inner, x: tool.inner.wx, y: tool.inner.wy + historyHeight, w: tool.inner.ww, h: tool.inner.wh]; UnHideViewer[tool.history]; tool.height _ tool.height + historyHeight; }; }; DestroyProc: ViewerEvents.EventProc = { }; MakeEntryForm: PROC [tool: BrowserTool] RETURNS [] ~ { tool.entryform _ RefTab.Create[]; FOR k: LIST OF LoganBerry.AttributeType _ tool.fields, k.rest WHILE k # NIL DO field: FormField _ NEW[FormFieldRec]; field.patternButton _ ChoiceButtons.BuildEnumTypeSelection[viewer: tool.details, x: patternHSpace, y: tool.height, buttonNames: patternChoices, default: defaultPattern, style: flipThru, maxWidth: patternButtonWidth]; field.textViewer _ ChoiceButtons.BuildTextPrompt[viewer: tool.outer, x: field.patternButton.nextx, y: tool.height, title: Rope.Concat[Atom.GetPName[k.first], ":"]].textViewer; field.feedbackArea _ Labels.Create[info: [parent: tool.details, wx: feedbackHSpace, wy: tool.height, ww: 1000, wh: 0, border: FALSE, scrollable: FALSE]]; tool.height _ tool.height + itemHeight + interItemHeight; [] _ RefTab.Store[tool.entryform, k.first, field]; ENDLOOP; tool.inputArea _ ChoiceButtons.BuildTextPrompt[viewer: tool.outer, x: patternHSpace, y: tool.height, title: "other attributes:"].textViewer; tool.height _ tool.height + itemHeight + interItemHeight; }; MakeSortButton: PROC [tool: BrowserTool] RETURNS [] ~ { choices: LIST OF ROPE _ NIL; FOR i: LIST OF LoganBerryExtras.IndexInfo _ tool.dbInfo.indices, i.rest WHILE i#NIL DO choices _ CONS[Atom.GetPName[i.first.key], choices]; ENDLOOP; choices _ CONS["ANY", choices]; tool.height _ tool.height + interItemHeight; tool.sortButton _ ChoiceButtons.BuildEnumTypeSelection[viewer: tool.details, x: patternHSpace, y: tool.height, title: "Order by:", buttonNames: choices, default: "ANY", style: menuSelection --vs. flipThru--]; tool.queryFeedback _ Labels.Create[info: [parent: tool.details, wx: MAX[tool.sortButton.nextx, feedbackHSpace], wy: tool.height, ww: 1000, wh: 0, border: FALSE, scrollable: FALSE]]; tool.height _ tool.height + itemHeight + interItemHeight; }; ReadEntryForm: PUBLIC PROC [tool: BrowserTool] RETURNS [form: LoganBerryBrowser.AttributePatterns, orderBy: LoganBerry.AttributeType] ~ { NextField: RefTab.EachPairAction = { text: ROPE; label: ATOM = NARROW[key]; field: FormField = NARROW[val]; text _ ViewerTools.GetContents[field.textViewer]; IF NOT Rope.Equal[text, ""] THEN { fv: LoganBerryBrowser.AttributePattern _ NEW[LoganBerryBrowser.AttributePatternRec]; fv.attr.type _ label; fv.attr.value _ text; fv.ptype _ ChoiceButtons.GetSelectedButton[field.patternButton]; IF form=NIL THEN form _ end _ LIST[fv] ELSE {end.rest _ LIST[fv]; end _ end.rest;}; }; Labels.Set[field.feedbackArea, NIL]; RETURN[quit: FALSE]; }; other: ROPE; end, otherPatterns: LoganBerryBrowser.AttributePatterns; form _ NIL; [] _ RefTab.Pairs[tool.entryform, NextField]; other _ ViewerTools.GetContents[tool.inputArea]; AddToHistory[tool, form, other]; otherPatterns _ ReadAttributePatterns[IO.RIS[other]]; form _ AppendPatterns[form, otherPatterns]; orderBy _ Atom.MakeAtom[ChoiceButtons.GetSelectedButton[tool.sortButton]]; IF orderBy = $ANY THEN orderBy _ NIL; Labels.Set[tool.queryFeedback, NIL]; }; RestoreEntryForm: PROC [tool: BrowserTool, form: LoganBerryBrowser.AttributePatterns, other: ROPE] RETURNS [] ~ { RestoreField: RefTab.EachPairAction = { label: ATOM = NARROW[key]; field: FormField = NARROW[val]; f: LoganBerryBrowser.AttributePatterns; FOR f _ form, f.rest WHILE f # NIL DO IF f.first.attr.type = label THEN EXIT; ENDLOOP; IF f # NIL THEN { ChoiceButtons.UpdateChoiceButtons[tool.details, field.patternButton, f.first.ptype]; ViewerTools.SetContents[field.textViewer, f.first.attr.value, FALSE]; } ELSE { ViewerTools.SetContents[field.textViewer, "", FALSE]; }; Labels.Set[field.feedbackArea, NIL, FALSE]; RETURN[quit: FALSE]; }; ChoiceButtons.UpdateChoiceButtons[tool.details, tool.sortButton, Atom.GetPName[form.first.attr.type]]; Labels.Set[tool.queryFeedback, NIL, FALSE]; [] _ RefTab.Pairs[tool.entryform, RestoreField]; ViewerTools.SetContents[tool.inputArea, other, FALSE]; ViewerOps.PaintViewer[viewer: tool.outer, hint: all]; }; ReportFeedback: PUBLIC PROC [category: ATOM, info: Rope.ROPE, tool: BrowserTool] RETURNS [] ~ { ff: FormField; info _ Rope.Cat["(",info,")"]; SELECT category FROM $QUERY => Labels.Set[tool.queryFeedback, info]; ENDCASE => { ff _ NARROW[RefTab.Fetch[tool.entryform, category].val]; IF ff#NIL THEN Labels.Set[ff.feedbackArea, info]; }; }; AddToHistory: PROC [tool: BrowserTool, form: LoganBerryBrowser.AttributePatterns, other: ROPE] RETURNS [] ~ { data: HistoryData = NEW[HistoryDataRec _ [tool, NIL, form, other]]; line: STREAM _ IO.ROS[]; WriteAttributePatterns[line, form]; IO.PutRope[line, " "]; IO.PutRope[line, other]; [] _ TiogaButtons.CreateButton[viewer: tool.history, rope: IO.RopeFromROS[line], proc: HistoryButtonProc, clientData: data]; }; HistoryButtonProc: TiogaButtons.TiogaButtonProc = { data: HistoryData _ NARROW[clientData]; RestoreEntryForm[data.tool, data.form, data.other]; }; DividingLine: PROC [tool: BrowserTool] RETURNS [] ~ { rule: Rules.Rule; rule _ Rules.Create[info: [parent: tool.outer, wx: 0, wy: tool.height, ww: 9999, wh: ruleHeight]]; Containers.ChildXBound[tool.outer, rule]; -- constrain rule to be width of parent tool.height _ tool.height + ruleHeight; }; HideViewer: PROC [v: Viewer] RETURNS [] ~ { ViewerOps.MoveViewer[viewer: v, x: 2000, y: v.wy, w: v.ww, h: v.wh]; }; UnHideViewer: PROC [v: Viewer] RETURNS [] ~ { ViewerOps.MoveViewer[viewer: v, x: 0, y: v.wy, w: v.ww, h: v.wh]; }; ResetViewer: PROC [v: Viewer] RETURNS [] ~ { v.class.init[v]; }; CreateTool: PUBLIC PROC [db: LoganBerry.OpenDB, tool: BrowserTool] = { innerInfo: ViewerClasses.ViewerRec; shortname: ROPE; cp: FS.ComponentPositions; tool.db _ dbInfoCache.db _ db; tool.dbInfo _ dbInfoCache.dbInfo _ LoganBerryExtras.Describe2[db: tool.db]; SetDefaults[tool]; [tool.dbInfo.dbName, cp] _ FS.ExpandName[tool.dbInfo.dbName]; shortname _ Rope.Substr[base: tool.dbInfo.dbName, start: cp.base.start, len: cp.base.length]; Menus.InsertMenuEntry[menu: tool.menu, entry: Menus.CreateEntry[name: "STOP!", proc: StopProc, clientData: tool]]; Menus.AppendMenuEntry[menu: tool.menu, entry: Menus.CreateEntry[name: "History", proc: HistoryProc, clientData: tool]]; Menus.AppendMenuEntry[menu: tool.menu, entry: Menus.CreateEntry[name: "Details", proc: DetailsProc, clientData: tool]]; tool.outer _ Containers.Create[info: [ name: Rope.Concat["LoganBerry Browser: ", tool.dbInfo.dbName], icon: tool.icon, label: shortname, iconic: FALSE, column: left, menu: tool.menu, scrollable: FALSE]]; Menus.ChangeNumberOfLines[tool.menu, 1]; ViewerBLT.ChangeNumberOfLines[tool.outer, 1]; ViewerOps.AddProp[viewer: tool.outer, prop: $BrowserTool, val: tool]; [] _ ViewerEvents.RegisterEventProc[proc: DestroyProc, event: destroy, filter: tool.outer]; tool.details _ Containers.Create[info: [ parent: tool.outer, ww: 1000, wh: 9999, scrollable: FALSE, border: FALSE]]; Containers.ChildYBound[container: tool.outer, child: tool.details]; tool.height _ interItemHeight; MakeEntryForm[tool]; MakeSortButton[tool]; DividingLine[tool]; tool.history _ TiogaButtons.CreateViewer[info: [ parent: tool.outer, wy: tool.height, ww: 1000, wh: historyHeight, border: TRUE]]; HideViewer[tool.history]; innerInfo _ [ name: "LoganBerry Browser output", parent: tool.outer, wx: 0, wy: tool.height, ww: 9999, wh: 9999, border: FALSE]; tool.inner _ SELECT tool.innerFlavor FROM $Typescript => TypeScript.Create[info: innerInfo], ENDCASE => ViewerOps.CreateViewer[flavor: tool.innerFlavor, info: innerInfo]; Containers.ChildXBound[tool.outer, tool.inner]; -- constrain rule to be width of parent Containers.ChildYBound[tool.outer, tool.inner]; -- constrain rule to be height of parent ViewerOps.CloseViewer[viewer: tool.outer]; ViewerOps.OpenIcon[icon: tool.outer]; }; PrintEntry: PUBLIC PROC [entry: LoganBerryStub.Entry, tool: BrowserTool] = { FOR e: LoganBerry.Entry _ entry, e.rest UNTIL e = NIL DO TypeScript.ChangeLooks[tool.inner, 'b]; -- print atribute type in bold TypeScript.PutRope[tool.inner, Atom.GetPName[e.first.type]]; TypeScript.ChangeLooks[tool.inner, ' ]; -- restore normal looks TypeScript.PutRope[tool.inner, ": "]; TypeScript.PutRope[tool.inner, e.first.value]; TypeScript.PutChar[tool.inner, '\n]; ENDLOOP; TypeScript.PutChar[tool.inner, '\n]; }; SyntaxError: PUBLIC ERROR [explanation: ROPE _ NIL] = CODE; ReadAttributePatterns: PUBLIC PROC [s: IO.STREAM] RETURNS [ap: LoganBerryBrowser.AttributePatterns] ~ { ReadAttributePattern: PROC [s: STREAM] RETURNS [a: LoganBerryBrowser.AttributePattern] ~ { ENABLE IO.EndOfStream => ERROR SyntaxError["unexpected end of input"]; ch: CHAR; token: ROPE; token _ IO.GetTokenRope[s, IO.TokenProc ! IO.EndOfStream => GOTO none].token; a _ NEW[LoganBerryBrowser.AttributePatternRec]; a.attr.type _ Atom.MakeAtom[token]; ch _ IO.GetChar[s]; -- attribute separation char or "(" IF ch = '( THEN { -- read pattern type a.ptype _ IO.GetTokenRope[s, IO.TokenProc].token; ch _ IO.GetChar[s]; IF ch # ') THEN ERROR SyntaxError[Rope.Concat["unmatched ()'s: last read ", a.ptype]]; ch _ IO.GetChar[s]; }; IF ch # ': THEN ERROR SyntaxError[Rope.Concat["missing colon: last read ", a.ptype]]; [] _ IO.SkipWhitespace[stream: s, flushComments: FALSE]; IF IO.PeekChar[s] = '" THEN { a.attr.value _ IO.GetRopeLiteral[s ! IO.Error => ERROR SyntaxError["Malformed rope literal"]]; } ELSE a.attr.value _ IO.GetTokenRope[s, IO.IDProc].token; EXITS none => RETURN[NIL]; }; a: LoganBerryBrowser.AttributePattern; endOfAp: LoganBerryBrowser.AttributePatterns _ NIL; a _ ReadAttributePattern[s]; WHILE a # NIL DO IF endOfAp = NIL THEN ap _ endOfAp _ LIST[a] ELSE { endOfAp.rest _ LIST[a]; endOfAp _ endOfAp.rest; }; a _ ReadAttributePattern[s]; ENDLOOP; }; WriteAttributePatterns: PUBLIC PROC [s: IO.STREAM, ap: LoganBerryBrowser.AttributePatterns] RETURNS [] ~ { WriteAttributePattern: PROC [s: IO.STREAM, a: LoganBerryBrowser.AttributePattern] RETURNS [] ~ { IO.PutRope[s, Atom.GetPName[a.attr.type]]; IF a.ptype # NIL THEN IO.PutF[s, "(%g)", IO.rope[a.ptype]]; IO.PutF[s, ": ""%g"" ", IO.rope[a.attr.value]]; }; FOR p: LoganBerryBrowser.AttributePatterns _ ap, p.rest WHILE p # NIL DO WriteAttributePattern[s, p.first]; ENDLOOP; }; PatternsToEntry: PUBLIC PROC [ap: LoganBerryBrowser.AttributePatterns] RETURNS [entry: LoganBerry.Entry] ~ { endOfEntry: LoganBerry.Entry _ NIL; FOR p: LoganBerryBrowser.AttributePatterns _ ap, p.rest WHILE p # NIL DO IF endOfEntry = NIL THEN entry _ endOfEntry _ LIST[p.first.attr] ELSE { endOfEntry.rest _ LIST[p.first.attr]; endOfEntry _ endOfEntry.rest; }; ENDLOOP; }; EntryToPatterns: PUBLIC PROC [entry: LoganBerry.Entry] RETURNS [ap: LoganBerryBrowser.AttributePatterns] ~ { pattern: LoganBerryBrowser.AttributePattern; endOfPattern: LoganBerryBrowser.AttributePatterns _ NIL; FOR e: LoganBerry.Entry _ entry, e.rest WHILE e # NIL DO pattern _ NEW[LoganBerryBrowser.AttributePatternRec _ [attr: e.first]]; IF endOfPattern = NIL THEN ap _ endOfPattern _ LIST[pattern] ELSE { endOfPattern.rest _ LIST[pattern]; endOfPattern _ endOfPattern.rest; }; ENDLOOP; }; AppendPatterns: PROC [p1, p2: LoganBerryBrowser.AttributePatterns] RETURNS [LoganBerryBrowser.AttributePatterns] ~ { temp: LoganBerryBrowser.AttributePatterns _ p1; IF p1 = NIL THEN RETURN[p2]; UNTIL temp.rest = NIL DO temp _ temp.rest ENDLOOP; temp.rest _ p2; RETURN[p1]; }; warningThreshold: REAL = 0.75; -- give warning if 3/4 of the database must be searched QueryWarning: PUBLIC SIGNAL = CODE; FilteredQuery: PUBLIC PROC [db: LoganBerry.OpenDB, patterns: LoganBerryBrowser.AttributePatterns, proc: LoganBerry.EntryProc, baseIndex: LoganBerry.AttributeType _ NIL, feedback: LoganBerryBrowser.FeedbackProc _ NIL] RETURNS [] ~ { start, end: ROPE _ NIL; qCost: REAL _ 1.0; computeBase: BOOLEAN _ baseIndex = NIL; cursor: LoganQuery.ComplexCursor; entry: LoganBerry.Entry; IF patterns = NIL THEN RETURN; FOR p: LoganBerryBrowser.AttributePatterns _ patterns, p.rest WHILE p # NIL DO IF computeBase THEN { tStart, tEnd: ROPE; tCost: REAL; iType: ATOM _ GetIndexType[db, p.first.attr.type]; IF iType = $notAnIndex THEN LOOP; [tStart, tEnd] _ BaseStartEnd[pattern: p.first.attr.value, ptype: p.first.ptype, itype: iType]; tCost _ QueryCost[tStart, tEnd, iType]; IF tCost <= qCost THEN { -- found a better base index start _ tStart; end _ tEnd; qCost _ tCost; baseIndex _ p.first.attr.type; }; } ELSE { IF p.first.attr.type = baseIndex THEN { iType: ATOM _ GetIndexType[db, p.first.attr.type]; IF iType = $notAnIndex THEN baseIndex _ NIL ELSE { [start, end] _ BaseStartEnd[pattern: p.first.attr.value, ptype: p.first.ptype, itype: iType]; qCost _ QueryCost[start, end, iType]; }; EXIT; -- got base index so no need to go on }; }; ENDLOOP; IF baseIndex = NIL THEN ERROR LoganBerry.Error[$NoIndex, "No suitable base index"]; feedback[category: $QUERY, info: IO.PutFR["%g - %3.2f", IO.atom[baseIndex], IO.real[qCost]]]; IF qCost > warningThreshold THEN SIGNAL QueryWarning; cursor _ LoganQuery.GenerateEntries[db: db, key: baseIndex, start: start, end: end]; FOR p: LoganBerryBrowser.AttributePatterns _ patterns, p.rest WHILE p # NIL DO pattern: LoganBerryBrowser.AttributePattern = p.first; IF NOT Rope.Equal[pattern.attr.value, ""] THEN { filter: LoganQuery.FilterProc; info: Rope.ROPE; [filter, info] _ AnalyzePattern[pattern]; IF info # NIL THEN feedback[pattern.attr.type, info]; cursor _ LoganQuery.FilterEntries[input: cursor, pattern: pattern.attr.value, filter: filter, atype: pattern.attr.type, stopIfNothingGreater: FALSE]; }; ENDLOOP; entry _ LoganQuery.NextEntry[cursor]; WHILE entry # NIL DO IF NOT proc[entry] THEN EXIT; entry _ LoganQuery.NextEntry[cursor]; ENDLOOP; LoganQuery.EndGenerate[cursor]; }; dbInfoCache: RECORD[ -- simple cache with one entry db: LoganBerry.OpenDB, dbInfo: LoganBerryExtras.SchemaInfo _ NIL ]; GetIndexType: PROC [db: LoganBerry.OpenDB, index: LoganBerry.AttributeType] RETURNS [type: ATOM] ~ { IF dbInfoCache.dbInfo = NIL OR dbInfoCache.db # db THEN dbInfoCache.dbInfo _ LoganBerryExtras.Describe2[db: db]; FOR i: LIST OF LoganBerryExtras.IndexInfo _ dbInfoCache.dbInfo.indices, i.rest WHILE i#NIL DO IF i.first.key = index THEN RETURN[i.first.order]; ENDLOOP; RETURN[$notAnIndex]; }; QueryCost: PROC [start, end: ROPE, itype: ATOM _ $lex] RETURNS [est: REAL _ 1.0] ~ { N: NAT = 5; run: INT; sChar, eChar: CHAR; IF end=NIL THEN RETURN[1.0]; IF start=NIL THEN start _ " "; SELECT itype FROM $lex, $ascii => { run _ Rope.Run[s1: start, s2: end, case: itype=$ascii]; IF run > N THEN RETURN[0.0]; -- the query has negligible cost est _ 1.0; THROUGH [1..run] DO est _ est*0.01; ENDLOOP; -- est _ 1/100run sChar _ IF Rope.Length[start] <= run THEN ' ELSE Rope.Fetch[start, run]; eChar _ IF Rope.Length[end] <= run THEN ' ELSE Rope.Fetch[end, run]; IF itype=$lex THEN { sChar _ Ascii.Upper[sChar]; eChar _ Ascii.Upper[eChar]; }; est _ est*MIN[eChar-sChar,100]*0.01; }; $gmt => { startDate, endDate: BasicTime.GMT; year: REAL _ 31536000.0; startDate _ Tempus.Parse[rope: start, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern].time; endDate _ Tempus.Parse[rope: end, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern].time; est _ BasicTime.Period[from: startDate, to: endDate]/year; -- assume one year is complete database IF est < 0 OR est > 1.0 THEN est _ 1.0; EXITS BadPattern => RETURN[1.0]; }; ENDCASE => RETURN[0.5]; -- don't know the index layout so assume half the index will be needed on average }; BaseStartEnd: PROC [pattern: ROPE, ptype: ROPE, itype: ATOM _ $lex] RETURNS [start, end: ROPE] ~ { ENABLE SyntaxError => {start _ end _ NIL; CONTINUE;}; -- raised by ParseSubrange Bump: PROC [old: ROPE] RETURNS [new: ROPE] ~ INLINE { last: INT = Rope.Length[old] - 1; new _ IF last >= 0 THEN Rope.Replace[base: old, start: last, len: 1, with: Rope.FromChar[SUCC[Rope.Fetch[old, last]]]] ELSE NIL; }; Upper: Rope.TranslatorType = { new _ Ascii.Upper[old]; }; Lower: Rope.TranslatorType = { new _ Ascii.Lower[old]; }; IF Rope.Equal[ptype, "DWIM", FALSE] THEN ptype _ DWIM[pattern]; SELECT itype FROM $lex => { SELECT TRUE FROM Rope.Equal[ptype, "exact", FALSE], Rope.Equal[ptype, "equal", FALSE] => { start _ pattern; end _ pattern; }; Rope.Equal[ptype, "prefix", FALSE] => { start _ pattern; end _ Bump[start]; }; Rope.Equal[ptype, "wildcard", FALSE] => { start _ Rope.Substr[base: pattern, len: Rope.Index[s1: pattern, s2: "*"]]; end _ Bump[start]; }; Rope.Equal[ptype, "reg. exp.", FALSE], Rope.Equal[ptype, "re", FALSE] => { i: INT _ Rope.SkipTo[s: pattern, skip: "\'#[^$*+(\\<{!"]; -- look for special chars IF NOT i = Rope.Length[pattern] AND Rope.Fetch[pattern, i] = '* THEN -- could be zero of previous char i _ i-1; start _ Rope.Substr[base: pattern, len: i]; end _ Bump[start]; }; Rope.Equal[ptype, "soundex", FALSE] => { start _ Rope.FromChar[Rope.Fetch[base: pattern, index: 0]]; end _ Bump[start]; }; Rope.Equal[ptype, "subrange", FALSE] => { [start, end] _ ParseSubrange[pattern]; }; Rope.Equal[ptype, "numrange", FALSE] => { s, e: ROPE; [s, e] _ ParseSubrange[pattern ! SyntaxError => {start _ NIL; CONTINUE}]; IF Rope.Length[s] = Rope.Length[e] THEN { start _ Rope.Substr[base: s, len: Rope.Run[s1: s, s2: e]]; end _ Bump[start]; } ELSE start _ end _ NIL; -- can do nothing intelligent, e.g. 300-3000 }; Rope.Equal[ptype, "daterange", FALSE] => { start _ end _ NIL; -- can do nothing intelligent }; Rope.Equal[ptype, "date", FALSE] => { start _ end _ NIL; -- can do nothing intelligent }; ENDCASE => start _ end _ NIL; }; $ascii => { start _ BaseStartEnd[Rope.Translate[base: pattern, translator: Upper], ptype, $lex].start; end _ BaseStartEnd[Rope.Translate[base: pattern, translator: Lower], ptype, $lex].end; }; $gmt => { SELECT TRUE FROM Rope.Equal[ptype, "date", FALSE] => { patternDate: BasicTime.GMT; patternPrecision: Tempus.Precision; [patternDate, patternPrecision] _ Tempus.Parse[rope: pattern, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern]; start _ Tempus.MakeRope[time: patternDate, precision: patternPrecision]; [patternDate, patternPrecision] _ SELECT patternPrecision FROM years => Tempus.Adjust[years: 1, baseTime: patternDate], months => Tempus.Adjust[months: 1, baseTime: patternDate], days => Tempus.Adjust[days: 1, baseTime: patternDate], hours => Tempus.Adjust[hours: 1, baseTime: patternDate], minutes => Tempus.Adjust[minutes: 1, baseTime: patternDate], seconds => Tempus.Adjust[seconds: 1, baseTime: patternDate], ENDCASE => Tempus.Adjust[seconds: 1, baseTime: patternDate]; end _ Tempus.MakeRope[time: patternDate, precision: patternPrecision]; EXITS BadPattern => start _ end _ NIL; }; Rope.Equal[ptype, "daterange", FALSE] => { patternDate: BasicTime.GMT; patternPrecision: Tempus.Precision; [start, end] _ ParseSubrange[pattern]; [patternDate, patternPrecision] _ Tempus.Parse[rope: start, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern]; start _ Tempus.MakeRope[time: patternDate, precision: patternPrecision]; [patternDate, patternPrecision] _ Tempus.Parse[rope: end, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern]; end _ Tempus.MakeRope[time: patternDate, precision: patternPrecision]; EXITS BadPattern => start _ end _ NIL; }; ENDCASE => start _ end _ NIL; }; $int => { SELECT TRUE FROM Rope.Equal[ptype, "exact", FALSE], Rope.Equal[ptype, "equal", FALSE] => start _ end _ pattern; Rope.Equal[ptype, "intrange", FALSE] => [start, end] _ ParseSubrange[pattern]; ENDCASE => start _ end _ NIL; }; ENDCASE => start _ end _ NIL; -- search complete database }; AnalyzePattern: PROC [p: LoganBerryBrowser.AttributePattern] RETURNS [filter: LoganQuery.FilterProc, info: Rope.ROPE _ NIL] ~ { start, end: Rope.ROPE; SELECT TRUE FROM Rope.Equal[p.ptype, "exact", FALSE], Rope.Equal[p.ptype, "equal", FALSE] => { filter _ LoganQuery.Equal; }; Rope.Equal[p.ptype, "prefix", FALSE] => { filter _ LoganQuery.Prefix; }; Rope.Equal[p.ptype, "wildcard", FALSE] => { filter _ LoganQuery.Wildcard; }; Rope.Equal[p.ptype, "reg. exp.", FALSE], Rope.Equal[p.ptype, "re", FALSE] => { filter _ LoganQuery.RE; }; Rope.Equal[p.ptype, "soundex", FALSE] => { filter _ LoganQuery.Soundex; }; Rope.Equal[p.ptype, "subrange", FALSE] => { filter _ Subrange; [start, end] _ ParseSubrange[p.attr.value ! SyntaxError => GOTO BadPattern]; IF Rope.Compare[s1: start, s2: end, case: FALSE] = greater THEN GOTO BadPattern; EXITS BadPattern => info _ "NOT A SUBRANGE"; }; Rope.Equal[p.ptype, "numrange", FALSE] => { startInt, endInt: INT; filter _ NumSubrange; [start, end] _ ParseSubrange[p.attr.value ! SyntaxError => GOTO BadPattern]; startInt _ Convert.IntFromRope[start ! Convert.Error => GOTO BadPattern]; endInt _ Convert.IntFromRope[end ! Convert.Error => GOTO BadPattern]; IF startInt > endInt THEN GOTO BadPattern; EXITS BadPattern => info _ "NOT AN INTEGER RANGE"; }; Rope.Equal[p.ptype, "daterange", FALSE] => { startDate, endDate: BasicTime.GMT; filter _ DateSubrange; [start, end] _ ParseSubrange[p.attr.value ! SyntaxError => GOTO BadPattern]; startDate _ Tempus.Parse[rope: start, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern].time; endDate _ Tempus.Parse[rope: end, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern].time; IF BasicTime.Period[from: startDate, to: endDate] < 0 THEN GOTO BadPattern; info _ IO.PutFR["%g-%g", IO.time[startDate], IO.time[endDate]]; EXITS BadPattern => info _ "NOT A DATE RANGE"; }; Rope.Equal[p.ptype, "date", FALSE] => { patternDate: BasicTime.GMT; patternPrecision: Tempus.Precision; filter _ DateAndTime; [patternDate, patternPrecision] _ Tempus.Parse[rope: p.attr.value, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern]; info _ Tempus.MakeRope[time: patternDate, precision: patternPrecision]; EXITS BadPattern => info _ "NOT A DATE AND TIME"; }; Rope.Equal[p.ptype, "DWIM", FALSE] => { p.ptype _ DWIM[p.attr.value]; [filter, info] _ AnalyzePattern[p]; -- recursive call info _ Rope.Cat["using ", p.ptype, "...", info]; }; ENDCASE => { filter _ LoganQuery.Equal; }; }; DWIM: PROC [pattern: ROPE] RETURNS [ptype: ROPE _ "prefix"] ~ { start, end: ROPE; ok: BOOLEAN _ TRUE; [start, end] _ ParseSubrange[pattern ! SyntaxError => {ok _ FALSE; CONTINUE}]; IF ok THEN { -- determine what type of subrange ok _ TRUE; [] _ Convert.IntFromRope[start ! Convert.Error => {ok _ FALSE; CONTINUE}]; [] _ Convert.IntFromRope[end ! Convert.Error => {ok _ FALSE; CONTINUE}]; IF ok THEN RETURN["numrange"]; ok _ TRUE; [] _ Tempus.Parse[rope: start, search: FALSE ! Tempus.Unintelligible => {ok _ FALSE; CONTINUE}]; [] _ Tempus.Parse[rope: end, search: FALSE ! Tempus.Unintelligible => {ok _ FALSE; CONTINUE}]; IF ok THEN RETURN["daterange"]; RETURN["subrange"]; } ELSE { -- not a subrange IF Rope.Find[pattern, "*"] # -1 THEN RETURN["wildcard"]; ok _ TRUE; [] _ Tempus.Parse[rope: pattern, search: FALSE ! Tempus.Unintelligible => {ok _ FALSE; CONTINUE}]; IF ok THEN RETURN["date"]; RETURN["prefix"]; }; }; srCache: RECORD [ -- very simple cache with one entry lastr: ROPE _ NIL, start, end: ROPE ]; nsrCache: RECORD [ -- very simple cache with one entry lastr: ROPE _ NIL, start, end: INT ]; dsrCache: RECORD [ -- very simple cache with one entry lastr: ROPE _ NIL, start, end: BasicTime.GMT ]; dtCache: RECORD [ -- very simple cache with one entry lastr: ROPE _ NIL, time: BasicTime.GMT, precision: Tempus.Precision ]; ParseSubrange: PROC [r: ROPE] RETURNS [start, end: ROPE] ~ { ENABLE IO.EndOfStream, IO.Error => ERROR SyntaxError["Ill-formed subrange"]; ToDash: IO.BreakProc = { RETURN[SELECT char FROM '- => sepr, ENDCASE => other]; }; s: IO.STREAM; IF r=srCache.lastr THEN RETURN[srCache.start, srCache.end]; s _ IO.RIS[r]; start _ IF IO.PeekChar[s] = '" THEN IO.GetRopeLiteral[s] ELSE IO.GetTokenRope[s, ToDash].token; IF IO.GetChar[s] # '- THEN ERROR SyntaxError["Not a subrange"]; end _ IF IO.PeekChar[s] = '" THEN IO.GetRopeLiteral[s] ELSE IO.GetLineRope[s]; srCache.lastr _ r; srCache.start _ start; srCache.end _ end; }; Subrange: LoganQuery.FilterProc ~ { -- should be in LoganQuery? start, end: Rope.ROPE; [start, end] _ ParseSubrange[pattern ! SyntaxError => GOTO BadPattern]; IF Rope.Compare[s1: start, s2: end, case: FALSE] = greater THEN GOTO BadPattern; SELECT Rope.Compare[s1: value, s2: start, case: FALSE] FROM less => match _ FALSE; equal => match _ TRUE; greater => SELECT Rope.Compare[s1: Rope.Substr[value, 0, Rope.Length[end]], s2: end, case: FALSE] FROM less => match _ TRUE; equal => match _ TRUE; greater => {match _ FALSE; nothingGreater _ TRUE} ENDCASE => ERROR; ENDCASE => ERROR; EXITS BadPattern => RETURN[match: FALSE, nothingGreater: TRUE]; }; NumSubrange: LoganQuery.FilterProc ~ { -- should be in LoganQuery? start, end: Rope.ROPE; startInt, endInt, valueInt: INT; IF pattern=nsrCache.lastr THEN { -- get from cache startInt _ nsrCache.start; endInt _ nsrCache.end; } ELSE { -- parse pattern and keep for future use [start, end] _ ParseSubrange[pattern ! SyntaxError => GOTO BadPattern]; startInt _ Convert.IntFromRope[start ! Convert.Error => GOTO BadPattern]; endInt _ Convert.IntFromRope[end ! Convert.Error => GOTO BadPattern]; IF startInt > endInt THEN GOTO BadPattern; nsrCache.lastr _ pattern; nsrCache.start _ startInt; nsrCache.end _ endInt; }; valueInt _ Convert.IntFromRope[value ! Convert.Error => GOTO BadValue]; match _ startInt <= valueInt AND valueInt <= endInt; EXITS BadPattern => RETURN[match: FALSE, nothingGreater: TRUE]; BadValue => RETURN[match: FALSE, nothingGreater: FALSE]; }; DateSubrange: LoganQuery.FilterProc ~ { -- should be in LoganQuery? start, end: Rope.ROPE; startDate, endDate, valueDate: BasicTime.GMT; IF pattern=dsrCache.lastr THEN { -- get from cache startDate _ dsrCache.start; endDate _ dsrCache.end; } ELSE { -- parse pattern and keep for future use [start, end] _ ParseSubrange[pattern ! SyntaxError => GOTO BadPattern]; startDate _ Tempus.Parse[rope: start, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern].time; endDate _ Tempus.Parse[rope: end, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern].time; IF BasicTime.Period[from: startDate, to: endDate] < 0 THEN GOTO BadPattern; dsrCache.lastr _ pattern; dsrCache.start _ startDate; dsrCache.end _ endDate; }; valueDate _ Tempus.Parse[rope: value, search: FALSE ! Tempus.Unintelligible => GOTO BadValue].time; match _ BasicTime.Period[from: startDate, to: valueDate] >= 0 AND BasicTime.Period[from: valueDate, to: endDate] >= 0; EXITS BadPattern => RETURN[match: FALSE, nothingGreater: TRUE]; BadValue => RETURN[match: FALSE, nothingGreater: FALSE]; }; DateAndTime: LoganQuery.FilterProc ~ { -- should be in LoganQuery? patternDate, valueDate: BasicTime.GMT; patternPrecision: Tempus.Precision; IF pattern=dtCache.lastr THEN { -- get from cache patternDate _ dtCache.time; patternPrecision _ dtCache.precision; } ELSE { -- parse pattern and keep for future use [patternDate, patternPrecision] _ Tempus.Parse[rope: pattern, search: FALSE ! Tempus.Unintelligible => GOTO BadPattern]; dtCache.lastr _ pattern; dtCache.time _ patternDate; dtCache.precision _ patternPrecision; }; valueDate _ Tempus.Parse[rope: value, search: FALSE ! Tempus.Unintelligible => GOTO BadValue].time; valueDate _ Tempus.Adjust[baseTime: valueDate, precisionOfResult: patternPrecision].time; match _ BasicTime.Period[from: patternDate, to: valueDate] = 0; EXITS BadPattern => RETURN[match: FALSE, nothingGreater: TRUE]; BadValue => RETURN[match: FALSE, nothingGreater: FALSE]; }; Data: TYPE ~ REF DataRec; -- default client data DataRec: TYPE ~ RECORD[ lastDeletes: LIST OF LoganBerry.Entry _ NIL -- entries recently deleted ]; SetDefaults: PROC [tool: BrowserTool] RETURNS [] ~ { IF tool.menu = NIL THEN { tool.menu _ StandardMainMenu[tool]; tool.clientData _ NEW[DataRec]; }; IF tool.innerFlavor = NIL THEN tool.innerFlavor _ $TypeScript; IF tool.icon = unInit THEN tool.icon _ browserIcon; IF tool.fields = NIL THEN tool.fields _ IndexKeys[tool]; }; IndexKeys: PROC [tool: BrowserTool] RETURNS [fields: LIST OF LoganBerry.AttributeType] ~ { tail: LIST OF LoganBerry.AttributeType _ NIL; FOR i: LIST OF LoganBerryExtras.IndexInfo _ tool.dbInfo.indices, i.rest WHILE i#NIL DO IF tail#NIL THEN {tail.rest _ LIST[i.first.key]; tail _ tail.rest;} ELSE {fields _ tail _ LIST[i.first.key];} ENDLOOP; }; StandardMainMenu: PROC [tool: BrowserTool] RETURNS [menu: Menus.Menu] ~ { AppendMenu: PROC[menu: Menus.Menu, name: ROPE, proc: Menus.ClickProc, line: Menus.MenuLine _ 0, guarded: BOOL _ FALSE] = { Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[name: name, proc: proc, clientData: tool, guarded: guarded], line: line ]; }; menu _ Menus.CreateMenu[lines: 2]; AppendMenu[menu, "Browse", BrowseProc]; AppendMenu[menu, "Update", UpdateProc]; AppendMenu[menu, "Delete", DeleteProc, 0, TRUE]; AppendMenu[menu, "UnDelete", UnDeleteProc, 0, TRUE]; AppendMenu[menu, "AdminOps", AdminOpsProc]; AppendMenu[menu, "Open", OpenProc, 1]; AppendMenu[menu, "Close", CloseProc, 1, TRUE]; AppendMenu[menu, "BuildIndices", BuildIndicesProc, 1, TRUE]; AppendMenu[menu, "CompactLogs", CompactLogsProc, 1, TRUE]; }; BrowseProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; IF mouseButton # blue THEN ResetViewer[tool.inner]; DoOp[$LBQuery, tool]; }; UpdateProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; IF mouseButton # blue THEN ResetViewer[tool.inner]; DoOp[$LBWrite, tool]; }; DeleteProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; IF mouseButton # blue THEN ResetViewer[tool.inner]; DoOp[$LBDelete, tool]; }; UnDeleteProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; IF mouseButton # blue THEN ResetViewer[tool.inner]; DoOp[$LBUnDelete, tool]; }; AdminOpsProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; newCount: CARDINAL _ IF Menus.GetNumberOfLines[tool.menu] = 1 THEN 2 ELSE 1; Menus.ChangeNumberOfLines[tool.menu, newCount]; ViewerBLT.ChangeNumberOfLines[tool.outer, newCount]; }; OpenProc: Menus.ClickProc = { msg: ROPE _ NIL; tool: BrowserTool _ NARROW[clientData]; tool.db _ LoganBerry.Open[dbName: tool.dbInfo.dbName ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; }; CloseProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; LoganBerry.Close[db: tool.db ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; }; BuildIndicesProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; LoganBerry.BuildIndices[db: tool.db ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; }; CompactLogsProc: Menus.ClickProc = { tool: BrowserTool _ NARROW[clientData]; LoganBerry.CompactLogs[db: tool.db ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; }; DoOp: PROC [cmd: ATOM, tool: BrowserTool] RETURNS [] ~ { ENABLE { LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}; QueryWarning => {MessageWindow.Append[message: "Browsing complete database...", clearFirst: TRUE]; MessageWindow.Blink[]; RESUME}; ABORTED => GOTO Aborted; }; PutEntry: LoganBerry.EntryProc = { PrintEntry[entry, tool]; RETURN[TRUE]; }; DeleteEntry: LoganBerry.EntryProc = { LoganBerry.DeleteEntry[db: tool.db, key: primaryKey, value: GetAttributeValue[entry, primaryKey]]; data.lastDeletes _ CONS[entry, data.lastDeletes]; PrintEntry[entry, tool]; RETURN[TRUE]; }; Feedback: LoganBerryBrowser.FeedbackProc ~ { ReportFeedback[category, info, tool]; }; form: LoganBerryBrowser.AttributePatterns; primaryKey, base: LoganBerry.AttributeType; entry: LoganBerry.Entry; data: Data _ NARROW[tool.clientData]; TRUSTED {tool.process _ LOOPHOLE[Process.GetCurrent[], PROCESS];}; IF cmd # $LBUnDelete THEN [form, base] _ ReadEntryForm[tool ! SyntaxError => {ReportLBError[$MalformedInput, explanation, tool]; CONTINUE}]; SELECT cmd FROM $LBQuery => { FilteredQuery[db: tool.db, patterns: form, proc: PutEntry, baseIndex: base, feedback: Feedback]; }; $LBWrite => { entry _ PatternsToEntry[form]; LoganBerry.WriteEntry[db: tool.db, entry: entry]; PrintEntry[entry, tool]; }; $LBDelete => { primaryKey _ tool.dbInfo.indices.first.key; data.lastDeletes _ NIL; TypeScript.PutRope[tool.inner, "\n-- Deleting...\n"]; FilteredQuery[db: tool.db, patterns: form, proc: DeleteEntry, baseIndex: base, feedback: Feedback]; TypeScript.PutRope[tool.inner, "\n-- Done.\n"]; }; $LBUnDelete => { TypeScript.PutRope[tool.inner, "\n-- UnDeleting...\n"]; FOR l: LIST OF LoganBerry.Entry _ data.lastDeletes, l.rest WHILE l#NIL DO LoganBerry.WriteEntry[db: tool.db, entry: l.first ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE};]; PrintEntry[l.first, tool]; ENDLOOP; TypeScript.PutRope[tool.inner, "\n-- Done.\n"]; data.lastDeletes _ NIL; -- can't undo an undelete }; ENDCASE => NULL; EXITS Aborted => TypeScript.PutRope[tool.inner, "\n-- Aborted.\n"]; }; GetAttributeValue: PROC [entry: LoganBerry.Entry, type: LoganBerry.AttributeType] RETURNS [LoganBerry.AttributeValue] ~ { FOR e: LoganBerry.Entry _ entry, e.rest WHILE e # NIL DO IF e.first.type = type THEN RETURN[e.first.value]; ENDLOOP; RETURN[NIL]; }; ReportLBError: PROC [ec: LoganBerry.ErrorCode, explanation: ROPE, tool: BrowserTool] RETURNS [] ~ { TypeScript.PutRope[tool.inner, IO.PutFR["ERROR: %g - %g", IO.atom[ec], IO.rope[explanation]]]; }; browserIcon _ Icons.NewIconFromFile[file: "LoganBerry.icons", n: 0 ! ANY => CONTINUE]; END. B>LoganBerryBrowserImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Doug Terry, March 4, 1987 10:48:27 am PST A tool for interactively browsing LoganBerry databases. Types Viewer layout Command menu [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] (client supplied menu procedures can be supplied; see below for defaults.) [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS [abort: BOOL _ FALSE] -- Does nothing at the moment tool: BrowserTool _ NARROW[ViewerOps.FetchProp[viewer: viewer, prop: $BrowserTool]]; LoganBerry.Close[db: tool.db ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; Entry form [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN] [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN] History viewer The History viewer contains lines representing commands that could have been typed to a commandTool to achieve the same result as the operations invoked via the browser tool. Each of these lines is a TiogaButton that, when buttoned, causes the entry form to be filled in with the various values. [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] Left mouse button => restore the entry form; Middle => restore form and re-execute (not currently implemented). Misc. viewer stuff Create main tool initialize tool find short database name create command section create input form Containers.ChildXBound[container: tool.outer, child: tool.details]; create history viewer Containers.ChildXBound[tool.outer, tool.history]; -- constrain rule to be width of parent create output browser Try to get viewer to initially echo typed input ViewerOps.PaintViewer[viewer: tool.outer, hint: all]; Write entry in browser's typescript viewer. Attribute patterns An attribute pattern is of the form: atype(ptype): avalue. The ptype is optional, and there may be whitespace preceeding the avalue. The avalue can be either a token or rope literal. Read attribute patterns until there are no more and build up a list of them. Query execution This signal is raised by FilteredQuery if the estimated query cost exceeds the warningThreshold specified above (i.e. the query will take a potentially long time); this signal can be resumed. Retrieves entries in the LoganBerry database that match the attribute patterns and calls the given procedure for each one. The baseIndex parameter governs which attribute type is used as the base index for the query; for baseIndex=NIL, an index is chosen that attempts to optimize the execution of the query (use this unless you really care about the order in which database entries are retrieved). figure out where to start and end in database to save work, and pick a "good" base index if necessary warn the caller if the query will search the complete database; the caller can resume execution or unwind to abort the query simple base query build up filters retrieve entries and call EntryProc Estimates the cost of enumerating an index of the given type from start to end. The cost model is quite simple: it assumes that the probability of any given character being the nth character of a string is 0.01 for n in [1..N]. The estimate returned is the estimated fraction of the index that must be traversed. For instance, start="a" and end="b" for itype=$lex yields est=0.01, while start="aaa" and end="aac" for itype=$lex yields est=0.000002. This routine exists solely for performance reasons. The intent is to identify a range [start..end] of entries in an index that includes all possible values that match the given pattern; clearly this range depends on the ordering of entries in the index ($lex, $ascii, $gmt, etc.). It is always acceptable to return [NIL, NIL], which indicates that the whole index must be enumerated; returning a range that is too small or incorrect, on the other hand, leads to incomplete answers to a filtered query. [old: CHAR] RETURNS [new: CHAR] [old: CHAR] RETURNS [new: CHAR] find greatest common prefix for start and end Trys to deduce an appropriate filter type from the given pattern. LoganQuery filters The following FilterProcs use very simple caching to avoid having to parse the pattern on every call. We keep a copy of the result of the last parse and do fast pointer comparisons to see if the pattern is the same as before. This single-entry caching scheme does not help if several attribute patterns are using subrange filters at the same time. This should eventually be replaced by caches with several entries. r should be of the form "start-end". [char: CHAR] RETURNS [IO.CharClass] [value: ROPE, pattern: ROPE] RETURNS [match: BOOLEAN, nothingGreater: BOOLEAN _ FALSE] Checks if the value is in the range specified by the pattern. The pattern consists of two prefixes separated by a "-". parse pattern check start <= prefix(value) <= end [value: ROPE, pattern: ROPE] RETURNS [match: BOOLEAN, nothingGreater: BOOLEAN _ FALSE] Checks if the value is in the numerical range specified by the pattern. The pattern should consist of two positive integers separated by a "-". If the value can not be parsed as an integer then match=FALSE is returned; if the pattern is bad then match=FALSE and nothingGreater=TRUE is returned. parse pattern check start <= value <= end [value: ROPE, pattern: ROPE] RETURNS [match: BOOLEAN, nothingGreater: BOOLEAN _ FALSE] Checks if the value is in the chronological range specified by the pattern. The pattern should consist of two date and times (as can be parsed by Tempus) separated by a "-". If the value can not be parsed as a date and time then match=FALSE is returned; if the pattern is bad then match=FALSE and nothingGreater=TRUE is returned. parse pattern check start <= value <= end [value: ROPE, pattern: ROPE] RETURNS [match: BOOLEAN, nothingGreater: BOOLEAN _ FALSE] Checks if the value matches the pattern within the time precision of the pattern. The pattern, as well as the value, should be a date and time (as can be parsed by Tempus). If the value can not be parsed as a date and time then match=FALSE is returned; if the pattern is bad then match=FALSE and nothingGreater=TRUE is returned. A match can not occur unless the pattern is no more precise than the value. For example, a pattern of "Wednesday" will match a value of "Wednesday at 2 pm", but not vice versa. parse pattern check value = pattern Default browser operations --first line-- AppendMenu[menu, "STOP!", StopProc]; AppendMenu[menu, "History", HistoryProc]; AppendMenu[menu, "Details", DetailsProc]; --second line-- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [entry: LoganBerry.Entry] RETURNS [continue: BOOL] [entry: LoganBerry.Entry] RETURNS [continue: BOOL] [category: ATOM, info: Rope.ROPE] RETURNS [] If an OpProc is registered, then it is called before and after performing the desired operation. The before procedure can return FALSE to indicate that the operation should not be done. Initialization Doug Terry, November 27, 1985 3:22:06 pm PST taken from WPBrowserToolImpl.mesa changes to: DIRECTORY, LoganBerryBrowserTool, browserIcon, db, dbFileName, NamedFilter, Commander, END, BrowseProc, DetailsProc, HistoryProc, MakeMainMenu, BrowserToolRec, MakeBrowserTool, itemHeight, interItemHeight, ruleHeight, patternButtonWidth, patternHSpace, historyHeight, AppendMenu (local of MakeMainMenu), MakeEntryForm, AppendEntry (local of MakeEntryForm), MakeSortButton, DividingLine, FormFieldRec, AddToSortList (local of MakeSortButton), StopProc, BrowseProc, DoQuery, PutEntry (local of DoQuery), NewFilter (local of DoQuery), ROPE, STREAM Doug Terry, November 27, 1985 4:05:43 pm PST changes to: PutEntry (local of DoQuery), MakeSortButton, DIRECTORY Doug Terry, November 27, 1985 5:56:01 pm PST changes to: MakeSortButton, AddToSortList (local of MakeSortButton), DIRECTORY, DestroyProc, MakeMainMenu, MakeBrowserTool, browserIcon, Commander, LoganBerryBrowserTool Doug Terry, November 27, 1985 6:18:58 pm PST changes to: MakeBrowserTool Doug Terry, January 23, 1986 8:41:38 pm PST changes to: DestroyProc, MakeBrowserTool Doug Terry, January 31, 1986 2:46:52 pm PST changes to: AdminOpsProc, OpenProc, CloseProc, BuildIndiciesProc, CompactLogsProc, DestroyProc, MakeMainMenu, DIRECTORY, AppendMenu (local of MakeMainMenu), MakeBrowserTool, LoganBerryBrowserTool Doug Terry, January 31, 1986 2:53:26 pm PST changes to: AdminOpsProc, MakeMainMenu, MakeBrowserTool, AppendMenu (local of MakeMainMenu) Doug Terry, January 31, 1986 5:21:25 pm PST changes to: AppendMenu (local of MakeMainMenu), MakeMainMenu, MakeBrowserTool, BrowseProc, DIRECTORY Doug Terry, March 5, 1986 6:18:08 pm PST Now calls LoganBerry.Describe to obtain the list of available indices and catches LoganBerry.Errors. changes to: DIRECTORY, MakeEntryForm, OpenProc, CloseProc, BuildIndiciesProc, CompactLogsProc, DestroyProc, MakeBrowserTool, PutEntry (local of DoQuery) Doug Terry, April 24, 1986 9:47:20 am PST Changed to use LoganBerryStub. changes to: DIRECTORY, LoganBerryBrowserTool, BrowserToolRec, OpenProc, MakeBrowserTool, DoQuery, ReportLBError Doug Terry, April 24, 1986 9:51:16 am PST changes to: DIRECTORY, LoganBerryBrowserTool, BrowserToolRec, OpenProc, MakeBrowserTool, DoQuery, ReportLBError Doug Terry, July 1, 1986 6:00:40 pm PDT Added History facility. changes to: FormFieldRec, FormValue, FormValueRec, FormContents, MakeSortButton, ReadEntryForm, NextField (local of ReadEntryForm), AddToHistory, AddToLine (local of AddToHistory), RestoreFromHistory, HistoryButtonProc, DoQuery, BrowseProc, RestoreEntryForm, PutEntry, GetAttributeValue, DIRECTORY, LoganBerryBrowserTool, ROPE, STREAM, HistoryData, HistoryDataRec, RestoreField (local of RestoreEntryForm), HistoryProc, DetailsProc, UnHideViewer, HideViewer, MakeBrowserTool Doug Terry, July 1, 1986 9:04:42 pm PDT changes to: BuildIndicesProc, MakeMainMenu Doug Terry, October 16, 1986 5:58:19 pm PDT changes to: DoQuery Doug Terry, October 22, 1986 4:10:21 pm PDT Changed to use LoganBerryCommands and provide a similar model of operation; added support for updates and deletes. changes to: BrowserToolRec, UpdateProc, DeleteProc, DetailsProc, MakeMainMenu, MakeInputArea, MakeSortButton, MakeBrowserTool, FormContents, BrowseProc, AddToHistory, HistoryButtonProc, NextField (local of ReadEntryForm), RestoreField (local of RestoreEntryForm), RestoreEntryForm, DoQuery, AttributePattern, AttributePatternRec, HistoryDataRec, MakeEntryForm, ReadEntryForm, DoQuery, DIRECTORY, LoganBerryBrowserTool, PutEntry (local of DoOp), DeleteEntry (local of DoOp) Doug Terry, October 31, 1986 4:56:08 pm PST Exports and uses CreateTool operation. changes to: DIRECTORY, LoganBerryBrowserImpl, BrowserToolRec, AttributePattern, AttributePatternRec, AttributePatterns, MakeEntryForm, AddToHistory, CreateTool, DoOp, PutEntry (local of DoOp), MakeBrowserTool, browserIcon Doug Terry, November 2, 1986 7:00:17 pm PST Added support for subrange filtering and UnDelete. changes to: BrowserToolRec, UnDeleteProc, DetailsProc, MakeEntryForm, DeleteEntry (local of DoOp), DoOp Doug Terry, November 23, 1986 9:29:41 pm PST Moved support for filtered queries to this module; removed any command registrations. changes to: LoganBerryBrowserImpl, HistoryDataRec, historyHeight, MakeMainMenu, HistoryButtonProc, UnHideViewer, CreateTool, SyntaxError, Read, LoganQuery, FilteredQuery, Subrange, ReportLBError, browserIcon, DIRECTORY, BrowserToolRec, PrintEntry, PutEntry (local of DoOp), DoOp Doug Terry, November 24, 1986 4:58:48 pm PST Added new filters: DWIM, NumSubrange, DateSubrange, DateAndTime. changes to: OptimalStart, NamedFilter, subrangeCache, ParseSubrange, ToDash (local of ParseSubrange), Subrange, NumSubrange, DateSubrange, srCache, nsrCache, dsrCache, DateSubrange, DIRECTORY, LoganBerryBrowserImpl, FilteredQuery, NamedFilter, MakeEntryForm Doug Terry, December 4, 1986 6:59:04 pm PST Changed CreateTool to create inner (output) viewers of various flavors. changes to: DIRECTORY, CreateTool Doug Terry, December 15, 1986 6:31:17 pm PST Stop! now calls Process.Abort, which raises ABORTED, which is caught in DoOp. changes to: OptimalStart, BaseStartEnd, DIRECTORY, LoganBerryBrowserImpl, BrowserToolRec, StopProc, DoOp, PutEntry (local of DoOp), DeleteEntry (local of DoOp) Doug Terry, December 22, 1986 5:12:02 pm PST changes to: historyHeight, patternChoices, defaultPattern, MakeEntryForm Doug Terry, December 23, 1986 4:11:02 pm PST changes to: ReadEntryForm, NextField (local of ReadEntryForm), SyntaxError, QueryWarning, FilteredQuery, DoOp Doug Terry, December 29, 1986 3:04:30 pm PST Added computation of start and end for various types of base indices; uses estimated query cost to select the optimal base index if one is not specified. changes to: BaseStartEnd, Bump (local of BaseStartEnd), Upper (local of BaseStartEnd), Lower (local of BaseStartEnd), MakeEntryForm, ReadEntryForm, CreateTool, FilteredQuery, GetIndexType, QueryCost changes to: Bump (local of BaseStartEnd), warningThreshold, QueryWarning, FilteredQuery Doug Terry, December 29, 1986 4:36:38 pm PST changes to: warningThreshold, QueryWarning, FilteredQuery, Bump (local of BaseStartEnd), Feedback, RegisterFeedbackArea Doug Terry, January 27, 1987 5:57:51 pm PST changes to: DIRECTORY, LoganBerryBrowserImpl, DoOp, BrowserToolRec, UnDeleteProc, MakeEntryForm, MakeSortButton, CreateTool Doug Terry, January 28, 1987 10:58:00 am PST Added calls to LoganBerryExtras to get the ordering of indices; reduced assumptions about the flavor of the inner viewer (used to assume it was a typescript in various places); made Delete and UnDelete call the client-provided DisplayProc; added calls to the OpProc for $LBUnDelete and $LBStop. changes to: MakeEntryForm changes to: DIRECTORY, LoganBerryBrowserImpl, EntryToPatterns, AppendPatterns, DoOp changes to: BrowseProc, DeleteProc, UnDeleteProc, UnHideViewer, ResetViewer, ReportLBError Doug Terry, January 28, 1987 9:26:14 pm PST changes to: BrowseProc, UpdateProc, DeleteProc, UnDeleteProc, OpenProc, CloseProc, BuildIndicesProc, CompactLogsProc, DestroyProc, UnHideViewer, ResetViewer, DoOp, DeleteEntry (local of DoOp), UnDeleteEntry (local of DoOp), ReportLBError Doug Terry, January 29, 1987 1:47:06 pm PST changes to: BrowserToolRec, MakeSortButton, DIRECTORY, LoganBerryBrowserImpl, FilteredQuery, DeleteEntry (local of DoOp), DoOp, ClearFeedback, ReportFeedback, ReportLBError Doug Terry, January 29, 1987 3:42:53 pm PST changes to: patternHSpace, feedbackHSpace, MakeSortButton, DIRECTORY, FormFieldRec, MakeEntryForm, NextField (local of ReadEntryForm), ReadEntryForm, RestoreField (local of RestoreEntryForm), RestoreEntryForm, DeleteEntry (local of DoOp), ReportFeedback (local of DoOp), DoOp, FilteredQuery, AnalyzePattern Doug Terry, February 24, 1987 2:04:02 pm PST Changed the way browsers are customized; clients are now responsible for providing the main menu and operations (see "Default browser operations") changes to: BrowserTool, DetailsProc, CreateTool, SetDefaults, StandardMainMenu, BrowseProc, DoOp, MakeEntryForm, StandardMainMenu, ReadEntryForm, RestoreEntryForm, ReportFeedback, AddToHistory, PrintEntry, PutEntry (local of DoOp), DeleteEntry (local of DoOp), Feedback (local of DoOp), DataRec, SetDefaults, ReportLBError, DIRECTORY Doug Terry, February 24, 1987 2:30:23 pm PST changes to: StandardMainMenu, CreateTool, SetDefaults, DIRECTORY Doug Terry, March 4, 1987 10:48:08 am PST changes to: defaultPattern ÊL1˜– "Cedar" stylešœ™Icode– "Cedar" stylešœ Ïmœ1™Kšœ žœdžœ˜‹Jšœžœ˜ŠKšœžœ˜$Kšœžœ%˜2Kšœžœ4˜@Jš œžœžœžœžœžœ2˜ŽJšœžœ˜Kšœžœ6˜BJšœ žœ/˜AJšœ žœ,˜ -- [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN]š¤ œ˜'Kš¢:™:Jšœžœžœ˜Jšœžœ˜Jšœ'˜'šžœžœžœž˜%Kšžœžœžœ˜'Kšžœ˜—šžœžœžœ˜KšœT˜TJšœ>žœ˜EJšœ˜—šžœ˜Jšœ.žœ˜5J˜—Jšœžœžœ˜+Kšžœžœ˜K˜—Kšœf˜fKšœž œ˜+Jšœ0˜0Jšœ/žœ˜6Jšœ5˜5K˜K˜—š ¤œž œ žœ žœžœ˜_Kšœ˜K˜šžœ ž˜Kšœ/˜/šžœ˜ Kšœžœ-˜8Kšžœžœžœ#˜1K˜——K˜——J™™J™J™®J™xJ™š¤ œžœGžœžœ˜mKšœžœžœ˜CKšœžœžœžœ˜Kšœ#˜#Kšœ˜Kšœ˜Kšœ|˜|K˜—K˜–‚ -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]š¤œ"˜3Kš¢~™~K™oKšœžœ ˜'Kšœ3˜3K˜K˜——J™™J™š¤ œžœžœ˜5K˜Jšœb˜bKšœ+Ïc'˜RJšœ'˜'K˜K˜—š¤ œžœ žœ˜+K–n[viewer: ViewerClasses.Viewer, x: INTEGER, y: INTEGER, w: INTEGER, h: INTEGER, paint: BOOL _ TRUE]šœD˜DK˜—K˜š¤ œžœ žœ˜-KšœA˜AK˜K˜—š¤ œžœ žœ˜,Kšœ˜Kšœ˜K˜——J™™K˜š¤ œž œ/˜FKšœ#˜#Kšœ žœ˜Kšœžœ˜K™Kšœ˜KšœK˜KK˜K™Kšœžœ ˜=K–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šœ]˜]Kšœ™Kšœr˜rKšœw˜wKšœw˜wšœ&˜&Kšœ>˜>Kšœ˜Kšœ˜Kšœžœ˜K˜ Kšœ˜Kšœ žœ˜—Jšœ(˜(J˜-K–>[viewer: ViewerClasses.Viewer, prop: ATOM, val: REF ANY]šœE˜EK–s[proc: ViewerEvents.EventProc, event: ViewerEvents.ViewerEvent, filter: REF ANY _ NIL, before: BOOL _ TRUE]šœ[˜[Kšœ™šœ(˜(Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ žœ˜Jšœžœ˜—J–B[container: Containers.Container, child: ViewerClasses.Viewer]šœC™CJ–B[container: Containers.Container, child: ViewerClasses.Viewer]šœC˜CJšœ˜Jšœ˜Jšœ˜Kšœ˜Kšœ™šœ0˜0Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœžœ˜—Kšœ3¥'™ZJšœ˜Kšœ™šœ ˜ Kšœ"˜"Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœžœ˜—šœ žœž˜)Kšœ2˜2K–ê[flavor: ViewerClasses.ViewerFlavor, info: ViewerClasses.ViewerRec _ [class: NIL, wx: 0, wy: 0, ww: 0, wh: 0, cx: 0, cy: 0, cw: 0, ch: 0, lock: [process: PROCESS#0B, count: 0B (0)], tipTable: NIL, name: NIL, file: NIL, label: NIL, menu: NIL, icon: 177777B?, column: left, caption: FALSE, scrollable: TRUE, hscrollable: FALSE, iconic: TRUE, border: TRUE, newVersion: FALSE, newFile: FALSE, visible: TRUE, offDeskTop: FALSE, destroyed: FALSE, init: FALSE, saveInProgress: FALSE, inhibitDestroy: FALSE, guardDestroy: FALSE, paintingWedged: FALSE, spare0: FALSE, spare1: FALSE, spare2: FALSE, spare3: FALSE, spare4: FALSE, spare5: FALSE, spare6: FALSE, position: 0, openHeight: 0, link: NIL, parent: NIL, sibling: NIL, child: NIL, props: NIL, data: NIL], paint: BOOL _ TRUE]šžœF˜M—Kšœ1¥'˜XKšœ1¥(˜YJ™/Jšœ5™5Jšœ*˜*Jšœ%˜%K˜K˜—š¤ œžœžœ5˜LKšœ+™+šžœ%žœžœž˜8Kšœ)¥˜GKšœ<˜K– [base: ROPE, index: INT _ 0]˜ Kšžœ žœžœ¥Ðcu˜?Kšœžœžœžœ˜IKšœžœžœžœ˜Ešžœ žœ˜Kšœ˜Kšœ˜K˜—Kšœ žœ˜$K˜—˜ Kšœžœ˜"Kšœžœ˜Kšœ.žœžœ˜eK–@[rope: ROPE, baseTime: Tempus.Packed, search: BOOL _ TRUE]šœ*žœžœ˜aKšœ<¥'˜cKšžœ žœ žœ ˜'šž˜Kšœžœ˜—K˜—Kšžœžœ¥Q˜j—K˜K˜—š¤ œžœ žœ žœ žœ žœžœ˜bK™÷Kšžœžœžœ¥˜Q–# -- [old: CHAR] RETURNS [new: CHAR]š ¤œžœžœžœžœžœ˜5K–M[base: ROPE, start: INT _ 0, len: INT _ 2147483647, with: ROPE _ NIL]šœžœ˜!Kš œžœ žœBžœžœžœ˜€K˜—–# -- [old: CHAR] RETURNS [new: CHAR]š¤œ˜Kšœžœžœžœ™K˜K˜—–# -- [old: CHAR] RETURNS [new: CHAR]š¤œ˜Kšœžœžœžœ™K˜K˜—Kšžœžœžœ˜?šžœž˜˜ šžœžœž˜šœžœžœ˜IK˜K˜K˜—šœžœ˜'K˜K–\[base: ROPE, start: INT _ 0, len: INT _ 2147483647, translator: Rope.TranslatorType]šœ˜K˜—šœžœ˜)K˜JK–\[base: ROPE, start: INT _ 0, len: INT _ 2147483647, translator: Rope.TranslatorType]šœ˜K˜—šœžœžœ˜JKšœžœ"Ïoœ¥˜Tš žœžœžœžœ¥!˜gK˜—Kšœ+˜+K–\[base: ROPE, start: INT _ 0, len: INT _ 2147483647, translator: Rope.TranslatorType]šœ˜K˜—šœžœ˜(Kšœ;˜;K–\[base: ROPE, start: INT _ 0, len: INT _ 2147483647, translator: Rope.TranslatorType]šœ˜K˜—šœžœ˜)Kšœ&˜&K˜—šœžœ˜)K™-Kšœžœ˜ Kšœ9žœžœ˜Išžœ ˜"–O[s1: ROPE, pos1: INT _ 0, s2: ROPE, pos2: INT _ 0, case: BOOL _ TRUE]šžœ˜Kšœ:˜:K–\[base: ROPE, start: INT _ 0, len: INT _ 2147483647, translator: Rope.TranslatorType]šœ˜K˜—Kšžœžœ¥,˜E—K˜—šœžœ˜*Kšœžœ¥˜1K˜—šœžœ˜%Kšœžœ¥˜1K˜—Kšžœžœ˜—Kšœ˜—˜ K–\[base: ROPE, start: INT _ 0, len: INT _ 2147483647, translator: Rope.TranslatorType]šœZ˜ZK–\[base: ROPE, start: INT _ 0, len: INT _ 2147483647, translator: Rope.TranslatorType]šœV˜VKšœ˜—˜ šžœžœž˜šœžœ˜%Kšœžœ˜Kšœ#˜#K–@[rope: ROPE, baseTime: Tempus.Packed, search: BOOL _ TRUE]šœFžœžœ ˜xK–z[time: Tempus.Packed, precision: Tempus.Precision _ minutes, includeDayOfWeek: BOOL _ FALSE, useAMPM: BOOL _ TRUE]šœH˜Hšœ"žœž˜>Kšœ8˜8Kšœ:˜:Kšœ6˜6Kšœ8˜8Kšœ<˜[s1: ROPE, s2: ROPE, pos1: INT _ 0, case: BOOL _ TRUE]šžœžœžœ ˜8Kšœžœ˜ K–@[rope: ROPE, baseTime: Tempus.Packed, search: BOOL _ TRUE]šœ)žœ"žœžœ˜bKšžœžœžœ ˜Kšžœ ˜K˜——K˜K˜——K™šœ™K–' -- [char: CHAR] RETURNS [IO.CharClass]˜KšœäÏz½™¡K˜šœ žœ¥#˜6Kšœžœžœ˜Kšœ ž˜K˜—šœ žœ¥#˜7Kšœžœžœ˜Kšœ ž˜K˜K˜—šœ žœ¥#˜7Kšœžœžœ˜Kšœž˜K˜K˜—šœ žœ¥#˜6Kšœžœžœ˜Kšœž˜Kšœ˜K˜K˜—š ¤ œžœžœžœžœ˜žœ5˜všž˜Kšœžœžœžœ˜9Kšœ žœžœžœ˜8—K˜K˜—š¤ œ¥˜CKš¢V™VKšœý™ýKšœ"žœ˜&Kšœ#˜#K™ šžœ˜šžœ¥˜Kšœ˜Kšœ%˜%K˜—šžœ¥(˜0K–@[rope: ROPE, baseTime: Tempus.Packed, search: BOOL _ TRUE]šœFžœžœ ˜xKšœ˜Kšœ˜Kšœ%˜%K˜——Kšœ.žœžœ˜cK™KšœY˜YKšœ?ž˜@šž˜Kšœžœžœžœ˜9Kšœ žœžœžœ˜8—K˜K˜——J™™K˜Kšœžœžœ ¥˜1šœ žœžœ˜Jšœ žœžœžœ¥˜HJ˜—K˜š¤ œžœžœ˜4šžœ žœžœ˜Kšœ#˜#Kšœžœ ˜K˜—šžœžœž˜Kšœ˜—šžœž˜Kšœ˜—šžœžœž˜Kšœ˜—K˜K˜—š ¤ œžœžœ žœžœ˜ZKšœžœžœžœ˜-š žœžœžœ:žœžœž˜V–*[l1: GList.List, l2: GList.List _ NIL]šžœž˜ Kšžœžœ!˜7Kšžœžœ˜)—Kšžœ˜—K˜—K˜š¤œžœžœ˜Iš ¤ œžœžœ<žœžœ˜zšœ˜Jšœ ˜ JšœU˜UJ˜ Jšœ˜—J˜—Kšœ"˜"Kšœ™Kšœ$™$Kšœ'˜'Kšœ'˜'Kšœ*žœ˜0Kšœ.žœ˜4Kšœ)™)Kšœ)™)Kšœ+˜+Kšœ™Kšœ&˜&Kšœ(žœ˜.Kšœ6žœ˜