! Directory package for new filestores.
! Uses B-tree package for key manipulation, and related data-storage
! mechanism for keeping large equivalence records.
!
! Strategy is that if the translation is a single file ID then it
! is inserted directly into the tree.  Otherwise, if it has several
! versions, or is a textual equivalent, then it is packaged up in a
! separate record as follows:
!     Each record starts with a tag field, indicating whether the translation
!     is a list of IDs, a local translation, or an external translation.
!     If it is a list of IDs, then there is a count field, followed by the
!     corresponding IDs.  If it is a textual translation, then the actual
!     text appears as a string immediately after the tag field.
!
! Paths can be either absolute (relative to the root) or start with a
! file ID.  Paths are presented as a list of components (already split).
!
! IDs which do not explicitly specify a partition are taken to be in the
! same partition as their parent.  The partition ID is taken as 4 bits of
! partition-within-drive + 2 bits of drive.  This is a NASTY HACK -- it's
! necessary to allow entire drives to be moved to another system which might
! well result in partition number clashes otherwise.  If the drive-ID isn't
! specified it too defaults to the parent's drive.  This has the unfortunate
! side-effect that we can't refer to files on drive 0 from directories on
! other drives except via the root (redirector).  We'll worry about this at
! some other time, though -- just make everything drive-relative for now...
! BEWARE: the constants are hard-coded in!!
!
! The top few bits of IDs are reserved for use by the directory structure.
! <non ID> and <directory> are defined at present

%externalstring(47) copyright %alias "GDMR_(C)_DIR" = %c
   "Copyright (C) 1987 George D.M. Ross"

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

%constinteger NUL = 0

%constinteger external textual         OK =    1
%constinteger local    textual         OK =    2
%constinteger dud path              error = -201
%constinteger not a directory       error = -202
%constinteger dud component         error = -203
%constinteger non empty directory   error = -204
%constinteger no versions           error = -205
%constinteger version not found     error = -206
%constinteger too many versions     error = -207

%include "GDMR_H:FSysAcc.Inc"
%include "GDMR_H:FSys.Inc"
%include "GDMR_H:Tree.Inc"

%externalroutinespec fsys initialise
%externalintegerfnspec fsys get full ID(%integer partial, %integername full)

%constinteger directory          flag = 16_40000000
%constinteger non ID             flag = 16_80000000
%constinteger directory relative flag = non ID flag

!%include "Moose:Mouse.Inc"
%include "Sys:Util.Imp"

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

!constinteger partition shift = 24
!constinteger partition mask  = 16_3F000000
!constinteger ID        mask  = 16_00FFFFFF

%constinteger max compound ID = 32;  ! Meantime
%recordformat compound fm(%short tag,
                          ((%short count,
                            %integerarray ID(1 : max compound ID)) %c
                       %or %string(255) text))
%constinteger compound ID           tag = 1
%constinteger local    translation  tag = 2
%constinteger external translation  tag = 3

%constinteger ID escape character = 2


! Root directory stuff

%owninteger root directory ID = -1
%constinteger root ID = 16_01000002;  ! No sequence

%routine get root ID
   %integer status, x, token, flags
      fsys initialise
      status = fsys get full ID(root ID, root directory ID)
      %if status # 0 %start
         printstring("Failed to find root directory's ID: ")
         write(status, 0);  newline
         prompt("Would you like to create a new one? ")
         read symbol(x) %until x # NL
         %stop %unless x = 'y' %or x = 'Y'
         status = B tree create(nil, "Root", 1, 0, root directory ID)
         %if status # 0 %start
            printstring("Failed to create new root ");  write(status, 0)
            newline
            %stop
         %finish
         printstring("New root directory is ");  phex(root directory ID)
         newline
         ! Bump the refcount.  It's not actually IN any directories,
         ! but it'll stop it being deleted.
         status = fsys bump refcount(nil, root directory ID, 1)
         %if status # 0 %start
            printstring("Failed to bump refcount for new root");  newline
         %finish
         ! Now insert BootArea
         prompt("Would you like to insert BootArea as 04000000? ")
         read symbol(x) %until x # NL
         %if x = 'y' %or x = 'Y' %start
            status = B tree open by ID(nil, root directory ID, 1, token, flags)
            %if status # 0 %start
               printstring("Failed to open new root directory: ")
               write(status, 0);  newline
            %else
               status = B tree add entry(token, "BootArea", 16_04000000)
               %if status # 0 %start
                  printstring("Failed to insert BootArea: ")
                  write(status, 0);  newline
               %finish
               status = B tree close(token, status)
            %finish
         %finish
      %finish
      root directory ID = root directory ID ! directory flag
      !! printstring("Root directory is ");  phex(root directory ID)
      !! newline
