!%option "-nonstandard-low-nocheck-nodiag-noline-nostack"
%option "-nonstandard-low"

%constinteger processes = 1
%constinteger priority = 6
%constinteger file tokens = 32
%constinteger max roots = 32

%constinteger T Read File   = 1
%constinteger T Modify File = 2
%constinteger T Create File = 3
%constinteger T Dir Listing = 4

%include "b:Moose:Mouse.Inc"
%include "GDMR_H:FSysAcc.Inc"
%include "GDMR_H:FacMess.Inc"
%include "nfsd:udpint.inc"
%include "nfsd:nfs.inc"
%include "nfsd:mount.inc"
%include "nfsd:errors.inc"

%externalpredicatespec FS lookup(%string(31) what, %integername value)

%constinteger change file = modify file mode ! append to file mode

%conststring(1) SNL = "
"

%recordformat path fm(%record(path fm)%name next,
                      %integer version, %string(*)%name key,
                      %string(255) text)

%systemroutinespec phex(%integer i)
%systemroutinespec phex2(%integer i)
%systemstring(31)%fnspec itos(%integer i, j)
%systemintegerfnspec free store

%externalroutinespec FS insert(%string(31) name, %integer value)

%externalroutinespec dump(%integer n, %bytename b)

%ownrecord(semaphore fm) request semaphore = 0
%ownrecord(mailbox fm) request mailbox = 0


! Diagnostic

{} %routine show path(%record(path fm)%name p)
{}    %while p ## nil %cycle
{}       print symbol('@');  phex(addr(p))
{}       printstring(" -> ");  phex(addr(p_next))
{}       printstring(" v ");  write(p_version, 0)
{}       printstring(" c ");  printstring(p_key)
{}       newline
{}       p == p_next
{}    %repeat
{} %end

{} %routine xprintstring(%string(255) s)
{}    %integer i, ch
{}       %return %if s = ""
{}       %for i = 1, 1, length(s) %cycle
{}          ch = charno(s, i)
{}          %if ' ' <= ch <= '~' %start
{}             print symbol(ch)
{}          %else
{}             print symbol('<')
{}             write(ch, 0)
{}             print symbol('>')
{}          %finish
{}       %repeat
{} %end

%routine copy block(%integer n, %bytename from, to)
   D0 = D0 - 1
L: *move.b (A0)+, (A1)+
   *dbra D0, L
%end

! File tokens (issued by us, incorporating lower-level ones)

%recordformat file token fm(%record(mailbox fm)%name followup mailbox,
                            %record(UDPStreamfm) UDP stream, 
                            %record(fhandlefm) fhandle,
                            %string(15) domain,
                            %string(15) user,
                            %integer size,
                            %integer mode,
                            %record(dirptrfm) dirptr)

%ownrecord(file token fm)%array our file tokens(1 : file tokens) = 0(*)
%ownrecord(semaphore fm) file token semaphore = 0

%record(file token fm)%map get new file token
   %record(file token fm)%name t
   %integer i
      semaphore wait(file token semaphore)
      %for i = 1, 1, file tokens %cycle
         t == our file tokens(i)
         %if t_followup mailbox == nil %start
            ! Found a free one
            t_followup mailbox == request mailbox
            signal semaphore(file token semaphore)
            %result == t
         %finish
      %repeat
      signal semaphore(file token semaphore)
      %result == nil
%end

%routine validate file token(%record(fs message fm)%name m)
   %record(file token fm)%name t
   %integer i
      %for i = 1, 1, file tokens %cycle
         %if m_file token = addr(our file tokens(i)) %start
            t == record(m_file token)
            %exit %if t_followup mailbox == nil;  ! Invalid
            m_error code = 0
            %return
         %finish
      %repeat
      m_error code = -302;  m_status = -3
      m_error text = "Invalid file token"
%end

%routine zap (%record(file token fm)%name t)
   t=0
%end

%recordformat rootfm    (%string(31)        host,
                         %string(127)       path,
                         %record(fhandlefm) fhandle,
                         %integer           blocksize)

%ownrecord(rootfm)%array  roots (1:maxroots) =0(*)
%owninteger last root = 0

%integerfn get file handle (%string(31) host, %string(127) path, 
                            %record(fhandlefm)%name fhandle)
   %record(rootfm)%name root
   %record(UDPstreamfm) c
   %integer i, x, status

   %for i = 1, 1, last root %cycle
      root == roots(i)
      %if root_host = host %and root_path = path %start
         fhandle = root_fhandle
         %result = 0
      %finish
   %repeat

