; REMSND - Send a reminder, invoked by scheduler when it thinks time has come ; for first reminder on list. Must check truth of this, then: ; Must first pull into current MSG-LSE, find next time to send ; and include this information in message if desired, then do a ; normal-priority sending (all local, but queue if anything for net) ; Then pop it off RML list and put new time back on in right place, ; and write out new RML, etc... Then re-schedule self for next ; due reminder. REMSND: PUSHAE P,[A,B,C,D,E,L] SKIPN RMLAR ; Ensure RML in core. PUSHJ P,RMLGET MOVE L,$ARLOC+RMLAR PUSHJ P,TIMGT ; Get current time for comparision, in A. MOVE D,$LLLST(L) ; Get LP to first LLN MOVE B,LISTAR(D)+1 ; Get LP to first LN of first RML node. MOVE C,LISTAR(B)+1 ; And now get time! CAMLE C,A ; Less than or eq to current time?? JRST REMS80 ; No, still in future. Must re-schedule. ; Send reminder! First pull into MSG-LSE. FINDA C,[A$ID,,[B]] ; Find ID SLN. JSR AUTPSY ; Bleah?? MOVE A,LISTAR(C)+1 ; Get SPT ADD A,$LSLOC(L) ; Make abs ASCNT ptr. PUSHJ P,MSGGET ; Now snarf up. JRST [ LNDEL D,$LLLST(L) ; Ugh, not found. Flush, assuming MASTER is right. PUSHJ P,RMLPUT ; Store out new RML, JRST REMS80] ; and all done. MOVE L,$ARLOC+MSGAR ; Make MSG-LSE current SETZM RTMMSG ; Clear SLP to remind-note. PUSHJ P,RTMFND ; Now find next time to send. JRST REMS30 ; None - go inform that this is last reminder. FINDA C,[A$TEXP,,[$LLLST(L)] ; Find expiration spec. JRST REMS40 ; None? then don't bother with any status report. MOVE B,LISTAR(C)+1 ; Get it TLNE B,-1 ; If anything in LH, it's an expiration time. JRST [ CAMLE A,B ; Compare with next time JRST REMS30 ; Ugh, next time is greater than expiration. No more. JRST REMS40] ; Is OK... TLO F,%MSGMD SOSLE B,LISTAR(C)+1 ; Expiration count. Decrement count JRST REMS40 ; And jump if still OK. ; No more reminders after this one. REMS30: MAKELN A,[0 ? %LTSTR,,[[ASCNT [This is the last reminder.]]]] MOVEM A,RTMMSG ; Store SLP to remind-note. SETZM RTMNXT ; Clear next-time, and fall thru. ; A has SLP if any to insert in text. ; Must generate new ID temporarily and send. REMS40: SKIPN C,MSGIDP ; Get current pointer to MSG ID JSR AUTPSY ; Just a check... MOVEI B,A$PID ; Turn ID into Parent-ID. DPB B,[$LAFLD,,LISTAR(C)] PUSHJ P,IDGET ; Now create a new ID MOVE B,$LLLST(L) HRRM B,LISTAR(A) MOVEM A,$LLLST(L) ; Cons onto MSG list. MOVEM A,MSGIDP ; And update MSG ID pointer. TLO F,%MSGMD ; And indicate it's modified. PUSHJ P,MAIL ; Send it! This will queue if necessary. JFCL FINDA A,[A$ID,,[$LLLST(L]] ; Find ptr to ID again JSR AUTPSY ; We damn well put one there! LNDEL A,$LLLST(L) ; Now flush it, FINDA A,[A$PID,,[$LLLST(L)]] ; Now find parent ID JSR AUTPSY ; miscegnation. MOVEM A,MSGIDP ; Udpate MSG ID ptr to it MOVEI B,A$ID DPB B,[$LAFLD,,LISTAR(A)] ; and make it MSG ID again. ; Reminder sent, now see what time if any to resend. SKIPN A,RTMNXT ; Get and skip if there's one. JRST REMS60 ; Nope, don't send. Flush from MASTER & RML. FINDA B,[A$TSND,,[$LLLST(L)]] ; Find LN to hold time-of-next-sending JRST [ MAKELN B,[A$TSND,,[$LLLST(L)] %LTVAL,,0] ; If none, make one. MOVEM B,$LLLST(L) JRST .+1] MOVEM A,LISTAR(B)+1 ; Store time to send next. ; Now pop off RML list, and put back in right place. MOVE L,$ARLOC+RMLAR ; Make RML current MOVE E,$LLLST(L) ; Get LP to LLN of this reminder. HRRZ C,LISTAR(E) ; Get its CDR MOVEM C,$LLLST(L) ; And store back... it's popped! MOVE C,LISTAR(E)+1 ; Get LP to LN holding time MOVEM A,LISTAR(C)+1 ; And store new time there! ; Now loop looking for place to re-insert it. SETZ D, SKIPN C,$LLLST(L) ; Get LP to RML JRST [ MOVEM E,$LLLST(L) ; If nothing there, JRST REMS50] ; Trivial. REMS46: MOVE B,LISTAR(C)+1 ; Get LP to time LN CAMGE A,LISTAR(B)+1 ; Compare desired time with that of current node JRST REMS47 ; Found place! MOVE D,C ; Not yet. save LP to this HRRZ C,LISTAR(C) ; Get CDR to next LLN JUMPN C,REMS46 ; and try that. ; Found place to insert. D has LP to previous LLN, ; C has LP to current, which new entry must precede. REMS47: HRRM C,LISTAR(E) ; Make current entry CDR of new JUMPE D,[MOVEM E,$LLLST(L) ; (Check for beg of list) JRST REMS50] HRRM E,LISTAR(D) ; Make new entry CDR of previous. ; Reminder put back on RML. Now write back MSG-LSE, MASTER, and RML. REMS50: PUSHJ P,MSGPUT ; This will handle MASTER too. PUSHJ P,RMLPUT JRST REMS80 ; All done... ; Here when reminder isn't to be sent any more. Flush. REMS60: MOVE B,MSGIDP MOVE A,LISTAR(A)+1 ; Get SPT for MSG ID. ADD A,$LSLOC(L) ; Abs. PUSHJ P,MSGDEL ; Glurgle! Down it goes. PUSHJ P,RMLPUT ; Out with RML, and that's it. REMS80: PUSHJ P,SCHREM ; Schedule next reminder. REMS90: POPAE P,[L,E,D,C,B,A] POPJ P, IFN 0,[ Algorithm for finding next time to send is: 1) Make list of all time specs, along with earliest time for each. 2) Sort, earliest first. 3) Earliest one greater than time of copy already sent? If not, fall thru to 4 If latter time is 0 = not sent yet, is earliest one already past current time? If so, also fall through, else go to 5. 4) Must increment according to repeat specification until greater. Put back in table, go to 2. 5) Is time obtained past expiration date if any? If so, flush. 6) Voila! To find earliest time for a spec: 1) Get current time (or base time if given) 2) Plug given time into all wild slots. 3) Later than current time? If so, go to 7! 4) not late enough. If using a base time, use current time instead and go to 2. 5) using current time. Increment, using LOWEST wild item or given abs increment. (Must check to ensure that eventual winnage is actually possible. obviously if year is '65, we'll never get anywhere) 6) Go back to 3 and so forth, until get a time in the future. 7) Check: is obtained time past specified expiration date if any? 8) If not, found im... stop and rest. ] TM%DOW==700000 ; 2.9-2.7 0-7 1-7 TM%HR== 76000 ; 2.6-2.2 0-31 0-23 TM%MIN== 1760 ; 2.1-1.5 0-63 0-59 TM$DOW==(.BP TM%DOW,) TM$HR== (.BP TM%HR,) TM$MIN==(.BP TM%MIN,) TM$YR2==(.BP >,) ; Byte ptr to bottom 2 bits of TM%YR. ; Wildness is indicated by an 0 value for all of YEAR, MONTH, DAY. ; DOW is special; the following cases exist. ; DAY DOW ; * * DAY is wild and message is sent every day. ; * n Message sent only on given DOW. ; n * Message sent only on given DOM. ; n n Message is sent only when both DOM and DOW agree. This ; is great for things like "Friday the 13th"! ; RTMFND - Given current MSG-LSE, finds next (future) time to send. ; Hacks all time-specs, returns time in A and skips. ; non-skip indicates no more reminders to be sent. ; Does NOT hack any of: Expiration count/date or Next-send time. RTMFND: PUSH P,B SETZM A,RTMNXT ; Clear "best-so-far" variable. PUSHJ P,TIMGT ; Get current time MOVEM A,RTMCUR SKIPA B,$LLLST(L) ; Get LP to MSG-LSE list. RTMFN2: HRRZ B,LISTAR(B) ; on loop, get CDR to next. FINDA A,[A$TLST,,[B]] ; Try to find spec list JRST RTMFN5 ; No more. PUSHJ P,RTMFN ; Get next-time for this spec. JRST RTMFN2 ; None available. SKIPE RTMNXT ; If no spec stored yet, always store it. CAMG A,RTMNXT ; Aha, compare with currently best (earliest) MOVEM A,RTMNXT ; New time is less, use that! JRST RTMFN2 ; Try the rest. RTMFN5: SKIPE A,RTMNXT ; Return this... AOS -1(P) ; And skip if returning a real time. POP P,B POPJ P, ; RTMFN - Given LP in A to a reminder-time-spec LLN, figures out next ; time to send according to that spec. RTMCUR must have been set up. ; Returns time in A and skips if found. RTMFN: PUSH P,B FINDA B,[A$TBAS,,[LISTAR(A)+1]] ; See if base time specified. TDCA B,B MOVE B,LISTAR(B)+1 ; Get it if so, else set 0. MOVEM B,RTMBAS FINDA B,[A$TINC,,[LISTAR(A)+1]] ; See if time increment specified. TDCA B,B MOVE B,LISTAR(B)+1 ; Same as above. MOVEM B,RTMINC FINDA B,[A$TSPC,,[LISTAR(A)+1]] ; Now find the time spec! JSR AUTPSY ; Foo! must always have!! MOVE A,LISTAR(B)+1 ; Get it, and POP P,B PJRST RTMF ; Now figure out next time! RTMCUR: 0 ; Holds current time in standard format. (only Y/M/D used) RTMBAS: 0 ; Holds base time to use, also standard format. ( " ) RTMINC: 0 ; Holds desired incr, if no wild specs. Val in minutes. ; RTMF - Messy monster to figure out next time to send a reminder, ; given a reminder-time spec and the assumption a reminder has ; just been sent (so no need to worry about sending one in the past) ; RTMCUR must be set up with current time, RTMBAS with base time ; to use... RTMFND: PUSHAE P,[B,C,D,E] SKIPN B,RTMBAS ; If base time given, use it initially. MOVE B,RTMCUR ; Else use current time. RTMF05: AND B,[TM%YR+TM%MON+TM%DAY] TLNE A,(TM%YR) ; Has year specification? TLZ B,(TM%YR) ; Yes, wipe from base time. TLNE A,(TM%MON) ; Has month? TLZ B,(TM%MON) ; Yes, wipe from base. TLNE A,(TM%DAY) ; This is getting TLZ B,(TM%DAY) ; monotonous. IOR B,A ; Now IOR them together to produce complete spec! LDB C,[TM$HR,,B] ; Start getting time LDB D,[TM$MIN,,B] ; into IMULI C,60. ADDI C,(D) IMULI C,60.*2. ; Standard halfsec form. HRR B,C ; Now have specified time in standard format! RTMF10: CAMG B,RTMCUR ; Is resultant greater than current time? JRST [ SKIPN RTMBAS ; No. Were we using a base time? JRST RTMF30 ; If not, just go increment since time is already past. SETZM RTMBAS ; If using base time, zap it and don't use any more. MOVE B,RTMCUR JRST RTMF05] ; Time is greater than current! Now must check esoteric stuff ; like DOW, unless using RTMINC. SKIPE RMTINC ; Using RMTINC? JRST RMTF70 ; Yeah, ignore all else and return. TRNN A,TM%DOW ; Is DOW specified? JRST RTMF70 ; No, all's well. LDB C,[TM$DOW,,A] ; Bleah. Get DOW to check for. PUSHAE P,[A,B] MOVE A,B ; Set up current stuff for munching. PUSHJ P,TIMADY ; Get time in abs # days IDIVI A,7 ; Get DOW-1 in B MOVEI D,1(B) ; Get DOW in D POPAE P,[B,A] ; Restore stuffs. CAIN C,(D) ; Compare with desired DOW. JRST RTMF70 ; Hurray! SUBI D,(C) ; Foo. Find difference in days. CAIL D,0 SUBI D,7 ; If pos, adjust, to get # days in future for DOW. MOVMS D ; It really works... JRST RTMF32 ; and jump into increment-day rtn. ; Day increment! Any attempt to find a more future date must start here! ; If RTMINC is specified, it is applied without question. RTMF30: MOVEI D,1 ; Before testing wildness, set up increment. RTMF32: SKIPE RMTINC JRST RMTF60 ; Uh-oh. RMTINC non-Z means simply add increment. TLNE A,(TM%DAY) ; Wild day? JRST RTMF40 ; Nope, try incrementing month. LDB C,[TM$DAY,,B] ; Get current day ADDI C,(D) ; Increment CAIG C,28. ; If less than final DOM for smallest month, JRST [ DPB C,[TM$DAY,,B] ; Win! put back into time JRST RTMF10] ; and continue. ; Ugh, must check out day increment. LDB D,[TM$MON,,B] ; Find what month it is. CAIN D,3 ; February? JRST [ LDB E,[TM$YR2,,B] ; Pouah! Why can't the world be simple? CAMG C,(E)[29. ? 28. ? 28. ? 28.] JRST RTMF10 ; Is OK... JRST RTMF37] ; NOT OK! CAMG C,TMONLN(D) ; Check max month length. JRST RTMF10 ; OK... ; Day-increment went into next month! RTMF37: MOVEI C,1 DPB C,[TM$DAY,,B] ; Reset to 1, and drop thru to try incrementing month! ; Increment month! RTMF40: TLNE A,(TM%MON) ; Wild month? JRST RTMF50 ; Nope, try incrementing year. LDB C,[TM$MON,,B] ; Get current month ADDI C,1 CAIG C,12. ; Within year bounds? JRST [ DPB C,[TM$MON,,B] ; Yep, success. JRST RTMF10] ; Go back & try again. MOVEI C,1 ; Oops, overstepped! Must reset to 1 and increment year DPB C,[TM$MON,,B] ; Drop through... ; Increment year! RTMF50: TLNE A,(TM%YR) ; Last chance! wild year? JRST RTMF90 ; No, no hope... can't find any more times to send. ; Wild year - Increment year. This loses only if going past 2027. LDB C,[TM$YR,,B] ; Get year of current spec ADDI C,1 CAILE C,177 ; Overflow? JRST RTMF90 ; Yeah, this reminder is a goner. DPB C,[TM$YR,,A] ; Put back. JRST RTMF10 ; Back to check... ; Increment via RMTINC. RMTF60: PUSH P,A MOVE A,B PUSHJ P,TIMASC ; Convert current spec to abs # secs MOVE C,RMTINC IMULI C,60. ; Get # of secs to add as increment. ADD A,C ; Not ADDI, may be very large. PUSHJ P,TIMCAS ; Convert back now. MOVE B,A POP P,A JRST RMTF10 ; Now try again. RTMF70: MOVE A,B ; Return in A the time obtained. AOSA -4(P) RTMF90: SETZ A, ; Return here when failed to find another time. POPAE P,[E,D,C,B] POPJ P,