%end


! Diagnostic string-putter

!! %routine zput string(%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 print key list(%record(key list fm)%name list)
!!    %while list ## nil %cycle
!!       spaces(15 - length(list_key));   zput string(list_key)
!!       spaces(2);  phex(list_value);  newline
!!       list == list_next
!!    %repeat
!! %end


%predicate path component OK(%string(*)%name s)
   %integer i
      %false %if s = ""
      %for i = 1, 1, length(s) %cycle
         %false %unless ' ' < charno(s, i) <= '~'
      %repeat
      %true
%end

%externalintegerfn directory lookup one(%record(fsys access fm)%name access,
                                        %integer directory ID,
                                        %string(*)%name key, %integer version,
                                        %integername resulting ID,
                                        %string(*)%name textual translation)
   %record(compound fm) c
   %integer token, status, size, x, flags, drive mask
      !! printstring("Lookup one : component ");  zput string(key)
      !! printstring(" in ");  phex(directory ID);  newline
      %result = not a directory error %if directory ID & directory flag = 0
      %if key = "" %start
         ! Null name -> directory self-reference
         resulting ID = directory ID
         %result = 0
      %else %if length(key) = 5 %and charno(key, 1) = ID escape character
         ! Absolute ID escape in path.  First character is escape, next
         ! four are (binary) ID.
         resulting ID = integer(addr(key) + 2)
         !! printstring("Absolute lookup: ");  phex(resulting ID);  newline
         %result = 0
      %finish
      %result = dud component error %unless path component OK(key)
      status = B tree open by ID(access, directory ID, 0, token, flags)
      textual translation = key %and %result = status %if status # 0
      status = B tree find entry(token, key, resulting ID)
      %if status # 0 %start
         x = B tree close(token, 0)
         !! %if x # 0 %start
         !!    printstring("Lookup one: close status ")
         !!    write(x, 0);  newline
         !! %finish
         textual translation = key
         %result = status
      %finish
      %if resulting ID & non ID flag = 0 %start
         ! Simple ID
         x = B tree close(token, 0)
         !! %if x # 0 %start
         !!    printstring("Lookup one: close status ")
         !!    write(x, 0);  newline
         !! %finish
         %result = version not found error %unless version = 0
done ID: %if resulting ID & 16_3F000000 = 0 %start
            ! Drive and partition defaulted
            resulting ID = resulting ID ! (directory ID & 16_3F000000)
         %else %if resulting ID & 16_30000000 = 0
            ! Drive only defaulted
            resulting ID = resulting ID ! (directory ID & 16_30000000)
         %finish
         textual translation = ""
         !! printstring("Result is ");  phex(resulting ID);  newline
         %result = 0
      %else
         ! Compound ID
         status = B tree data value(token, resulting ID & (\ non ID flag),
                                    size, c)
         x = B tree close(token, 0)
         !! %if x # 0 %start
         !!    printstring("Lookup one: close status ")
         !!    write(x, 0);  newline
         !! %finish
         %if status # 0 %start
            textual translation = key
            %result = status
         %finish
         %if c_tag = compound ID tag %start
            version = 1 - version
            %result = version not found error %unless 0 < version <= c_count
            resulting ID = c_ID(version)
            -> done ID
         %else %if c_tag = local translation tag
            ! Local translation -- pass it back to the message interface
            !! printstring("Local textual equivalent");  newline
            textual translation = c_text
            %result = local textual OK
         %else {*assume* %if c_tag = external translation tag
            ! External translation -- pass it back to the client
            !! printstring("External textual equivalent");  newline
            textual translation = c_text
            %result = external textual OK
         %finish
      %finish
%end

! Search for the specified path.  Halts search at last-but-one component.
! Returns: file ID (> 0) if a local filename; or component count + translation
! if non-local; or component count + offending component + error status.
! Always start looking in the file system root directory.

%externalrecord(path fm)%map directory penultimate %c
                                    (%record(fsys access fm)%name access,
                                     %record(path fm)%name path, 
                                     %integername components translated,
                                     %integername resulting ID,
                                     %string(*)%name textual translation,
                                     %integername status)
   %integer ID, current directory ID
      status = dud path error %and %result == nil %if path == nil
      get root ID %if root directory ID < 0
      current directory ID = root directory ID
      ID = root directory ID
      components translated = 0
      %cycle
         %if path_next == nil %start
            ! Stop at the penultimate one
            !! printstring("Lookup terminating with ");  phex(ID);  newline
            resulting ID = ID
            textual translation = ""
            status = 0
            %result == path
         %finish
         current directory ID = ID
         !! printstring("Lookup: component ");  zput string(path_key)
         !! printstring(" in ");  phex(current directory ID);  newline
         status = directory lookup one(access, current directory ID,
                                       path_key, 0 {not until final},
                                       ID, textual translation)
         resulting ID = 0 %and %result == nil %if status < 0;  ! Not there
         %result == path %if status > 0;  ! Non-local
         components translated = components translated + 1
         path == path_next
      %repeat
%end

%externalintegerfn directory lookup(%record(fsys access fm)%name access,
                                    %record(path fm)%name path,
                                    %integername components translated,
                                    %integername file ID, penultimate ID,
                                    %string(*)%name textual translation)
   %integer status
      !! printstring("Directory lookup");  newline
      path == directory penultimate(access, path, components translated,
                                    penultimate ID, textual translation, status)
      %result = status %if status # 0
      status = directory lookup one(access, penultimate ID,
                                    path_key, path_version,
                                    file ID, textual translation)
      components translated = components translated + 1 %if status = 0
      !! printstring("Returning file ");  phex(file ID)
      !! printstring(", penultimate ");  phex(penultimate ID)
      !! printstring(", status ");  write(status, 0);  newline
      %result = status
%end


%integerfn insert new compound ID(%integer token, previous ID, new ID,
                                  %integername resulting)
   %record(compound fm) c
   %integer status, size, i
      !! printstring("Insert new compound ID: ");  phex(new ID)
      !! printstring(", previous: ");  phex(previous ID);  newline
      %if previous ID & non ID flag = 0 %start
         ! Inserting new with previously simple
         !! printstring("New + previously simple: ")
         !! phex(previous ID);  newline
         %result = no versions error %c
            %if previous ID & (directory flag ! non ID flag) # 0 %c
            %or new ID & (directory flag ! non ID flag) # 0
         c = 0
         c_tag = compound ID tag
         c_count = 2
         c_ID(1) = new ID
         c_ID(2) = previous ID
         %result = B tree data insert(token, 12, c, resulting)
      %else
         ! Previously compound
         previous ID = previous ID & (\ non ID flag)
         !! printstring("New + previous compound @ ")
         !! phex(previous ID);  newline
         status = B tree data value(token, previous ID, size, c)
         %result = status %if status # 0
         %result = no versions error %unless c_tag = compound ID tag
         %result = too many versions error %if c_count = max compound ID
         c_count = c_count + 1
         c_ID(i) = c_ID(i - 1) %for i = c_count, -1, 2
         c_ID(1) = new ID
         status = B tree data insert(token, 4 * (c_count + 1), c, resulting)
         %result = status %if status # 0
         %result = B tree data delete(token, previous ID)
      %finish
%end

%externalintegerfn directory insert ID(%record(fsys access fm)%name access,
                                       %integer directory ID,
                                       %string(*)%name inserting key,
                                       %integer inserting ID)
   %integer status, ID, token, x, flags, resulting, bump ID
      %result = dud component error %if inserting ID & 16_0F000000 = 0
      %result = dud component error %unless path component OK(inserting key)
      %result = not a directory error %if directory ID & directory flag = 0
      inserting ID = inserting ID & (\ directory relative flag);  ! Obsolete
      bump ID = inserting ID
      %if inserting ID & 16_3F000000 = directory ID & 16_3F000000 %start
         ! Drive and partition same, so default them
         inserting ID = inserting ID & (16_00FFFFFF ! directory flag)
      %else %if inserting ID & 16_30000000 = directory ID & 16_30000000
         ! Drive only same -- default it
         inserting ID = inserting ID & (16_0FFFFFFF ! directory flag)
      %finish
      !! printstring("Inserting ");  phex(inserting ID)
      !! printstring(" into ");  phex(directory ID);  newline
      status = B tree open by ID(access, directory ID, 1, token, flags)
      %result = status %if status # 0
      status = B tree find entry(token, inserting key, ID)
      %if status = 0 %start
         ! Entry is already there, so compound it.
         status = insert new compound ID(token, ID, inserting ID, resulting)
         status = B tree modify entry(token, inserting key,
                                      resulting ! non ID flag) %if status = 0
      %else
         ! Not there, so insert a new simple ID
         status = B tree add entry(token, inserting key, inserting ID)
      %finish
      x = B tree close(token, status)
      !! %if x # 0 %start
      !!    printstring("Insert ID: close status ")
      !!    write(x, 0);  newline
      !! %finish
      %result = status %if status # 0
      %result = fsys bump refcount(access, bump ID, 1)
%end

%integerfn directory insert textual(%record(fsys access fm)%name access,
                                    %integer directory ID,
                                    %string(*)%name inserting key,
                                    %integer inserting mode,
                                    %string(*)%name inserting text)
   %integer status, ID, token, x, flags, resulting
   %record(compound fm) c
      %result = dud component error %unless path component OK(inserting key)
      %result = not a directory error %if directory ID & directory flag = 0
      !! printstring("Inserting ");  printstring(inserting text)
      !! printstring(" into ");  phex(directory ID)
      !! printstring(" as ");  printstring(inserting key)
      !! printstring(", mode ");  write(inserting mode, 0);  newline
      status = B tree open by ID(access, directory ID, 1, token, flags)
      %result = status %if status # 0
      status = B tree find entry(token, inserting key, ID)
      %if status = 0 %start
         ! Entry is already there & versions aren't allowed
         x = B tree close(token, -1)
         !! %if x # 0 %start
         !!    printstring("Insert textual: close status ")
         !!    write(x, 0);  newline
         !! %finish
         %result = no versions error
      %finish
      ! Not there, so insert a new textual translation
      c = 0
      c_tag = inserting mode;  ! Assume correct
      c_text = inserting text
      status = B tree data insert(token, length(inserting text) + 3,
                                  c, resulting)
      %if status # 0 %start
         x = B tree close(token, -1)
         !! %if x # 0 %start
         !!    printstring("Insert textual: close status ")
         !!    write(x, 0);  newline
         !! %finish
         %result = status
      %finish
      !! printstring("Data inserted as ");  phex(resulting);  newline
      status = B tree add entry(token, inserting key, resulting ! non ID flag)
      x = B tree close(token, status)
      !! %if x # 0 %start
      !!    printstring("Insert textual: close status ")
      !!    write(x, 0);  newline
      !! %finish
      %result = status
%end

%externalintegerfn directory insert local(%record(fsys access fm)%name access,
                                          %integer directory ID,
                                          %string(*)%name inserting key,
                                          %string(*)%name inserting text)
   %result = directory insert textual(access, directory ID, inserting key,
                                      local translation tag, inserting text)
%end

%externalintegerfn directory insert external(%record(fsys access fm)%name access,
                                             %integer directory ID,
                                             %string(*)%name inserting key,
                                             %string(*)%name inserting text)
   %result = directory insert textual(access, directory ID, inserting key,
                                      external translation tag, inserting text)
%end


%externalintegerfn directory delete entry(%record(fsys access fm)%name access,
                                          %integer directory ID,
                                          %string(255) key, %integer version)
   %integer status, token, flags, translated ID, bump status, x
   %integer size, site, new site, i
   %record(compound fm) c
      %result = dud component error %unless path component OK(key)
      %result = not a directory error %if directory ID & directory flag = 0
      !! printstring("Deleting ");  printstring(key)
      !! printstring(" from ");  phex(directory ID);  newline
      status = B tree open by ID(access, directory ID, 1, token, flags)
      %result = status %if status # 0
      status = B tree find entry(token, key, translated ID)
      %if status = 0 %start
         %if translated ID & non ID flag = 0 %start
            ! Simple translation
            status = B tree delete entry(token, key)
         %else
            ! Translation is non-simple
            site = translated ID & (\ non ID flag)
            status = B tree data value(token, site, size, c)
            %if status # 0 %start
               x = B tree close(token, -1)
               !! %if x # 0 %start
               !!    printstring("Delete entry: close status ")
               !!    write(x, 0);  newline
               !! %finish
               %result = status
            %finish
            %if c_tag # compound ID tag %start
               ! Non-ID, so just zap the entry & translation
               status = B tree data delete(token, site)
               status = B tree delete entry(token, key)
               x = B tree close(token, status)
               !! %if x # 0 %start
               !!    printstring("Delete entry: close status ")
               !!    write(x, 0);  newline
               !! %finish
               %result = status
            %finish
            version = 1 - version
            %unless 0 < version <= c_count %start
               x = B tree close(token, -1)
               !! %if x # 0 %start
               !!    printstring("Delete entry: close status ")
               !!    write(x, 0);  newline
               !! %finish
               %result = version not found error
            %finish
            translated ID = c_ID(version)
            %if version < c_count %start
               ! Shuffle down
               c_ID(i) = c_ID(i + 1) %for i = version, 1, c_count - 1
            %finish
            c_count = c_count - 1
            %if c_count = 1 %start
               status = B tree modify entry(token, key, c_ID(1))
               %if status # 0 %start
                  x = B tree close(token, -1)
                  !! %if x # 0 %start
                  !!    printstring("Delete entry: close status ")
                  !!    write(x, 0);  newline
                  !! %finish
                  %result = status
               %finish
               status = B tree data delete(token, site)
            %else
               status = B tree data insert(token, 4 * (c_count + 1), c, new site)
               %if status # 0 %start
                  x = B tree close(token, -1)
                  !! %if x # 0 %start
                  !!    printstring("Delete entry: close status ")
                  !!    write(x, 0);  newline
                  !! %finish
                  %result = status
               %finish
               status = B tree modify entry(token, key,
                                            new site ! non ID flag)
               %if status # 0 %start
                  x = B tree close(token, -1)
                  !! %if x # 0 %start
                  !!    printstring("Delete entry: close status ")
                  !!    write(x, 0);  newline
                  !! %finish
                  %result = status
               %finish
               status = B tree data delete(token, site)
            %finish
         %finish
      %finish
      x = B tree close(token, status)
      !! %if x # 0 %start
      !!    printstring("Delete entry: close status ")
      !!    write(x, 0);  newline
      !! %finish
      %if translated ID & 16_3F000000 = 0 %start
         ! Drive and partition defaulted
         translated ID = translated ID ! (directory ID & 16_3F000000)
      %else %if translated ID & 16_30000000 = 0
         ! Drive only defaulted
         translated ID = translated ID ! (directory ID & 16_30000000)
      %finish
      bump status = fsys bump refcount(access, translated ID, -1)
      %result = bump status %if bump status # 0
      %result = status
%end


%externalintegerfn create directory(%record(fsys access fm)%name access,
                                    %record(path fm)%name path,
                                    %integer desired partition,
                                    %integer no inherit,
                                    %integername components translated,
                                    %integername final directory ID,
                                    %string(*)%name textual translation)
   %record(path fm)%name final
   %integer status, ID, token, x, actual partition, flags, benefactor ID
      final == directory penultimate(access, path, components translated,
                                     final directory ID, textual translation,
                                     status)
      %result = status %if status # 0
      %result = not a directory error %if final directory ID & directory flag = 0
      %if desired partition > 0 %start
         actual partition = desired partition
      %else
         actual partition = (final directory ID & partition mask) >> partition shift
      %finish
      !! printstring("Creating directory ");  printstring(final_key)
      !! printstring(" in ");  phex(final directory ID)
      !! printstring(", partition ");  write(actual partition, 0);  newline
      ! Note that when we create the directory below we use its parent as
      ! its benefactor, so that directories inherit ownership, protection
      ! and so on from the directory they were created in.  This has the
      ! side effect of requiring modify access to the parent directory
      ! (because of FSys's version replacement check), but that's OK as we
      ! are going to be trying to modify the parent directory anyway...
      benefactor ID = final directory ID
      benefactor ID = 0 %if no inherit # 0
      status = B tree create(access, final_key, actual partition,
                             benefactor ID, ID)
      !! printstring("Create status ");  write(status, 0);  newline
      %result = status %if status # 0
      ID = ID ! directory flag
      ! Bump the refcount
      status = fsys bump refcount(access, ID, 1)
      !! printstring("Bump status ");  write(status, 0);  newline
      %if status # 0 %start
         ! For some reason we don't have access to our own newly-created
         ! directory, so we'll just (attempt to) delete it again.
         x = fsys delete file(nil, ID)
         %result = status
      %finish
      ! Now insert in the final directory
      %if ID & 16_3F000000 = final directory ID & 16_3F000000 %start
         ! Drive and partition same, so default them
         ID = ID & (16_00FFFFFF ! directory flag)
      %else %if ID & 16_30000000 = final directory ID & 16_30000000
         ! Drive only same -- default it
         ID = ID & (16_0FFFFFFF ! directory flag)
      %finish
      status = B tree open by ID(access, final directory ID, 1, token, flags)
      %result = status %if status # 0
      status = B tree find entry(token, final_key, x)
      %if status = 0 %start
         ! Directory already exists, so we'll have to delete the new one
         x = B tree close(token, -1)
         !! %if x # 0 %start
         !!    printstring("Create directory: close status ")
         !!    write(x, 0);  newline
         !! %finish
         x = fsys delete file(nil, ID)
         %result = no versions error
      %finish
      status = B tree add entry(token, final_key, ID)
      x = B tree close(token, status)
      !! %if x # 0 %start
      !!    printstring("Create directory: close status ")
      !!    write(x, 0);  newline
      !! %finish
      %result = status
%end

%externalintegerfn directory check empty(%record(fsys access fm)%name access,
                                         %integer directory ID)
   %integer token, flags, status, empty
      %result = not a directory error %if directory ID & directory flag = 0
      status = B tree open by ID(access, directory ID, 0, token, flags)
      %result = status %if status # 0
      %if flags & multiple references # 0 %or B tree empty(token) %start
         ! Directories with multiple references to them are always
         ! assumed to be empty (so all but one can be deleted).
         empty = 0
      %else
         empty = non empty directory error
      %finish
      status = B tree close(token, 0)
      !! %if status # 0 %start
      !!    printstring("Check empty: close status ")
      !!    write(status, 0);  newline
      !! %finish
      %result = empty
%end


%externalrecord(*)%map directory contents(%record(fsys access fm)%name access,
                                          %integer ID, %integername status, flags)
   %record(key list fm)%name key list, last == nil, current, next
   %integer token, x, i, size
   %record(compound fm) c
   %string(255) text
      %if root directory ID < 0 %start
         printstring("*** Calling ""directory contents"" before a lookup??")
         newline
         status = dud path error
         %result == nil
      %finish
      status = not a directory error %and %result == nil %c
         %if ID & directory flag = 0
      !! printstring("Get key list for ");  phex(ID);  newline
      status = B tree open by ID(access, ID, 0, token, flags)
      %result == nil %if status # 0
      key list == B tree key list(token, status)
      current == key list
      %while current ## nil %cycle
         %if current_value & non ID flag = 0 %start
            last == nil
         %else
            ! Compound with (maybe) versions)
            status = B tree data value(token, current_value & (\ non ID flag),
                                       size, c)
            %if status # 0 %start
               ! Error on lookup, so just assume it's simple
               last == nil
            %else %if c_tag = compound ID tag
               text = current_key
               next == current_next
               last == current
               current_value = c_ID(1)
               %for i = 2, 1, c_count %cycle
                  current == record(heap get(length(text) + 9 + 4))
                  current_value = c_ID(i)
                  current_key = text . to string(NUL) . itos(1 - i, 0)
                  current_next == next
                  last_next == current
                  last == current
               %repeat
            %finish
         %finish
         current == current_next
      %repeat
      x = B tree close(token, 0)
      !! %if x # 0 %start
      !!    printstring("Directory contents: close status ")
      !!    write(x, 0);  newline
      !! %finish
      !! print key list(key list)
      %result == key list
%end

%end %of %file