{} Printstring("Mounting ".path." on ".host);newline
   
   c_uid = -1; c_gid = -1

   x = connect to mnt (host, status, c)
   %result = status %if status # 0

   x = mnt mnt (c, status, path, fhandle)
   x = udp close stream (c)
   %result = status %if status # 0

   last root = last root + 1
   root == roots(lastroot)
   root_host = host; root_path = path; root_fhandle = fhandle

   %result = 0
%end      

%integerfn lookup file (%record(udpstreamfm)%name c, %string(127) filename,
                        %record(fhandlefm) did, %record(fhandlefm)%name fid,
                        %integername size)
   %integer x, status
   %record(diropargsfm) dir
   %record(diropresfm)  res

   dir_fhandle = did
   dir_filename = filename
   x = NFS lookup (c, status, dir, res)
      %result = status %if status # 0
   fid = res_fhandle
   size = res_fattr_size
   %result = 0
%end

%integerfn create file (%record(udpstreamfm)%name c, %string(127) filename,
                        %record(fhandlefm) did, %record(fhandlefm)%name fid)
   %integer x, status
   %record(diropresfm) res
   %record(diropargsfm) dir
   %record(sattrfm) sattr=0
   
   dir_fhandle = did
   dir_filename = filename

   sattr_mode = 8_0100777
   sattr_uid = c_uid; sattr_gid = c_gid
   sattr_size = 1

   x = NFS Create (c, status, dir, sattr, res)
      %result = status %if status # 0
   fid = res_fhandle
   %result = 0
%end
         
! Error code interpretation

%routine set status(%record(fs message fm)%name m)
   %if m_error code = 0 %start
      m_status = 0
   %else
      m_status = -2;  ! Meantime
      m_error text = "Unknown error " . itos(m_error code, 0)
   %finish
%end

! One action routine for each of the request (sub)types.

%routine do open file(%record(fs message fm)%name m)
   %record(file token fm)%name file token
   %record(UDPStreamfm)%name c
   %integer mode, x, status
   %record(pathfm)%name p
   %record(fhandlefm) fhandle, dhandle
   %string(31) host
   %string(127) root

   %integerfn guddle (%record(dirptrfm)%name d)
      %record(direntfm)%name f
      %integer siz = 0

      f == d_root
      %while f ## nil %cycle
         siz = siz + length(f_filename)+1
         f == f_next
      %repeat
      %result = siz
   %end

      !! printstring("Do open file: mode ");  phex2(m_access mode)
      !! printstring(", compatible ");  phex2(m_compatible mode)
      !! printstring(", flags ");  phex(m_request flags);  newline
      !! show path(m_filename)
      m_response flags = 0;  m_bytes = 0;  m_file token = 0;  ! Provisionally

   %if m_access mode & change file = 0 %start
      ! open for reading only
      mode = T Read File
   %else
      %if m_request flags & create flag = 0 %then mode = T Modify File %c
                                            %else mode = T Create File
   %finish
   p == m_filename; host = p_key

   file token == get new file token
   %if file token == NIL %start
      m_error code = -1; m_status = -1
      m_error text = "(NFS) No free file token"
      %return
   %finish
   c == file token_UDP stream

   p == p_next
   root = p_key

   x = get file handle (host, root, dhandle)
   %if x # 0 %start
      zap (file token)
      m_error code = -1; m_status = -1
      m_error text = "(NFS) Failed to mount root ".root
   %finish

   c_uid = 38; c_gid = 20 {*** should get these from m_access ***}      

   x = connect to nfs (host, c)

   p == p_next
   %while p ## nil %cycle
      %if p_key = "" %start
         mode = T Dir Listing
         x = NFS Read Dir (c, status, dhandle, filetoken_dirptr)
         file token_size = guddle (filetoken_dirptr)
         %if status # 0 %start
            x = udp close stream (c)
            zap (file token)
            m_error code = -1; m_status = -1
            m_error text = "Failed to read directory"
            %return
         %finish
      %else
         %if mode = T Create File %and p_next == NIL %start 
            x = create file (c, p_key, dhandle, fhandle)
            %if x # 0 %start
               x = udp close stream (c)
               zap (file token)
               m_error code = -1; m_status = -1
               m_error text = "Failed to create file"
               %return
            %finish
            %exit
         %else
            x = lookup file (c, p_key, dhandle, fhandle, file token_size)
            %if x # 0 %start
               x = udp close stream (c)           
               zap (file token)
               m_error code = -1; m_status = -1
               m_error text = "Failed to open file"
               %return
            %finish
            dhandle = fhandle
         %finish
      %finish
      p == p_next
   %repeat

   file token_fhandle = fhandle
   file token_mode = mode

   m_file token = addr(file token)
   m_bytes = file token_size
   m_response flags = 0
   m_error code = 0; m_status = 0
%end

