package door import ( "log" "regexp" "strconv" "strings" "syscall" ) var writerChannel chan string func Writer(handle int) { for output := range writerChannel { buffer := []byte(output) n, err := syscall.Write(handle, buffer) if (err != nil) || (n != len(buffer)) { close(writerChannel) break } } } var FindANSIColor *regexp.Regexp func init() { FindANSIColor = regexp.MustCompile("\x1b\\[([0-9;]*)m") } /* reading from a closed channel is easy to detect. res, ok := <-channel ok == false writing to a closed channel is a panic. */ func find_ansicolor(text string) [][]int { var color_codes [][]int // word, _ := regexp.Compile("\x1b\\[([0-9;]+)m") colors := FindANSIColor.FindAllStringIndex(text, -1) // regexp seems to be ignoring the capture groups. // colors := word.FindAllSubmatchIndex([]byte(text), len(text)) for _, pos := range colors { txt := text[pos[0]+2 : pos[1]-1] if txt == "" { txt = "0" } // log.Printf("Text: [%s]\n", txt) codes := strings.Split(txt, ";") // log.Printf("Codes: [%#v]\n", codes) code := make([]int, len(codes)) for idx, c := range codes { var err error code[idx], err = strconv.Atoi(c) if err != nil { log.Printf("Atoi: %#v [%s]\n", err, c) } color_codes = append(color_codes, code) } } return color_codes } type ANSIColorParts struct { Bold bool Blink bool FG int BG int } func ParseColorArray(colorarray []int) ANSIColorParts { var acp ANSIColorParts acp.FG = -1 acp.BG = -1 for _, c := range colorarray { switch c { case 0: acp.FG = 7 acp.BG = 0 case 1: acp.Bold = true case 5: acp.Blink = true case 30, 31, 32, 33, 34, 35, 36, 37: acp.FG = c - 30 case 40, 41, 42, 43, 44, 45, 46, 47: acp.BG = c - 40 } } return acp } func (d *Door) ParseLastColor(output string) { // use the last color information + whatever is in the string to // track the last color set updated := ParseColorArray(d.LastColor) colors := find_ansicolor(output) for _, codes := range colors { if codes[0] == 0 { updated = ParseColorArray(codes) } else { newCode := ParseColorArray(codes) if newCode.Bold { updated.Bold = true } if newCode.Blink { updated.Blink = true } if (newCode.FG != -1) && (newCode.FG != updated.FG) { updated.FG = newCode.FG } if (newCode.BG != -1) && (newCode.BG != updated.BG) { updated.BG = newCode.BG } } } d.LastColor = make([]int, 1) d.LastColor[0] = 0 if updated.Blink { d.LastColor = append(d.LastColor, 5) } if updated.Bold { d.LastColor = append(d.LastColor, 1) } if updated.FG != -1 { d.LastColor = append(d.LastColor, updated.FG+30) } if updated.BG != -1 { d.LastColor = append(d.LastColor, updated.BG+40) } } // Write string to client. func (d *Door) Write(output string) { if d.Disconnected { return } defer func() { if r := recover(); r != nil { log.Println("Write error/HANGUP.") d.Disconnected = true } }() if strings.HasSuffix(output, RestorePos) { output += Color(d.LastColor...) } else { d.ParseLastColor(output) } /* temp := strings.Replace(output, "\x1b", "^[", -1) log.Printf("Parse: [%s]\n", temp) */ writerChannel <- output /* buffer := []byte(output) n, err := syscall.Write(d.WRITEFD, buffer) if err != nil { fmt.Println("Write error/HANGUP?", n) d.Disconnected = true } // No, this isn't it. The # of bytes in buffer == bytes written. if n != len(buffer) { fmt.Printf("Write fail: %d != %d\n", len(buffer), n) } */ }