// SpruceFilesInit.Bcpl
// Errors 2000

get "SpruceFiles.d"
get "isf.d"

external // Internal
[
CreateSpruceFile
ForgetSpruceFile
CreateSpruceSubfile
InitSpruceSubfile
]

external [
// from OS
DefaultArgs; MoveBlock
// from ISF
InitFmap; IndexedPageIO
// From Spruce Utilities
Max; Min; PageToPos
// From SpruceFiles
DiskObject; InitSpruceFile; ResetSpruceFile
// From Other Spruce Sources
SpruceError; FSGetX; FSPut; SpruceZone
// From SpruceFilesMl
VpageToRpage // virtual to real
]

// ------------------------------------------------------
let CreateSpruceFile(fP, numPages, zone, deviceCode, baseZone; numargs na) = valof
// ------------------------------------------------------

//
The SpruceFiles functions neither create nor allocates pages for files (although ISF is capable of
// the latter.) Nor do they manipulate the name directory. CreateSpruceFile will validate a file’s length (if
// numPages ne -1) and build the ISF map for it. It returns an intialized SPruceFile structure, with the
// map embedded. The returned file is "invalid", in that an InitSpruceFile(..) must be performed before use
//
Return 0 if fP is invalid, the map cannot be built, or the file is the wrong length (~~ Consider
// a way to report the nature of the problem.) . zone is used for temporary
// allocations, and baseZone (defaults to zone) for the permanent SPruceFile and map allocation. deviceCode
// is DISK31, DISK31B, or DISKT80. Only fP is mandatory; the others default as described below.
[
DefaultArgs(lv na, -1, -1, SpruceZone, DISK31, 0)
unless baseZone do baseZone = zone

let checkPages = numPages
// Create map, verify file’s existence, size
let map = FSGetX(maxFmapLen, zone) // big enough for biggest reasonable map, compressed below
let extendSize = Min(48, numPages)
unless InitFmap(map,maxFmapLen,fP,false,extendSize,zone,DiskObject(deviceCode), lv numPages) do
resultis 0 // check error
if checkPages ne -1 & numPages ne checkPages resultis 0 // # pages doesn’t match

// Create file structure
let sF = FSGetX(lenSPruceFile, baseZone, 0)
sF>>SPruceFile.numPages = numPages
sF>>SPruceFile.maxPages = numPages
sF>>SPruceFile.deviceCode = deviceCode
sF>>SPruceFile.map = map
InitSpruceFile(sF, 0, 0, zone); ResetSpruceFile(sF) // leaves invalid
let sP = nil
// extend file, set up map -- do not let it get too long
let evenExtend = (numPages/extendSize)*extendSize
let buffer = FSGetX(minLenSPrucePage+sF>>SPruceFile.pageSize, zone)
let numChars =
IndexedPageIO(map, evenExtend, buffer, 1, isfRead)
unless evenExtend eq numPages do
[
map>>FM.extend = numPages-evenExtend // creep out to end
numChars =
IndexedPageIO(map, numPages, buffer, 1, isfRead)
]
sF>>SPruceFile.numChars = numChars
FSPut(buffer, zone)

// compress map to baseZone
let lenMap = map>>FM.last+6
map>>FM.end = lenMap
let newMap = FSGetX(lenMap, baseZone)
MoveBlock(newMap, map, lenMap)
FSPut(map, zone)
sF>>SPruceFile.map = newMap
resultis sF
]

// ------------------------------------------------------
and ForgetSpruceFile(spruceFile) be
// ------------------------------------------------------
//
Free the structure’s storage. Illegal if file allocated from checkpoint zone.
[
let zone = spruceFile>>SPruceFile.zone
ResetSpruceFile(spruceFile)
unless zone do zone = SpruceZone // ~~ This is probably a kludge
if zone ne 0 then//file must be initialized!
[
let map = spruceFile>>SPruceFile.map
unless spruceFile>>SPruceFile.isSubFile do FSPut(map,zone)
]
FSPut(spruceFile,zone)
]

// ------------------------------------------------------
and CreateSpruceSubfile(lvSpruceFile, firstPage, numPages, numChars, zone; numargs na) = valof
// ------------------------------------------------------

//
A subfile is a contiguous region of a SPruceFile, which behaves to its clients in most ways like
// a standard SPruceFile. It may not exceed the parent (superFile) file in length, but it can "wrap
// around": if page n is superFile’s page max, page n+1 is superFile’s page 1. It can also be mapped
// in reverse page order into superFile: page 1 to superFile’s page x, page 2 to page x-1, ... page m to
// page 1, page m+1 to page max, etc.
//
The functions VpageToRpage and RpageToVpage (SpruceUtils and SpruceMl) produce mappings
// from subfile to superfile and the inverse. If the lvSpruceFile static pointer indicates another subfile,
// its parent is used, with adjusted firstPage, so nesting is never more than one deep.
//
The superFile’s structure must be resident whenever the subfile structure is used.
[
let spruceFile = @lvSpruceFile
if spruceFile>>SPruceFile.isSubFile then // never more than one deep!
[ // create subfile of specified file’s parent -- guaranteed not to be subfile
firstPage = VpageToRpage(spruceFile, firstPage)
lvSpruceFile, spruceFile = spruceFile>>SPruceFile.lvSuperFile, @lvSpruceFile
]
DefaultArgs(lv na, -3, #77777, spruceFile>>SPruceFile.zone)
let sF = FSGetX(lenSPruceFile, zone, 0)
let map = spruceFile>>SPruceFile.map
sF>>SPruceFile.map = map
sF>>SPruceFile.lvSuperFile = lvSpruceFile
sF>>SPruceFile.deviceCode = spruceFile>>SPruceFile.deviceCode
InitSpruceFile(sF, 0, 0, zone); ResetSpruceFile(sF) // fill in some more of the blanks
InitSpruceSubfile(sF, firstPage, numPages, numChars)
resultis sF
]

// ------------------------------------------------------
and InitSpruceSubfile(sF, firstPage, numPages, numChars; numargs na) be
// ------------------------------------------------------
//
Allows reassignment of mapping from existing subfile to parent file locations.
[
// numPages<0 means file is to be arranged in reverse page order
if na<4 then numChars = #77777
let isBackwards = false
sF>>SPruceFile.backwards = false
if numPages<0 then
[ isBackwards = true; numPages = 0 - numPages ]
let superFile = @sF>>SPruceFile.lvSuperFile
unless sF>>SPruceFile.lvSuperFile & superFile do SpruceError(2080, sF)
let maxPages = superFile>>SPruceFile.maxPages
sF>>SPruceFile.maxPages = maxPages
unless 1 le firstPage & firstPage le maxPages & numPages le maxPages do
SpruceError(2020)
sF>>SPruceFile.offSet = firstPage-1
sF>>SPruceFile.numPages = numPages
let realLastPage = VpageToRpage(sF, numPages)
let cPP = sF>>SPruceFile.pageSize lshift 1
let lpChars = superFile>>SPruceFile.numChars
if realLastPage < firstPage & lpChars ne cPP then
SpruceError(2070, superFile) // too hard to wrap around
if realLastPage eq maxPages & isBackwards & lpChars ne cPP then
SpruceError(2090, superFile) // backwards cannot start here
sF>>SPruceFile.backwards = isBackwards
sF>>SPruceFile.numChars = Min(numChars,
(realLastPage ne maxPages? cPP, superFile>>SPruceFile.numChars))
]

// ------- History . . .

// DCS, March 10, 1978 8:49 AM, extracted from SpruceFiles
// September 3, 1978 6:44 PM, superFile -> lvSuperFile, allow subfile as arg to Create...Subfile
//
(meaning: create another subfile of subfile arg’s parent.)
// September 12, 1978 3:49 PM, add CreateSpruceFileCfa
// September 12, 1978 10:51 PM, compress map to baseZone after creation
// September 14, 1978 10:48 AM, CreateSpruceFile... returns 0 if no such file
// September 18, 1978 8:48 AM, numPages to CreateSpruceFile is -1 for "don’t check", else just
//
a value to be checked against actual current file length -- Create.. fails if doesn’t check.
//
Map extends faster, requires file to be full length already.
// September 19, 1978 6:42 PM, formatting, documentation
// October 16, 1978 8:25 AM, modifications for fast files
//