// IfsScavDump.bcpl
// Copyright Xerox Corporation 1979, 1980, 1981
// Last modified June 2, 1981  2:50 PM by Taft

get "Streams.d"
get "IfsFiles.decl"
get "BTree.decl"

external
[
// outgoing procedures
DumpLPT; DumpTree

// incoming procedures
GetLpteIfsName; GetLpteIfp; GetLpteDIFRec; GetLptFa
OpenLPT; EnumerateLPT; CloseLPT; GetLptHome; GetLpteType
OpenDisk; CloseDisk; Scratch; DirEntryLength
OpenFile; Closes; Resets; Puts
Ws; Wss; PutTemplate; ReadBlock; GetCurrentFa
Allocate; CallSwat; TruePredicate; Zero
OpenFPTree; CloseIFSTree; MapTree
InitCmd; GetFile; Confirm
UNPACKDT; WRITEUDT

// incoming statics
sysZone; scratchDisk
]

static [ onlyDIFs; printTreeStructure; printFileIDs ]

structure String [ length byte; char↑1,1 byte ]

// These routines dump the contents of Scavenger.lpt and Ifs.dir.
// They are mostly useful for debugging the scavenger, but may be
//  of use in getting an independant opinion on the contents of the
//  system directory.

//----------------------------------------------------------------------------
let DumpLPT() be
//----------------------------------------------------------------------------
[
if scratchDisk eq 0 then unless Scratch(nil) return
let ok, list, lpt = true, 0, 0

if ok then
   [
   lpt = OpenLPT("IfsScavenger.lpt", false)
   Resets(lpt)
   GetCurrentFa(lpt, GetLptFa(lpt))
   ]

if ok then
   [
   let cs = InitCmd(100, 5); if cs ne 0 then
      [
      Ws("*NWhat shall I call the output file on DP0? ")
      list = GetFile(cs, ksTypeWriteOnly, charItem)
      onlyDIFs = Confirm(cs, "*NDo you want just page usage info? ")
      printFileIDs = not onlyDIFs
      Puts(cs, $*N)
      Closes(cs)
      ]
   if cs eq 0 then ok = false
   ]

if ok then
   [
   PrintHome(list, GetLptHome(lpt))
   EnumerateLPT(lpt, InterpretLPT, list)
   ]

if lpt ne 0 then CloseLPT(lpt, false)
if list ne 0 then Closes(list)
]

//----------------------------------------------------------------------------
and InterpretLPT(lpt, lpte, list) be
//----------------------------------------------------------------------------
   if GetLpteType(lpte) eq dvTypeFile then
      unless onlyDIFs & GetLpteDIFRec(lpte) eq 0 do
         PrintInfo(list, GetLpteIfsName(lpte), GetLpteIfp(lpte),
          GetLpteDIFRec(lpte))

//----------------------------------------------------------------------------
and DumpTree() be
//----------------------------------------------------------------------------
[
let ok, disk, tree, list = true, 0, 0, 0
disk = OpenDisk("*NWhich disk is it on? ")
if disk eq 0 then ok = false

if ok then
   [
   let fp = vec lFP; Zero(fp, lFP)
   let ifsDir = OpenFile("Ifs.dir", ksTypeReadOnly, 0, 0,
    fp, 0, 0, 0, disk)
   test ifsDir ne 0
      ifso Closes(ifsDir)
      ifnot
         [
         Ws("*NI can't open *"Ifs.dir*"")
         ok = false
         ]
   if ok then
      tree = OpenFPTree(fp, disk, CallSwat, DirEntryLength, false)
   ]

if ok then
   [
   let cs = InitCmd(200, 5); if cs ne 0 then
      [
      Ws("*NWhat shall I call the output file on DP0? ")
      list = GetFile(cs, ksTypeWriteOnly, charItem)
      onlyDIFs = Confirm(cs, "*NDo you want just page usage info? ")
      printTreeStructure = onlyDIFs? false,
       Confirm(cs,"*nShow the tree structure? ")
      printFileIDs = onlyDIFs? false,
       Confirm(cs,"*nShow the file IDs? ")
      Closes(cs)
      ]
   if cs eq 0 then ok = false
   ]

if ok then
   [
   let home = OpenFile("Ifs.home", ksTypeReadOnly, 0, 0, 0, 0, 0, 0, disk)
   test home eq 0
      ifso
         [
         Ws("*NI can't open *"Ifs.home*"")
         ok = false
         ]
      ifnot
         [
         let h = vec lenHome
         test ReadBlock(home, h, lenHome) eq lenHome
            ifso PrintHome(list, h)
            ifnot
               [
               Ws("*NMalformed home block")
               ok = false
               ]
         Closes(home)
         ]
   ]

if ok then MapTree(tree, 0, InterpretTree, list, TruePredicate, true)

if list ne 0 then Closes(list)
if tree ne 0 then CloseIFSTree(tree)
if disk ne 0 then CloseDisk(disk, true)
]

