DIRECTORY BasicTime USING [GMT, Now, Period, Update, nullGMT], Commander USING [CommandProc, Handle, Register], CommanderOps USING [DoCommand, ArgumentVector, Failed, Parse], Convert USING [Error, IntFromRope], IO USING [PutF, PutRope], List USING [Assoc], Process USING [Pause, SecondsToTicks], ProcessProps USING [GetPropList], Rope USING [Cat, ROPE], RepeatCommand, Tempus USING [Parse, Unintelligible]; RepeatCommandImpl: CEDAR PROGRAM IMPORTS BasicTime, Commander, CommanderOps, Convert, IO, List, Process, ProcessProps, Rope, Tempus EXPORTS RepeatCommand ~ BEGIN OPEN RepeatCommand; ROPE: TYPE = Rope.ROPE; repeatCommandDoc: ROPE = "\"Command line to be performed\" [period [\"Start time interpretable by Tempus\" [nTimes]]] [&] period defaults to 24*60*60 (one day) startTime defaults to BasicTime.Now[] nTimes defaults to LAST[INT] use the & (as usual) if you want this forked"; atCommandDoc: ROPE = "\"Start time interpretable by Tempus\" \"Command line to be performed\" [&] use the & (as usual) if you want this forked"; Repeat: PUBLIC PROC [cmd: ROPE, period: INT _ Days, start: BasicTime.GMT _ Immediately, nTimes: INT _ Forever] ~ { aborting: BOOL _ FALSE; nextTime: BasicTime.GMT _ IF start = BasicTime.nullGMT THEN BasicTime.Now[] ELSE start; parent: Commander.Handle = NARROW[List.Assoc[$CommanderHandle, ProcessProps.GetPropList[]]]; FOR i: INT IN [0..nTimes) WHILE NOT aborting DO parent.out.PutF["-- Waiting until %g to execute: %g\n", [time[nextTime]], [rope[cmd]]]; IF WaitForTime[nextTime] THEN { [] _ CommanderOps.DoCommand[commandLine: cmd, parent: parent]; nextTime _ BasicTime.Update[nextTime, period]; } ELSE aborting _ TRUE; ENDLOOP; parent.out.PutRope[IF aborting THEN "... aborting command.\n" ELSE "Done.\n"]; }; WaitForTime: PROC [time: BasicTime.GMT] RETURNS [ok: BOOL] ~ { ENABLE { ABORTED => GOTO AbortRequested; }; timeLeft: INT; WHILE (timeLeft _ BasicTime.Period[ from: BasicTime.Now[], to: time ]) > 0 DO Process.Pause[Process.SecondsToTicks[MIN[timeLeft, 1000]]]; ENDLOOP; RETURN[TRUE]; EXITS AbortRequested => RETURN[FALSE]; }; RepeatCommandProc: Commander.CommandProc = { argv: CommanderOps.ArgumentVector _ CommanderOps.Parse[cmd: cmd ! CommanderOps.Failed => {msg _ Rope.Cat[ "usage: ", repeatCommandDoc]; GO TO failed}]; argc: NAT _ argv.argc; commandLine: ROPE; startTime: BasicTime.GMT _ BasicTime.nullGMT; period: INT _ Days; nTimes: INT _ LAST[INT]; IF argc < 2 THEN {msg _ Rope.Cat[ "usage: ", repeatCommandDoc]; GO TO failed}; commandLine _ argv[1]; IF argc >= 3 THEN period _ Convert.IntFromRope[argv[2] ! Convert.Error => { msg _ Rope.Cat[ "Can't Parse \"", argv[2], "\" as an INT"]; GO TO failed}]; IF argc >= 4 THEN startTime _ Tempus.Parse[argv[3] ! Tempus.Unintelligible => { msg _ Rope.Cat[ "Can't Parse \"", argv[3], "\" as a time"]; GO TO failed}].time; IF argc >= 5 THEN nTimes _ Convert.IntFromRope[argv[4] ! Convert.Error => { msg _ Rope.Cat[ "Can't Parse \"", argv[4], "\" as an INT"]; GO TO failed}]; Repeat[commandLine, period, startTime, nTimes]; EXITS failed => {result _ $Failure}; }; AtCommandProc: Commander.CommandProc = { argv: CommanderOps.ArgumentVector _ CommanderOps.Parse[cmd: cmd ! CommanderOps.Failed => {msg _ Rope.Cat[ "usage: ", atCommandDoc]; GO TO failed}]; argc: NAT _ argv.argc; commandLine: ROPE; startTime: BasicTime.GMT _ BasicTime.nullGMT; IF argc # 3 THEN {msg _ Rope.Cat[ "usage: ", atCommandDoc]; GO TO failed}; startTime _ Tempus.Parse[argv[1] ! Tempus.Unintelligible => { msg _ Rope.Cat[ "Can't Parse \"", argv[1], "\" as a time"]; GO TO failed}; ].time; commandLine _ argv[2]; Repeat[commandLine, 0, startTime, 1]; EXITS failed => {result _ $Failure}; }; Commander.Register[ key: "Repeat", proc: RepeatCommandProc, doc: repeatCommandDoc, clientData: NIL, interpreted: TRUE ]; Commander.Register[ key: "At", proc: AtCommandProc, doc: atCommandDoc, clientData: NIL, interpreted: TRUE ]; END. J RepeatCommandImpl.mesa Copyright c 1986, 1987, 1988, 1990, 1991 by Xerox Corporation. All rights reserved. Carl Hauser, February 3, 1987 11:02:30 am PST Wes Irish, May 25, 1988 11:08:38 am PDT Willie-s, February 21, 1991 9:26 pm PST [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] When parsing the command line, be prepared for failure. The error is reported to the user. [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] When parsing the command line, be prepared for failure. The error is reported to the user. Initialization Carl Hauser, February 3, 1987 11:02:02 am PST Register command in current working directory rather than ///Commands/ Wes Irish, May 19, 1988 11:07:16 pm PDT Added "At" command Added the ability to abort pending commands and "Waiting until ..." message Êù•NewlineDelimiter ™™Icodešœ ÏmœI™TKšœ*Ïk™-Kšœ'™'K™'—J™šž ˜ Kšœ žœžœ ˜4Kšœ žœ!˜0Kšœ žœ,˜>Kšœžœ˜#Kšžœžœ˜Kšœžœ ˜Kšœžœ˜&Kšœ žœ˜!Kšœžœžœ˜Kšœ˜Kšœžœ˜%K˜—šÏnœžœžœ˜!Jšžœ.žœ+˜bJšžœ˜Jšœžœ˜Jšžœ˜J˜—šžœžœžœ˜J˜—Kšœžœÿ˜•K˜Kšœžœ˜‘K˜šŸœžœžœžœ žœžœžœ˜rKšœ žœžœ˜Kš œžœžœžœžœ˜WKšœžœ;˜\š žœžœžœ žœžœ ž˜/KšœW˜Wšžœžœ˜Kšœ>˜>K˜.K˜Kšžœ žœ˜—Kšžœ˜—Kšœžœ žœžœ ˜NK˜K˜—š Ðbn œžœžœžœžœ˜>šžœ˜Kšžœžœ˜K˜—Kšœ žœ˜šžœFž˜MKšœ%žœ˜;Kšžœ˜—Kšžœžœ˜ šž˜Kšœžœžœ˜ —K˜K˜—•StartOfExpansionL -- [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]šŸœ˜,Kš œžœ žœžœžœžœžœ™H–I[cmd: Commander.Handle, starExpand: BOOL _ FALSE, switchChar: CHAR]šœ?˜?KšœHžœžœ ˜WKšœ[™[—Kšœžœ ˜Kšœ žœ˜Kšœžœ˜-Kšœžœ˜Kšœžœžœžœ˜K˜Kšžœ žœ0žœžœ ˜NK˜šžœ žœ:˜KKšœ<žœžœ ˜K—šžœ žœ>˜OKšœ<žœžœ˜P—šžœ žœ:˜KKšœ<žœžœ ˜K—K˜K˜/K˜šž˜Jšœ˜—K˜K˜K˜—–L -- [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]šŸ œ˜(Kš œžœ žœžœžœžœžœ™H–I[cmd: Commander.Handle, starExpand: BOOL _ FALSE, switchChar: CHAR]šœ?˜?KšœDžœžœ ˜SKšœ[™[—Kšœžœ ˜Kšœ žœ˜Kšœžœ˜-K˜Kšžœ žœ,žœžœ ˜Jšœ ˜ šœ˜Kšœ<žœžœ ˜J—Kšœ˜—Kšœ˜K˜Kšœ%˜%K˜šž˜Jšœ˜—K˜K˜K˜—head–x[key: ROPE, proc: Commander.CommandProc, doc: ROPE _ NIL, clientData: REF ANY _ NIL, interpreted: BOOL _ TRUE]™šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ žœ˜Kšœ ž˜Kšœ˜K˜—šœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ žœ˜Kšœ ž˜Kšœ˜K˜——Jšžœ˜šœ*ž™-K™F—K™šœ$ž™'K™K™K—K™—…—L