github.com/nf/docker@v1.8.1/pkg/ansiescape/split.go (about) 1 package ansiescape 2 3 import "bytes" 4 5 // dropCR drops a leading or terminal \r from the data. 6 func dropCR(data []byte) []byte { 7 if len(data) > 0 && data[len(data)-1] == '\r' { 8 data = data[0 : len(data)-1] 9 } 10 if len(data) > 0 && data[0] == '\r' { 11 data = data[1:] 12 } 13 return data 14 } 15 16 // escapeSequenceLength calculates the length of an ANSI escape sequence 17 // If there is not enough characters to match a sequence, -1 is returned, 18 // if there is no valid sequence 0 is returned, otherwise the number 19 // of bytes in the sequence is returned. Only returns length for 20 // line moving sequences. 21 func escapeSequenceLength(data []byte) int { 22 next := 0 23 if len(data) <= next { 24 return -1 25 } 26 if data[next] != '[' { 27 return 0 28 } 29 for { 30 next = next + 1 31 if len(data) <= next { 32 return -1 33 } 34 if (data[next] > '9' || data[next] < '0') && data[next] != ';' { 35 break 36 } 37 } 38 if len(data) <= next { 39 return -1 40 } 41 // Only match line moving codes 42 switch data[next] { 43 case 'A', 'B', 'E', 'F', 'H', 'h': 44 return next + 1 45 } 46 47 return 0 48 } 49 50 // ScanANSILines is a scanner function which splits the 51 // input based on ANSI escape codes and new lines. 52 func ScanANSILines(data []byte, atEOF bool) (advance int, token []byte, err error) { 53 if atEOF && len(data) == 0 { 54 return 0, nil, nil 55 } 56 57 // Look for line moving escape sequence 58 if i := bytes.IndexByte(data, '\x1b'); i >= 0 { 59 last := 0 60 for i >= 0 { 61 last = last + i 62 63 // get length of ANSI escape sequence 64 sl := escapeSequenceLength(data[last+1:]) 65 if sl == -1 { 66 return 0, nil, nil 67 } 68 if sl == 0 { 69 // If no relevant sequence was found, skip 70 last = last + 1 71 i = bytes.IndexByte(data[last:], '\x1b') 72 continue 73 } 74 75 return last + 1 + sl, dropCR(data[0:(last)]), nil 76 } 77 } 78 if i := bytes.IndexByte(data, '\n'); i >= 0 { 79 // No escape sequence, check for new line 80 return i + 1, dropCR(data[0:i]), nil 81 } 82 83 // If we're at EOF, we have a final, non-terminated line. Return it. 84 if atEOF { 85 return len(data), dropCR(data), nil 86 } 87 // Request more data. 88 return 0, nil, nil 89 }