| // Copyright 2014 The gocui Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package gocui |
| |
| import ( |
| "errors" |
| "strconv" |
| ) |
| |
| type escapeInterpreter struct { |
| state escapeState |
| curch rune |
| csiParam []string |
| curFgColor, curBgColor Attribute |
| defaultFgColor, defaultBgColor Attribute |
| } |
| |
| type escapeState int |
| |
| const ( |
| stateNone escapeState = iota |
| stateEscape |
| stateCSI |
| stateParams |
| ) |
| |
| var ( |
| errNotCSI = errors.New("Not a CSI escape sequence") |
| errCSINotANumber = errors.New("CSI escape sequence was expecting a number or a ;") |
| errCSIParseError = errors.New("CSI escape sequence parsing error") |
| errCSITooLong = errors.New("CSI escape sequence is too long") |
| ) |
| |
| // runes in case of error will output the non-parsed runes as a string. |
| func (ei *escapeInterpreter) runes() []rune { |
| switch ei.state { |
| case stateNone: |
| return []rune{0x1b} |
| case stateEscape: |
| return []rune{0x1b, ei.curch} |
| case stateCSI: |
| return []rune{0x1b, '[', ei.curch} |
| case stateParams: |
| ret := []rune{0x1b, '['} |
| for _, s := range ei.csiParam { |
| ret = append(ret, []rune(s)...) |
| ret = append(ret, ';') |
| } |
| return append(ret, ei.curch) |
| } |
| return nil |
| } |
| |
| // newEscapeInterpreter returns an escapeInterpreter that will be able to parse |
| // terminal escape sequences. |
| func newEscapeInterpreter() *escapeInterpreter { |
| ei := &escapeInterpreter{ |
| defaultFgColor: ColorWhite, |
| defaultBgColor: ColorBlack, |
| state: stateNone, |
| curFgColor: ColorWhite, |
| curBgColor: ColorBlack, |
| } |
| return ei |
| } |
| |
| // reset sets the escapeInterpreter in inital state. |
| func (ei *escapeInterpreter) reset() { |
| ei.state = stateNone |
| ei.curFgColor = ei.defaultFgColor |
| ei.curBgColor = ei.defaultBgColor |
| ei.csiParam = nil |
| } |
| |
| // paramToColor returns an attribute given a terminfo coloring. |
| func paramToColor(p int) Attribute { |
| switch p { |
| case 0: |
| return ColorBlack |
| case 1: |
| return ColorRed |
| case 2: |
| return ColorGreen |
| case 3: |
| return ColorYellow |
| case 4: |
| return ColorBlue |
| case 5: |
| return ColorMagenta |
| case 6: |
| return ColorCyan |
| case 7: |
| return ColorWhite |
| } |
| return ColorDefault |
| } |
| |
| // parseOne parses a rune. If isEscape is true, it means that the rune is part |
| // of an escape sequence, and as such should not be printed verbatim. Otherwise, |
| // it's not an escape sequence. |
| func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) { |
| // Sanity checks to make sure we're not parsing something totally bogus. |
| if len(ei.csiParam) > 20 { |
| return false, errCSITooLong |
| } |
| if len(ei.csiParam) > 0 && len(ei.csiParam[len(ei.csiParam)-1]) > 255 { |
| return false, errCSITooLong |
| } |
| ei.curch = ch |
| switch ei.state { |
| case stateNone: |
| if ch == 0x1b { |
| ei.state = stateEscape |
| return true, nil |
| } |
| return false, nil |
| case stateEscape: |
| if ch == '[' { |
| ei.state = stateCSI |
| return true, nil |
| } |
| return false, errNotCSI |
| case stateCSI: |
| if ch >= '0' && ch <= '9' { |
| ei.state = stateParams |
| ei.csiParam = append(ei.csiParam, string(ch)) |
| return true, nil |
| } |
| return false, errCSINotANumber |
| case stateParams: |
| switch { |
| case ch >= '0' && ch <= '9': |
| ei.csiParam[len(ei.csiParam)-1] += string(ch) |
| return true, nil |
| case ch == ';': |
| ei.csiParam = append(ei.csiParam, "") |
| return true, nil |
| case ch == 'm': |
| if len(ei.csiParam) < 1 { |
| return false, errCSIParseError |
| } |
| for _, param := range ei.csiParam { |
| p, err := strconv.Atoi(param) |
| if err != nil { |
| return false, errCSIParseError |
| } |
| switch { |
| case p >= 30 && p <= 37: |
| ei.curFgColor = paramToColor(p - 30) |
| case p >= 40 && p <= 47: |
| ei.curBgColor = paramToColor(p - 40) |
| case p == 1: |
| ei.curFgColor |= AttrBold |
| case p == 4: |
| ei.curFgColor |= AttrUnderline |
| case p == 7: |
| ei.curFgColor |= AttrReverse |
| case p == 0 || p == 39: |
| ei.curFgColor = ei.defaultFgColor |
| ei.curBgColor = ei.defaultBgColor |
| } |
| } |
| ei.state = stateNone |
| ei.csiParam = nil |
| return true, nil |
| } |
| } |
| return false, nil |
| } |