missingnewline/main.go

124 lines
2.2 KiB
Go
Raw Normal View History

2023-04-07 16:32:39 +01:00
package main
import (
"bytes"
"os"
"time"
"unsafe"
2023-04-07 16:32:39 +01:00
"golang.org/x/sys/unix"
"golang.org/x/term"
)
type dsrResult struct {
row int
column int
extraBytes []byte
}
2023-04-07 16:32:39 +01:00
func main() {
st, err := term.MakeRaw(0)
if err != nil {
os.Exit(1)
}
var res dsrResult
2023-04-07 16:32:39 +01:00
select {
case <-time.After(100 * time.Millisecond):
case res = <-awaitDSR():
2023-04-07 16:32:39 +01:00
}
term.Restore(0, st)
if res.column > 1 {
2023-04-07 16:32:39 +01:00
unix.Write(1, []byte("🚫\n")) // U+1F6AB
}
for _, b := range res.extraBytes {
unix.Syscall(unix.SYS_IOCTL, 0, unix.TIOCSTI, uintptr(unsafe.Pointer(&b)))
}
2023-04-07 16:32:39 +01:00
}
func awaitDSR() <-chan dsrResult {
ch := make(chan dsrResult)
2023-04-07 16:32:39 +01:00
go func() {
row, col, extra := readDSR()
ch <- dsrResult{
row: row,
column: col,
extraBytes: extra,
}
2023-04-07 16:32:39 +01:00
}()
return ch
}
func readDSR() (row, col int, extra []byte) {
2023-04-07 16:32:39 +01:00
unix.Write(1, []byte{
'\033', '[', // ANSI escape code: CSI
'6', 'n', // DSR / device status report
})
inputBuf := bytes.NewBuffer(make([]byte, 0, 128))
termIn := make([]byte, 64)
for {
// read some bytes from stdin and append to our input buffer
termIn = termIn[:64]
n, err := unix.Read(0, termIn)
inputBuf.Write(termIn[:n])
if err != nil {
return 0, 0, inputBuf.Bytes()
}
2023-04-07 16:32:39 +01:00
// does the input buffer contain a valid DSR result sequence?
b := inputBuf.Bytes()
row, col, pos, count, ok := parseDSR(b)
if ok {
// replay the non-DSR sequence bytes
return row, col, append(b[:pos], b[pos+count:]...)
}
2023-04-07 16:32:39 +01:00
}
}
func parseDSR(input []byte) (row, col, pos, count int, ok bool) {
// locate the start of the result
pos = bytes.Index(input, []byte{'\033', '['})
2023-04-07 16:32:39 +01:00
if pos == -1 {
return
}
// parse the row and column numbers, separated by ';'
p := pos + 2
rowLoop:
for {
if p == len(input) {
return
}
switch input[p] {
case ';':
break rowLoop
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
row *= 10
row += int(input[p] - '0')
default:
return
}
p++
2023-04-07 16:32:39 +01:00
}
p++
for {
if p == len(input) {
return
}
switch input[p] {
case 'R':
count = p - pos + 1
ok = true
return
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
col *= 10
col += int(input[p] - '0')
default:
return
}
p++
2023-04-07 16:32:39 +01:00
}
}