DIRECTORY Ascii USING [Upper, Lower], Basics USING [Comparison], BasicTime USING [Now], Commander USING [CommandProc, Register], CommandTool USING [ArgumentVector, Parse], DefaultRemoteNames USING [Get], DFUtilities USING [DirectoryItem, FileItem, ParseFromStream, ProcessItemProc, SyntaxError], EditSpan USING [Copy], FS USING [ComponentPositions, EnumerateForNames, Error, ExpandName, NameProc, StreamOpen], IO USING [BreakProc, Close, EndOf, EndOfStream, Error, GetChar, GetTokenRope, PutF, PutF1, PutFR, PutFR1, RIS, rope, SetIndex, SkipWhitespace, STREAM, time], List USING [Nconc1], NodeProps USING [PutProp, true], Process USING [CheckForAbort], PutGet USING [FromFile], RedBlackTree USING [Compare, Create, DuplicateKey, EnumerateIncreasing, GetKey, Insert, UserData, Size, Table], Rope USING [Cat, Compare, Concat, Equal, Fetch, Find, IsEmpty, Length, Match, ROPE, Substr, Translate], TextNode USING [Body, Level, Location, MakeNodeLoc, NodeRope, nullLocation, nullSpan, Ref, Root, Span, StepForward], TiogaFileOps USING [AddLooks, CreateRoot, InsertAsLastChild, InsertNode, NodeBody, SetContents, SetFormat, Store], UserCredentials USING [Get], UserProfile USING [Token], ViewerIO USING [CreateViewerStreams]; CatalogImpl: CEDAR PROGRAM IMPORTS Ascii, BasicTime, Commander, CommandTool, DefaultRemoteNames, DFUtilities, EditSpan, FS, IO, List, NodeProps, RedBlackTree, Process, PutGet, Rope, TextNode, TiogaFileOps, UserCredentials, UserProfile, ViewerIO EXPORTS TiogaFileOps -- export NodeBody to convince Compiler we know that TiogaFileOps.Ref is really TextNode.Ref ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Ref: TYPE ~ REF NodeBody; NodeBody: PUBLIC TYPE ~ TextNode.Body; CatalogCmdProc: Commander.CommandProc ~ { systemHost: ROPE _ DefaultRemoteNames.Get[].systemHost; serverName: ROPE _ Rope.Substr[systemHost, 1, systemHost.Length-2]; -- strip off the brackets catalogName: ROPE; catalogFileName: ROPE; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd]; quietly: BOOLEAN _ FALSE; i: NAT _ 1; WHILE i < argv.argc DO IF Rope.Fetch[argv[i], 0] = '- THEN { SELECT Ascii.Lower[Rope.Fetch[argv[i], 1]] FROM 's => { i _ i+1; -- consume following option as the server name IF i >= argv.argc THEN RETURN [$Failure, "-server option must be followed by a server name; please supply one."]; serverName _ argv[i]; }; 'q => quietly _ TRUE; ENDCASE => RETURN [$Failure, Rope.Concat[argv[i], " is an unrecognized option; use only -server or -quietly"]]; } ELSE { IF catalogName # NIL THEN RETURN [$Failure, "multiple catalog names supplied; only one allowed."]; catalogName _ argv[i]; }; i _ i+1; ENDLOOP; IF catalogName = NIL THEN RETURN; catalogFileName _ Catalog[serverName, catalogName, IF quietly THEN NIL ELSE cmd.out]; RETURN [NIL, Rope.Cat["\n", catalogFileName, " created."]]; }; Catalog: PROC [serverName: ROPE, catalogName: ROPE, out: IO.STREAM _ NIL] RETURNS [catalogFileName: ROPE] ~ { dfFilePattern: ROPE _ Rope.Cat["/", serverName, "/", catalogName, "/Top/*.DF!H"]; TryThisDFFile: FS.NameProc ~ { base, ext: ROPE; [base, ext] _ ParseFileName[fullFName]; Process.CheckForAbort[]; IF Rope.Equal[s1~ext, s2~"DF", case~FALSE] THEN { AddCatalogEntry[doc~catalogDoc, packageName~base, dfFileName~fullFName]; }; RETURN[continue~TRUE]; }; catalogDoc: CatalogDoc ~ InitializeCatalogDoc[catalogName]; logName: ROPE ~ Rope.Concat[catalogName, "Catalog.log"]; catalogDoc.out _ out; catalogDoc.log _ ViewerIO.CreateViewerStreams[ name: logName, backingFile: logName, editedStream: FALSE].out; IO.PutF[catalogDoc.log, "Catalog of %g, %g\n\n", IO.rope[catalogName], IO.time[]]; FS.EnumerateForNames[pattern~dfFilePattern, proc~TryThisDFFile]; FinishCatalogDoc[catalogDoc]; RETURN [catalogDoc.fileName] }; CatalogDoc: TYPE ~ REF CatalogDocRec; CatalogDocRec: TYPE ~ RECORD[ out: IO.STREAM, -- for recording our progress log: IO.STREAM, -- for logging errors fileName: ROPE, packageName: ROPE, root: Ref, latestLevel1Node: Ref, latestLevel2Node: Ref, latestLevel3Node: Ref, commandIndex: IndexTable, keywordIndex: IndexTable ]; InitializeCatalogDoc: PROC [catalogName: ROPE] RETURNS [doc: CatalogDoc] ~ { UpperCase: PROC [r: ROPE] RETURNS [ROPE] ~ { upper: PROC [old: CHAR] RETURNS [new: CHAR] ~ { new _ Ascii.Upper[old] }; RETURN[Rope.Translate[base: r, translator: upper]]; }; PutPropRope: PROC [n: TextNode.Ref, name: ATOM, value: ROPE] ~ { NodeProps.PutProp[n: n, name: name, value: value]; }; doc _ NEW[CatalogDocRec]; doc.commandIndex _ CreateIndexTable[]; doc.keywordIndex _ CreateIndexTable[]; doc.fileName _ Rope.Concat[catalogName, "Catalog.tioga"]; doc.latestLevel1Node _ NIL; doc.root _ TiogaFileOps.CreateRoot[]; PutPropRope[doc.root, $Postfix, "(firstPageNumber) 0 .def (firstHeadersAfterPage) 0 .def"]; AppendNode[level~1, doc~doc, r~doc.fileName, format~NIL]; NodeProps.PutProp[doc.latestLevel1Node, $Comment, NodeProps.true]; AppendNode[level~2, doc~doc, format~NIL, r~IO.PutFR["%g %t", IO.rope[UserProfile.Token["EditorComforts.LastEdited", UserCredentials.Get[].name]], IO.time[BasicTime.Now[]]]]; NodeProps.PutProp[doc.latestLevel2Node, $Comment, NodeProps.true]; AppendNode[level~1, doc~doc, r~Rope.Concat[UpperCase[catalogName], " PACKAGE CATALOG"], format~"unleaded"]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~0, len~LAST[INT], look~'s, root~doc.root]; PutPropRope[doc.latestLevel1Node, $Mark, "centerHeader"]; AppendNode[level~1, doc~doc, r~"CEDAR 6.0 FOR INTERNAL XEROX USE ONLY", format~"unleaded"]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~0, len~LAST[INT], look~'s, root~doc.root]; PutPropRope[doc.latestLevel1Node, $Mark, "centerFooter"]; AppendNode[level~1, doc~doc, r~Rope.Concat["Package Catalog\nfor ", catalogName], format~"title"]; AppendNode[level~1, doc~doc, r~"c Copyright 1985 by Xerox Corporation. All rights reserved.", format~"abstract"]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~0, len~1, look~'m, root~doc.root]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~0, len~LAST[INT], look~'s, root~doc.root]; AppendNode[level~1, doc~doc, r~"Abstract: This catalog is a list of interesting packages and tools. The catalog is automatically created from the collection of maintainer-supplied entries.", format~"abstract"]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~0, len~8, look~'b, root~doc.root]; AppendNode[level~1, doc~doc, r~"XEROX\t\t\tXerox Corporation\n\t\t\t\tPalo Alto Research Center\n\t\t\t\t3333 Coyote Hill Road\n\t\t\t\tPalo Alto, California 94304\n\nFor Internal Xerox Use Only", format~"boilerplate"]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~0, len~5, look~'q, root~doc.root]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~5, len~LAST[INT], look~'o, root~doc.root]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~115, len~LAST[INT], look~'b, root~doc.root]; TiogaFileOps.AddLooks[x~doc.latestLevel1Node, start~115, len~LAST[INT], look~'x, root~doc.root]; AppendNode[level~1, doc~doc, r~"Catalog Components", format~"head"]; }; FinishCatalogDoc: PROC [doc: CatalogDoc] ~ { Process.CheckForAbort[]; IF NOT IsTableEmpty[doc.commandIndex] THEN AddIndex[doc, command]; IF NOT IsTableEmpty[doc.keywordIndex] THEN AddIndex[doc, keyword]; Process.CheckForAbort[]; [] _ TiogaFileOps.Store[filename~doc.fileName, x~doc.root]; IF doc.log#NIL THEN IO.Close[doc.log]; }; AddCatalogEntry: PROC [doc: CatalogDoc, packageName: ROPE, dfFileName: ROPE] ~ { lastDirectoryPath: ROPE _ NIL; packageDoc: ROPE ~ Rope.Concat[packageName, "Doc.tioga"]; documentationFileName: ROPE _ NIL; documentationFiles: ROPE _ NIL; listOfCommands: LIST OF REF ANY _ NIL; TryThisDFItem: DFUtilities.ProcessItemProc ~ { WITH item SELECT FROM directory: REF DFUtilities.DirectoryItem => { lastDirectoryPath _ directory.path1; }; file: REF DFUtilities.FileItem => { base, ext, shortName: ROPE _ NIL; [base, ext, shortName] _ ParseFileName[file.name ! FS.Error => IF error.group#bug THEN { Log[doc, IO.PutFR1["FS.Error... %g", IO.rope[error.explanation]]]; GOTO Fail; }; ]; SELECT TRUE FROM Rope.Equal["load", ext, FALSE] => { listOfCommands _ List.Nconc1[listOfCommands, base]; }; Rope.Equal["tioga", ext, FALSE] --AND Rope.Match["*Doc", base, FALSE]-- => { IF NOT Rope.Match["*Documentation*", lastDirectoryPath, FALSE] THEN { Log[doc, IO.PutFR1["%g is not in the proper Documentation directory.", IO.rope[shortName]]]; }; IF documentationFiles.IsEmpty THEN documentationFiles _ shortName ELSE documentationFiles _ Rope.Cat[documentationFiles, ", ", shortName]; IF Rope.Equal[shortName, packageDoc, FALSE] THEN documentationFileName _ Rope.Concat[lastDirectoryPath, file.name]; RETURN; }; ENDCASE => NULL; EXITS Fail => NULL; }; ENDCASE => NULL; }; dfStream: IO.STREAM _ NIL; Process.CheckForAbort[]; BeginPackage[doc, packageName]; dfStream _ FS.StreamOpen[fileName~dfFileName ! FS.Error => { Log[doc, IO.PutFR1["FS.Error... %g", IO.rope[error.explanation]]]; GO TO FileProblem; }; ]; DFUtilities.ParseFromStream[in~dfStream, proc~TryThisDFItem, filter~[comments~FALSE, filterA~all, filterB~public, filterC~all] ! DFUtilities.SyntaxError => { Log[doc, IO.PutFR1["DFUtilities.SyntaxError... %g", IO.rope[reason]]]; CONTINUE; }; ]; IO.Close[dfStream]; Process.CheckForAbort[]; { -- create the catalog entry author, creator, maintainer: ROPE _ NIL; abstractSpan: TextNode.Span _ TextNode.nullSpan; keywords: ROPE; singlePageDocument: BOOLEAN; IF documentationFileName.IsEmpty THEN Log[doc, "No package documentation."] ELSE { [abstractSpan: abstractSpan, author: author, creator: creator, maintainer: maintainer, keywords: keywords, singlePageDocument: singlePageDocument] _ ExtractStuffFromDocFile[doc, documentationFileName]; IF author.IsEmpty[] AND maintainer.IsEmpty[] THEN { Log[doc, "No author or maintainer listed in documentation."] }; }; AppendNode[level~2, doc~doc, r~packageName, format~"head"]; AppendNode[level~3, doc~doc, r~Rope.Cat["DF file: ", packageName, ".df"], format~"indent"]; TiogaFileOps.AddLooks[x~doc.latestLevel3Node, start~0, len~8, look~'b, root~doc.root]; IF NOT creator.IsEmpty THEN { AppendNode[level~3, doc~doc, r~creator, format~"indent"]; TiogaFileOps.AddLooks[x~doc.latestLevel3Node, start~0, len~11 --Created by:--, look~'b, root~doc.root]; }; IF NOT maintainer.IsEmpty THEN { AppendNode[level~3, doc~doc, r~maintainer, format~"indent"]; TiogaFileOps.AddLooks[x~doc.latestLevel3Node, start~0, len~14 --Maintained by:--, look~'b, root~doc.root]; }; IF NOT documentationFiles.IsEmpty AND NOT (Rope.Equal[documentationFiles, packageDoc, FALSE] AND singlePageDocument) THEN { AppendNode[level~3, doc~doc, r~Rope.Cat["Documentation: ", documentationFiles], format~"indent"]; TiogaFileOps.AddLooks[x~doc.latestLevel3Node, start~0, len~14, look~'b, root~doc.root]; }; IF NOT author.IsEmpty THEN { AppendNode[level~3, doc~doc, r~Rope.Cat["Author: ", author], format~"indent"]; TiogaFileOps.AddLooks[x~doc.latestLevel3Node, start~0, len~7 --Author:--, look~'b, root~doc.root]; }; IF NOT keywords.IsEmpty THEN { CommaSeparated: IO.BreakProc ~ { RETURN [SELECT char FROM ', => sepr, ENDCASE => other] }; s: IO.STREAM _ IO.RIS[keywords]; firstBlank: INT _ Rope.Find[keywords, " "]; IO.SetIndex[self~s, index~firstBlank]; -- position past "Keywords:" [] _ IO.SkipWhitespace[s]; WHILE NOT IO.EndOf[s] DO ENABLE IO.Error, IO.EndOfStream => EXIT; keyword: ROPE _ NIL; keyword _ IO.GetTokenRope[stream~s, breakProc~CommaSeparated].token; IF NOT keyword.IsEmpty THEN InsertIndexTerm[doc.keywordIndex, keyword, packageName]; [] _ IO.GetChar[s]; [] _ IO.SkipWhitespace[s]; ENDLOOP; IO.Close[s]; AppendNode[level~3, doc~doc, r~keywords, format~"indent"]; TiogaFileOps.AddLooks[x~doc.latestLevel3Node, start~0, len~10, look~'b, root~doc.root]; }; { contents: ROPE _ NIL; FOR l: LIST OF REF ANY _ listOfCommands, l.rest WHILE l # NIL DO commandName: ROPE _ NARROW[l.first]; InsertIndexTerm[doc.commandIndex, commandName, packageName]; contents _ IF contents.IsEmpty THEN Rope.Concat["Commands: ", commandName] ELSE Rope.Cat[contents, ", ", commandName]; ENDLOOP; IF NOT contents.IsEmpty THEN { AppendNode[level~3, doc~doc, r~contents, format~"indent"]; TiogaFileOps.AddLooks[x~doc.latestLevel3Node, start~0, len~9, look~'b, root~doc.root]; }; }; IF abstractSpan # TextNode.nullSpan THEN { [] _ EditSpan.Copy[dest~TextNode.MakeNodeLoc[doc.latestLevel3Node], source~abstractSpan, destRoot~doc.root, sourceRoot~TextNode.Root[abstractSpan.start.node]]; FOR node: Ref _ Forward[doc.latestLevel3Node], Forward[node] WHILE TextNode.Level[node] >= 3 DO TiogaFileOps.SetFormat[node, "indent"]; ENDLOOP; }; }; EXITS FileProblem => NULL; }; ExtractStuffFromDocFile: PROC [ doc: CatalogDoc, documentationFile: ROPE ] RETURNS [ abstractSpan: TextNode.Span _ TextNode.nullSpan, author, creator, maintainer, keywords: ROPE _ NIL, singlePageDocument: BOOLEAN _ FALSE ] ~ { tiogaDocRoot: Ref ~ PutGet.FromFile[documentationFile ! FS.Error => { Log[doc, IO.PutFR1["FS.Error... %g", IO.rope[error.explanation]]]; GOTO Quit; }; ]; prev: Ref _ NIL; hasCopyright: BOOL _ FALSE; AbstractNode: PROC [node: Ref, begin: BOOL _ FALSE] ~ { nodeLoc: TextNode.Location ~ TextNode.MakeNodeLoc[node]; IF abstractSpan.start=TextNode.nullLocation THEN { IF begin OR hasCopyright THEN abstractSpan.start _ abstractSpan.end _ nodeLoc; } ELSE { IF abstractSpan.end.node=prev THEN abstractSpan.end _ nodeLoc; }; }; RopeOf: PROC [n: Ref] RETURNS [ROPE] ~ { RETURN[TextNode.NodeRope[n]] }; FOR node: Ref _ Forward[tiogaDocRoot], Forward[prev _ node] WHILE node#NIL DO SELECT node.formatName FROM $authors => { IF NOT author.IsEmpty[] THEN author _ author.Concat["; "]; author _ author.Concat[RopeOf[node]]; }; $abstract => { contents: ROPE ~ RopeOf[node]; shortContents: ROPE ~ contents.Substr[len: 100]; SELECT TRUE FROM Rope.Match[pattern: "Copyright*", object: shortContents, case: FALSE] OR Rope.Match[pattern: "* Copyright *", object: shortContents, case: FALSE] => { hasCopyright _ TRUE; }; Rope.Match[pattern: "Abstract*", object: shortContents, case: FALSE] => { AbstractNode[node, TRUE]; }; Rope.Match[pattern: "Created by*", object: shortContents, case: FALSE] => { creator _ contents; }; Rope.Match[pattern: "Maintained by*", object: shortContents, case: FALSE] => { maintainer _ contents; }; Rope.Match[pattern: "Keyword*", object: shortContents, case: FALSE] => { keywords _ contents; }; ENDCASE => { AbstractNode[node]; }; }; $boilerplate => { peek: Ref ~ Forward[node]; -- peek to see if there are any following nodes IF peek=NIL THEN singlePageDocument _ TRUE; EXIT; }; ENDCASE; ENDLOOP; EXITS Quit => NULL; }; AddIndex: PROC [doc: CatalogDoc, kindOf: {command, keyword}] ~ { indexTable: IndexTable _ IF kindOf = command THEN doc.commandIndex ELSE doc.keywordIndex; lastTerm: ROPE; PerIndexTerm: EnumerateProc ~ { IF NOT Rope.Equal[term, lastTerm, FALSE] THEN AppendNode[level~2, doc~doc, r~Rope.Cat[term, ": ", location], format~"item"] ELSE doc.latestLevel2Node.rope _ Rope.Cat[doc.latestLevel2Node.rope, ", ", location]; lastTerm _ term; RETURN [FALSE]; }; AppendNode[level~1, doc~doc, r~IF kindOf = command THEN "Command Index" ELSE "Keyword Index", format~"head"]; EnumerateIndexTerms[indexTable, PerIndexTerm]; }; ParseFileName: PROC [fileName: ROPE] RETURNS [base, ext, shortName: ROPE] ~ { cp: FS.ComponentPositions; fullName: ROPE; [fullName, cp] _ FS.ExpandName[fileName]; base _ Rope.Substr[fullName, cp.base.start, cp.base.length]; ext _ Rope.Substr[fullName, cp.ext.start, cp.ext.length]; shortName _ Rope.Substr[fullName, cp.base.start, cp.ext.start+cp.ext.length-cp.base.start]; }; AppendNode: PROC [doc: CatalogDoc, level: NAT, r: ROPE, format: ROPE] ~ { InnerAppendNode: PROC [subtree: Ref, latest: Ref, r: ROPE, format: ROPE] RETURNS [newNode: Ref] ~ { IF subtree = NIL THEN ERROR; newNode _ IF latest = NIL THEN TiogaFileOps.InsertNode[x~subtree, child~TRUE] ELSE TiogaFileOps.InsertAsLastChild[x~subtree, prevLast~latest]; TiogaFileOps.SetContents[x~newNode, txt~r]; TiogaFileOps.SetFormat[x~newNode, format~format]; }; SELECT level FROM 1 => { doc.latestLevel1Node _ InnerAppendNode[doc.root, doc.latestLevel1Node, r, format]; doc.latestLevel2Node _ doc.latestLevel3Node _ NIL; }; 2 => { doc.latestLevel2Node _ InnerAppendNode[doc.latestLevel1Node, doc.latestLevel2Node, r, format]; doc.latestLevel3Node _ NIL; }; 3 => { doc.latestLevel3Node _ InnerAppendNode[doc.latestLevel2Node, doc.latestLevel3Node, r, format]; }; ENDCASE; }; Forward: PROC [node: Ref] RETURNS [next: Ref] ~ { RETURN [TextNode.StepForward[node]]; }; BeginPackage: PROC [doc: CatalogDoc, packageName: ROPE] ~ { out: IO.STREAM ~ doc.out; IF out#NIL THEN out.PutF1["%g ... ", IO.rope[packageName]]; doc.packageName _ packageName; }; Log: PROC [doc: CatalogDoc, msg: ROPE] ~ { log: IO.STREAM ~ doc.log; IF log#NIL THEN log.PutF["%g: %g\n", IO.rope[doc.packageName], IO.rope[msg]]; }; IndexTable: TYPE ~ RedBlackTree.Table; CreateIndexTable: PROC [] RETURNS [IndexTable] ~ { RETURN [RedBlackTree.Create[getKey: GetIndexKey, compare: CompareIndexKeys]]; }; IsTableEmpty: PROC [table: IndexTable] RETURNS [BOOLEAN] ~ { RETURN [RedBlackTree.Size[table] = 0]; }; Pair: TYPE ~ REF PairRec; PairRec: TYPE ~ RECORD[ term: ROPE, location: ROPE ]; InsertIndexTerm: PROC [table: IndexTable, term: ROPE, location: ROPE] ~ { data: REF _ NEW[PairRec _ [term, location]]; RedBlackTree.Insert[table, data, data ! RedBlackTree.DuplicateKey => CONTINUE]; }; GetIndexKey: RedBlackTree.GetKey ~ { RETURN [data]; }; CompareIndexKeys: RedBlackTree.Compare ~ { pair1: Pair _ NARROW[k]; pair2: Pair _ NARROW[data]; comparison: Basics.Comparison _ Rope.Compare[pair1.term, pair2.term, FALSE]; IF comparison = equal THEN comparison _ Rope.Compare[pair1.location, pair2.location, FALSE]; RETURN [comparison]; }; EnumerateProc: TYPE ~ PROC [term: ROPE, location: ROPE] RETURNS [BOOLEAN]; EnumerateIndexTerms: PROC [table: IndexTable, proc: EnumerateProc] ~ { ApplyProc: PROC [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE] ~ { pair: Pair _ NARROW[data]; RETURN [proc[pair.term, pair.location]]; }; RedBlackTree.EnumerateIncreasing[table, ApplyProc]; }; Commander.Register[ key~"Catalog", proc~CatalogCmdProc, doc~"Catalog [-quietly] [-server <ServerName>] <CatalogDirectoryName> make a catalog of software packages stored as DF files in /<ServerName>/<CatalogDirectoryName>/Top/*.DF" ]; END. ��~��CatalogImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Rick Beach, October 5, 1985 11:52:43 am PDT Doug Wyatt, October 14, 1985 6:10:53 pm PDT PROC [fullFName: ROPE] RETURNS [continue: BOOLEAN]; Use this to force the type of a string literal to be ROPE rather than REF TEXT. each component is added underneath this heading ProcessItemProc: TYPE = PROC [item: REF ANY] RETURNS [stop: BOOL _ FALSE]; command name is the name of the .Load file the list of documentation files either has multiple files (since its not just PackageDoc.Tioga) or points to a long document (since its not a single page document) PROC [char: CHAR] RETURNS [CharClass]; Begin with this node if it says "Abstract: ..." or we've seen the copyright notice. Continue the abstract if this node is contiguous. Documentation may contain several abstract format nodes The following heuristics are used to determine the span of the abstract: Begin the abstract if the node begins with "Abstract" Begin the abstract at the node following the copyright notice which is a node matching "Copyright *" or "* Copyright *" End the abstract if the node does not have abstract format End the abstract if the node begins with "Keyword", "Created by", or "Mainained by" collect head nodes here for cumulative table of contents PROC [data: UserData] RETURNS [Key] PROC [k: Key, data: UserData] RETURNS [Basics.Comparison] Ê��˜�code™Kšœ Ïmœ1™<K™+K™+—K™�šÏk ˜ Kšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœ žœ˜(Kšœžœ˜*Kšœžœ˜KšœžœJ˜[Kšœ žœ˜KšžœžœR˜ZKšžœžœbžœ"žœ˜Kšœžœ ˜Kšœ žœ˜ Kšœžœ˜Kšœžœ˜Kšœ žœ]˜oKšœžœDžœ˜gKšœ žœf˜tKšœ žœ`˜rKšœžœ˜Kšœžœ ˜Kšœ žœ˜%—K˜�KšÐlnœž ˜KšžœÒ˜ÙKšžœÏc\˜qšœž˜K™�Kšžœžœžœ˜Kšœžœžœ ˜Kšœ žœ˜&K˜�šÏnœ˜)Kšœžœ'˜7Kšœžœ4 ˜]Kšœ žœ˜Kšœžœ˜K˜:Kšœ žœžœ˜Kšœžœ˜šžœž˜šžœžœ˜%šžœ%ž˜/šœ˜Kšœ .˜8šžœž˜KšžœT˜Z—K˜Kšœ˜—Kšœžœ˜Kšžœžœ^˜o—Kšœ˜—šžœ˜šžœžœžœ˜KšžœB˜H—K˜Kšœ˜—K˜Kšžœ˜—Kšžœžœžœžœ˜!K˜�Kš œ3žœ žœžœžœ ˜UK˜�Kšžœžœ0˜;K˜K˜�—š¡œžœžœžœžœžœžœžœžœ˜nKšœžœ>˜Qš¡ œ˜Kšžœ žœžœžœ™3Kšœžœ˜Kšœ'˜'K˜šžœ"žœžœ˜1KšœH˜HK˜—Kšžœ žœ˜K˜—Kšœ;˜;Kšœ žœ+˜8K˜Kšœbžœ˜mKšžœ/žœžœ ˜RK˜@K˜Kšžœ˜K˜K˜�—Kšœžœžœ˜%šœžœžœ˜Kšœžœžœ ˜-Kšœžœžœ ˜%Kšœ žœ˜Kšœ žœ˜K˜ K˜K˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜�—š¡œžœžœžœ˜Lš ¡ œžœžœžœžœ˜,Kš œžœžœžœžœ˜IKšžœ-˜3K˜—š¡œžœžœ žœ˜@Kšœ5žœ žœžœ™OK˜2K˜—Kšœžœ˜Kšœ&˜&Kšœ&˜&K˜9Kšœžœ˜K˜�šœ%˜%Kšœ[˜[K˜�—šœ4žœ˜9KšœB˜BK˜�—š œ$žœžœžœSžœ˜KšœB˜BK˜�—šœDÏsœ˜kKšœ;žœžœ˜^Kšœ9˜9K˜�—šœ ¢'œ˜]Kšœ;žœžœ˜^Kšœ9˜9K˜�—šœb˜bK˜�—šœr˜rKšœV˜VKšœ;žœžœ˜^K˜�—šœÓ˜ÓKšœV˜VK˜�—šœÛ˜ÛKšœV˜VKšœ;žœžœ˜^Kšœ=žœžœ˜`Kšœ=žœžœ˜`K˜�—šœD˜DK™/—K˜K˜�—š¡œžœ˜,K˜Kšžœžœ žœ˜BKšžœžœ žœ˜BK˜Kšœ;˜;Kšžœ žœžœžœ˜&K˜K˜�—š¡œžœ žœžœ˜PKšœžœžœ˜Kšœžœ)˜9Kšœžœžœ˜"Kšœžœžœ˜Kšœžœžœžœžœžœ˜&š¡ œ!˜.Kš¡œžœžœžœžœžœžœžœ™Jšžœžœž˜šœžœ˜-Kšœ$˜$Kšœ˜—šœžœ˜#Kšœžœžœ˜!šœ2˜2šžœ žœžœ˜%Kšœ žœžœ˜BKšžœ˜ Kšœ˜—Kšœ˜—šžœžœž˜šœžœ˜#K™*Kšœ3˜3Kšœ˜—šœžœ 'œ˜Lšžœžœ2žœžœ˜EKšœ žœ<žœ˜\Kšœ˜—Kšžœžœ˜AKšžœD˜Hšžœ#žœž˜0KšœB˜B—Kšžœ˜Kšœ˜—Kšžœžœ˜—Kšžœ žœ˜Kšœ˜—Kšžœžœ˜—K˜—Kšœ žœžœžœ˜K˜�K˜Kšœ˜K˜�šœ.˜.šœ ˜ Kšœ žœžœ˜BKšžœžœ ˜Kšœ˜—Kšœ˜—šœNžœ-˜€šœ˜Kšœ žœ)žœ˜FKšžœ˜ Kšœ˜—Kšœ˜—Kšžœ˜K˜�K˜K˜�šœ ˜Kšœžœžœ˜(K˜0Kšœ žœ˜Kšœžœ˜Kšžœžœ&˜Kšžœ˜KšœÉ˜Éšžœžœžœ˜3Kšœ<˜<Kšœ˜—Kšœ˜—Kšœ;˜;šœ[˜[KšœV˜V—šžœžœžœ˜šœ9˜9Kšœg˜g—Kšœ˜—šžœžœžœ˜ šœ<˜<Kšœj˜j—Kšœ˜—šžœžœžœžœ-žœžœžœ˜{K™£šœ)Ït œ+˜aKšœW˜W—Kšœ˜—šžœžœžœ˜šœ)£œ˜NKšœb˜b—Kšœ˜—šžœžœžœ˜š¡œžœ˜ Kšžœžœžœ ™&šžœžœž˜Kšœ˜Kšžœ ˜—K˜—Kš œžœžœžœžœ˜ Kšœžœ˜+Kšžœ& ˜DKšœžœ˜šžœžœžœ ž˜Kšžœžœžœžœ˜(Kšœ žœžœ˜Kšœžœ8˜Dšžœžœž˜Kšœ8˜8—Kšœžœ˜Kšœžœ˜Kšžœ˜—Kšžœ ˜šœ:˜:KšœW˜W—Kšœ˜—šœ˜Kšœ žœžœ˜šžœžœžœžœžœžœžœž˜@Kšœ žœžœ ˜$K˜<šœžœ˜Kšžœ£œ˜+Kšžœ'˜+—Kšžœ˜—šžœžœžœ˜šœ:˜:KšœV˜V—Kšœ˜—Kšœ˜—šžœ"žœ˜*KšœŸ˜Ÿšžœ:žœž˜_K˜'Kšžœ˜—Kšœ˜—Kšœ˜—šž˜Kšœžœ˜—K˜K˜�—š¡œžœ'žœžœ\žœžœžœžœ˜äšœ7˜7šžœ˜ Kšœ žœžœ˜BKšžœ˜ K˜—K˜—Kšœžœ˜Kšœžœžœ˜š¡œžœžœžœ˜7K˜8šžœ*žœ˜2šžœžœžœ1˜NK™S—K˜—šžœ˜šžœžœ˜>K™1—K˜—K˜—Kš ¡œžœ žœžœžœ˜Hšžœ9žœžœž˜Mšžœž˜šœ ˜ Kšžœžœžœ˜:Kšœ%˜%K˜—šœ˜K™7™HK™5K™wK™:K™S—Kšœ žœ˜Kšœžœ˜0šžœžœž˜Kšœ?žœ˜EšžœCžœ˜PKšœžœ˜K˜—šœ>žœ˜IJšœžœ˜K˜—šœ@žœ˜KKšœ˜K˜—šœCžœ˜NKšœ˜K˜—šœ=žœ˜HKšœ˜K˜—šžœ˜K˜K˜——K˜—šœ˜Kšœ /˜JKšžœžœžœžœ˜+K™8Kšžœ˜K˜—Kšžœ˜—Kšžœ˜—Kšžœ žœ˜K˜K˜�—š¡œžœ2˜@Kšœžœžœžœ˜YKšœ žœ˜š¡œ˜šžœžœžœ˜(KšžœN˜RKšžœQ˜U—K˜Kšžœžœ˜K˜—Kšœžœžœžœ!˜mK˜.K˜K˜�—š ¡ œžœžœžœžœ˜MKšœ˜Kšœ žœ˜Kšœžœ˜)Kšœ<˜<Kšœ9˜9Kšœ[˜[K˜K˜�—š ¡ œžœžœžœ žœ˜Iš ¡œžœ žœ žœžœ˜cKšžœžœžœžœ˜šœ žœ žœž˜Kšœ)žœ˜.—šžœ˜Kšœ;˜;—Kšœ+˜+Kšœ1˜1K˜—šžœž˜šœ˜K˜RKšœ.žœ˜2Kšœ˜—šœ˜K˜^Kšœžœ˜Kšœ˜—šœ˜K˜^Kšœ˜—Kšžœ˜—K˜K˜�—š¡œžœ žœ˜1Kšžœ˜$K˜K˜�—š¡œžœ žœ˜;Kšœžœžœ˜Kšžœžœžœžœ˜;K˜K˜K˜�—š¡œžœžœ˜*Kšœžœžœ˜Kš žœžœžœžœžœ˜MK˜K˜�—šœžœ˜&K˜�—š¡œžœžœ˜2KšžœG˜MK˜K˜�—š¡œžœžœžœ˜<Kšžœ ˜&K˜K˜�—Kšœžœžœ ˜šœ žœžœ˜Kšœžœ˜Kšœ ž˜Kšœ˜K˜�—š¡œžœžœžœ˜IKšœžœžœ˜,KšœEžœ˜OK˜K˜�—š¡œ˜$Jšžœžœ™#Jšžœ˜J˜J˜�—š¡œ˜*Kšžœžœ™9Kšœžœ˜Kšœžœ˜KšœEžœ˜LKšžœžœ;žœ˜\Kšžœ˜K˜K˜�—š œžœžœžœžœžœžœ˜JK˜�—š¡œžœ-˜Fš ¡ œžœžœžœžœ˜NKšœ žœ˜Kšžœ"˜(K˜—Kšœ3˜3K˜K˜�—˜K˜K˜K˜²K˜K˜�——K˜�Kšžœ˜—�…—����Gr��`��