github.com/gunjan5/docker@v1.8.2/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  }