# tscan$ --- traverse tree in the file system

   integer function tscan$ (path, buf, clev, nlev, action)
   integer buf (MAXDIRENTRY), clev, nlev, action
   character path (MAXPATH)

   include SWT_COMMON

   integer type, code, i, l, pwd (3), opwd (3), npwd (3)
   integer follow, ctoc, expand, equal, upkfn$

   procedure check_postorder forward
   procedure reattach forward
   procedure enter_pwd forward
   procedure enter_info forward
   procedure fix_path forward

   Ts_at = NO           # no reattach done on this call, yet
   if (clev == 0) {     # first call, initialize everything

      ### Set up the state vectors
      if (expand ("=GaTech="s, Ts_path, MAXPATH) == ERR
            || equal (Ts_path, "yes"s) == NO)
         Ts_gt = NO
      else
         Ts_gt = YES
      clev = 1
      Ts_ps (clev) = ctoc (path, Ts_path, MAXPATH) + 1
      if (and (action, REATTACH) ~= 0)
         reattach

      ### Open the current directory for reading
      call srch$$ (KREAD + KGETU, KCURR, 0, Ts_un (clev), type, code)
      if (code ~= 0) {
         clev = 0
         return (EOF)
         }
      call dir$rd (KINIT, Ts_un (clev), loc (buf), MAXDIRENTRY, code)
      Ts_state = GET_NEXT_ENTRY
      }

   repeat {
      select (Ts_state)
         when (DESCEND) {        # descend to next level
            if (and (action, REATTACH) ~= 0 && (Ts_at == NO))
               reattach
            if (clev >= nlev) {  # are we at the limit?
               Ts_state = GET_NEXT_ENTRY
               check_postorder
               next
               }

            ### Attach to the new directory
            enter_pwd
            call at$swt (Ts_bf (2, clev), 32, 0, pwd, KICUR, code)
            if (code ~= 0) {
               Ts_state = COULDNT_DESCEND
               return (ERR)
               }
            clev += 1

            ### Open it for reading
            call srch$$ (KREAD + KGETU, KCURR, 0, Ts_un (clev), type, code)
            if (code ~= 0) {
               Ts_state = ASCEND
               return (ERR)
               }
            Ts_state = GET_NEXT_ENTRY
            }

         when (COULDNT_DESCEND) {   # couldn't descend into last dir
            if (Ts_at == NO)
               reattach
            Ts_state = GET_NEXT_ENTRY
            check_postorder
            }

         when (GET_NEXT_ENTRY) {    # get next entry from this level
            if (and (action, REATTACH) ~= 0 && (Ts_at == NO))
               reattach
            path (Ts_ps (clev)) = EOS
            call dir$rd (KREAD, Ts_un (clev), loc (buf), MAXDIRENTRY, code)
            if (code ~= 0)
               Ts_state = ATEOD
            elif (rs(buf(1),8) == 2 || rs(buf(1),8) == 3) {
               buf (1) = 0          # indicate preorder encounter
               fix_path
               if (and (buf (20), 8r10007) == 4) { # a ufd but not mfd
                  enter_info        # next time we're called, we will
                  Ts_state = DESCEND   # descend another level
                  }
               if ( ~(and (buf (20), 7) == 4)     # file type is NOT ufd
                     || and (action, PREORDER) ~= 0)
                  return (OK)
               }
            # else stay in this state
            }

         when (ATEOD) {          # at end of directory
            if (and (action, REATTACH) ~= 0 && (Ts_at == NO))
               reattach
            call srch$$ (KCLOS, 0, 0, Ts_un (clev), 0, code)
            Ts_state = ASCEND
            if (and (action, EODPAUSE) ~= 0)
               return (EOD)
            }

         when (ASCEND) {         # pop up one level
            clev -= 1
            if (clev <= 0)
               break
            reattach
            Ts_state = GET_NEXT_ENTRY
            check_postorder
            }

      else
         call error ("in tscan$: can't happen"s)

      }

   return (EOF)


# check_postorder --- return entry on postorder encounter if desired

   procedure check_postorder {

      if (and (action, POSTORDER) ~= 0) {
         call move$ (Ts_bf (1, clev), buf, MAXDIRENTRY)
         buf (1) = 1       # indicate postorder encounter
         return (OK)
         }

      }


# enter_pwd --- get password for next lower directory

   procedure enter_pwd {

      local valid_name; bool valid_name
      local junk; integer junk

      call gpas$$ (Ts_bf (2, clev), 32, opwd, npwd, code)
      call texto$ (opwd, 6, junk, valid_name)

      if (code == ENRIT) {
         pwd (1) = "  "
         pwd (2) = "  "
         pwd (3) = "  "
         }
      elif (Ts_gt == YES && valid_name) {
         pwd (1) = npwd (1)
         pwd (2) = npwd (2)
         pwd (3) = npwd (3)
         }
      else {
         pwd (1) = opwd (1)
         pwd (2) = opwd (2)
         pwd (3) = opwd (3)
         }
      do i = 1, 3
         Ts_pw (i, clev) = pwd (i)
      l = Ts_eos
      if (pwd (1) ~= "  " && pwd (1) ~= 0) {
         path (l) = ':'c
         l += 1 + upkfn$ (pwd, 6, path (l + 1), MAXPATH - l)
         }
      Ts_ps (clev + 1) = l
      }


# fix_path --- add name of current entry to pathname

   procedure fix_path {

      l = Ts_ps (clev)
      if (l > 1) {
         path (l) = '/'c
         l += 1
         }
      l += upkfn$ (buf (2), 32, path (l), MAXPATH - l + 1)
      Ts_eos = l
      }


# enter_info --- save info in current directory entry

   procedure enter_info {

      call move$ (buf, Ts_bf (1, clev), MAXDIRENTRY)
      }


# reattach --- attach back to the same place

   procedure reattach {

      Ts_at = YES
      call at$hom (code)
      if (follow (Ts_path, 0) == ERR) {
         Ts_state = ATEOD
         return (ERR)
         }

      for (i = 1; i < clev; i += 1) {
         call at$swt (Ts_bf (2, i), 32, 0, Ts_pw (1, i), KICUR, code)
         if (code ~= 0) {
            Ts_state = ATEOD
            return (ERR)
            }
         }
      }

   end
