// OeditCom.bcpl 
// Copyright Xerox Corporation 1979,1980

get "streams.d"
get "oedit.d"

external
	[
//import from the OS
	Gets; Puts; dsp; keys; Wss
	Resets; Closes; Endofs
	DoubleAdd; Usc; Ws

//import from Template
	PutTemplate

//import from UtilDP
	AssignDP
	NegateDP
	DoubleLShift
	DoubleUsc
	zeroDP
	oneDP
	minusOneDP

//import from OEdit
	Display; DisplayValue
	WriteFile
	PositionFile
	GetKeys
	PutbackKeys
	AsciiToEbcdic;  EbcdicToAscii
	modes; files; addr; data; lastVal
	writeFlag;  dataFlag; outstream; standard
	]

external
	[
//export to OEdit
	ReadAndDoCommand
	]

let ReadAndDoCommand(fullPageOnDisplay) = valof
[
let numToShow = nil
AssignDP(data, zeroDP)
dataFlag = false
Wss(outstream, "#")
if GetData() eq errRet then 
	[
	Wss(outstream, "  ??")
	Display(standard)
	resultis false
	]
let c=GetKeys()
switchon c into [
//First, the control characters for display
	  case #17:  //control-O, diplay in octal
	  case #10:  //control-H, diplay in halfOctal
	  case #30:  //control-X, diplay in halfHex
	  case #1:  case #3:  case #23:
			//control-(A or C or S), diplay in halfAscii
	  case #5:  //control-E, diplay in halfEbcdic
	  case #4:  //control-D, diplay in decimal
	  case #16:  //control-N, diplay in halfDecimal
		if dataFlag then AssignDP(addr, data)
		Puts(outstream, $↑)
		Puts(outstream, c+#100)
		Display(modes ! c)
		resultis false
//Now, for the store and then display standard mode
	  case #26:  case $V:  case $v:  case $=:  //display value
		Puts(outstream, $=)
		DisplayValue()
		Display(standard)
		resultis false
	  case #12: if dataFlag then WriteFile()
		DoubleAdd(addr, oneDP)
		Display(standard)
		resultis false
	  case $>:  if dataFlag then WriteFile()
		Resets(outstream)
		Display(standard)
		for i=1 to #40 do
			[
			DoubleAdd(addr, oneDP)
			Display(standard)
			]
		resultis true
	  case $↑: case #27: 
		Puts(outstream, $↑)
		if dataFlag then WriteFile()
		DoubleAdd(addr, minusOneDP)
		Display(standard)
		resultis false
	  case $<: if dataFlag then WriteFile()
		if fullPageOnDisplay then
		  for i=1 to #40 do DoubleAdd(addr, minusOneDP)
		for i=1 to #40 do DoubleAdd(addr, minusOneDP)
		Resets(outstream)
		Display(standard)
		for i=1 to #40 do
			[
			DoubleAdd(addr, oneDP)
			Display(standard)
			]
		resultis true
//And the cr, tab, slash, and bang commands
	  case $*N:
		if dataFlag then WriteFile()
		Display(standard)
		resultis false
	  case #11:
		Wss(outstream, "↑I")
		if dataFlag then WriteFile()
		addr!0=0;  addr!1=lastVal
		Display(standard)
		resultis false
	  case $/:
		Wss(outstream, "/")
		if dataFlag then AssignDP(addr, data)
		Display(standard)
		resultis false
	  case $!:
		Wss(outstream, "!")
		if dataFlag then AssignDP(addr, data)
		Resets(outstream)
		Display(standard)
		for i=1 to #40 do 
			[
			DoubleAdd(addr, oneDP)
			Display(standard)
			]
		resultis true
//finally, the funny guys all by themselves
	  case $F:  case $f:  case #6:
		Puts(outstream, $F)
		test dataFlag ifso
			[
			Wss(outstream, "*NSearching [hit any key to interrupt]...")
			FindAddr()
			Display(standard)
			resultis false
			]
		  ifnot
			[
			DoWizardCommand()
			resultis false
			]
	  case $Q: case $q: case #21:
		Wss(outstream,"*NQuit [confirm with CR]")
		if GetKeys() eq $*N then [
		  Closes(outstream)
		  if writeFlag do Closes(files!1)
		  finish
		  ]
		Wss(outstream, "*NCancelling the Quit!")
		Display(standard)
		resultis false
	  default: PutTemplate(outstream,"$C ??",c)
		Display(standard)
		resultis false
	]
]

and GetData() = valof
[
let c=GetKeys()
let d = nil
switchon c into
		[
	  case $0 to $7: case $-:
		PutbackKeys(c)
		if GetDatum(octal) eq errRet then resultis errRet
		dataFlag = true
		loop
	  case $H:  case $A:  case $S: case $C: case $X:
	   case $O: case $E: case $D: case $N:
		Puts(outstream, c)
		d=GetKeys()
		if d ne $: then
			[
			Puts(outstream, d)
			resultis errRet
			]
		Puts(outstream, $:)
		if GetDatum(modes!(c-#100)) eq errRet then resultis errRet
		dataFlag = true
		loop
	  case $h:  case $a:  case $s: case $c: case $x:
	   case $o: case $e: case $d: case $n:
		Puts(outstream, c)
		d=GetKeys()
		if d ne $: then
			[
			Puts(outstream, d)
			resultis errRet
			]
		Puts(outstream, $:)
		if GetDatum(modes!(c-#140)) eq errRet then resultis errRet
		dataFlag = true
		loop
	  case $W:  case $w:
		Puts(outstream, c)
		d=GetKeys()
		if d ne $: then
			[
			Puts(outstream, d)
			resultis errRet
			]
		Puts(outstream, $:)
		if GetDatum(oneWordOctal) eq errRet then resultis errRet
		dataFlag = true
		loop
	  case $*S:
		Puts(outstream, c)
		loop
	  default: 
		PutbackKeys(c)
		resultis okRet
		]
] repeat


and GetDatum(mode) = valof
[
let res= vec 1
AssignDP(res, zeroDP)
switchon mode into
	[
  case octal:
	GetOctal(res, true)
	AssignDP(data, res)
	resultis okRet
  case oneWordOctal:
	GetOctal(res, true)
	data!0=data!1;  data!1=res!1
	resultis okRet
  case halfOctal:
	DoubleLShift(data, data, 8)
	GetOctal(res, true)
	data!1<<right = res!1<<right
	resultis okRet
  case decimal:
	GetDecimal(res, true)
	AssignDP(data,res)
	resultis okRet
  case halfDecimal:
	DoubleLShift(data, data, 8)
	GetDecimal(res, true)
	data!1<<right = res!1<<right
	resultis okRet
  case halfAscii:
	DoubleLShift(data, data, 8)
	data!1<<right = GetKeys()
	Puts(outstream, data!1<<right)
	resultis okRet
  case halfHex:
	DoubleLShift(data, data, 8)
	GetHexadecimal(res, true)
	data!1<<right = res!1<<right
	resultis okRet
  case halfEbcdic:
		[
		let cAscii = GetKeys()
		let cEbcdic = AsciiToEbcdic(cAscii)
		if cEbcdic eq 0 then 
			[
			Puts(outstream, cAscii)
			resultis errRet
			]
		Puts(outstream, EbcdicToAscii(cEbcdic))
		DoubleLShift(data, data, 8)
		data!1<<right = cEbcdic
		resultis okRet
		]
  default:
	resultis errRet
	]
]

and GetOctal(res, firstTime) be
[
let c=GetKeys()
if c eq $- & firstTime then
	[
	Puts(outstream, c)
	GetOctal(res, false)
	NegateDP(res, res)
	return
	]
if c gr $7 % c ls $0 then
	[
	PutbackKeys(c)
	return
	]
DoubleLShift(res, res, 3)
let temp=vec 1
temp>>DP.High=0;  temp>>DP.Low=c-$0
DoubleAdd(res,temp)
Puts(outstream, c)
GetOctal(res, false)
]

and GetHexadecimal(res, firstTime) be
[
let c=GetKeys()
if c eq $- & firstTime then
	[
	Puts(outstream, c)
	GetHexadecimal(res, false)
	NegateDP(res, res)
	return
	]
switchon c into
	[
  case $0 to $9:  case $A to $F:  case $a to $f:
	endcase
  default:
	PutbackKeys(c)
	return
	]
DoubleLShift(res, res, 4)
let temp=vec 1
temp>>DP.High=0
temp>>DP.Low=selecton c into
	[
  case $0 to $9:   c-$0
  case $A to $F:   c-$A+10
  case $a to $f:   c-$a+10
	]
DoubleAdd(res,temp)
Puts(outstream, c)
GetHexadecimal(res, false)
]

and GetDecimal(res, firstTime) be
[
let c=GetKeys()
if c eq $- & firstTime then
	[
	Puts(outstream, c)
	GetDecimal(res, false)
	NegateDP(res, res)
	return
	]
if c gr $9 % c ls $0 then
	[
	PutbackKeys(c)
	return
	]
let temp=vec 1
AssignDP(temp, zeroDP)
for i=1 to 10 do DoubleAdd(temp, res)
AssignDP(res,temp)
temp>>DP.High=0;  temp>>DP.Low=c-$0
DoubleAdd(res,temp)
Puts(outstream, c)
GetDecimal(res, false)
]

and FindAddr() be
	[
	DoubleAdd(addr, oneDP)
	PositionFile(1, addr)
	let v, s = data>>DP.Low, files!1
	while Endofs(keys) & not Endofs(s) do
		[
		let fV = Gets(s)
		if fV eq v then return
		DoubleAdd(addr,oneDP)
		]
	unless Endofs(keys) do
		[
		Wss(outstream, "*NSearching interrupted!")
		Gets(keys)  //To flush the "any char"
		]
	]

and DoWizardCommand() be
[
let startAddr = vec 1
let endAddr = vec 1
let diffAddr = vec 1
let mask = vec 1
let wizardData = vec 1
let replaceFlag = nil
Wss(outstream, "*NFor address ← ")
if GetData() eq errRet then
	[
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
test dataFlag ifso AssignDP(startAddr, data)
		ifnot AssignDP(startAddr, addr)
PutTemplate(outstream, " = $UEO", startAddr)
AssignDP(data, zeroDP); dataFlag=false
let c=GetKeys()
if c ne #33 & c ne $$ & c ne $*C then 
	[
	Puts(outstream, c)
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
Wss(outstream, "*N to ")
if GetData() eq errRet then 
	[
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
test dataFlag ifso AssignDP(endAddr, data)
  ifnot	AssignDP(endAddr, minusOneDP)
PutTemplate(outstream, " = $UEO", endAddr)
AssignDP(data, zeroDP); dataFlag=false
c=GetKeys()
if c ne #33 & c ne $$ & c ne $*C then 
	[
	Puts(outstream, c)
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
Wss(outstream, "*N by ")
if GetData() eq errRet then 
	[
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
test dataFlag ifso AssignDP(diffAddr, data)
		ifnot AssignDP(diffAddr, oneDP)
PutTemplate(outstream, " = $UEO", diffAddr)
AssignDP(data, zeroDP); dataFlag=false
c=GetKeys()
if c ne #33 & c ne $$ & c ne $*C then 
	[
	Puts(outstream, c)
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
if diffAddr!0 ls 0 then
	[
	Wss(outstream, "*NOnly forward-going loops allowed!*N")
	return
	]
Wss(outstream, "*N with mask ")
if GetData() eq errRet then 
	[
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
test dataFlag ifso AssignDP(mask, data)
		ifnot AssignDP(mask, minusOneDP)
mask=mask!1
PutTemplate(outstream, " = $UO", mask)
AssignDP(data, zeroDP); dataFlag=false
c=GetKeys()
if c ne #33 & c ne $$ & c ne $*C then 
	[
	Puts(outstream, c)
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
Wss(outstream, "*N S[earch for]  or  R[eplace by]:")
c=GetKeys()
switchon c into
	[
  case $S:  case $s:  
	replaceFlag = false
	endcase
  case $R:  case $r:
	replaceFlag = true
	endcase
  default:
	Puts(outstream, GetKeys())
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
Puts(outstream, c)
Wss(outstream, "*N the data word ")
if GetData() eq errRet then 
	[
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
test dataFlag ifso AssignDP(wizardData, data)
		ifnot AssignDP(wizardData, zeroDP)
wizardData = wizardData!1
PutTemplate(outstream, " = $UO", wizardData)
AssignDP(data, zeroDP); dataFlag=false
c=GetKeys()
if c ne #33 & c ne $$ & c ne $*C then 
	[
	Puts(outstream, GetKeys())
	Wss(outstream, "  ??*NTerminating F command!*N")
	return
	]
Wss(outstream, "*N Performing the command [hit any key to interrupt]")
AssignDP(addr, startAddr)
trynext:  if PositionFile(1, addr) eq 0 then 
	[
	Wss(outstream, "*N Overflow, terminating F command!*N")
	return
	]
let fileData = Gets(files!1)
test replaceFlag 
  ifso
	[
	data!1 = (fileData & (not mask)) % (wizardData & mask)
	PositionFile(1, addr)
	WriteFile()
	]
  ifnot
	if (fileData&mask) eq (wizardData&mask) then
		[
		Wss(outstream, "*N Found!")
		Display(standard)
		return
		]
DoubleAdd(addr, diffAddr)
if DoubleUsc(addr, endAddr) eq 1 then 
  test replaceFlag
   ifso
	[
	Wss(outstream, "*N Done.*N")
	return
	]
    ifnot
	[
	Wss(outstream, "*N Not found.*N")
	return
	]
if not Endofs(keys) then 
	[ GetKeys()
	  Wss(outstream, "*NCommand interrupted from keyboard!*N")
	  return
	]
goto trynext
]