//----------------------------------------------------------------------------
and InterpretTree(dr, list, pathStk) = valof
//----------------------------------------------------------------------------
[
unless onlyDIFs & dr>>DR.type ne drTypeDIF do
   [
   if printTreeStructure then
      [
      let pse = lv pathStk>>PS.PSE↑(pathStk>>PS.PathStkTop)
      let indentString = selecton pathStk>>PS.PathStkTop into
         [
         case 1:  ""
         case 2:  "   "
         case 3:  "      "
         default: "         "  // never happens
         ]
      if pse>>PSE.Offset eq Rec1Offset then
         PutTemplate(list, "$S[Page $OB, $OB words free]*n",
          indentString, pse>>PSE.PageNo,
          (dr - offset BTE.Record/16 - Rec1Offset)>>BTP.FreeWords)
      Wss(list, indentString)
      ]
   PrintInfo(list, lv dr>>DR.pathName, lv dr>>DR.fp,
    (dr>>DR.type eq drTypeDIF? dr+dr>>DR.length-lenDIFRec, 0))
   ]
resultis true
]

//----------------------------------------------------------------------------
and PrintHome(stream, home) be
//----------------------------------------------------------------------------
[
PutTemplate(stream, "*NFile system name *"$S*", ID *"$S*"",
 lv home>>Home.name, lv home>>Home.id)
PutTemplate(stream, "*Ntype $S, num units $D, created ",
 selecton home>>Home.type into
    [
    case ifsTypePrimary: "primary"
    case ifsTypeBackup: "backup"
    default: "unknown"
    ], home>>Home.numUnits)
let utv = vec 10
UNPACKDT(lv home>>Home.created, utv)
WRITEUDT(stream, utv, true)
Wss(stream, "*n*n")
]

//----------------------------------------------------------------------------
and PrintInfo(stream, pathName, fp, difRec) be
//----------------------------------------------------------------------------
// I recommend an 8pt fixed pitch font when listing this output
[
test onlyDIFs
   ifnot Wss(stream, pathName)
   ifso for i = 2 to pathName>>String.length-3 do
      Puts(stream, pathName>>String.char↑i)

if printFileIDs % onlyDIFs then
   [
   let padding = onlyDIFs? 20,45
   test pathName>>String.length gr padding
      ifso Wss(stream, "   ")
      ifnot for i = pathName>>String.length to padding do Puts(stream, $*S)
   ]

if printFileIDs then
   PutTemplate(stream, "$EUO;$UO, un $UO vda $UO", lv fp>>IFP.serialNumber,
    fp>>IFP.version, @lv fp>>IFP.unit, fp>>IFP.page)

if difRec ne 0 then
   PutTemplate(stream, "  $5EUD pages used out of $5EUD",
    lv difRec>>DIFRec.diskPageUsage, lv difRec>>DIFRec.diskPageLimit)

Puts(stream, $*N)
]