// PDInstallUtils.bcpl -- PDPrint installation utilities
// derived from PressInitUtils 2/7/83

//  errors 280

get "PDInternals.d"
get "AltoFileSys.d"
get "Disks.d"
get "Streams.d"
get "SysDefs.d"
get "IfsIsf.d"

structure BFSDSK:  
 [	@DSK
	@KDH
	//other stuff not used here....
 ]
structure KD:
[
@KDH			//Header part
diskBitTable ↑1,1 word	//The bit table itself (1,1 means nothing)
]

// outgoing procedures
external
	[
	IndexFile
	GetFileStatic
	SetupDrive1
	CloseDrive1
	]

// outgoing statics
//external
//	[
//	]
//static
//	[
//	]

// incoming procedures
external
	[
//PDPRINT
	PDError
	DblShift

//PDML
	DoubleAdd
	Ugt

//Used for initialization of files, etc. -- removed by Junta
	OpenFile
	DeleteFile
	Closes
	ReadBlock
	WriteBlock
	FileLength
	GetCompleteFa
	Resets
	PositionPage
	ReadLeaderPage
	WriteLeaderPage

//WINDOW
	WindowInit
	WindowClose
	WindowWriteBlock

//SCANSTRINGS
	TypeForm
	ReadNumber

//ALLOC
	Allocate

//OS
	MoveBlock
	Zero
	ActOnDiskPages
	ReturnFrom
	BFSClose
	BFSInit

//TFS
	TFSInit
	ReleaseDiskPage
	AssignDiskPage

//DiskFindHole
	DiskFindHole

//IfsIsf
	InitFmap; IndexedPageIO; LookupFmap;
	]

// incoming statics
external
	[
	BitsFile
	ScratchFile
	LeftOverFile1
	LeftOverFile2
	MeterFile
	PDFile
	RunFile
	tridentVec
	tridentUsed

	sysDisk
	PDZone
	]

// internal statics
static
	[
	drive1disk
	]

// File-wide structure and manifest declarations.

structure STR[
	length byte
	char↑1,255 byte
	]

// Procedures

