//		A L T O   E X E C U T I V E
//	SetTime.bcpl
// Copyright Xerox Corporation 1979

//	last edited by R. Johnsson, May 14, 1980  10:41 AM

get "AltoDefs.d"
get "sysdefs.d"
get "streams.d"
get "altofilesys.d"
get "Time.d"
get "ComStruct.bcpl"

external [ WRITEUDT; FINDMONTH ]
static [
	char
	]

structure NTime:
[
time word 2
zone:
   [
   sign bit 1
   hour bit 7
   minute bit 8
   ]
beginDST word
endDST word
]
manifest lNTime = size NTime/16

structure EDB:		// Ethernet Device Block
[
status word
interruptBit word
inputFinishCount word
collisionMagic word
inputCount word
inputPointer word
outputCount word
outputPointer word
hostNumber word
]
manifest [ edb = #600; output = 1; input = 2; reset = 3 ]

structure Pup:
[
eDest bit 8
eSource bit 8
eWord2 word
pupLength word
transportControl bit 8
pupType bit 8
pupID1 word
pupID2 word
destNet bit 8
destHost bit 8
destSocket1 word
destSocket2 word
sourceNet bit 8
sourceHost bit 8
sourceSocket1 word
sourceSocket2 word
// data
xSum word  // checksum
]
manifest lPup = size Pup/16

//----------------------------------------------------------------------------
let SetTime(ISTREAM,DSTREAM; numargs na) be
//----------------------------------------------------------------------------
[
if na ls 2 then DSTREAM = 0
if (StartIO(0)&#377) eq #377 %
  (DSTREAM ne 0 & IsSwitch($M)) then
   [
   if (StartIO(0)&#377) eq #377 then SetNetNumber(0)
   ManualSetTime(DSTREAM)
   return
   ]
unless NetworkSetTime(DSTREAM) do
	[ SetNetNumber(0); ManualSetTime(DSTREAM) ]
]

//----------------------------------------------------------------------------
and IsSwitch(sw) = valof
//----------------------------------------------------------------------------
[
if userParamsVec>>UPE.type eq globalSwitches then
  [
  for i = 1 to userParamsVec>>UPE.length do
    if (userParamsVec!i & #137) eq sw then resultis true
  ]
resultis false
]

//----------------------------------------------------------------------------
and NetworkSetTime(DSTREAM) = valof
//----------------------------------------------------------------------------
[
manifest inputCount = lPup+20
let request = vec lPup
let answer = vec lPup+20
edb>>EDB.interruptBit = 0
let host = StartIO(reset) & 377b
Zero(edb,size EDB/16)
edb>>EDB.inputPointer = answer
edb>>EDB.inputCount = inputCount
edb>>EDB.outputPointer = request
edb>>EDB.outputCount = 13
edb>>EDB.hostNumber = host
Zero(request, lPup)
request>>Pup.eSource = host
request>>Pup.eWord2 = #1000
request>>Pup.pupLength = 22
request>>Pup.pupType = #206  // Alto time request (new standard)
request>>Pup.destSocket2 = 4
request>>Pup.sourceHost = host
request>>Pup.xSum = -1
for i = 1 to 3 do
   [
   edb>>EDB.status = 0
   edb>>EDB.collisionMagic = 0
   request>>Pup.sourceSocket2 = realTimeClock!0
   request>>Pup.pupID1 = realTimeClock!0 lshift 1
   StartIO(output)
   let start = realTimeClock!0
      [
      if (edb>>EDB.status & #377) ne 0 then
         [
         if (edb>>EDB.status & #177400) eq 0 &
           answer>>Pup.eWord2 eq #1000 &
           answer>>Pup.pupType eq #207 &  // Alto time reply
	   answer>>Pup.destHost eq host &
	   edb>>EDB.inputFinishCount le inputCount - (lPup + lNTime) &
	   answer>>Pup.pupLength ge 32 &
	   answer>>Pup.destSocket2 eq request>>Pup.sourceSocket2 &
	   answer>>Pup.pupID1 eq request>>Pup.pupID1 then
            [
            let ltp = vec 1
            ltp!0, ltp!1 = 0, 0
            let nTime = lv answer>>Pup.xSum
            ltp>>LTP.sign = nTime>>NTime.zone.sign
            ltp>>LTP.zoneH = nTime>>NTime.zone.hour
            ltp>>LTP.zoneM = nTime>>NTime.zone.minute
            ltp>>LTP.beginDST = nTime>>NTime.beginDST
            ltp>>LTP.endDST = nTime>>NTime.endDST
            InstallTime(lv nTime>>NTime.time, ltp, DSTREAM eq 0)
	    SetNetNumber(answer>>Pup.destNet)
            resultis true
            ]
         edb>>EDB.status = 0
         StartIO(input)
         ]
      if realTimeClock!0-start > 13 then break
      ] repeat
   StartIO(reset)
   ]
resultis false
]


//----------------------------------------------------------------------------
and InstallTime(ptv, ltp, quiet) be
//----------------------------------------------------------------------------
[
MoveBlock(timeParams, ltp, 2)
SetCalendar(ptv)
ShouldSetTime = false
if quiet then return
let s = OpenFile("Sys.Boot", ksTypeReadWrite, wordItem, verLatest,
 fpSysBoot)
test s ne 0
   ifso
      [
      SetFilePos(s, 0, #775 lshift 1)
      Puts(s, timeParams!0)
      Puts(s, timeParams!1)
      Closes(s)
      ]
   ifnot WRITE("Unable to install time parameters in Sys.Boot.*n")
WRITE("Date and time set to ")
WRITEUDT(USERSTR, 0, true)
WRITE($*n)
]

//----------------------------------------------------------------------------
and ManualSetTime(DSTREAM) be
//----------------------------------------------------------------------------
[
if DSTREAM eq 0 then return
let ltp = vec 2
MoveBlock(ltp, timeParams, 2)
let utv = vec lenUTV
Zero(utv, lenUTV)
let ptv = vec 2
let s = vec size QS/16
INITQ(s)
let preload = vec size QS/16
INITQ(preload)
   [
   EMPTYOUTQ(s)
   unless EDITCHARS(s, preload,
     "Enter date and time, e.g. *"4-Jul-76 15:00*": ") do
      [ WRITE($*n); return ]
   WRITE($*n)
   utv>>UTV.day = GetNumber(s)
   if char ne $- loop
   let name = vec 10
   name!0 = 0
      [
      char = GETQF(s)
      unless (char ge $A & char le $Z) % (char ge $a & char le $z) break
      name>>STRING.length = name>>STRING.length + 1
      name>>STRING.char↑(name>>STRING.length) = char
      ] repeat
   if char ne $- loop
   utv>>UTV.month = FINDMONTH(name)
   if utv>>UTV.month ls 0 loop
   utv>>UTV.year = GetNumber(s)
   if char ne $*s loop
   if utv>>UTV.year ls 1900 then utv>>UTV.year = utv>>UTV.year + 1900
   utv>>UTV.hour = GetNumber(s)
   if char ne $: loop
   utv>>UTV.minute = GetNumber(s)
   if char eq $: then utv>>UTV.second = GetNumber(s)
   if char ne $*n loop
   if PACKDT(utv, ptv) ne 0 loop
   break
   ] repeat
if timeParams!0 eq 0 % timeParams!1 eq 0 % IsSwitch($Z) then
   [
   Zero(ltp, 2)
   EMPTYOUTQ(s)
   unless EDITCHARS(s, preload,
     "Enter local time zone (Eastern, Central, Mountain, Pacific, or*n + (west of Greenwich) or - (east) followed by *"hours:minutes*"): ") do
      [ WRITE($*n); return ]
   WRITE($*n)
   char = GETQF(s)
   switchon char into
      [
      case $E: case $e:
         [ ltp>>LTP.zoneH = 5; endcase ]
      case $C: case $c:
         [ ltp>>LTP.zoneH = 6; endcase ]
      case $M: case $m:
         [ ltp>>LTP.zoneH = 7; endcase ]
      case $P: case $p:
         [ ltp>>LTP.zoneH = 8; endcase ]
      case $-:
         ltp>>LTP.sign = 1
      case $+:
         [
         let n = GetNumber(s)
         if n ls 0 % n gr 13 loop
         ltp>>LTP.zoneH = n
         if char eq $: then
            [
            n = GetNumber(s)
            if n ls 0 % n gr 59 loop
            ltp>>LTP.zoneM = n
            ]
         if char ne $*n loop
         endcase
         ]
      default:
         loop
      ]
   ltp>>LTP.beginDST, ltp>>LTP.endDST = 121, 305
   MoveBlock(timeParams, ltp, 2)
   PACKDT(utv, ptv)
   break
   ] repeat
EMPTYOUTQ(s)
InstallTime(ptv, ltp, false)
]

//----------------------------------------------------------------------------
and GetNumber(s) = valof
//----------------------------------------------------------------------------
[
let n = 0
let gotNumber = false
   [
   char = GETQF(s)
   unless char ge $0 & char le $9 break
   n = 10*n + char-$0
   gotNumber = true
   ] repeat
unless gotNumber do char = 0
resultis n
]