// IfsBackupCmd3.bcpl -- operator interface for backup system
// Copyright Xerox Corporation 1979, 1980, 1981

// Last modified November 17, 1981  8:36 PM by Taft

get "Ifs.decl"
get "IfsFiles.decl"
get "Disks.d"
get "IfsBackup.decl"
get "IfsDirs.decl"
get "Streams.d"

external
[
// outgoing procedures
BackupRestore; BackupReload

// incoming procedures
OpenIFS; CloseIFS; RestoreLoop; RestoreFile; CopyFile
LookupIFSFile; DestroyFD; GetDiskFromFD
IFSOpenFile; IFSDeleteFile; WriteBlock; TruncateDiskStream
OpenIFSFile; CloseIFSFile; DeleteFileFromFD
OpenIFSTree; CloseBTree
InitCmd; GetString; AbortCmd; TelnetAborting; TelnetConfirm; TerminatingChar
Ws; Wss; Gets; Puts; Endofs; Closes; Resets; PutTemplate
IFSPrintError; ConcatenateStrings
DefaultArgs; SysFree; FreePointer

// incoming statics
primaryIFS; dsp
]

//----------------------------------------------------------------------------
let BackupRestore(cs, lvOpenedFS) be
//----------------------------------------------------------------------------
// Command to restore individual files from backup system
[
Wss(cs, " (from backup pack) ")
let ec = nil
let backupFS = @lvOpenedFS
if backupFS eq 0 then
   [
   let name = GetString(cs, 0, Wss, "pack name")
   backupFS = OpenIFS(name, lv ec)
   SysFree(name)
   if backupFS eq 0 then
      [ Puts(dsp, $*n); IFSPrintError(dsp, ec); AbortCmd(cs) ]
   PutTemplate(dsp, "*n$S ($S) mounted.", backupFS>>IFS.id,
    backupFS>>IFS.name)
   ]

   [
   cs = InitCmd(maxPathNameChars+25, 1)
   if cs ne 0 then
      [
      Wss(cs, "*nRestore file(s): ")
      let name = GetString(cs, 0, Wss, "file name ('**' permitted)")
      Closes(cs)
      if name>>String.length eq 0 then [ SysFree(name); break ]
      let ec = RestoreLoop(name, backupFS, primaryIFS)
      if ec ne 0 then
         [ Ws(" -- "); IFSPrintError(dsp, ec) ]
      SysFree(name)
      ]
   ] repeatuntil TelnetAborting()

CloseIFS(backupFS)
@lvOpenedFS = 0
]

//----------------------------------------------------------------------------
and BackupReload(cs, lvOpenedFS) be
//----------------------------------------------------------------------------
// Command to reload entire file system from backup
[
if @lvOpenedFS ne 0 then
   [ CloseIFS(@lvOpenedFS); @lvOpenedFS = 0 ]
Ws(" (file system)")

// Open temporary file on primaryIFS to hold latest <System>BackupIFS.Dir
// for controlled restore.
let bName = "<System>BackupIFS.Dir"
let ec = nil
let dirFD = LookupIFSFile(bName, lcVHighest+lcCreate, lv ec)
if dirFD eq 0 then [ CantOpen(bName, primaryIFS, ec); return ]
ec = OpenIFSFile(dirFD, modeReadWrite)
if ec ne 0 then [ CantOpen(bName, primaryIFS, ec); DestroyFD(dirFD); return ]
let tree = 0
let relDirSt = TelnetConfirm("*nReload entire file system?")? 0,
 GetReloadDirectoryList()
Ws("*nImportant: mount the LAST backup pack first.")

until TelnetAborting() do
   [ // repeat (for each backup file system)
   cs = InitCmd(50, 1)
   if cs ne 0 then
      [
      Wss(cs, "*nMount backup pack: ")
      let name = GetString(cs, 0, Wss, "pack name")
      let backupFS = OpenIFS(name, lv ec)
      SysFree(name)
      if backupFS eq 0 then
         [ Puts(dsp, $*n); IFSPrintError(dsp, ec); AbortCmd(cs) ]
      Closes(cs)
      PutTemplate(dsp, "*n$S ($S) mounted.", backupFS>>IFS.id,
       backupFS>>IFS.name)
      if tree eq 0 then
         [  //first file system mounted, copy its <System>BackupIFS.Dir
         let backupFD = LookupIFSFile(bName, lcVHighest, lv ec, backupFS)
         if backupFD eq 0 then
            [ CantOpen(bName, backupFS, ec); CloseIFS(backupFS); loop ]
         Ws("*nCopying backup directory...")
         CopyFile(GetDiskFromFD(dirFD), lv dirFD>>FD.dr>>DR.fp,
          GetDiskFromFD(backupFD), lv backupFD>>FD.dr>>DR.fp, 0, 0, true)
         DestroyFD(backupFD)
         tree = OpenIFSTree(lv dirFD>>FD.dr>>DR.fp, primaryIFS)
         Ws("*nBeginning reload.")
         ]

      //restore files from backup as appropriate.
      test relDirSt eq 0
         ifso RestoreLoop("<**", backupFS, primaryIFS, tree)
         ifnot
            [
            Resets(relDirSt)
            until TelnetAborting() do
               [
               let dirName = NextReloadDirName(relDirSt)
               if dirName eq 0 break
               RestoreLoop(dirName, backupFS, primaryIFS, tree)
               SysFree(dirName)
               ]
            ]
      CloseIFS(backupFS)
      ]
   ]

if relDirSt ne 0 then [ Closes(relDirSt); IFSDeleteFile("DirNames.temp") ]
if tree ne 0 then CloseBTree(tree)
CloseIFSFile(dirFD)
DeleteFileFromFD(dirFD)
DestroyFD(dirFD)
]

//----------------------------------------------------------------------------
and GetReloadDirectoryList() = valof
//----------------------------------------------------------------------------
// Inputs a list of directory names from the operator, puts them in a
// temporary file (opened in word mode), and returns the open stream.
// Returns zero if can't open the temporary file.
[
let dName = "DirNames.temp"
let ec = nil
let str = IFSOpenFile(dName, lv ec, modeReadWrite, wordItem)
if str eq 0 then [ CantOpen(dName, primaryIFS, ec); resultis 0 ]
Ws("*nEnter directory names, separated by spaces and terminated by Return.*n")
until TelnetAborting() do
   [
   let cs = InitCmd(50, 1)
   if cs ne 0 then
      [
      Puts(cs, $*s)
      let name = GetString(cs, 0, Wss, "directory name")
      if name>>String.length ne 0 then
         WriteBlock(str, name, name>>String.length rshift 1 +1)
      SysFree(name)
      let char = TerminatingChar(cs)
      Closes(cs)
      if char eq $*n break
      ]
   ]
TruncateDiskStream(str)
resultis str
]

//----------------------------------------------------------------------------
and NextReloadDirName(str) = valof
//----------------------------------------------------------------------------
// Returns next directory name from str in form "<dirName>*",
// or zero if list is exhausted.
[
if Endofs(str) resultis 0
let name = vec lenDirName
let i = 0
   [ name!i = Gets(str); i = i+1 ] repeatuntil
    i ge name>>String.length rshift 1 +1
resultis ConcatenateStrings(ConcatenateStrings("<", name), ">**", true)
]

//----------------------------------------------------------------------------
and CantOpen(name, fs, ec) be
//----------------------------------------------------------------------------
   PutTemplate(dsp, "*nCan't open $S in $S:*n  $P", name, fs>>IFS.id,
    IFSPrintError, ec)