//----------------------------------------------------------------------------
let IndexFile(p, findex, givenFileName; numargs na) = valof
//----------------------------------------------------------------------------
//Index a file: (called from above and PressInit)
//	p=vector in which to index
//	findex=index of file for the structure
[
// Get code for where to look (0= main Model 31 disk, 1=alternate disks, then main Model 31)
	let where=GetFileWhere(findex)

// Alter=0 (RO,silent), 1 (RO,print), 2 (RW,print)
	let alter=GetFileType(findex)
	let filetype=(alter eq 2)? FILERW,FILERO

//Now index the file 
	let str=vec 20
	let fname=vec 20
	let s=0
	let disk=nil
	let bestDisk=nil

	[ //giant loop, deleting all inappropriate versions of file

	//first, find most likely file
	for i=(where? (2+NTridentDrives*NPartitions-1),0) to 0 by -1 do
	 [ disk=DiskStuff(i, 2)
	   if disk eq 0 then loop
	   for j=0 to 1 do //try PDPrint.xxx and Press.xxx
		[
		test findex eq FILEPD then
		[ fname=givenFileName
		  if j eq 1 then loop	 ] or		//No need to look twice for pd file
		[ fname>>STR.length=0
		  if j eq 1 & findex eq FILEPDProgram then loop	//Don't get Press.run
		  test j eq 0 then AppendStr(fname,"PDPrint.") or
		   AppendStr(fname,"Press.")
		  AppendStr(fname,GetFileExtension(findex)) ]
		s=OpenFile(fname,ksTypeReadOnly,0,0,0,0,PDZone,0,disk)
		bestDisk=i
		if s ne 0 then break
		]
	   if s ne 0 then break
	 ] //end of "find most likely file"

	if alter eq 0 & s eq 0 then resultis 0	//Error

//Remake proper file name if it wasn't found
	if s eq 0 then
		[
		fname>>STR.length=0
		AppendStr(fname,"PDPrint.")
		AppendStr(fname,GetFileExtension(findex))
		]
	if alter then TypeForm("File: ", fname)
	if alter ne 0 test s ne 0 then
	   [
	   TypeForm(" on ",DiskStuff(bestDisk, 0))
	   let v=vec 1
	   FileLength(s, v)
	   DblShift(v, disk>>DSK.lnPageSize+1)
	   TypeForm(", length is ",10,v!1," pages.")
	   test alter eq 2 then
		 [	TypeForm(" Ok?",1,str)
			let c=str>>STR.char↑1
			if str>>STR.length ne 0 & (c eq $n % c eq $N) then
			 [ Closes(s)
				if fname>>STR.char↑2 ne $r then //don't delete Press.xxx
					DeleteFile(fname, 0, 0, PDZone, 0, disk)
				s=0
			 ]
		 ] 
		or TypeForm(0)
	   ] 
	or TypeForm(" -- does not exist.*n")

 	//now, decide if we're done with giant loop
	if s ne 0 then break	//found it!!!
	if bestDisk eq 0 then break	//nowhere!!!
 ] repeat //end of giant loop, either choosing file, or deleting all versions

	if s eq 0 then
	 [ for i=2+NTridentDrives*NPartitions-1 to 0 by -1 do if DiskStuff(i, 2) then
		 [ TypeForm("Do you want it on the ",DiskStuff(i, 0),"?",1,str)
			let c=str>>STR.char↑1
			if str>>STR.length ne 0 & (c eq $n % c eq $N) then loop
			bestDisk=i
			break
		 ] //end of for i=2+NTridentDrives*NPartitions-1 to 0 loop

	   disk=DiskStuff(bestDisk, 2)
	   TypeForm("How long do you want it to be (in pages): ",1,str)
	   let pagcnt=ReadNumber(str)
	   if bestDisk ge 2 then
		 [ let bVDA=DiskFindHole(disk,pagcnt+2)	//1 for leader, 1 for numchrs=0
			if bVDA eq -1 then
			 [ TypeForm("Unable to find contiguous hole that size on the disk!  Try again.*n")
				loop
			 ]
			ReleaseDiskPage(disk,
			  AssignDiskPage(disk,bVDA-1))
		 ]
	   s=OpenFile(fname,ksTypeWriteOnly,0,0,0,0,PDZone,0,disk)
	   if bestDisk ge 2 then
		 [ ReadLeaderPage(s, p)
			p>>LD.consecutive=true
			WriteLeaderPage(s, p)
		 ]
	   // Because of bug in TransferPages, go slowly:
	   let cp=0
	   while cp ne pagcnt+1 do
		 [ let toPage=pagcnt+1
			if Ugt(toPage-cp, 200) then toPage=cp+200
			PositionPage(s, toPage)
			cp=toPage
		 ]
	   break					//Normal case -- done
	  ] repeat	//keep going until you find a contiguous hole

	let consecutive=false
	ReadLeaderPage(s, p)
	if p>>LD.consecutive then consecutive=true	//Believe the hint!!!

	Zero(p,3000)
	Resets(s)
	let cfa=vec lCFA
	GetCompleteFa(s, cfa)
	p>>F.version=cfa>>CFA.fp.version
	MoveBlock(lv p>>F.serialNumber, lv cfa>>CFA.fp.serialNumber, lSN)

   //Compute number of pages included in FileLength:
   let lnPageSize = disk>>DSK.lnPageSize;  // log of words per page
   let fileLength = vec 1; FileLength(s, fileLength);  // length in bytes
   let pageCount = vec 1;
   pageCount!0 = 0;
   pageCount!1 = 1 lshift (lnPageSize + 1) - 1;  // i.e. bytes per page - 1
   DoubleAdd(pageCount, fileLength);  // round up to next page boundary
   DblShift(pageCount, lnPageSize + 1);
   p>>F.Pagecnt = pageCount!1;

	Closes(s)

   let plen = nil;  // length of this F

   test consecutive
      ifso
         [
         p>>F.DAFirst = cfa>>CFA.fa.da;  // DA for first data page.
         plen = (offset F.fmap / 16);  // length is just length of F
         ]
      ifnot
         [
         let fmap = lv p>>F.fmap;
         let initOK = InitFmap(fmap, 3000 - offset F.fmap / 16,
          lv cfa>>CFA.fp, 0, 10, -1, disk);
         if initOK then
            [
            let page = vec 1024;
            IndexedPageIO(fmap, p>>F.Pagecnt, page, 1, 0);
            ]
         let lastDA = LookupFmap(fmap, p>>F.Pagecnt, true);
         unless initOK & lastDA ne fillInDA do
            [
            TypeForm("This non-consecutive file cannot be used by PDPrint.*n");
            resultis 0;
            ]
         let last = fmap>>FM.last; fmap>>FM.end = last;
         plen = lv fmap>>FM.fmap!last + lenMapEntry - p;  // include fmap
         ]


//*******************************************************
//********** remove this when file stuff rationalized ***********
//Special treatment for bits file -- always make it look like 1024 word pages
	if findex eq FILEBits then
		[
		p>>F.Pagecnt=p>>F.Pagecnt rshift (10-lnPageSize)
		lnPageSize=10
		]

	p>>F.Name=findex
	p>>F.Device=DiskStuff(bestDisk, 1)
	p>>F.Pagesize=1 lshift lnPageSize
	p>>F.LogPagesize=lnPageSize
	p>>F.Type=filetype

	resultis plen
]

