// SpruceInstall.Bcpl -- Spruce Spooler Installation
// errors 250

// get "Spruce.d" was too big: only need
get "SprucePrinters.d"
get "Sprucedoc.d"
get "spruceMisc.d"
get "SpruceFiles.d"
get "Streams.d"
get "BcplFiles.d"
get "MenuDefs.d"
get "SpruceParamsNames.d"
get "SpruceFilesNames.d"

external // defined here
[
SpruceInstall
]

external // external
[
// SpruceFiles
ForgetSpruceFile
InitSpruceFile
ResetSpruceFile

// SpruceInUtil
DiskObject
MakeValidSpruceFile
SetupDrive
StatusOf31s

// SpruceCheck
CheckPoint
LevelPos
OpenCheckPointFile
RestoreFromCheckPoint

// Spruce Utils
CreateFPRD
CursorChar
FSGetX
FSInit
FSPut
InitRam
LoadRam
SpruceError

// StringUtil
CopyString
StringCompare

// ReadUserCmItem
ReadUserCmItem

// Template
PutTemplate

// TFS
TFSClose
TFSInit

// BFS
BFSClose

// OS
CallSwat
dsp
Gets
MyFrame
OpenFile
ShowDisplayStream
SetFilePos
FilePos
ReadBlock
Wss
Wo
Zero

// statics
BandFile
BitMarginAdjust
breakPage
Capabilities
CheckPrAttr
Debug
DebugSystem
DutyCycle
drive1Disk
LogoFont
LogoText
ErrorFile
PressServer
Facets
FontFile
QueueFile
LandscapeDevice
maxQueued
menu
menuBuf
seed
Verbose
xmFonts

// MenuData
menuStream
NumBins
OverlayTop
PaperDimensionB
PaperDimensionS
PaperSpeedInches
PermanentBottom
PolygonRatio
printerDevice
printerForward
printerName
PRamImage
ResolutionB
ResolutionS
ScanLengthInches
ScanMarginAdjust
SpoolFile
sprintFPRD
spruceFPRD
SpruceZone
tridentDisk
tridentUsed
statusOf31s
UserName
// **** See SpruceStatics, SpruceUtils, below -- more memory for installation
MoreLow; MoreHigh
]

static InstallStackSize = 2000
static map = 0
static [ menu; menuStream; menuBuf; MenuInitHelp=0; ChangeRoutine ]
static [ errorSpecs; fontSpecs; spoolSpecs; bandSpecs; okToInstall ]
structure SPECS: [ fileId word; devId word; defDev word; sizeId word; defSize word ]

manifest
//param types
[
pNum = 1
pString = 2
pFile = 3
pResFile = 4//file that already existed
]

// ------------------------------------------------------
let SpruceInstall(doInstall, runCfa) be
// ------------------------------------------------------
//
After loading RAM, and reclaiming its storage, validate the Checkpoint file.
// If the Sprint version statics are NIL, or the checkpoint file doesn’t exist,
// Sprint probably hasn’t been installed - inform the user and exit.
// Otherwise, proceed with installation.
// The objective of installation is to somehow get default values into the CheckPoint
// file. This is accomplished by extracting the defaults from the User.CM file.
// Defaults are stored in User.CM as pairs of keyword & value(s).
//
(i.e.DebugSystem:040)
// For simple statics, we can just assign values to the appropriate static locations.
// For structures (e.g. SpoolFile, etc.) we need to parse the values found in User.CM
// and create the structures before checkpointing.
//
// Since the installation defaults are few in number
[
InitRam(PRamImage, OrbitRMR)
OverlayTop=LoadRam//Bottom of free storage for FSInit
let PermanentTop = MyFrame()-InstallStackSize
@#335 = PermanentTop; PermanentBottom = PermanentTop
statusOf31s = StatusOf31s()

// (if allowed&necc.), create and init. file -- if had to init, install independent of request

let CpFileOK = OpenCheckPointFile(runCfa)

if (not doInstall) & CpFileOK return

PutTemplate(dsp, "Installing...*N")

FSInit()

InitStatics()

// Compute FPRDs (InLd, OutLd arguments) for self and Spruce printer
CursorChar($B)
spruceFPRD = CreateFPRD("Spruce.Boot", true)
unless spruceFPRD do SpruceError(257)

CheckPoint(LEVSharedInstStatics, LEVInstallationStatics)

finish
]

