mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-07-02 18:14:42 +08:00
278 lines
6.0 KiB
Go
278 lines
6.0 KiB
Go
package terminalparser
|
||
|
||
import (
|
||
"bytes"
|
||
"log"
|
||
"strconv"
|
||
"unicode/utf8"
|
||
|
||
"github.com/mattn/go-runewidth"
|
||
)
|
||
|
||
type Screen struct {
|
||
Rows []*Row
|
||
|
||
Cursor *Cursor
|
||
|
||
pasteMode bool // Set bracketed paste mode, xterm. ?2004h reset ?2004l
|
||
|
||
title string
|
||
}
|
||
|
||
func (s *Screen) Parse(data []byte) []string {
|
||
s.Cursor.Y = 1
|
||
s.Rows = append(s.Rows, &Row{
|
||
dataRune: make([]rune, 0, 1024),
|
||
})
|
||
rest := data
|
||
for len(rest) > 0 {
|
||
code, size := utf8.DecodeRune(rest)
|
||
rest = rest[size:]
|
||
switch code {
|
||
case ESCKey:
|
||
code, size = utf8.DecodeRune(rest)
|
||
rest = rest[size:]
|
||
switch code {
|
||
case '[':
|
||
// CSI
|
||
rest = s.parseCSISequence(rest)
|
||
continue
|
||
case ']':
|
||
// OSC
|
||
rest = s.parseOSCSequence(rest)
|
||
continue
|
||
default:
|
||
if existIndex := bytes.IndexRune([]byte(string(Intermediate)), code); existIndex >= 0 {
|
||
// ESC
|
||
rest = s.parseIntermediate(code, rest)
|
||
continue
|
||
}
|
||
if existIndex := bytes.IndexRune([]byte(string(Parameters)), code); existIndex >= 0 {
|
||
|
||
log.Printf("Screen 未解析 ESC `%q` %xParameters字符\n", code, code)
|
||
continue
|
||
}
|
||
if existIndex := bytes.IndexRune([]byte(string(Uppercase)), code); existIndex >= 0 {
|
||
log.Printf("Screen 未解析 ESC `%q` %x Uppercase字符\n", code, code)
|
||
continue
|
||
}
|
||
|
||
if existIndex := bytes.IndexRune([]byte(string(Lowercase)), code); existIndex >= 0 {
|
||
log.Printf("Screen 未解析 ESC `%q` %x Lowercase字符\n", code, code)
|
||
continue
|
||
}
|
||
log.Printf("Screen 未解析 ESC `%q` %x\n", code, code)
|
||
}
|
||
continue
|
||
case Delete:
|
||
continue
|
||
default:
|
||
if existIndex := bytes.IndexRune([]byte(string(C0Control)), code); existIndex >= 0 {
|
||
s.parseC0Sequence(code)
|
||
} else {
|
||
if len(s.Rows) == 0 && s.Cursor.Y == 0 {
|
||
s.Rows = append(s.Rows, &Row{
|
||
dataRune: make([]rune, 0, 1024),
|
||
})
|
||
s.Cursor.Y++
|
||
}
|
||
s.appendCharacter(code)
|
||
}
|
||
continue
|
||
}
|
||
|
||
}
|
||
result := make([]string, len(s.Rows))
|
||
for i := range s.Rows {
|
||
result[i] = s.Rows[i].String()
|
||
}
|
||
return result
|
||
}
|
||
|
||
func (s *Screen) parseC0Sequence(code rune) {
|
||
switch code {
|
||
case 0x07:
|
||
//bell 忽略
|
||
case 0x08:
|
||
// 后退1光标
|
||
s.Cursor.MoveLeft(1)
|
||
case 0x0d:
|
||
/*
|
||
\r
|
||
*/
|
||
s.Cursor.X = 0
|
||
s.Cursor.Y++
|
||
if s.Cursor.Y > len(s.Rows) {
|
||
s.Rows = append(s.Rows, &Row{
|
||
dataRune: make([]rune, 0, 1024),
|
||
})
|
||
}
|
||
case 0x0a:
|
||
/*
|
||
\n
|
||
*/
|
||
s.Cursor.Y++
|
||
if s.Cursor.Y > len(s.Rows) {
|
||
s.Rows = append(s.Rows, &Row{
|
||
dataRune: make([]rune, 0, 1024),
|
||
})
|
||
}
|
||
default:
|
||
log.Printf("未处理的字符 %q %v\n", code, code)
|
||
}
|
||
|
||
}
|
||
|
||
func (s *Screen) parseCSISequence(p []byte) []byte {
|
||
endIndex := bytes.IndexFunc(p, IsAlphabetic)
|
||
params := []rune(string(p[:endIndex]))
|
||
switch rune(p[endIndex]) {
|
||
case 'Y':
|
||
// /*
|
||
// ESC Y Ps Ps
|
||
// Move the cursor to given row and column.
|
||
// */
|
||
if len(p[endIndex+1:]) < 2 {
|
||
return p[endIndex+1:]
|
||
}
|
||
if row, err := strconv.Atoi(string(p[endIndex+1])); err == nil {
|
||
s.Cursor.Y = row
|
||
}
|
||
if col, err := strconv.Atoi(string(p[endIndex+2])); err == nil {
|
||
s.Cursor.X = col
|
||
}
|
||
return p[endIndex+3:]
|
||
|
||
}
|
||
|
||
funcName, ok := CSIFuncMap[rune(p[endIndex])]
|
||
if ok {
|
||
funcName(s, params)
|
||
} else {
|
||
log.Printf("screen未处理的CSI %s %q\n", DebugString(string(params)), p[endIndex])
|
||
}
|
||
|
||
return p[endIndex+1:]
|
||
}
|
||
|
||
func (s *Screen) parseIntermediate(code rune, p []byte) []byte {
|
||
switch code {
|
||
case '(':
|
||
terminationIndex := bytes.IndexFunc(p, func(r rune) bool {
|
||
if insideIndex := bytes.IndexRune([]byte(string(Alphabetic)), r); insideIndex < 0 {
|
||
return false
|
||
}
|
||
return true
|
||
})
|
||
params := p[:terminationIndex+1]
|
||
switch string(params) {
|
||
case "B":
|
||
/*
|
||
ESC ( C Designate G0 Character Set, VT100, ISO 2022.
|
||
C = B ⇒ United States (USASCII), VT100.
|
||
*/
|
||
}
|
||
p = p[terminationIndex+1:]
|
||
return p
|
||
case ')':
|
||
terminationIndex := bytes.IndexFunc(p, func(r rune) bool {
|
||
if insideIndex := bytes.IndexRune([]byte(string(Alphabetic)), r); insideIndex < 0 {
|
||
return false
|
||
}
|
||
return true
|
||
})
|
||
p = p[terminationIndex+1:]
|
||
default:
|
||
log.Printf("Screen 未解析 ESC `%q` %x Intermediate字符\n", code, code)
|
||
}
|
||
return p
|
||
}
|
||
|
||
func (s *Screen) parseOSCSequence(p []byte) []byte {
|
||
if endIndex := bytes.IndexRune(p, BEL); endIndex >= 0 {
|
||
return p[endIndex+1:]
|
||
}
|
||
|
||
if endIndex := bytes.IndexRune(p, ST); endIndex >= 0 {
|
||
return p[endIndex+1:]
|
||
}
|
||
log.Println("未处理的 parseOSCSequence")
|
||
return p
|
||
}
|
||
|
||
func (s *Screen) appendCharacter(code rune) {
|
||
currentRow := s.GetCursorRow()
|
||
currentRow.changeCursorToX(s.Cursor.X)
|
||
currentRow.appendCharacter(code)
|
||
width := runewidth.StringWidth(string(code))
|
||
s.Cursor.X += width
|
||
}
|
||
|
||
func (s *Screen) eraseEndToLine() {
|
||
currentRow := s.GetCursorRow()
|
||
currentRow.changeCursorToX(s.Cursor.X)
|
||
currentRow.eraseRight()
|
||
|
||
}
|
||
|
||
func (s *Screen) eraseRight() {
|
||
currentRow := s.GetCursorRow()
|
||
currentRow.changeCursorToX(s.Cursor.X)
|
||
currentRow.eraseRight()
|
||
}
|
||
|
||
func (s *Screen) eraseLeft() {
|
||
log.Printf("Screen %s Erase Left cursor(%d,%d) 总Row数量 %d",
|
||
UnsupportedMsg, s.Cursor.X, s.Cursor.Y, len(s.Rows))
|
||
}
|
||
|
||
func (s *Screen) eraseAbove() {
|
||
s.Rows = s.Rows[s.Cursor.Y-1:]
|
||
}
|
||
|
||
func (s *Screen) eraseBelow() {
|
||
s.Rows = s.Rows[:s.Cursor.Y]
|
||
}
|
||
|
||
func (s *Screen) eraseAll() {
|
||
s.Rows = s.Rows[:0]
|
||
//htop?
|
||
s.Cursor.X = 0
|
||
s.Cursor.Y = 0
|
||
}
|
||
|
||
func (s *Screen) eraseFromCursor() {
|
||
if s.Cursor.Y > len(s.Rows) {
|
||
s.Cursor.Y = len(s.Rows)
|
||
}
|
||
s.Rows = s.Rows[:s.Cursor.Y]
|
||
currentRow := s.GetCursorRow()
|
||
currentRow.changeCursorToX(s.Cursor.X)
|
||
currentRow.eraseRight()
|
||
}
|
||
|
||
func (s *Screen) deleteChars(ps int) {
|
||
currentRow := s.GetCursorRow()
|
||
currentRow.changeCursorToX(s.Cursor.X)
|
||
currentRow.deleteChars(ps)
|
||
}
|
||
|
||
func (s *Screen) GetCursorRow() *Row {
|
||
if s.Cursor.Y == 0 {
|
||
s.Cursor.Y++
|
||
}
|
||
if len(s.Rows) == 0 {
|
||
s.Rows = append(s.Rows, &Row{
|
||
dataRune: make([]rune, 0, 1024),
|
||
})
|
||
}
|
||
index := s.Cursor.Y - 1
|
||
if index >= len(s.Rows) {
|
||
log.Printf("总行数 %d 比当前行 %d 小,可能存在解析错误 \n", len(s.Rows), s.Cursor.Y)
|
||
return s.Rows[len(s.Rows)-1]
|
||
}
|
||
return s.Rows[s.Cursor.Y-1]
|
||
}
|
||
|
||
const UnsupportedMsg = "Unsupported"
|