// TFUutils.bcpl
// Copyright Xerox Corporation 1979

//   Last modified November 22, 1980  12:40 PM by Taft

get "AltoFileSys.d"
get "Streams.d"

external
   [
//TFU -- export
   ReadToken
   BackToken
   Disambiguate
   ReadFile
   ReadDrive
   StrEq
   ReadNumber
   Switch
   Error
   RunEther
   PrintEther
   InitLog
   CloseLog
   BigDisplay
   SmallDisplay
   ReportDebugStats

//TFU -- import
   InitDisk

//GP
   SetupReadParam
   ReadParam

//Template
   PutTemplate

//OS
   MoveBlock; SetBlock
   DoubleAdd
   Wss; Wns
   Ws; Gets; Puts; Resets; keys; dsp
   StartIO
   DefaultArgs
   MyFrame; Usc; Allocate; SysErr
   OpenFile; TruncateDiskStream; Closes
   CreateDisplayStream; ShowDisplayStream

//INTERRUPT
   FindInterruptMask
   InitializeInterrupt

// incoming statics
   defaultDrive
   sysDisk
   str
   sw
   noConfirm
   sysZone

// outgoing static
   lastToken
   ]
   

static
   [
   lastToken
   backUpToken
   EtherCount
   sysDsp; myDsp; log; lenBigDisplay; displayLineCount; save335
   parsing = false
   ]

// ReadToken() returns value
// token values:
//   1 = eof on command line
//   2 = command terminator (|)
//   3 = number
//   4 = "Erase"
//   5 = ... remaining commands -- see Disambiguate, below
//   1000 = ambiguous command

//----------------------------------------------------------------------------
let ReadToken() = valof
//----------------------------------------------------------------------------
[
let backedUp = backUpToken
lastToken = valof
   [
   let a=backUpToken
   if a then
      [
      unless a eq 1 then backUpToken=0
      resultis a
      ]
   let ans=ReadParam($P,0,0,0,true)
   if ans eq -1 then
      [
      backUpToken=1
      resultis 1
      ]
   resultis Disambiguate(str)
   ]
unless backedUp do
   switchon lastToken into
      [
      case 1: case 2:
         parsing = false; Ws("*n~~~~~~~~~~*n")
         endcase
      default:
         unless parsing do Ws("*n~~~~~~~~~~*n")
         parsing = false
         Ws(str)
         if sw!0 ne 0 then PutTemplate(dsp, "/$US", sw)
         Puts(dsp, $*s)
         parsing = true
         endcase
      ]
resultis lastToken
]

//----------------------------------------------------------------------------
and BackToken() be backUpToken = lastToken
//----------------------------------------------------------------------------