// ------------------------------------------------------
and GetStaticInfo(s) = valof
// ------------------------------------------------------
// given a static name, return the address of the cooresponding table entry.
[

unless map do
[
map = FSGetX(100)

map!1 = "Debug"; map!2 = lv Debug; map!3 = pNum
map!4 = "Verbose"; map!5 = lv Verbose; map!6 = pNum
map!7 = "PressServer"; map!8 = lv PressServer; map!9 = pNum
map!10 = "DebugSystem"; map!11 = lv DebugSystem; map!12 = pNum
map!13 = "xmFonts"; map!14 = lv xmFonts; map!15 = pNum

map!16 = "ErrorFile"; map!17 = lv ErrorFile; map!18 = pResFile
map!19 = "SpoolFile"; map!20 = lv SpoolFile; map!21 = pFile
map!22 = "QueueFile"; map!23 = lv QueueFile; map!24 = pFile
map!25 = "BandFile"; map!26 = lv BandFile; map!27 = pFile
map!28 = "FontFile"; map!29 = lv FontFile; map!30 = pResFile

map!31 = "printerName"; map!32 = lv printerName; map!33 = pString
map!34 = "LogoFont"; map!35 = lv LogoFont; map!36 = pString
map!37 = "LogoText"; map!38 = lv LogoText; map!39 = pString

map!40 = "LandscapeDevice"; map!41 = lv LandscapeDevice; map!42 = pNum
map!43 = "PaperDimensionB"; map!44 = lv PaperDimensionB; map!45 = pNum
map!46 = "PaperDimensionS"; map!47 = lv PaperDimensionS; map!48 = pNum
map!49 = "printerDevice"; map!50 = lv printerDevice; map!51 = pNum
map!52 = "printerForward"; map!53 = lv printerForward; map!54 = pNum
map!55 = "PaperSpeedInches"; map!56 = lv PaperSpeedInches; map!57 = pNum
map!58 = "ScanLengthInches"; map!59 = lv ScanLengthInches; map!60 = pNum
map!61 = "ScanMarginAdjust"; map!62 = lv ScanMarginAdjust; map!63 = pNum
map!64 = "BitMarginAdjust"; map!65 = lv BitMarginAdjust; map!66 = pNum
map!67 = "Capabilities"; map!68 = lv Capabilities; map!69 = pNum
map!70 = "DutyCycle"; map!71 = lv DutyCycle; map!72 = pNum
map!73 = "Facets"; map!74 = lv Facets; map!75 = pNum
map!76 = "PolygonRatio"; map!77 = lv PolygonRatio; map!78 = pNum
map!79 = "NumBins"; map!80 = lv NumBins; map!81 = pNum
map!82 = "ResolutionB"; map!83 = lv ResolutionB; map!84 = pNum
map!85 = "ResolutionS"; map!86 = lv ResolutionS; map!87 = pNum
map!88 = "BreakPage"; map!89 = lv breakPage; map!90 = pNum
map!91 = 0
]

let pos = 1
until map!pos eq 0 do
[
unless StringCompare(s, map!pos) do
resultis lv (map!pos)
pos = pos + 3
]
resultis 0
]

// ------------------------------------------------------
and InitStatics() be
// ------------------------------------------------------
// For simple statics, the value found in the UserCmSlice is deposited in the
// statics’ location. Other statics may reference structures (i.e. strings, SpruceFiles, etc),
// which may need to be created, and initialized.
[
let s = OpenFile("User.Cm", ksTypeReadOnly, charItem, 0, 0, 0, SpruceZone)
unless s do CallSwat("Can’t read file User.Cm for installation defaults")

let addr, buf, buf1 = 0, FSGetX(80), FSGetX(80)
let needSpruce = true
[
switchon ReadUserCmItem(s, buf) into
[
case $E: break; endcase
case $N: needSpruce = StringCompare(buf, "Spruce"); endcase
case $L: unless needSpruce do
[
addr = GetStaticInfo(buf)
unless addr do
CallSwat("Unknown static mentioned in User.Cm: ", buf)
]
endcase
case $P: case $S: unless needSpruce do
[
CallSwat("Additional parameter in User.Cm file:", buf)
finish
]
endcase
]
if addr ne 0 then
[
if @(addr+2) eq pNum then
[
unless GetNextItem(s, $P, buf1) do
CallSwat("User.Cm Error - numeric paramater expected after:", buf)
unless StringCompare(buf1, "true") do @(@(addr+1)) = -1
unless StringCompare(buf1, "false") do @(@(addr+1)) = 0
@(@(addr+1)) = StringToNumber(buf1)
]
if @(addr+2) eq pString then
[
unless GetNextItem(s, $P, buf1) do
CallSwat("User.Cm Error - string paramater expected after:", buf)
@(@(addr+1)) = FSGetX((buf1>>STRING.length+2) rshift 1)
CopyString(@(@(addr+1)), buf1)
]
if @(addr+2) eq pFile % @(addr+2) eq pResFile then
[
Wss(dsp, "*N")
let fileName, pages, device = 0, 0, 0
unless GetNextItem(s, $P, buf1) do//get filename
CallSwat("User.Cm Error - fileName expected after:", buf)
fileName = FSGetX((buf1>>STRING.length+2) rshift 1)
CopyString(fileName, buf1)
unless GetNextItem(s, $P, buf1) do//get number of pages (if any)
CallSwat("User.Cm Error - page-length expected for:", buf)
pages = StringToNumber(buf1)
if pages eq 0 % @(addr + 2) eq pResFile then pages = -1
unless GetNextItem(s, $P, buf1) do//get device spec [dp0, dp1, tp0]
CallSwat("User.Cm Error - device specification expected for:", buf)
unless StringCompare("DP0", buf1) do [ device = DISK31 ]
unless StringCompare("DP1", buf1) do [ device = DISK31B ]
unless StringCompare("TP0", buf1) do [ device = DISKT80 ]
let OK = MakeValidSpruceFile(@(addr+1), fileName, pages, device)
if @(addr+2) eq pResFile & not OK then
CallSwat("Installation error - non-existant file:", fileName)
Wss(dsp, fileName); Wss(dsp, " ")
]
addr = 0
]
] repeat
FSPut(buf)
]

// ------------------------------------------------------
and StringToNumber(s) = valof
// ------------------------------------------------------
[
// return numeric value for string
// convert in octal if leading digit is zero.
let octal, pwr, pos, result = false, 1, 0, 0
let length = s>>STRING.length
if s>>STRING.char↑1 eq #60 then octal = true
[
result = result +
(((s>>STRING.char↑(length - pos)) - #60) * (octal ? (1 lshift (pos*3)), pwr))
pos = pos + 1; pwr = pwr * 10
] repeatwhile pos ne length
resultis result
]

// ------------------------------------------------------
and GetNextItem(s, item, buf) = valof
// ------------------------------------------------------
[
let itemType = ReadUserCmItem(s, buf)
if itemType eq $E % itemType eq $N % itemType ne item then resultis false
resultis itemType
]