! LOG
! Logs off the current user.
! If a parameter is given, this is a new user name, whom we attempt
! to log on.  This user name may include (or, in fact, just be) a
! filestore identifier, in which case we connect to the new filestore.
! This involves disconnecting from the current filestore if the new
! filestore is a different one.
! If no parameter is given, we always disconnect.
! Admin statistics are recorded in a log file.
! On Gandalf machines DCD is waggled, causing the connection to be broken.
{JHB added FS "D" 6/2/87}

%option "-nocheck-nodiag"
%include "inc:fs.imp"
%include "inc:util.imp"
%include "inc:fsutil.imp"
%include "inc:dict.imp"

%begin
%ownstring(3)terminal = ":t"

%record(dictf)%map ofildict
  %result == fildict
%end

%record(dictf)%map oextdict
  %result == extdict
%end

%record(dictf)%map xxx(%integer parm)
  %integer p
  p = integer(16_3f9c)+parm
  %result == record(integer(p))
%end

%record(dictf)%map fildict
  %result == xxx(740)
%end

%record(dictf)%map extdict
  %result == xxx(736)
%end

%constinteger max=6
%conststring(15)%array fname(1:max)=
  "ALPHA","BRAVO","CHARLIE","DEMO",
  "VAX","MET"
%constbytearray fnumber(1:max)=
  16_14, 16_15, 16_1B, 16_35,
  16_72, 16_44

%routine confirm filestore(%integer x)
%integer i
  printstring("You are using filestore "); phex2(x)
  %for i = 1,1,max %cycle
    %if fnumber(i)=x %start
      printstring(" - "); printstring(fname(i))
      %exit
    %finish
  %repeat
  newline
%end

%integerfn filestore(%string(255)s)
%string(15)fore,aft
%integer i,k,n
  %result = rdte %if s=""
  toupper(s)
  %for i = 1,1,max %cycle
    %result = fnumber(i) %if fname(i) -> fore.(s).aft %and fore=""
  %repeat
  n = 0
  %for i = 1,1,length(s) %cycle
    k = charno(s,i)
    %if '0'<=k<='9' %start
      n = n<<4+k-'0'
    %elseif 'A'<=k<='F'
      n = n<<4+k-'A'+10
    %else
      n = 0; %exit
    %finish
  %repeat
  %if n=0 %start
    print line("Unknown filestore ".s)
    n = rdte
  %finish
  %result = n
%end

%routine do nothing
%end

%predicate nacked(%integer port)
%integer bit
  bit = 1<<port
  %falseif nak&bit=0
  nak = nak&\bit
  %true
%end

%routine ackwait(%integer port)
%integer bit,deadline,marktime
  bit = 1<<port
  deadline = cputime+15000;  marktime=cputime+1000
  %cycle
    %returnif ack&bit#0
    printsymbol('.') %and marktime=marktime+1000 %if cputime>marktime
  %repeatuntil cputime>deadline
  print line("No ACK/NAK")
  %signal 9
%end

%routine dtxwait(%integer port)
%integer bit,deadline,marktime
  bit = 1<<port
  deadline = cputime+7500; marktime=cputime+1000
  %cycle
    %returnif dtx&bit#0
    printsymbol('.') %and marktime=marktime+1000 %if cputime>marktime
  %repeatuntil cputime>deadline
  print line("No response")
  %signal 9
%end

%routine disconnect from filestore
%byte disc = {4}12
  %returnif rdte=0
  etherwrite(fsport,disc,1)
  ackwait(fsport)
  do nothing %if nacked(fsport)
  rdte = 0; rsap = 0
%end

%routine connect to filestore(%integer remote)
@16_ff7 %byte default
%string(80)s
%byte con = 2
%integer port,original=rdte
  %on 9 %start
    original = default %if original=0
    remote = original
    printstring("Going for "); phex2(remote); newline
  %finish
  %returnif remote=rdte
  disconnect from filestore
  lsap = 1; fsport = 1
  etheropen(fsport,remote<<8)
  etherwrite(fsport,con,1)
  ackwait(fsport)
  %if nacked(fsport) %start
    printstring("No Ack from station "); phex2(remote)
    %return
  %finish
  dtxwait(fsport)
  length(s) = etherread(fsport,charno(s,1),80)
  port = charno(s,1)-'0'
  %if port<0 %start
    printstring(substring(s,3,length(s)))
    %return
  %finish
  rdte = remote; rsap = port
  etheropen(fsport,remote<<8+port)