//----------------------------------------------------------------------------
and Disambiguate(str) = valof
//----------------------------------------------------------------------------
// Given a string, disambiguate it from several commands, and return one.
//   returns 1000 if cannot make sense of it
[
if str>>STRING.length eq 0 then resultis false
let c=str>>STRING.char↑1
if c eq $| then resultis 2   //vertical bar -- like end of file
if c ge $0 & c le $9 then resultis 3   //Number
let len=str>>STRING.length
let matchNo=nil
let matchCnt=0
for i=4 to 100 do
   [
   let s=selecton i into
      [
      case 4: "Erase"
      case 5: "Copy"
      case 6: "Delete"
      case 7: "CreateFile"
      case 8: "Directory"
      case 9: "Addresses"
      case 10: "Certify"
      case 11: "Drive"
      case 12: "Exercise"
      case 13: "Convert"
      case 14: "BadSpots"
      case 15: "ResetBadSpots"
      case 16: "Rename"
      default: 1000
      ]
   if s eq 1000 then break   //Done
   if len gr s>>STRING.length then loop
   let match=true
   for j=1 to len do
      if ((str>>STRING.char↑j xor s>>STRING.char↑j)&(not #40)) ne 0 then
         match=false
   if match then
      [
      matchCnt=matchCnt+1
      matchNo=i
      ]
   ]
if matchCnt eq 1 then resultis matchNo
resultis 1000
]

//----------------------------------------------------------------------------
and ReadFile(nam) = valof
//----------------------------------------------------------------------------
// Read and parse a file name for the disk name first (s: t: tn:) and
// return a pointer to the relevant "disk" object.
// Also update the name to omit the disk tag.
[
let a=ReadToken()
if a le 2 then resultis 0   //End of command ....

let colonfound=0
let len=str>>STRING.length
for i=1 to len do if str>>STRING.char↑i eq $: then colonfound=i
for i=colonfound+1 to len do
   nam>>STRING.char↑(i-colonfound)=str>>STRING.char↑i
nam>>STRING.length=len-colonfound

if colonfound le 1 then resultis InitDisk(defaultDrive)

str>>STRING.length=colonfound-1
let drive = ReadDrive(str)
resultis selecton drive into
   [
   case -1: sysDisk
   case -2: Error("Illegal drive -- ", str)
   default: InitDisk(drive)
   ]
]

//----------------------------------------------------------------------------
and ReadDrive(str) = valof
//----------------------------------------------------------------------------
// Interprets string of the form "TPn" or "DPn".
// Returns drive number for TPn, -1 for DP0, and -2 for anything else.
[
let number = 0
let lastNonDigit = 0
for i = 1 to str>>STRING.length do
   [
   let char = str>>STRING.char↑i
   test char ge $0 & char le $7
      ifso number = 8*number + (char-$0)
      ifnot [ lastNonDigit = i; number = 0 ]
   ]
str>>STRING.length = lastNonDigit
if StrEq(str, "TP") % StrEq(str, "T") then resultis number
if (StrEq(str, "DP") % StrEq(str, "S")) & number eq 0 then resultis -1
resultis -2
]
         
//----------------------------------------------------------------------------
and StrEq(a,b) = valof
//----------------------------------------------------------------------------
[
let len=a>>STRING.length
if len ne b>>STRING.length then resultis false
for i=1 to len do
   [
   let c=a>>STRING.char↑i
   let d=b>>STRING.char↑i
   if c gr $a & c le $z then c=c-$a+$A
   if d gr $a & d le $z then d=d-$a+$A
   if c ne d then resultis false
   ]
resultis true
]

//----------------------------------------------------------------------------
and ReadNumber(str, radix; numargs na) = valof
//----------------------------------------------------------------------------
[
DefaultArgs(lv na, -1, 10)
let n=0
for i=1 to str>>STRING.length do
   [
   let c=(str>>STRING.char↑i)-$0
   if c ls 0 % c ge radix then break
   n=n*radix+c
   ]
resultis n
]

// Returns true if switch ch is in switch vector sw.

//----------------------------------------------------------------------------
and Switch(ch) = valof
//----------------------------------------------------------------------------
[
let swnum=sw!0
for i=1 to swnum do
   [
   let c=sw!i
   if c ge $a & c le $z then c=c-$a+$A
   if c eq ch then resultis true
   ]
resultis false
]

//----------------------------------------------------------------------------
and Error(a,b; numargs n) be
//----------------------------------------------------------------------------
[
Ws("Error -- ")
Ws(a)
if n ne 1 then Ws(b)
Ws("*n")
finish
]


//Stuff to run the EtherNet when disking.

//----------------------------------------------------------------------------
and RunEther() be
//----------------------------------------------------------------------------
[
static eBuf
let EtherRead() be   //Interrupt routine
   [
   @#604=300
   @#605=eBuf
   StartIO(2)      //Restart input
   EtherCount=EtherCount+1
   ]
eBuf=@#335
@#335=eBuf+400
let mask=FindInterruptMask(1)
InitializeInterrupt(eBuf+300, 100, mask, EtherRead)
@#610=0      //Promiscuous reader
@#601=mask   //Interrupt mask
]

//----------------------------------------------------------------------------
and PrintEther(s) be if @#601 ne 0 then
//----------------------------------------------------------------------------
[
if EtherCount gr 1 then
   [
   PutTemplate(s, "[read $UD packets]", EtherCount-1)
   EtherCount=0
   ]
StartIO(3)
]

//----------------------------------------------------------------------------
and ReportDebugStats() be
//----------------------------------------------------------------------------
[
let p = @#645
let s = p!3
if p ne 0 then while s ne p!4 do
   [
   if s!0 eq -1 break
   if s eq p!3 then [ BigDisplay(); Ws("*nTFS debug statistics:") ]
   PutTemplate(dsp, "*n  status: $6UO, count: $6ED", s!0, s+1)
   s = s+3
   ]
SmallDisplay()
]

//----------------------------------------------------------------------------
and InitLog() be
//----------------------------------------------------------------------------
[
lenBigDisplay = MyFrame()-@#335-5000
if Usc(lenBigDisplay, 10000) gr 0 then lenBigDisplay = 10000
sysDsp = dsp
myDsp = dsp
dsp = Allocate(sysZone, lST)
SetBlock(dsp, SysErr, lST)
dsp>>ST.puts = DisplayPuts
dsp>>ST.reset = DisplayReset
log = OpenFile("TFU.log", ksTypeWriteOnly, charItem)
TruncateDiskStream(log)
]

//----------------------------------------------------------------------------
and CloseLog() be
//----------------------------------------------------------------------------
[
SmallDisplay()
Closes(log)
]

//----------------------------------------------------------------------------
and BigDisplay() be
//----------------------------------------------------------------------------
[
if myDsp eq sysDsp then
   [
   myDsp = CreateDisplayStream(25, @#335, lenBigDisplay)
   save335 = @#335
   @#335 = @#335 + lenBigDisplay
   ShowDisplayStream(myDsp)
   displayLineCount = 0
   Wss(log, "*n*n")
   ]
]

//----------------------------------------------------------------------------
and SmallDisplay() be
//----------------------------------------------------------------------------
[
if myDsp ne sysDsp then
   [
   unless noConfirm do
      [ Wss(myDsp, "    (continue?...)"); Gets(keys) ]
   Closes(myDsp)
   @#335 = save335
   myDsp = sysDsp
   Wss(log, "*n*n")
   ]
]

//----------------------------------------------------------------------------
and DisplayPuts(nil, char) be
//----------------------------------------------------------------------------
[
if parsing then
   [ parsing = false; Ws("*n~~~~~~~~~~*n") ]
if myDsp ne sysDsp & not noConfirm do
   if char eq $*n then
      [
      displayLineCount = displayLineCount+1
      if displayLineCount eq 25 then
         [
         Wss(myDsp, "    (continue?...)"); Gets(keys)
         displayLineCount = 0
         ]
      ]
Puts(myDsp, char)
Puts(log, char)
]

//----------------------------------------------------------------------------
and DisplayReset(nil) be Resets(myDsp)
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and Ws(string) be Wss(dsp, string)
//----------------------------------------------------------------------------