2015-08-07 18:24:18 -04:00
|
|
|
// Package tailfile provides helper functions to read the nth lines of any
|
2015-07-25 04:35:07 -04:00
|
|
|
// ReadSeeker.
|
2018-02-05 16:05:59 -05:00
|
|
|
package tailfile // import "github.com/docker/docker/pkg/tailfile"
|
2014-06-03 07:09:33 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
2015-07-03 09:50:06 -04:00
|
|
|
"io"
|
2014-06-03 07:09:33 -04:00
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
const blockSize = 1024
|
|
|
|
|
|
|
|
var eol = []byte("\n")
|
|
|
|
|
2015-07-25 04:35:07 -04:00
|
|
|
// ErrNonPositiveLinesNumber is an error returned if the lines number was negative.
|
|
|
|
var ErrNonPositiveLinesNumber = errors.New("The number of lines to extract from the file must be positive")
|
|
|
|
|
2018-02-10 06:28:12 -05:00
|
|
|
//TailFile returns last n lines of reader f (could be a nil).
|
2015-07-03 09:50:06 -04:00
|
|
|
func TailFile(f io.ReadSeeker, n int) ([][]byte, error) {
|
2014-06-03 07:09:33 -04:00
|
|
|
if n <= 0 {
|
|
|
|
return nil, ErrNonPositiveLinesNumber
|
|
|
|
}
|
|
|
|
size, err := f.Seek(0, os.SEEK_END)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
block := -1
|
|
|
|
var data []byte
|
|
|
|
var cnt int
|
|
|
|
for {
|
|
|
|
var b []byte
|
|
|
|
step := int64(block * blockSize)
|
|
|
|
left := size + step // how many bytes to beginning
|
|
|
|
if left < 0 {
|
|
|
|
if _, err := f.Seek(0, os.SEEK_SET); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
b = make([]byte, blockSize+left)
|
|
|
|
if _, err := f.Read(b); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data = append(b, data...)
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
b = make([]byte, blockSize)
|
2016-11-09 22:16:47 -05:00
|
|
|
if _, err := f.Seek(left, os.SEEK_SET); err != nil {
|
2014-06-03 07:09:33 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if _, err := f.Read(b); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data = append(b, data...)
|
|
|
|
}
|
|
|
|
cnt += bytes.Count(b, eol)
|
|
|
|
if cnt > n {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
block--
|
|
|
|
}
|
|
|
|
lines := bytes.Split(data, eol)
|
|
|
|
if n < len(lines) {
|
|
|
|
return lines[len(lines)-n-1 : len(lines)-1], nil
|
|
|
|
}
|
|
|
|
return lines[:len(lines)-1], nil
|
|
|
|
}
|