// ISFINIT -- initialize indexed sequential file
// last edited November 12, 1976  12:42 PM
// file reassignments in local copies begun January 20, 1978, by Swinehart

	get "isf.d"

external	// entry procedures
[	InitFmap	// (fmap, len, fp[, check, xtnd, zone, disk]) -> ok
	morepageio	// for extending files -- used at init time only in Spruce
	WriteFmap	// used if at all only at init time in Spruce
]

external
[		// O.S.
	CallSwat
	MoveBlock
	ActOnDiskPages
	DefaultArgs
	Allocate; Free
	Dvec
	sysDisk
	sysZone
	SetBlock
	Usc
	WriteDiskPages
	Zero
		// Isf (utilities)
	NextFmap
	LookupFmap
		// Spruce utilities
	Min
]


let InitFmap(fmap, len, fp, check, xtnd, zone, disk, lvPages; numargs n) = valof
[	DefaultArgs(lv n, -3, false, 10, -1, sysDisk, 0)
	// DCS 9-14-78 returns false if len too small or check error accessing file
	if len ls mapoffset+3 then resultis false
	fmap>>FM.seal = version
	MoveBlock(lv fmap>>FM.fp, fp, lFP)
	let leaderVda = fp>>FP.leaderVirtualDa
	fmap>>FM.DA0 = leaderVda
	fmap>>FM.DA1, fmap>>FM.DA2 = fillInDA, fillInDA
	fmap>>FM.last = mapoffset+2
	let end = (len-2)&-2
	fmap>>FM.onern, fmap>>FM.oneda = 0, fmap>>FM.DA0
	let map = fmap+mapoffset
	map!0, map!1, map!2 = 0, leaderVda, 1
	fmap>>FM.rewrite = check
	fmap>>FM.extend = (xtnd ls ppc? xtnd, ppc)
	if zone eq 0 then zone = sysZone
	let pagesize = 1 lshift disk>>DSK.lnPageSize
	let scratch, nch = pagesize, (check? 1, 0) // nch used for last page input until after Act..
	test zone eq -1 then Dvec(InitFmap, lv scratch) or scratch = Allocate(zone, scratch)
	let res = ActOnDiskPages(disk, 0, lv fmap>>FM.DA0, fp,
				    0, nch, DCreadD, lv nch, DCreadD, scratch, 0,0, true) ge 0
	if res then test check then
	   [
	   if nch eq 2*pagesize & (scratch>>FM.last ge mapoffset+2) & (scratch!mapoffset eq 0) &
	    valof [ for i = 1 to checksize-1 do if scratch!i ne fmap!i resultis false; resultis true ] then
	      [
	      let last = scratch>>FM.last // stored map seems all right -- use it
	      if last gr end then last = end
	      if last ge pagesize then last = pagesize-2
	      MoveBlock(fmap, scratch, last+1)
	      ]
	   ]
	 or if lvPages then
	   [
	   @lvPages = scratch>>LD.hintLastPageFa.pageNumber
	   if @lvPages > 0 & scratch>>LD.hintLastPageFa.charPos eq 0 then @lvPages = @lvPages-1
	   ]
	if zone ne -1 then Free(zone, scratch)
	fmap>>FM.end = end
	fmap>>FM.disk = disk
	fmap>>FM.zone = zone
	resultis res
]



and morepageio(fmap, firstrn, scratch, npg, wflag) be
[	let pagesize, firstda = 1 lshift fmap>>FM.disk>>DSK.lnPageSize, LookupFmap(fmap, firstrn)
	let zone = fmap>>FM.zone
	if wflag eq -1 then
	 test zone eq -1
	  ifso [ scratch = pagesize; Dvec(morepageio, lv scratch) ]
	  ifnot scratch = Allocate(zone, pagesize)
	let lastrn, lastmaprn = firstrn+npg, NextFmap(fmap)
	let rn = lastmaprn-1
	let DAs = vec ppc+2	// ppc+3 words!
	DAs!0 = (rn eq 0? eofDA, LookupFmap(fmap, rn-1))
	let nch = nil
	while rn ls lastrn do
	 [ DAs!1 = LookupFmap(fmap, rn)
	   let np = Min(ppc, lastrn-rn)
	   SetBlock(DAs+2, fillInDA, np+1)
	   let nrn = ActOnDiskPages(fmap>>FM.disk, 0, DAs+1-rn, lv fmap>>FM.fp, rn, rn+np-1, DCreadD, lv nch, DCreadD, scratch)
	   if nch eq 0 then	// extend file
	    test nrn eq rn
	    ifnot	// too hard to pick up
		np = nrn-rn
	    ifso
	    [ let nxp = fmap>>FM.extend
	      unless (nxp gr 0) & (wflag ne 0) do CallSwat("Attempt to access non-existent page")
	      if np ls nxp then np = nxp
	      Zero(scratch, pagesize)
	      SetBlock(DAs+2, fillInDA, np+1)
	      DAs!(np+2) = eofDA
	      WriteDiskPages(fmap>>FM.disk, 0, DAs+1-rn, lv fmap>>FM.fp, rn, rn+np, DCwriteD, 0, 0, scratch)
	    ]
	   for j = 1 to np do
	    [ let xrn, xda = rn+j, DAs!(j+1)
	      ExtendFmap(fmap, xrn, xda)
	      if xrn eq firstrn then firstda = xda
	    ]
	   DAs!0 = DAs!np
	   rn = rn+np
	 ]
	ExtendFmap(fmap, firstrn, firstda)	// just in case map is full
	if NextFmap(fmap) gr lastmaprn then WriteFmap(fmap)
	if (wflag eq -1) & (zone ne -1) then Free(zone, scratch)
]

and WriteFmap(fmap) be
[	if fmap>>FM.rewrite then
	 [ ActOnDiskPages(fmap>>FM.disk, 0, lv fmap>>FM.DA0, lv fmap>>FM.fp, 1, 1, DCwriteD, 0, DCwriteD, fmap)
	 ]
]


and ExtendFmap(fmap, rn, da) = valof
[	fmap>>FM.onern, fmap>>FM.oneda = rn, da
	let last = fmap>>FM.last
	let lastp = lv (fmap!last)
	if rn ne @lastp then resultis false
	let curva = da
	test curva eq (lastp!-1)+@lastp-(lastp!-2)
	 ifso	// still in same chunk
	   @lastp = @lastp+1
	 ifnot	// start new chunk
	   test last eq fmap>>FM.end
	    ifso	// out of space
	      resultis false
	    ifnot
	    [ lastp!1, lastp!2 = curva, @lastp+1
	      fmap>>FM.last = last+2
	    ]
	resultis true
]

// DCS, January 20, 1978  10:41 PM, file reassignments
// September 14, 1978  10:26 AM, InitFmap checks existence of file, check or not,
//	returns false on check error (file with that fp does not exist)
// September 18, 1978  8:26 AM, if not checking and not check error (InitFmap), and if
//	lvPages (new) arg supplied, fill it with numPages value from length hint in leader page
//