// A L T O E X E C U T I V E
// Dump/Load module - DumpLoad.bcpl
// Copyright Xerox Corporation 1979
// This module implements the DUMP, COPY, and LOAD functions
// of the Executive.
// E. McCreight
// last edited by R. Johnsson May 23, 1980 10:04 AM
get "sysdefs.d"
get "altofilesys.d"
get "streams.d"
get "COMSTRUCT.bcpl"
external
[ Dump
Load
Copy
]
static
[ BreakBetweenPages
]
structure BYTES:
[ char↑1,1 byte
]
manifest
[ NameBlock = #377
DataBlock = #376
EndBlock = #374
ErrorBlock = #375
DateBlock = #373
]
let Dump(ISTREAM, DSTREAM) be
[ DumpCopy(ISTREAM, DSTREAM, true)
]
and Copy(IStream, DStream) be
[ DumpCopy(IStream, DStream, false)
]
and DumpCopy(ISTREAM, DSTREAM, ReallyDump) be
[ static
[ IsReallyDump
DIFile
DOFile
DIBlock
DIBlockSize
DOBlock
DOBlockSize
DOStream
]
let DOPutOverflow(S, item) = valof
[ WriteBlock(DOFile, DOBlock, DOBlockSize)
SetupFstream(S, DOBlock, 0, 2*DOBlockSize)
resultis RetryCall(S, item)
]
let PutBytes(NBytes, Block) be
[ for i=1 to NBytes do
Puts(DOStream, Block>>BYTES.char↑i)
]
let PutDataBlock(NBytes, Block) be
[ Puts(DOStream, DataBlock)
PutBytes(2, lv NBytes)
let Checksum = NBytes
for i=0 to (NBytes/2)-1 do
Checksum = Checksum+(Block!i)
PutBytes(2, lv Checksum)
PutBytes(NBytes, Block)
]
let FN = vec 200
let Sw = vec 100
let IFN = vec 200
let T = nil
IsReallyDump = ReallyDump
SetupReadParam(FN, Sw, ISTREAM, Sw)
let Clobber = AmongSwitches(Sw, $C)
BreakBetweenPages = AmongSwitches(Sw, $P)
if ReadParam($P, -1, FN) eq -1 then
[ WRITE(FORMATN("No <S>ee file!*N",
(IsReallyDump? "dump", "copy")))
return
]
let IFNFull = false
unless IsReallyDump do
if (ReadParam($P, -1, IFN) eq -1) %
(IFN!0 ne "←"!0) then
[ WRITE(FORMATN(
"*"←*" missing; OK to write into <S>? ",
FN))
switchon Gets(keys) into
[ case $Y:
case $y:
case $*N:
case $*L:
case $*S:
WRITE("Yes*N")
IFNFull = true
endcase
default:
WRITE("No*N")
return
]
]
DOFile = MyOpenFile(FN, (Clobber? ksTypeReadWrite,
ksTypeWriteOnly),
charItem)
INITDIRBLK(MADEBLK) // So no more allocations
// will happen due to directory
// block construction
DOStream = Allocate(CZ, lFS)
InitializeFstream(DOStream, charItem, DOPutOverflow, 0)
let TotalBlockSize = BiggestFreeBlock()
DIBlockSize = ((TotalBlockSize/256)*128)+1
if DIBlockSize ls 129 then DIBlockSize = 129
DIBlock = Allocate(CZ, DIBlockSize)
DOBlockSize = BiggestFreeBlock()
if DOBlockSize ls 100 then DOBlockSize = 100
DOBlock = Allocate(CZ, DOBlockSize)
SetupFstream(DOStream, DOBlock, 0, 2*DOBlockSize)
let fileCount = 0
let oldCreationDate = vec lTIME-1
while IFNFull % (ReadParam($P, -1, IFN) ne -1) do
[ MAKETIMELINE()
let DIFile = MyOpenFile(IFN, ksTypeReadOnly,
charItem)
IFNFull = false
test DIFile eq 0
ifso [ if WRITE(FORMATN(
"File <S> doesn't exist.*N",
IFN), BreakBetweenPages) ne 0 then
break
loop
]
ifnot if WRITE(FORMATN("<S>...*N", IFN),
BreakBetweenPages) ne 0 then
break
fileCount = fileCount + 1
// remember creation date of first file on copy
unless IsReallyDump % fileCount ne 1 do
GetCreationDate(DIFile, oldCreationDate)
if IsReallyDump then
[ Puts(DOStream, NameBlock)
for i=1 to 2 do Puts(DOStream, 0) // File attributes
for i=1 to IFN>>STRING.length do
Puts(DOStream, IFN>>STRING.char↑i) // File name
Puts(DOStream, 0) // terminating null
let date = vec 2
GetCreationDate(DIFile, date)
date!2 = 0
Puts(DOStream, DateBlock)
for i = 1 to 6 do Puts(DOStream, date>>BYTES.char↑i) ]
let CurDIBlock = DIBlock
let BytesInCurDIBlock = 0
until Endofs(DIFile) do
[ CurDIBlock = DIBlock
let WordsInDIBlock = BytesInCurDIBlock/2
BytesInCurDIBlock = 2*ReadBlock(DIFile,
DIBlock+WordsInDIBlock,
DIBlockSize-WordsInDIBlock)+
BytesInCurDIBlock
if Endofs(DIFile) then
if (FileLength(DIFile) & 1) ne 0 then
BytesInCurDIBlock =
BytesInCurDIBlock-1
test IsReallyDump
ifso
[ while BytesInCurDIBlock ge 258 do
[ PutDataBlock(256, CurDIBlock)
CurDIBlock = CurDIBlock+128
BytesInCurDIBlock =
BytesInCurDIBlock-256
]
MoveBlock(DIBlock, CurDIBlock,
(BytesInCurDIBlock+1)/2)
]
ifnot
[ for i=1 to BytesInCurDIBlock do
Puts(DOStream,
CurDIBlock>>BYTES.char↑i)
BytesInCurDIBlock = 0
]
]
if IsReallyDump then
[ if BytesInCurDIBlock ge 130 then
[ PutDataBlock(128, CurDIBlock)
CurDIBlock = CurDIBlock+64
BytesInCurDIBlock =
BytesInCurDIBlock-128
]
for i=1 to 2 do
CurDIBlock>>BYTES.char↑
(BytesInCurDIBlock+i) = 0
let BytesToTransfer = (BytesInCurDIBlock ls 2)?
2, BytesInCurDIBlock
PutDataBlock(BytesToTransfer, CurDIBlock)
]
Closes(DIFile)
]
if IsReallyDump then Puts(DOStream, EndBlock)
let CurPos = CurrentPos(DOStream)
if CurPos ge 2 then
WriteBlock(DOFile, DOBlock, CurPos/2)
if (CurPos&1) ne 0 then
Puts(DOFile, DOBlock>>BYTES.char↑CurPos)
TruncateDiskStream(DOFile)
// if copying only one file then copy creation date too
unless IsReallyDump % fileCount ne 1 do
SetCreationDate(DOFile, oldCreationDate)
Closes(DOFile)
Free(CZ, DOStream)
Free(CZ, DIBlock)
Free(CZ, DOBlock)
]
and GetCreationDate(file, date) be
[ let leader = vec 255
ReadLeaderPage(file, leader)
MoveBlock(date, lv leader>>LD.created, lTIME)
]
and SetCreationDate(file, date) be
[ let leader = vec 255
ReadLeaderPage(file, leader)
MoveBlock(lv leader>>LD.created, date, lTIME)
WriteLeaderPage(file, leader)
]
and AmongSwitches(Switches, char) = valof
[ for i=1 to Switches!0 do
if ((Switches!i xor char) &
($A eqv $a)) eq 0 then resultis true
resultis false
]
and BiggestFreeBlock() = valof
[ let Result = nil
Allocate(CZ, #77777, lv Result)
resultis Result
]
and Load(IStream, DStream) be
[ static
[ loadCreationDate
LDStream
LIFile
LOFile
LIBlock
LIBlockSize
LIStream
LOFileName
LOBlock
LOBlockSize
LOStream
IsReallyWriting
LClobber
LVerify
]
let OPutOverflow(S, char) = valof
[ if IsReallyWriting then
WriteBlock(LOFile, LOBlock, LOBlockSize)
SetupFstream(S, LOBlock, 0, 2*LOBlockSize)
resultis RetryCall(S, char)
]
let Cleanup() be
[ if IsReallyWriting then
[ let CurPos = CurrentPos(LOStream)
if CurPos ge 2 then
WriteBlock(LOFile, LOBlock,
CurPos/2)
if (CurPos&1) ne 0 then
Puts(LOFile, LOBlock>>BYTES.char↑
CurPos)
TruncateDiskStream(LOFile)
if (loadCreationDate!0 % loadCreationDate!1) ne 0 then
SetCreationDate(LOFile, loadCreationDate)
Closes(LOFile)
IsReallyWriting = false
]
Zero(loadCreationDate,3)
]
let IGetOverflow(S) = valof
[ if Endofs(LIFile) then
[ SetEof(S, true)
resultis RetryCall(S)
]
let BytesRead = 2*
ReadBlock(LIFile, LIBlock, LIBlockSize)
if Endofs(LIFile) then
if (FileLength(LIFile)&1) ne 0 then
BytesRead = BytesRead-1
SetupFstream(S, LIBlock, 0, BytesRead)
resultis RetryCall(S)
]
let GetBytes(NBytes, Block) be
[ for i=1 to NBytes do
Block>>BYTES.char↑i = Gets(LIStream)
]
let GetDataBlock() be
[ let NBytes = nil
let Checksum = nil
GetBytes(2, lv NBytes)
GetBytes(2, lv Checksum)
Checksum = Checksum-NBytes
for i=1 to (NBytes/2) do
[ let FirstByte = Gets(LIStream)
let SecondByte = Gets(LIStream)
Checksum = Checksum-
((FirstByte lshift 8)+
SecondByte)
Puts(LOStream, FirstByte)
Puts(LOStream, SecondByte)
]
if (NBytes&1) ne 0 then
Puts(LOStream, Gets(LIStream))
if Checksum ne 0 then
[ WRITE("*300N O T E: Load checksum differs from dump checksum*301*N*T*T(press any key to continue)*N")
Resets(keys)
Gets(keys)
]
]
let SetupOutputFile() = valof
[ for i=1 to 2 do Gets(LIStream)
let CharsInFileName = 0
let Char = Gets(LIStream)
while Char ne 0 do
[ CharsInFileName = CharsInFileName+1
LOFileName>>STRING.char↑CharsInFileName =
Char
Char = Gets(LIStream)
]
LOFileName>>STRING.length = CharsInFileName
WRITE(FORMATN("<S>...", LOFileName))
MAKETIMELINE() // make new time line
if LVerify then
[ WRITE(" OK? ")
switchon Gets(keys) into
[ case $Y:
case $y:
case $*N:
case $*L:
case $*S:
WRITE("Yes")
endcase
case $c:
case $C:
WRITE("Yes, but change its name to: ")
ReadString(LOBlock, "*N",
keys, LDStream)
EvalParam(LOBlock, $P, -1,
LOFileName)
endcase
default:
resultis (WRITE("No*N",
BreakBetweenPages) eq
0)
]
]
if (WRITE($*N, BreakBetweenPages) ne 0) then
resultis false
LOFile = MyOpenFile(LOFileName, (LClobber?
ksTypeReadWrite, ksTypeWriteOnly),
charItem)
if LOFile eq 0 then
[ let T = WRITE("*300Couldn't open the output file*301*N*T*T(press any key to continue)*N",
BreakBetweenPages)
Resets(keys)
Gets(keys)
resultis (T eq 0)
]
SetupFstream(LOStream, LOBlock, 0, 2*LOBlockSize)
IsReallyWriting = true
resultis true
]
let FN = vec 200
let Sw = vec 100
let date = vec 2; Zero(date, 3); loadCreationDate = date
LDStream = DStream
let T = nil
SetupReadParam(FN, Sw, IStream, Sw)
LClobber = AmongSwitches(Sw, $C)
LVerify = AmongSwitches(Sw, $V)
BreakBetweenPages = AmongSwitches(Sw, $P)
if ReadParam($P, -1, FN) eq -1 then
[ WRITE("No dump file!*N")
return
]
LIFile = MyOpenFile(FN, ksTypeReadOnly, charItem)
if LIFile eq 0 then
[ WRITE(FORMATN(
"File *"<S>*" doesn't exist.*N", FN))
return
]
INITDIRBLK(MADEBLK)
LIStream = Allocate(CZ, lFS)
InitializeFstream(LIStream, charItem, 0, IGetOverflow)
LOStream = Allocate(CZ, lFS)
InitializeFstream(LOStream, charItem, OPutOverflow)
LOFileName = Allocate(CZ, 129)
let TotalBlockSize = BiggestFreeBlock()
LIBlockSize = TotalBlockSize/2
if LIBlockSize ls 129 then LIBlockSize = 129
LIBlock = Allocate(CZ, LIBlockSize)
SetupFstream(LIStream, LIBlock, 0, 0)
LOBlockSize = BiggestFreeBlock()
LOBlock = Allocate(CZ, LOBlockSize)
SetupFstream(LOStream, LOBlock, 0, 2*LOBlockSize)
let dataCount = 0
while true do
[ let BlockType = Gets(LIStream)
switchon BlockType into
[ case EndBlock:
Cleanup()
break
endcase
case DataBlock:
GetDataBlock()
dataCount = dataCount + 1
endcase
case ErrorBlock:
Cleanup()
WRITE("Error block encountered!*N")
break
endcase
case DateBlock:
if dataCount ne 0 then Cleanup()
GetBytes(6,loadCreationDate)
endcase
case NameBlock:
Cleanup()
dataCount = 0
unless SetupOutputFile() do break
endcase
default:
WRITE(FORMATN(
"Strange block type #<OCT> encountered.*N",
BlockType))
Cleanup()
break
]
]
Closes(LIFile)
Free(CZ, LIStream)
Free(CZ, LOStream)
Free(CZ, LIBlock)
Free(CZ, LOBlock)
Free(CZ, LOFileName)
WIPEDIRBLK()
]