%end

%routine parse(%string(255)%name s)
%string(255)fore,aft,server,directory,pass=""
  s = fore %and pass = ",".aft %if s -> fore.(",").aft
  %if s -> fore.("::").aft %start
    %if fore="" %start
      s = aft
      %if s -> fore.(":").aft %start
        server = fore; directory = aft
      %else
        server = s; directory = ""
      %finish
    %else
      server = fore; directory = aft
    %finish
  %else
    server = ""; directory = s
  %finish
  s = directory.pass
  connect to filestore(filestore(server))
%end

@16_3ff0 %integer freebot
%constinteger bel=7
%string(255)line
%integer dummy,quickflag
%string (255) user, pass

%predicate ended in(%integer sym,%string(*)%name s)
  %bytename l
  l == length(s)
  %falseif l=0 %or charno(s,l)#sym
  l = l-1; %true
%end

%routine push(%string(15)n,%record(dictf)%name d)
%record(dictf)%name old
%integer i = d_lim-d_pos
  %if i<24 %start
    print line(n." dictionary too full")
    %return
  %finish
  old == record(d_pos)
  old = d
  old_lim = old_pos
  d_beg = d_pos+16; d_alt = d_pos; d_pos = d_beg+8
  integer(i) = 0 %for i = d_beg,4,d_lim-4
%end

%routine pop(%string(15)n,%record(dictf)%name d)
%record(dictf)%name old
  %if d_alt=0 %start
    print line(n." not popped")
    %return
  %finish
  old == record(d_alt)
  d_beg = old_beg; d_pos = old_pos; d_alt = old_alt
%end

%routine autoforget
%record(dictf)%name d
%string(255)s
%integername w1,w2
%integer tag,min
  min = freebot
  d == fildict
  tag = d_beg+8
  %while tag<d_pos %cycle
    tag = tag+4 %until byteinteger(tag)=0
    tag = tag+4
    w1 == integer(tag); w2 == integer(tag+4)
    %if w1#0 %and w1<w2 %start
      min = w1 %if w1<min
    %finish
    tag = tag+8
  %repeat
restart:
  d == fildict
  %cycle
    tag = d_beg+8
    %while tag<d_pos %cycle
      tag = tag+4 %until byteinteger(tag)=0
      tag = tag+4
      w1 == integer(tag); w2 == integer(tag+4)
      %if w1>=min %and w1<w2 %and w2=freebot %start
        w2 = w1; freebot = w1; ->restart
      %finish
      tag = tag+8
    %repeat
    %exitif d_alt=0
    d == record(d_alt)
  %repeat
%end

%predicate exists(%string(31)file)
  %onevent 3 %start
    %false
  %finish
  openinput(0,file); selectinput(0)
  %true
%end

%routine copy
%integer sym
  %onevent 9 %start
    closeinput
    %return
  %finish
  %cycle
    readsymbol(sym); printsymbol(sym)
  %repeat
%end

%routine log off
%integer dump
  %onevent 3 %start
    selectoutput(0)
    print line("Logoff: ".event_message)
    %return
  %finish
  dump = fcomm('M'<<8,"") %unless userno=0
%end

%routine stop
%constinteger bel=7,esc=27,dim='4',bright='3'
%integer original

  %routine await activity
    %onevent 0,9 %start
      openinput(0,terminal); selectinput(0)
      printsymbol(bel)
    %finish
    prompt(" } ")
    skipsymbol %whilenot ' '#nextsymbol#nl
  %end

  %predicate on gandalf
  %string(255)x
  %integer tag
    tag = refname("GANDALF",comdict)
    %falseif tag<=0
    tag = integer(tag)
    transname(tag,x)
    tag = length(x)
    %falseif tag=0
    tag = charno(x,tag)&1
    %falseif tag=0
    %true
  %end

  %routine disconnect from gandalf
  @16_4000c1 %byte s

    %routine wait
    %integer delay=40000
    %integer i
      %for i = 1,1,delay %cycle; %repeat
    %end

!   %routine break on
!     s = 16_71
!   %end

