// BUILD -- Sproull 7/77
// modified by E. McCreight, August 8, 1978 6:39 PM
// modified by R. Bates, January 18, 1979 (Page Number control)
// modified by R. Bates, May 11, 1979 (MultiWire switch control)

// Program to "help keep things straight" in the DA world

// bldr build builda parsetitle mdi nsilutil ctime fancytemplate stringstreams CursorStream

get "sysdefs.d"
get "altofilesys.d"
get "disks.d"
get "nsil.defs"
//For definitions of SIL file format

structure TERM:
[
next word
switchChar word
phrase @str
switchPhrase @str
]

manifest
[
null = 0
MaxFileNames=100
FileNameWordLength=20
fpLookSize=6
nBuiltMarks=4
maxBuildAdditions=nBuiltMarks*5+400
BuiltMarkWidth=80
BuiltMarkIncrement=BuiltMarkWidth+
(568-nBuiltMarks*BuiltMarkWidth)/(nBuiltMarks-1)
charItem=1
verLatest=1
]

external
[
ErrorMessage// defined here
reWork
useRoute
multiWire
revStr
remCm
fileNameVec
fileNameCount
ChangeExtension
firstTerm
lastTerm

ParseTitle
// defined elsewhere
CopyCarefully
InsureDiskSpace
RepairComment
UNPACKDT
CopyString
CopyWl
BackupFiles
Oracle
PutTemplateWithHelp

DefaultArgs
// OS statics
TruncateDiskStream
DeleteFile
FileLength
SetFilePos
ReadBlock
Ws
Wss
Wns
sysDisk
Noop
Allocate
Free
GetFixed
sysZone
InitCursor
WriteCursor
]

static
[
revStr
fileNameVec
fileNameCount
PageNumber=1
unbuiltNameVec
unbuiltNameCount
analyzeAll
useRoute
multiWire
remCm
reWork
NoMerge
fpTempFile

NewItem
SpaceTop

firstTerm
lastTerm
]