%routine do read data(%record(fs message fm)%name m)
   %record(file token fm)%name t
   %record(fattrfm) fattr
   %integer n, cnt, status, x, ad, sad
   %record(direntfm)%name f, dead

   %routine plant (%string(127) s, %integername ad)
      %integer i

      %for i = 1, 1, length (s) %cycle
         byteinteger(ad) = charno(s,i)
         ad = ad + 1
      %repeat
      byteinteger (ad) = nl
      ad = ad + 1
   %end

   validate file token(m);  %return %if m_error code < 0
   t == record(m_file token)

 Printstring ("Read "); write(m_bytes,0); printstring(" bytes from ")
 write(m_block,0);printstring (" to "); phex(addr(m_buffer));newline

   %if t_mode = T Read File %or t_mode = T Modify File %start
      x = NFS Read (t_udp stream,
                  status,t_fhandle,m_block<<9,512,addr(m_buffer),cnt,fattr)
      %if status # 0 %start
         m_error code = -1; m_status = -1
         printstring("NFS Read = ")
         write(status,0);newline
         m_error text = "(NFS) Failure while reading"
      %else
         m_bytes = cnt
         m_error code = 0; m_status = 0
      %finish
   %elseif t_mode = T Dir Listing
      m_bytes = 0; ad = addr(m_buffer) ; sad = ad
      f == t_dirptr_root
      %while f ## NIL %cycle
         plant (f_filename,ad)
         dead == f
         f == f_next
         dispose (dead)
      %repeat
      m_bytes = ad - sad
      t_dirptr_root == NIL
      m_error code = 0; m_status = 0
   %else
      m_error code = -1; m_status = -1
      m_error text = "(NFS) can't write to read-only file"
   %finish
%end

%routine do write data(%record(fs message fm)%name m)
   %record(file token fm)%name t
      validate file token(m);  %return %if m_error code < 0
      t == record(m_file token)
%end

%routine do close file(%record(fs message fm)%name m)
   %record(file token fm)%name t
   %integer x

   validate file token(m);  %return %if m_error code < 0
   t == record(m_file token)

   x = udp close stream (t_udp stream)
   zap (t)
%end