//----------------------------------------------------------------------------
and DiskStuff(i, what) = 
//----------------------------------------------------------------------------
  selecton what into
	 [ case 0:	selecton i into 
		 [		//Names
			case 0:  "Model 31"
			case 1:  "Model 31, drive 1"
			default: valof
			 [ let tString="Trident drive x, filesys x"
				tString>>STR.char↑15=$0+NTridentDrives-1-((i-2)/NPartitions)
				tString>>STR.char↑26=$0+NPartitions-1-((i-2) rem NPartitions)
				resultis tString
			 ]
		 ] //end of names
		case 1:	i	//PD code
		case 2:	selecton i into
		 [		//"disk" structures
			case 0: sysDisk
			case 1: drive1disk
			default: tridentVec!(i-2)
		 ]
	]

// Set up for dealing with second Model 31 drive, if it is there.

//----------------------------------------------------------------------------
and SetupDrive1() = valof
//----------------------------------------------------------------------------
[
drive1disk = BFSInit(PDZone, true, 1);
resultis drive1disk
]

//----------------------------------------------------------------------------
and CloseDrive1() be
//----------------------------------------------------------------------------
[
if drive1disk then drive1disk = BFSClose(drive1disk);
]

//----------------------------------------------------------------------------
and GetFileExtension(findex) = (
//----------------------------------------------------------------------------
   selecton findex into
	[
	case FILEBits:		"Bits"
	case FILEScratch:	"Scratch"
	case FILELeftOver1:	"LO1"
	case FILELeftOver2:	"LO2"
	case FILEMeter:		"Meter"
	case FILEPDProgram:	"Run"
	default:		PDError(280)
	]
)


//----------------------------------------------------------------------------
and GetFileType(findex) = (
//----------------------------------------------------------------------------
   selecton findex into
	[
	case FILEPD:		0	//Read-only, silent
	case FILEPDProgram: 1	//Read-only, print it out
	default:		2	//Read-write, print it out
	]
)

//----------------------------------------------------------------------------
and GetFileWhere(findex) = (
//----------------------------------------------------------------------------
   selecton findex into
	[
	case FILEPDProgram:	0
//	case FILEPD:
	default:		1
	]
)

//----------------------------------------------------------------------------
and GetFileStatic(findex) = (
//----------------------------------------------------------------------------
   selecton findex into
	[
	case FILEBits:		lv BitsFile
	case FILEScratch:	lv ScratchFile
	case FILELeftOver1:	lv LeftOverFile1
	case FILELeftOver2:	lv LeftOverFile2
	case FILEMeter:		lv MeterFile
	case FILEPDProgram:	lv RunFile
	default:		PDError(280)
	]
)

//----------------------------------------------------------------------------
and AppendStr(s1, s2) be
//----------------------------------------------------------------------------
[
	for i=1 to s2>>STR.length do
		[
		let j=s1>>STR.length+1
		s1>>STR.length=j
		s1>>STR.char↑j=s2>>STR.char↑i
		]
]