!   %routine break off
!     s = 16_91
!   %end

    %routine dcd off
      s = 16_55
    %end

    %routine dcd on
      s = 16_95
    %end

    %onevent 0 %start
      %return
    %finish

    %returnunless on gandalf
    printstring("Goodbye!"); newline
!   wait; break on; wait; break off
!   wait; break on; wait; break off
!   wait; break on; wait; break off
    wait; dcd off; wait; dcd on
    %cycle; %repeat; ! wait for ^Y or ^T/R
  %end
  original = rdte
  disconnect from filestore
  disconnect from gandalf
  await activity
  connect to filestore(original)
  %stop
%end

%routine get user pass(%string (255) line, %string (255) %name user, pass)
! Inspect the parameter line.  If it contains comma or space,
! then that is assumed to separate the ownername from the password.
! Otherwise we suppress echo and ask for the password.
  %integer sym
  %return %if line -> user.(" ").pass %or line -> user.(",").pass
  user=line; pass=""
  prompt("Pass:")
  set terminal mode(1); ! Disable echo
  %cycle
    readsymbol(sym); %exitif sym=nl
    pass = pass.tostring(sym)
  %repeat
  set terminal mode(0); ! Back to normal
%end

%routine pds(%string (255) user, %integer flag)
! Make an entry in the log file LOG:.logfile, giving the date,
! 1 for on, 0 for off, the machine number, and the username.
! encoding is to minimise space: Date/Time is stored as minutes since 1/1/86
! (23-bit integer). This is shifted left 1 and the logon/off bit added
! Bytes 1-3 as above
! Byte 4:  Station no.
! Bytes 5-10 User number, padded to right with spaces

%integerfn kday(%integer d,m,y)
      %if m>2 %then m=m-3 %else m=m+9 %and y=y-1
      %result=1461*y//4+(153*m+2)//5+d+58
%end; ! of kday

! Bytes 5-10: Username, padded with spaces

%integer lives=2
%integer dd,mm,yy,hh,nn,magic
%string(31)t
  %on 3 %start
    selectoutput(0)
!    printstring("Trouble"); write(event_sub,1); write(event_extra,1); newline
    lives = lives-1; ->retry %if lives>0
    %return
  %finish

  %returnif userno=0
  to upper(user)
  t = datetime
  dd=stoi(substring(t,1,2)); mm=stoi(substring(t, 4,5)); yy=stoi(substring(t, 7,8))
  hh=stoi(substring(t,11,12)); nn=stoi(substring(t,14,15))
  magic = (((kday(dd,mm,yy)-31411)*24+hh)*60+nn)<<1+flag
! 31411 is the day no for 1/1/86.   
! Date part of magic won't overflow this century.

retry: openappend(1,"LOG:.logfile"); selectoutput(1)
  printsymbol(magic>>16);printsymbol(magic>>8);printsymbol(magic);printsymbol(ldte)
  printstring(user); spaces(6-length(user))
  closeoutput; selectoutput(0)
%end

!*  MAIN PROGRAM

%onevent 3 %start
  selectoutput(0)
  event_message = "Fingertrouble" %if event_extra='T'-'0' %or event_extra=4
  print line("Logon: ".event_message)
  %stop
%finish

pam_groupsep = '/';  pam_keyflag = '-';  !restore defaults
%if fildict_alt#0 %start
  autoforget
  pop("System",sysdict)
  pop("File",fildict)
  pop("Symbol",comdict)
  pop("External",extdict)
  pop("Oldfile",ofildict)
  pop("Oldexternal",oextdict)
%finish

line = cliparam
selectinput(0); closeinput
openinput(0,terminal); selectinput(0)

quickflag = 0
quickflag = 1 %if ended in(pam_keyflag,line)
quickflag = 1 %if ended in(pam_groupsep,line)

pds(current user, 0) %if quickflag=0
log off

parse(line)
stop %if line=""; !user-name absent: log off only

get user pass(line, user, pass)
line = user.",".pass

dummy = fcomm('L'<<8,line) %unless line="";      ! Log on

push("System",sysdict)
push("External",extdict)
push("File",fildict)
push("Symbol",comdict)
push("Oldfile",ofildict)
push("Oldexternal",oextdict)

%if quickflag=0 %start
  pds(user, 1)
  confirm filestore(rdte)
  copy %if exists("fmac:alert")
  openinput(0,terminal) %unless exists("login.com")
%finish

%endofprogram
  