%routine do truncate file(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (truncate file) not implemented yet"
%end

%routine do make accessible(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (make accessible) not implemented yet"
%end

%routine do obtain attributes(%record(fs message fm)%name m, %integer type)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (obtain attributes) not implemented yet"
%end

%routine do short form attributes(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (short form attributes) not implemented yet"
%end

%routine do modify attributes(%record(fs message fm)%name m, %integer type)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (modify attributes) not implemented yet"
%end

%routine do list directory contents(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (list directory contents) not implemented yet"
%end

%routine do insert directory entry(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (insert directory entry) not implemented yet"
%end

%routine do remove directory entry(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (remove directory entry) not implemented yet"
%end

%routine do create new directory(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (create new directory) not implemented yet"
%end

%routine do rename file(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (rename file) not implemented yet"
%end

%routine do copy file(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (copy file) not implemented yet"
%end

%routine do exchange files(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (exchange files) not implemented yet"
%end

%routine do delete file(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (delete file) not implemented yet"
%end

%routine do generate unique name(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (unique name) not implemented yet"
%end

%routine do timestamp enquiry(%record(fs message fm)%name m)
   m_error code = -1;  m_status = -3
   m_error text = "Subrequest (timestamp enquiry) not implemented yet"
%end

! Request code demultiplexing routines, one for each of the major request
! codes.  These just switch on the subcodes....

%routine data access(%record(fs message fm)%name m)
   %if m_subcode = open file subcode %start
      do open file(m)
   %else %if m_subcode = read data subcode
      do read data(m)
   %else %if m_subcode = write data subcode
      do write data(m)
   %else %if m_subcode = close file subcode
      do close file(m)
   %else %if m_subcode = truncate file subcode
      do truncate file(m)
   %else %if m_subcode = make accessible subcode
      do make accessible(m)
   %else
      m_error code = -1;  m_status = -1
      m_error text = "Subrequest not recognised"
   %finish
%end

%routine file attributes access(%record(fs message fm)%name m)
   %if m_subcode = obtain attributes subcode %start
      do obtain attributes(m, 0)
   %else %if m_subcode = obtain attributes token subcode
      do obtain attributes(m, 1)
   %else %if m_subcode = short form attributes subcode
      do short form attributes(m)
   %else %if m_subcode = modify attributes subcode
      do modify attributes(m, 0)
   %else %if m_subcode = modify attributes token subcode
      do modify attributes(m, 1)
   %else
      m_error code = -1;  m_status = -1
      m_error text = "Subrequest not recognised"
   %finish
%end

%routine directory access(%record(fs message fm)%name m)
   %if m_subcode = list directory contents subcode %start
      do list directory contents(m)
   %else %if m_subcode = insert directory entry subcode
      do insert directory entry(m)
   %else %if m_subcode = remove directory entry subcode
      do remove directory entry(m)
   %else %if m_subcode = create new directory subcode
      do create new directory(m)
   %else
      m_error code = -1;  m_status = -1
      m_error text = "Subrequest not recognised"
   %finish
%end

%routine miscellaneous file operation(%record(fs message fm)%name m)
   %if m_subcode = rename file subcode %start
      do rename file(m)
   %else %if m_subcode = copy file subcode
      do copy file(m)
   %else %if m_subcode = exchange files subcode
      do exchange files(m)
   %else %if m_subcode = delete file subcode
      do delete file(m)
   %else %if m_subcode = translate redirections subcode
      ! Null operation, since we don't know about redirections.
      ! Just return success, and nothing translated.
      m_status = 0;  m_error code = 0
      m_components translated = 0
   %else
      m_error code = -1;  m_status = -1
      m_error text = "Subrequest not recognised"
   %finish
%end

%routine miscellaneous other operation(%record(fs message fm)%name m)
   %if m_subcode = generate unique name subcode %start
      do generate unique name(m)
   %else %if m_subcode = timestamp enquiry subcode
      do timestamp enquiry(m)
   %else
      m_error code = -1;  m_status = -1
      m_error text = "Subrequest not recognised"
   %finish
%end

!%routine nonstandard operation(%record(fs message fm)%name m)
!   %if m_subcode = 0 %start
!      do filestore logon(m)
!   %else %if m_subcode = 1
!      do filestore logoff(m)
!   %else %if m_subcode = 2
!      do filestore quote(m)
!   %else
!      m_error code = -1;  m_status = -1
!      m_error text = "Subrequest (nonstandard) not recognised"
!   %finish
!%end


! Main code of local file system process.  Loop, reading the mailbox and
! calling a code-demultiplexing routine to interpret the subcodes as
! appropriate.

%routine local filesystem process
   %record(fs message fm)%name m
   %ownrecord(semaphore fm) disaster = 0
   %record(poa fm)%name P
   %integer i
      %on 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 %start
         ! Last-chance disaster-trap
         P == POA
         printstring("NFSP: unexpected event ");  write(P_event, 0)
         space;  write(P_event sub, 0);  space;  phex(P_event extra)
         space;  printstring(P_event message)
         printstring(" at or about PC ");  phex(P_event PC)
         newline
         %for i = 0, 1, 15 %cycle
            phex(P_event r(i));  space
            newline %if i & 7 = 7
         %repeat
         semaphore wait(disaster)
      %finish
      open input(2, ":N");  select input(2)
      open output(2, ":T");  select output(2)
      setup semaphore(disaster)
      mark %if POA_heap_level = 1
      %cycle
         !! printstring("Waiting for message to ");  phex(addr(request mailbox))
         !! newline
         m == receive message(request mailbox)
         !! printstring("Code is ");  write(m_code, 0);  newline
         %if m_code = data access code %start
            data access(m)
         %else %if m_code = file attributes access code
            file attributes access(m)
         %else %if m_code = directory access code
            directory access(m)
         %else %if m_code = miscellaneous file operation code
            miscellaneous file operation(m)
         %else %if m_code = miscellaneous other operation code
            miscellaneous other operation(m)
!         %else %if m_code = 128
!            nonstandard operation(m)
         %else
            m_error code= -1;  m_status = -1
            m_error text = "Unknown request code"
         %finish
         !! printstring("Replying, status ");  write(m_status, 0)
         !! printstring(", error code ");  write(m_error code, 0)
         !! printstring(", text """);  printstring(m_error text)
         !! print symbol('"');  newline
         send message(m, m_system part_reply, nil)
      %repeat
%end

%begin
   %record(process fm)%name p
   %integer i
   %label x
      open input(3, ":T");  select input(3)
      open output(3, ":T");  select output(3)
      setup semaphore(request semaphore)
      setup mailbox(request mailbox, request semaphore)
      setup semaphore(file token semaphore)
      signal semaphore(file token semaphore)
      FS insert(NFS file system mailbox, addr(request mailbox))
      {} printstring("Starting ");  write(processes, 0)
      {} printstring(" NFS process");  newline
!      p == create process(10240, addr(x), priority, nil) %for i = 1, 1, processes - 1
      set priority(nil, priority)
      {} printstring("NFSP: ");  write(free store, 0)
      {} printstring(" free");  newline
      ! Fall through to form one of the processes....
x:    local filesystem process
      ! Never returns...
%end %of %program
