<> <> <> <> <<>> <> <> <<>> DIRECTORY Commander USING [CommandProc, Handle, Register], CommanderOps USING [ParseToList], IO, LoganBerry USING [AttributeType, AttributeValue, BuildIndices, Close, CompactLogs, DeleteEntry, Describe, Entry, EntryProc, Error, FlushDBCache, IndexInfo, LogInfo, Open, OpenDB, SchemaInfo, WriteEntry, nullDB], LoganQuery USING [AttributePatterns, BooleanFilterEntries, Cursor, EndGenerate, NextEntry, ParseBooleanQuery, ParseTree, PatternsToEntry, QueryEntries, ReadAttributePatterns, SyntaxError], PFS USING [PathFromRope, StreamOpen], Process USING [CheckForAbort], Rope USING [Concat, Equal, Find, Index, Length, ROPE, SkipTo, Substr], SystemNames USING [UserName]; LoganBerryCommandsImpl: CEDAR PROGRAM IMPORTS Commander, CommanderOps, IO, LoganBerry, LoganQuery, PFS, Process, Rope, SystemNames ~ BEGIN ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; currentName: ROPE ¬ "#"; currentDB: LoganBerry.OpenDB ¬ LoganBerry.nullDB; -- cache of most recently used db <> DescribeCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; dbname: ROPE; info: LoganBerry.SchemaInfo; [dbname,,msg] ¬ ParseArgs[cmd, FALSE]; IF msg#NIL THEN RETURN; info ¬ LoganBerry.Describe[db: Open[dbname]]; IO.PutF1[cmd.out, "Database: %g\n", IO.rope[info.dbName]]; IO.PutRope[cmd.out, " keys:"]; FOR l: LIST OF LoganBerry.IndexInfo ¬ info.indices, l.rest WHILE l # NIL DO IO.PutF1[cmd.out, " %g", IO.atom[l.first.key]]; ENDLOOP; IO.PutRope[cmd.out, "\n"]; IO.PutRope[cmd.out, " index files:"]; FOR l: LIST OF LoganBerry.IndexInfo ¬ info.indices, l.rest WHILE l # NIL DO IO.PutF1[cmd.out, " %g", IO.rope[l.first.file]]; ENDLOOP; IO.PutRope[cmd.out, "\n"]; IO.PutRope[cmd.out, " log files:"]; FOR l: LIST OF LoganBerry.LogInfo ¬ info.logs, l.rest WHILE l # NIL DO IO.PutF1[cmd.out, " %g", IO.rope[l.first.file]]; ENDLOOP; IO.PutRope[cmd.out, "\n"]; EXITS End => RETURN; }; QueryCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; PrintEntry: LoganBerry.EntryProc = { <<[entry: LoganBerry.Entry] RETURNS [continue: BOOL]>> Process.CheckForAbort[]; FOR e: LoganBerry.Entry ¬ entry, e.rest UNTIL e = NIL DO IO.PutF[cmd.out, "%g: %g\n", IO.atom[e.first.type], IO.rope[e.first.value]]; ENDLOOP; IO.PutRope[cmd.out, "\n"]; RETURN[TRUE]; }; dbname: ROPE; queryTree: LoganQuery.ParseTree; db: LoganBerry.OpenDB; cursor: LoganQuery.Cursor; entry: LoganBerry.Entry; primaryKey: LoganBerry.AttributeType; [dbname, queryTree, msg] ¬ ParseArgsQuery[cmd]; IF msg#NIL THEN RETURN; db ¬ Open[dbname]; primaryKey ¬ LoganBerry.Describe[db: db].info.indices.first.key; cursor ¬ LoganQuery.QueryEntries[db: db, patterns: NIL, baseIndex: primaryKey].cursor; <> cursor ¬ LoganQuery.BooleanFilterEntries[input: cursor, query: queryTree, inputOrder: primaryKey, primaryKey: primaryKey]; entry ¬ LoganQuery.NextEntry[cursor]; WHILE entry # NIL DO [] ¬ PrintEntry[entry]; entry ¬ LoganQuery.NextEntry[cursor]; ENDLOOP; LoganQuery.EndGenerate[cursor]; EXITS End => RETURN; }; WriteCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; dbname: ROPE; ap: LoganQuery.AttributePatterns; db: LoganBerry.OpenDB; entry: LoganBerry.Entry; [dbname, ap, msg] ¬ ParseArgs[cmd]; IF msg#NIL THEN RETURN; db ¬ Open[dbname]; entry ¬ LoganQuery.PatternsToEntry[ap]; LoganBerry.WriteEntry[db: db, entry: entry]; EXITS End => RETURN; }; DeleteCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; 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]; }; DeleteEntry: LoganBerry.EntryProc = { <<[entry: LoganBerry.Entry] RETURNS [continue: BOOL]>> value: LoganBerry.AttributeValue ¬ GetAttributeValue[entry, primaryKey]; Process.CheckForAbort[]; LoganBerry.DeleteEntry[db: db, key: primaryKey, value: value]; IO.PutF[cmd.out, "Deleted %g: %g\n", IO.atom[primaryKey], IO.rope[value]]; RETURN[TRUE]; }; dbname: ROPE; queryTree: LoganQuery.ParseTree; db: LoganBerry.OpenDB; cursor: LoganQuery.Cursor; entry: LoganBerry.Entry; primaryKey: LoganBerry.AttributeType; [dbname, queryTree, msg] ¬ ParseArgsQuery[cmd]; IF msg#NIL THEN RETURN; db ¬ Open[dbname]; primaryKey ¬ LoganBerry.Describe[db: db].info.indices.first.key; cursor ¬ LoganQuery.QueryEntries[db: db, patterns: NIL, baseIndex: primaryKey].cursor; <> cursor ¬ LoganQuery.BooleanFilterEntries[input: cursor, query: queryTree, inputOrder: primaryKey, primaryKey: primaryKey]; entry ¬ LoganQuery.NextEntry[cursor]; WHILE entry # NIL DO [] ¬ DeleteEntry[entry]; entry ¬ LoganQuery.NextEntry[cursor]; ENDLOOP; LoganQuery.EndGenerate[cursor]; EXITS End => RETURN; }; CloseCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; dbname: ROPE; [dbname,,msg] ¬ ParseArgs[cmd, FALSE]; IF msg#NIL THEN RETURN; LoganBerry.Close[db: Open[dbname]]; EXITS End => RETURN; }; FlushCacheCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; dbname: ROPE; [dbname,,msg] ¬ ParseArgs[cmd, FALSE]; IF msg#NIL THEN RETURN; IF Rope.Equal[dbname, "ALL", FALSE] THEN LoganBerry.FlushDBCache[] ELSE LoganBerry.FlushDBCache[db: Open[dbname]]; EXITS End => RETURN; }; BuildIndicesCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; dbname: ROPE; db: LoganBerry.OpenDB; retried: BOOLEAN ¬ FALSE; [dbname,,msg] ¬ ParseArgs[cmd, FALSE]; IF msg#NIL THEN RETURN; db ¬ Open[dbname ! LoganBerry.Error => IF ec=$BadIndex AND NOT retried THEN {retried ¬ TRUE; RETRY}; ]; LoganBerry.BuildIndices[db: db]; EXITS End => RETURN; }; CompactLogsCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE LoganBerry.Error => { IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; GOTO End; }; dbname: ROPE; [dbname,,msg] ¬ ParseArgs[cmd, FALSE]; IF msg#NIL THEN RETURN; LoganBerry.CompactLogs[db: Open[dbname]]; EXITS End => RETURN; }; CreateCmd: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> < ...>> dbname, shortname, mediumname, fullname, logname: ROPE; username: ROPE; i: INT; s: STREAM; args: LIST OF ROPE ¬ CommanderOps.ParseToList[cmd].list; IF args = NIL THEN GOTO Usage; <<>> <> dbname ¬ args.first; args ¬ args.rest; i ¬ Rope.Find[s1: dbname, s2: "."]; IF i # -1 THEN { -- fullname given, e.g. SampleLB.df or Sample.df fullname ¬ dbname; mediumname ¬ Rope.Substr[base: dbname, start: 0, len: i]; i ¬ Rope.Find[s1: mediumname, s2: "LB", case: FALSE]; IF i # -1 THEN shortname ¬ Rope.Substr[base: mediumname, start: 0, len: i] ELSE shortname ¬ mediumname; } ELSE { -- name given without suffix, e.g. SampleLB or just Sample i ¬ Rope.Find[s1: dbname, s2: "LB", case: FALSE]; IF i # -1 THEN { mediumname ¬ dbname; shortname ¬ Rope.Substr[base: mediumname, start: 0, len: i]; } ELSE { shortname ¬ dbname; mediumname ¬ Rope.Concat[base: shortname, rest: "LB"]; }; fullname ¬ Rope.Concat[base: mediumname, rest: ".df"]; }; <<>> <> username ¬ SystemNames.UserName[]; username ¬ Rope.Substr[base: username, start: 0, len: Rope.Index[username, 0, "."]]; <> logname ¬ Rope.Concat[base: shortname, rest: ".lblog"]; s ¬ PFS.StreamOpen[fileName: PFS.PathFromRope[logname], accessOptions: $create]; IO.PutRope[s, "\377"]; IO.Close[s]; <> IF args = NIL THEN args ¬ LIST["Key"]; -- default primary key s ¬ PFS.StreamOpen[fileName: PFS.PathFromRope[fullname], accessOptions: $create]; IO.PutF1[s, "-- %g\n", IO.rope[fullname]]; IO.PutRope[s, "-- A LoganBerry schema file\n\n"]; IO.PutF1[s, "Directory [User]<%g>Top>\n", IO.rope[username]]; IO.PutF1[s, " %g\n\n", IO.rope[fullname]]; IO.PutF[s, "Directory [User]<%g>%g>\n", IO.rope[username], IO.rope[mediumname]]; IO.PutRope[s, " --> log 0 readwrite\n"]; IO.PutF1[s, " %g\n\n", IO.rope[logname]]; IO.PutF1[s, " --> index \"%g\" primary\n", IO.rope[args.first]]; IO.PutF[s, " %g%g.lbindex\n", IO.rope[shortname], IO.rope[args.first]]; FOR skeys: LIST OF ROPE ¬ args.rest, skeys.rest WHILE skeys # NIL DO --add secondary indices IO.PutF1[s, " --> index \"%g\" secondary\n", IO.rope[skeys.first]]; IO.PutF[s, " %g%g.lbindex\n", IO.rope[shortname], IO.rope[skeys.first]]; ENDLOOP; IO.Close[s]; msg ¬ IO.PutFR["Created database schema file %g and log file %g\n", IO.rope[fullname], IO.rope[logname]]; EXITS Usage => IO.PutRope[cmd.err, "Usage: LBCreate ...\n e.g. LBCreate SampleLB.df Name Phone Address\n"]; }; ParseArgs: PROC [cmd: Commander.Handle, readAttributePatterns: BOOLEAN ¬ TRUE] RETURNS [dbname: ROPE ¬ NIL, ap: LoganQuery.AttributePatterns ¬ NIL, errMsg: ROPE ¬ NIL] ~ { argStream: IO.STREAM ¬ IO.RIS[cmd.commandLine]; dbname ¬ IO.GetTokenRope[argStream, IO.IDProc ! IO.EndOfStream => {errMsg ¬ "No database name supplied.\n"; GOTO End}].token; IF readAttributePatterns THEN ap ¬ LoganQuery.ReadAttributePatterns[argStream ! LoganQuery.SyntaxError => {errMsg ¬ explanation; GOTO End}]; EXITS End => RETURN; }; ParseArgsQuery: PROC [cmd: Commander.Handle] RETURNS [dbname: ROPE ¬ NIL, query: LoganQuery.ParseTree ¬ NIL, errMsg: ROPE ¬ NIL] ~ { argStream: IO.STREAM ¬ IO.RIS[cmd.commandLine]; dbname ¬ IO.GetTokenRope[argStream, IO.IDProc ! IO.EndOfStream => {errMsg ¬ "No database name supplied.\n"; GOTO End}].token; query ¬ LoganQuery.ParseBooleanQuery[argStream ! LoganQuery.SyntaxError => {errMsg ¬ Rope.Concat["Bad query: ", explanation]; GOTO End}]; EXITS End => RETURN; }; Open: PROC [dbname: ROPE] RETURNS [db: LoganBerry.OpenDB] ~ { <> prev, loc: INT ¬ 0; IF Rope.Equal[dbname, currentName] THEN RETURN[currentDB]; WHILE loc < Rope.Length[dbname] DO prev ¬ loc; loc ¬ Rope.SkipTo[s: dbname, pos: prev+1, skip: "/>]"]; ENDLOOP; IF Rope.Find[dbname, "!", prev] = -1 AND Rope.Find[dbname, ".", prev] = -1 THEN { dbname ¬ Rope.Concat[dbname, ".df"]; }; db ¬ currentDB ¬ LoganBerry.Open[dbName: dbname]; }; <> Commander.Register[key: "LBDescribe", proc: DescribeCmd, interpreted: TRUE, doc: "Get schema information about a LoganBerry database.\n usage: LBDescribe " ]; Commander.Register[key: "LBQuery", proc: QueryCmd, interpreted: FALSE, doc: "Query a LoganBerry database.\n usage: LBQuery " ]; Commander.Register[key: "LBRead", proc: QueryCmd, interpreted: FALSE, doc: "synonym for LBQuery." ]; Commander.Register[key: "LBWrite", proc: WriteCmd, interpreted: FALSE, doc: "Write an entry into a LoganBerry database.\n usage: LBWrite ..." ]; Commander.Register[key: "LBDelete", proc: DeleteCmd, interpreted: FALSE, doc: "Delete entries from a LoganBerry database.\n usage: LBDelete " ]; Commander.Register[key: "LBClose", proc: CloseCmd, interpreted: TRUE, doc: "Close a LoganBerry database.\n usage: LBClose " ]; Commander.Register[key: "LBFlushCache", proc: FlushCacheCmd, interpreted: TRUE, doc: "Flush one or all open database handles from LoganBerry's cache.\n usage: LBFlushCache { or ALL}" ]; Commander.Register[key: "LBBuildIndices", proc: BuildIndicesCmd, interpreted: TRUE, doc: "Rebuild the indices for a LoganBerry database.\n usage: LBBuildIndices " ]; Commander.Register[key: "LBCompactLogs", proc: CompactLogsCmd, interpreted: TRUE, doc: "Compact a LoganBerry database.\n usage: LBCompactLogs " ]; Commander.Register[key: "LBCreate", proc: CreateCmd, doc: "Create a LoganBerry database.\n usage: LBCreate ..." ]; END. <<>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<>>