let BuildMain() be
[
sysZone = InitializeZone(GetFixed(5000), 5000, 0, 0)
Ws("BUILD of July 24, 1979")
fpTempFile=table [ 0;0;0;0;0 ]

let v = vec 50 //this is to set up number display in cursor
InitCursor(v,50,0,0)

let s=OpenFile("Com.Cm", ksTypeReadOnly,1,0,fpComCm)

let leadingTerm = ParseTerm(s)
let tentative=false
analyzeAll=false
useRoute=false
multiWire=false
reWork = false
NoMerge = false
let Bstep=1
//Which step to do next?
for i=1 to leadingTerm>>TERM.switchPhrase.length do
[
let c=leadingTerm>>TERM.switchPhrase.char↑i
if c eq $t % c eq $T then tentative=true
if c eq $a % c eq $A then analyzeAll = true
if c eq $r % c eq $R then useRoute = true
if c eq $g % c eq $G then useRoute = false
if c eq $c % c eq $C then reWork = true
if c eq $m % c eq $M then [ multiWire = true; useRoute=true ]
if $1 le c & c le $9 then Bstep=c-$0
]
Free(sysZone, leadingTerm)

if tentative then if Bstep gr 4 then finish


if Bstep gr 6 then finish

//At this point, BUILD/xx has been parsed -- s remains poised for
// rest of command line.

fileNameCount=0
unbuiltNameCount = 0
let fileNameVecT=vec (FileNameWordLength+2)*MaxFileNames
let uNVT = vec MaxFileNames
fileNameVec=fileNameVecT
unbuiltNameVec = uNVT
let p=fileNameVec+MaxFileNames
for i=0 to MaxFileNames-1 do
[
@p=0//No string yet
fileNameVec!i = p
p=p+FileNameWordLength+1
]

let revStrT=vec 10; @revStrT=0; revStr=revStrT
//Holds revision string

firstTerm = null
let nextTerm = ParseTerm(s)
while nextTerm ne null do
[
if nextTerm>>TERM.switchChar eq 0 then
[
//File name
let NamePtr = fileNameVec!fileNameCount
MoveBlock(NamePtr, lv nextTerm>>TERM.phrase, FileNameWordLength)
NamePtr!-1 = PageNumber
fileNameCount=fileNameCount+1
PageNumber=PageNumber+1
if fileNameCount eq MaxFileNames then CallSwat("Too many file names")
Free(sysZone, nextTerm)
nextTerm = ParseTerm(s)
loop
]

let c=nextTerm>>TERM.switchPhrase.char↑1

//Might be some sort of switch for BUILD
if c eq $R then
[
MoveBlock(revStr, lv nextTerm>>TERM.phrase, 10)
Free(sysZone, nextTerm)
nextTerm = ParseTerm(s)
loop
]

if c eq $P then //want to specify PageNumber
[ //looks for val, .+val, .-val (where "." is val of previous file)
let val,base,minus = 0,0,false
for c = 1 to nextTerm>>TERM.phrase.length do
[
let chr = nextTerm>>TERM.phrase.char↑c
if chr eq $. then base = PageNumber-1
if chr eq $- then minus = true
if (chr ge $0)&(chr le $9) then val = val*10 + chr-$0
]

PageNumber = base + ( minus eq true? -val, val )
Free(sysZone, nextTerm)
nextTerm = ParseTerm(s)
loop
]

if c eq $G then
[
if nextTerm>>TERM.switchChar eq $\ then reWork=true
//passing old wl file by a different name
if nextTerm>>TERM.switchPhrase.char↑2 eq $C then NoMerge=true
]

nextTerm>>TERM.next = null
// add to term list
test firstTerm eq null
ifso firstTerm = nextTerm
ifnot lastTerm>>TERM.next = nextTerm
lastTerm = nextTerm

nextTerm = ParseTerm(s)
]
Closes(s)
if fileNameCount eq 0 then
[ Ws("*nAbort (no input files specified)..."); finish ] //quit if no files specified

// Finished parsing Com.Cm.
// File names recorded in fileNameVec, fileNameCount
// Other parameters recorded in "firstTerm" list

remCm=OpenFile("Rem.Cm",ksTypeWriteOnly,1,0,fpRemCm)

// Now dispatch to the proper routine -- will write stuff into Rem.Cm

[
switchon Bstep into
[
case 1: WriteCursor(Wss,"B1")
CopyCarefully("Com.cm", "Build.cm"); MarkBuilt(); endcase
case 2: WriteCursor(Wss,"B2")
PutTemplateWithHelp(Oracle, remCm,
(not analyzeAll & unbuiltNameCount eq 0?
"// No analysis required",
"Analyze/$ZWA$ZLA $ZN.er/E $ZVAN"),
(analyzeAll? fileNameVec, unbuiltNameVec),
(analyzeAll? fileNameCount, unbuiltNameCount))
break
case 3: WriteCursor(Wss,"B3")
CheckPrevErrors(".er", 1)
if not tentative do MergePN(); endcase
case 4: WriteCursor(Wss,"B4")
PutTemplateWithHelp(Oracle, remCm,
"$S$S$S$ZWG $ZLG $ZAN.nl", (useRoute? "Route", "Gobble"),
(useRoute? ( reWork? "/C", "/"), (reWork? "\", "/")), (multiWire? "M", "") )
break
case 5: WriteCursor(Wss,"B5")
if useRoute then CheckPrevErrors(".re", 1)
unless NoMerge do CopyWl()
endcase
case 6: WriteCursor(Wss,"B6")
BackupFiles(); break
]
Bstep=Bstep+1
] repeat

Puts(remCm, $*n)

// Now copy into Rem.Cm the continuation command : the
// original contents of Com.Cm, updated with a step switch.

let s=OpenFile("Com.Cm", ksTypeReadOnly,1,0,fpComCm)
let term = ParseTerm(s)
let j=0
let len=term>>TERM.switchPhrase.length
for i=1 to len do
[
let c=term>>TERM.switchPhrase.char↑i
if $0 le c & c le $9 then j=i
]
if j eq 0 then [ j=len+1; term>>TERM.switchPhrase.length=j ]
term>>TERM.switchChar=$/
term>>TERM.switchPhrase.char↑j=Bstep+1+$0
WriteTerm(remCm, term)

term = ParseTerm(s)
while term ne null do [ WriteTerm(remCm, term); term = ParseTerm(s) ]

Closes(s)
Puts(remCm, $*n)
TruncateDiskStream(remCm)
Closes(remCm)
finish
]

and MarkBuilt() be
[
let silFiles=vec MaxFileNames*fpLookSize
FindFiles(fileNameVec, silFiles, fileNameCount)
for i=0 to fileNameCount-1 do
[
let lfp = silFiles+i*fpLookSize
if @lfp eq 0 then CallSwat("Cannot open .SIL file",fileNameVec!i)

WriteCursor(Wns,(fileNameVec!i)!-1)
let s=OpenFile(fileNameVec!i,ksTypeReadWrite,0,0,lfp+1)
let a=FileLength(s)/2; Resets(s)
let base=@#335
if Usc(a+base+maxBuildAdditions+2000, lv base) gr 0 then
CallSwat("Too much space required for SIL file")
InsureDiskSpace(maxBuildAdditions)
@#335=base+a+maxBuildAdditions
ReadBlock(s, base, a)
let endP=base+a
let alreadyBuilt=(@base eq 34563b)
@base = 34563b
//Mark built
let titV=vec 300
for pass=0 to 3 do
//Parse title area, once with old hdg, once with new.
[
if (pass eq 0) % (pass eq 2) do ParseTitle(0, titV, 300)
let p=base+1
while p ne endP do
[
let a=ParseTitle(1, titV, p)
if pass eq 1 & a ne 0 then
[
let newP=vec 60
MakeNewTitle(a, newP, p, i, alreadyBuilt)
let ol=Length(p)
let nl=Length(newP)
if nl gr ol then
[
let diff=nl-ol
let q=endP
[ q!diff=q!0; q=q-1 ] repeatuntil q eq p
]
if ol gr nl then
[
let diff=ol-nl
let q=p
[ q!0=q!diff; q=q+1 ] repeatuntil q eq endP
]
endP=endP+nl-ol
MoveBlock(p, newP, nl)
]
p=p+Length(p)
]
]
test alreadyBuilt
ifnot for i=0 to nBuiltMarks-1 do
[
endP>>item.link=-1
endP>>item.xmin=i*BuiltMarkIncrement
endP>>item.xmax=i*BuiltMarkIncrement+BuiltMarkWidth
endP>>item.ymax=BuildYmax
endP>>item.ymin=BuildYmax-7
endP>>item.font=14
endP=endP+Length(endP)
]
ifso
[
let nlFileName = vec 30
CopyString(nlFileName,fileNameVec!i)
ChangeExtension(nlFileName,".nl")
let nlFile=OpenFile(nlFileName,ksTypeReadWrite,charItem,verLatest)
alreadyBuilt = nlFile? RepairComment(nlFile,titV),false
]
Resets(s)
WriteBlock(s, base, endP-base)
TruncateDiskStream(s)
@#335=base
Closes(s)
unless alreadyBuilt do
[
unbuiltNameVec!unbuiltNameCount = fileNameVec!i
unbuiltNameCount = unbuiltNameCount+1
]
]

]

and MakeNewTitle(what, newP, oldP, file, alreadyBuilt) be
[
MoveBlock(newP, oldP, Length(oldP))
let oldS=lv oldP>>item.string
let newS=lv newP>>item.string
if what eq 4 then
[
let Page = (fileNameVec!file)!-1
newS>>str.length=0
if Page ls 10 then AppendC($0, newS)
//Two digits!!
AppendN(Page, newS)
let num=true
for i=1 to oldS>>str.length do //copy characters after num in SIL file
[
let c=oldS>>str.char↑i
if num ne 0 & c ge $0 & c le $9 then loop
num=false
AppendC(c, newS)
]
return
]
if what eq 1 then MoveBlock(newS, fileNameVec!file, FileNameWordLength)
if what eq 2 & revStr>>str.length ne 0 then MoveBlock(newS, revStr, FileNameWordLength)
//update date if not built or first file seen
if (what eq 3) & (alreadyBuilt ne 0) & (file gr 0) then return
if what eq 3 then GetDateString(newS)
// But preserve capitalization, etc of original.
if StEq(newS, oldS, nil) then MoveBlock(newS, oldS, oldS>>str.length/2+1)
]

and MergePN() be
[
let silFiles=vec MaxFileNames*fpLookSize
FindFiles(fileNameVec, silFiles, fileNameCount)
let pnNameVec=vec MaxFileNames*(FileNameWordLength+1)
let p=pnNameVec+MaxFileNames
for i=0 to fileNameCount-1 do
[
MoveBlock(p, fileNameVec!i, FileNameWordLength)
ChangeExtension(p, ".pn")
pnNameVec!i=p
p=p+FileNameWordLength
]
let pnFiles=vec MaxFileNames*fpLookSize
FindFiles(pnNameVec, pnFiles, fileNameCount)

for i=0 to fileNameCount-1 do
[
let p=pnFiles+i*fpLookSize
if p!0 eq 0 then loop
//No pn’s to merge
let s=OpenFile(pnNameVec!i,ksTypeReadOnly,0,0,p+1)
if s eq 0 then CallSwat("Cannot open a .PN file", pnNameVec!i)
let out=OpenFile(fileNameVec!i,ksTypeReadWrite,0,0,silFiles+i*fpLookSize+1)
if out eq 0 then CallSwat("Cannot open a .SIL file", fileNameVec!i)
let a=FileLength(s); Resets(s)
InsureDiskSpace(a/2)
//This many more words.
FileLength(out)
//Get to end of existing SIL file
Gets(s)
//Past password
until Endofs(s) do Puts(out, Gets(s))
Closes(s)
Closes(out)
DeleteFile(pnNameVec!i)
//Get rid of .PN file
]
]

and CheckPrevErrors(ext, limit) be
[
let str = vec FileNameWordLength
MoveBlock(str, fileNameVec!0, FileNameWordLength)
ChangeExtension(str, ext)
let file = OpenFile(str, ksTypeReadOnly, charItem)
if file ne 0 then
[
Gets(file)
if Gets(file)-$0 gr limit then
[
Ws("*nBuild aborted due to error indicated in ")
Ws(str)
Ws(".")
finish
]
Closes(file)
]
]


and ChangeExtension(str, ext) be
[
let j=0
for i=1 to str>>str.length do [ if str>>str.char↑i eq $. then break; j=i ]
str>>str.length=j
AppendS(ext, str)
]


and ParseTerm(s) = valof
[
let v = vec size TERM/16

let reading = false
let intoStr=lv v>>TERM.phrase
Zero(v, (size TERM/16))
until Endofs(s) do
[
let c=Gets(s)
if c eq $*s % c eq $*t % c eq $*n then
[
if reading then break
loop
]
if c eq $/ % c eq $\ then
[
v>>TERM.switchChar=c
intoStr=lv v>>TERM.switchPhrase
loop
]
if intoStr eq lv v>>TERM.switchPhrase then //Capitalize switches
if c ge $a then c=c-$a+$A
AppendC(c, intoStr)
reading = true
]
unless reading do resultis null

let result = Allocate(sysZone, size TERM/16)
MoveBlock(result, v, size TERM/16)
resultis result
]

and WriteTerm(s, v) be
[
Puts(s, $*s)
WSS(s, lv v>>TERM.phrase)
if v>>TERM.switchChar ne 0 then
[
Puts(s, v>>TERM.switchChar)
WSS(s, lv v>>TERM.switchPhrase)
]
Free(sysZone, v)
]

and GetDateString(s) be
[
let uv=vec 7
UNPACKDT(0, uv)
@s=0
AppendN(uv!1+1, s)
//Month
AppendC($/, s)
if uv!2 ls 10 then AppendC($0, s)
//Two digits for day!!
AppendN(uv!2, s)
//Day
AppendC($/, s)
AppendN(uv!0-1900, s)
//Year
]

and FindFiles(names, pr, count) be
[
let s=OpenFile("SysDir",ksTypeReadOnly,0,0,fpSysDir)
LookUpEntries(s, names, pr, count, true)
Closes(s)
]

and ErrorMessage(msg) be
[
Ws(msg)
Resets(keys)
//Swallow typeahead
Gets(keys)
]