DIRECTORY AMEvents USING [Debugging, Debugged], CIFS USING [Error], CedarSnapshot USING [CheckpointProc, Register, RollbackProc, After], Convert USING [Value, ValueToRope], DB, DBNames, Directory USING[Lookup, ignore, Error, GetProps], File USING[ Capability ], IO USING [GetCard, EndOfStream, Close, CR, CreateFilterCommentsStream, GetChar, GetIndex, int, PeekChar, PutF, rope, STREAM, time, TAB, SP, GetToken, SkipOver, TokenProc, BreakProc, WhiteSpace, GreenwichMeanTime, CharProc, GetSequence], DBIcons USING [Failure], Icons USING [NewIconFromFile, IconFlavor], FileIO USING [Open, StreamFromCapability, OpenFailed], MessageWindow USING [Append, Blink], PageFault USING [AddressFault], Process USING [Detach], Rope USING [Cat, Equal, ROPE, ToRefText], System USING[ GreenwichMeanTime, GetGreenwichMeanTime ], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerOps USING [FindViewer, RestoreViewer] ; DBIconsImpl: CEDAR MONITOR IMPORTS AMEvents, CedarSnapshot, CIFS, Convert, FileIO, IO, MessageWindow, PageFault, Process, Rope, Icons, ViewerEvents, ViewerOps, DB, DBNames, Directory, System EXPORTS DBIcons = BEGIN OPEN DB, Rope; Failed: PUBLIC ERROR [why: DBIcons.Failure, reason: Rope.ROPE] = CODE; IconDomain: DB.Domain; -- the domain of icons IconRelation: DB.Relation; -- the relation giving the properties of an icon IconFileAttr: DB.Attribute; -- the name of the file for an icon IconIndexAttr: DB.Attribute; -- the index in the file IconIsAttr: DB.Attribute; -- the icon itself (the other attributes are treated as properties) IconFlavorAttr: DB.Attribute; -- the flavor of the icon (this is somewhat tricky) IconDBInfo: DB.Relation; -- the information about the creation of the database IconDBTime: DB.Attribute; -- the last time of a parse (to make sure the DB is consistent with the catalogue; the DB may well be an extension of the parse) RegisterIcon: PUBLIC ENTRY PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL, saveInCatalogue: BOOL _ FALSE] = { InternalRegisterIcon[ iconName, fileName, index ]; IF saveInCatalogue THEN WriteEntry[iconName, fileName, index]; DB.MarkTransaction[DB.TransactionOf[$Icons]] }; GetIcon: PUBLIC PROC [iconName: Rope.ROPE, default: Icons.IconFlavor _ unInit] RETURNS [Icons.IconFlavor] = { icon: Entity = DeclareEntity[IconDomain, iconName, OldOnly]; IF icon # NIL THEN { flavor: Icons.IconFlavor _ V2U[ GetP[ icon, IconFlavorAttr, IconIsAttr ] ]; fileName: ROPE = V2S[ GetP[ icon, IconFileAttr, IconIsAttr ] ]; index: CARDINAL = V2U[ GetP[ icon, IconIndexAttr, IconIsAttr ] ]; IF flavor = unInit THEN { flavor _ Icons.NewIconFromFile[fileName, index ! CIFS.Error => IF code # noSuchFile THEN REJECT ELSE IF default = unInit THEN GOTO Default ELSE ERROR Failed[fileNotFound, fileName]; PageFault.AddressFault => IF default # unInit THEN GOTO Default ELSE ERROR Failed[invalidIndex, Convert.ValueToRope[[signed[index]]]]; ]; -- NewIconFromFile raises this if given badindex. [] _ SetP[ icon, IconFlavorAttr, U2V[flavor], IconIsAttr ] }; RETURN[flavor]; }; IF default # unInit THEN GOTO Default; ERROR Failed[noSuchIcon, iconName]; EXITS Default => RETURN[default]; }; IsRegistered: PUBLIC ENTRY PROC[iconName: Rope.ROPE _ NIL, fileName: Rope.ROPE _ NIL, index: CARDINAL _ LAST[CARDINAL]] RETURNS[name: Rope.ROPE, file: Rope.ROPE, i: CARDINAL] = { IF iconName # NIL THEN { icon: Entity = DeclareEntity[IconDomain, iconName, OldOnly]; IF icon = NIL THEN RETURN[NIL, NIL, 0]; { file _ V2S[ GetP[ icon, IconFileAttr, IconIsAttr ] ]; i _ V2U[ GetP[ icon, IconIndexAttr, IconIsAttr ] ] } } ELSE {iconRels: RelshipSet = RelationSubset[IconRelation, LIST[AttributeValue[IconFileAttr, S2V[file]], AttributeValue[IconIndexAttr, U2V[index]]]]; iconRel: Relship = NextRelship[iconRels]; IF iconRel = NIL THEN { ReleaseRelshipSet[iconRels]; RETURN[NIL, NIL, 0] }; name _ DB.NameOf[V2E[GetF[iconRel, IconIsAttr]]]; file _ fileName; i _ index; ReleaseRelshipSet[iconRels] } }; InvalidateCache: PUBLIC ENTRY PROC [iconName: Rope.ROPE _ NIL] = { icons: EntitySet = DomainSubset[ IconDomain, iconName ]; FOR e: Entity _ NextEntity[icons], NextEntity[icons] UNTIL e = NIL DO [] _ SetP[ e, IconFlavorAttr, U2V[Icons.IconFlavor[unInit]], IconIsAttr ] ENDLOOP; ReleaseEntitySet[ icons ]; DB.MarkTransaction[DB.TransactionOf[$Icons]] }; InternalRegisterIcon: INTERNAL PROC[ iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL ] = { icon: DB.Entity = DeclareEntity[IconDomain, iconName]; avList: AttributeValueList = LIST[AttributeValue[IconIsAttr, icon], AttributeValue[IconFileAttr, S2V[fileName]], AttributeValue[IconIndexAttr, U2V[index]]]; [] _ DeclareRelship[ IconRelation, avList ]; [] _ SetP[ icon, IconFlavorAttr, U2V[Icons.IconFlavor[unInit]], IconIsAttr ] }; catalogueName: Rope.ROPE _ "DBIcons.Catalogue"; catalogueCap: File.Capability; Parse: INTERNAL PROCEDURE = { OPEN IO; stream: IO.STREAM; { ENABLE { Failed => REJECT; IO.EndOfStream => GOTO Out; }; stream _ CreateFilterCommentsStream[FileIO.StreamFromCapability[ fileName: catalogueName, capability: catalogueCap, accessOptions: read ! FileIO.OpenFailed => ERROR Failed[noCatalogue, catalogueName]]]; DO sepCount: INT; DBNameProc: IO.CharProc = { IF char = DBNames.Separator THEN { sepCount _ sepCount+1; RETURN[FALSE, TRUE] }; IF char = ': AND (sepCount = 2 OR sepCount = 0) THEN RETURN[TRUE, FALSE]; RETURN[FALSE, TRUE] }; whiteSpace: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE IO.WhiteSpace[char]] }; isACR: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE other] }; tokenProc: IO.BreakProc = { RETURN[SELECT char FROM IO.SP, IO.TAB, ', => sepr, IO.CR => break, ENDCASE => other ]; }; iconName, fileName: Rope.ROPE; index: CARDINAL; position: INT; IO.SkipOver[stream, IO.WhiteSpace]; position _ stream.GetIndex[]; sepCount _ 0; iconName _ IO.GetSequence[stream, DBNameProc]; IO.SkipOver[stream, whiteSpace]; -- skips over spaces, tabs, but stops at CR IF stream.PeekChar[] # ': THEN { IO.SkipOver[stream, isACR]; Report1[msg: Rope.Cat["missing : at [", Convert.ValueToRope[[signed[position]]], "]"]]; LOOP; }; [] _ stream.GetChar[]; -- the : IO.SkipOver[stream, whiteSpace]; fileName _ IO.GetToken[stream, tokenProc]; index _ IO.GetCard[stream]; InternalRegisterIcon[ iconName, fileName, index ] ENDLOOP; EXITS Out => NULL; }; IF stream # NIL THEN stream.Close[]; IF errorLog # NIL THEN { MessageWindow.Append["problems encountered, see IconRegistry.log."]; MessageWindow.Blink[]; errorLog.Close[]; errorLog _ NIL; }; }; WriteEntry: INTERNAL PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL] = { OPEN IO; viewer: ViewerClasses.Viewer; stream: IO.STREAM _ FileIO.Open[fileName: catalogueName, accessOptions: append, createOptions: oldOnly]; Write: PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL] = { stream.PutF["\n%g: %g %d", rope[iconName], rope[fileName], int[index]]; }; IF iconName # NIL THEN Write[iconName, fileName, index]; stream.Close[]; IF (viewer _ ViewerOps.FindViewer[catalogueName]) # NIL THEN ViewerOps.RestoreViewer[viewer]; }; WriteCatalogue: PUBLIC ENTRY PROC [] = { icons: EntitySet = DomainSubset[ IconDomain]; FOR e: Entity _ NextEntity[icons], NextEntity[icons] UNTIL e = NIL DO { fileName: ROPE = V2S[ GetP[ e, IconFileAttr, IconIsAttr ] ]; index: CARDINAL = V2U[ GetP[ e, IconIndexAttr, IconIsAttr ] ]; WriteEntry[ DBNames.EntityToName[e], fileName, index ] } ENDLOOP; ReleaseEntitySet[icons] }; Report1: INTERNAL PROCEDURE [msg: Rope.ROPE] = { OPEN IO; ENABLE { UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL; ANY => CONTINUE; }; IF errorLog = NIL THEN { errorLog _ FileIO.Open[fileName: "IconRegistry.log", accessOptions: overwrite]; errorLog.PutF["Processing %g at %t", rope[catalogueName], time[]]; }; errorLog.PutF["\n\n%g", rope[msg]]; }; errorLog: IO.STREAM _ NIL; NoticeChanges: CedarSnapshot.RollbackProc = { DB.Initialize[nCachePages: 256]; SetUpSegment[segmentFile: "[Local]Icons.segment", seg: $Icons, number: 140B]; IconDomain _ DeclareDomain["Icon", $Icons]; IconRelation _ DeclareRelation[ "IconInfo", $Icons ]; IconFileAttr _ DeclareAttribute[IconRelation, "file", StringType]; IconIndexAttr _ DeclareAttribute[IconRelation, "index", IntType]; IconIsAttr _ DeclareAttribute[IconRelation, "of", IconDomain, Key]; IconFlavorAttr _ DeclareAttribute[IconRelation, "flavor", IntType]; IconDBInfo _ DeclareRelation[ "IconDBInfo", $Icons ]; IconDBTime _ DeclareAttribute[IconDBInfo, "time", TimeType]; InvalidateCache[]; ParseCatalogue[]}; SetUpSegment: PROC[segmentFile: ROPE, seg: DB.Segment, number: NAT_ 0] = { success: BOOL_ TRUE; DB.DeclareSegment[segmentFile, seg, number ! DB.Error => { IF code=TransactionAlreadyOpen THEN GOTO AlreadyDone }]; DB.OpenTransaction[seg ! DB.Error => {IF code=FileNotFound THEN success_ FALSE; CONTINUE}]; IF NOT success THEN { DB.CloseTransaction[DB.TransactionOf[seg]]; -- DB has left the transaction open DB.DeclareSegment[segmentFile, seg, number,, NewOnly]; DB.OpenTransaction[seg]; DB.MarkTransaction[DB.TransactionOf[seg]] }; EXITS AlreadyDone => NULL }; CloseTrans: CedarSnapshot.CheckpointProc = { DB.CloseTransaction[trans: DB.TransactionOf[segment: $Icons]] }; ParseCatalogue: ENTRY PROC = TRUSTED { txt: REF TEXT = Rope.ToRefText[catalogueName]; name: LONG STRING = LOOPHOLE[txt]; failed: BOOLEAN _ FALSE; iconDBInfo: RelshipSet = RelationSubset[ IconDBInfo ]; infoRel: Relation = NextRelship[iconDBInfo]; timeInDB: IO.GreenwichMeanTime = IF infoRel # NIL THEN V2T[GetF[infoRel, IconDBTime]] ELSE System.GetGreenwichMeanTime[]; catalogueCap _ Directory.Lookup[fileName: name, permissions: Directory.ignore ! Directory.Error => {failed _ TRUE; CONTINUE}]; IF failed THEN { ReleaseRelshipSet[iconDBInfo]; ERROR Failed[noCatalogue, catalogueName] }; { createDate: IO.GreenwichMeanTime; [, , createDate, , ] _ Directory.GetProps[file: catalogueCap, name: name]; IF createDate # timeInDB THEN Parse[]; IF infoRel = NIL THEN SetF[ DeclareRelship[ IconDBInfo ], IconDBTime, T2V[createDate] ] ELSE SetF[ infoRel, IconDBTime, T2V[createDate] ]; DB.MarkTransaction[ DB.TransactionOf[ $Icons ] ] }; ReleaseRelshipSet[iconDBInfo] }; WasCatalogueEdited: ViewerEvents.EventProc -- [viewer: ViewerClasses.Viewer, event: ViewerEvent] -- = { IF Rope.Equal[viewer.name, catalogueName, FALSE] THEN TRUSTED {Process.Detach[FORK ParseCatalogue[]]}; }; [] _ ViewerEvents.RegisterEventProc[proc: WasCatalogueEdited, event: save, before: FALSE]; TRUSTED {CedarSnapshot.Register[c: CloseTrans, r: NoticeChanges]; NoticeChanges[checkpoint] }; END. ØDBIconsImpl.mesa; Edited by Teitelman on April 12, 1983 4:53 pm Last Edited by: Donahue, May 26, 1983 9:17 am Types Global variables for accessing the database The tuples in the relation have the structure IconRelation[ IconIsAttr: IconDomain, IconFileAttr: ROPE, IconIndexAttr: INT, IconFlavorAttr: Icons.IconFlavor ] (where the flavor is stored as an UNSPECIFIED) The tuple in the relation have the structure IconDBInfo[ IconDBTime: Time ] (there is only one tuple in this relation) Accessing Icon Catalogue Changes RegisteredIcons.Catalogue to reflect the indicated association. This definition will replace any previous definition. comments are for inclusion in the catalogue to help the user figure out what the icon looks like. Obtain an icon flavor for the icon associated with iconName. Create a new icon flavor if one has not previously been created. If for some reason it could not obtain the icon, and default # unInit, returns the default. Otherwise, raises Error (defined below) with appropriate parameters. Can be called with either iconName, or fileName and index; does not force creation of icon flavor (only GetIcon does that). If an iconFlavor was previous created for this icon, discard the flavor, i.e. the next call to GetIcon will create a new flavor. If iconName = NIL, do this for all registered icons. For use by iconeditor. Parsing Icon Catalogue ANY => GOTO Out; find a colon after seeing zero or two separators (one for the domain, one for the segment -- see if key is followed by a : Reporting errors When the Icon Catalogue changes A foolproof way to open a segment & transaction, regardless of whether the segment is already declared, doesn't exist on the disk, or already has a transaction open. Initialization Ê ±– "Cedar" style˜JšÏc™Jšœ™-JšÏn-™-šÏk ˜ Jšœ Ÿœ˜%JšŸœŸœ ˜JšœŸœ1˜DJšœŸœ˜#JšŸœ˜J˜Jšœ Ÿœ"˜1JšœŸœ˜Jš ŸœŸœŸœLŸœŸœŸœb˜ìJšœŸœ ˜JšœŸœ˜*JšœŸœ*˜6JšœŸœ˜$Jšœ Ÿœ˜JšœŸœ ˜JšœŸœŸœ ˜)JšœŸœ,˜8JšœŸœ ˜Jšœ Ÿœ ˜2Jšœ Ÿœ˜+J˜J˜—J˜JšÐbl œŸœŸ˜J˜JšŸœŸœŸœKŸœ˜¥J˜JšŸœ ˜J˜šœŸœ˜JšŸœŸœ˜—headšœ™Jš œŸœŸœ%ŸœŸœ˜F—™+Jšœ Ÿœ ˜.šœŸœ 0˜LJšœ:œ‘™Í—JšœŸœ #˜@JšœŸœ ˜6Jšœ Ÿœ C˜]JšœŸœ 3˜Qšœ Ÿœ 5˜OJšœv™v—Jšœ Ÿœ €˜›—™šž œŸœŸœŸœŸœŸœ ŸœŸœŸœ˜J™àJšœ2˜2JšŸœŸœ'˜>JšŸœŸœ˜,J˜—J˜š žœŸœŸœŸœ&Ÿœ˜mJš}™}™ Jšœ<˜