diff --git a/core/filex/file.go b/core/filex/file.go index e82fd8c86a57..904accf98494 100644 --- a/core/filex/file.go +++ b/core/filex/file.go @@ -35,6 +35,7 @@ func firstLine(file *os.File) (string, error) { for { buf := make([]byte, bufSize) n, err := file.ReadAt(buf, offset) + if err != nil && err != io.EOF { return "", err } @@ -45,6 +46,10 @@ func firstLine(file *os.File) (string, error) { } } + if err == io.EOF { + return string(append(first, buf[:n]...)), nil + } + first = append(first, buf[:n]...) offset += bufSize } @@ -56,25 +61,34 @@ func lastLine(filename string, file *os.File) (string, error) { return "", err } + bf := int64(bufSize) var last []byte offset := info.Size() for { - offset -= bufSize - if offset < 0 { + if offset < bufSize { + bf = offset offset = 0 + } else { + offset -= bf } - buf := make([]byte, bufSize) + + buf := make([]byte, bf) n, err := file.ReadAt(buf, offset) if err != nil && err != io.EOF { return "", err } + if n == 0 { + return "", nil + } + if buf[n-1] == '\n' { buf = buf[:n-1] n-- } else { buf = buf[:n] } + for n--; n >= 0; n-- { if buf[n] == '\n' { return string(append(buf[n+1:], last...)), nil @@ -82,5 +96,9 @@ func lastLine(filename string, file *os.File) (string, error) { } last = append(buf, last...) + + if offset == 0 { + return string(last), nil + } } } diff --git a/core/filex/file_test.go b/core/filex/file_test.go index d79e06a89150..f0660d7e6c9a 100644 --- a/core/filex/file_test.go +++ b/core/filex/file_test.go @@ -52,6 +52,7 @@ last line` second line last line ` + emptyContent = `` ) func TestFirstLine(t *testing.T) { @@ -79,6 +80,26 @@ func TestFirstLineError(t *testing.T) { assert.Error(t, err) } +func TestFirstLineEmptyFile(t *testing.T) { + filename, err := fs.TempFilenameWithText(emptyContent) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := FirstLine(filename) + assert.Nil(t, err) + assert.Equal(t, "", val) +} + +func TestFirstLineWithoutNewline(t *testing.T) { + filename, err := fs.TempFilenameWithText(longLine) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := FirstLine(filename) + assert.Nil(t, err) + assert.Equal(t, longLine, val) +} + func TestLastLine(t *testing.T) { filename, err := fs.TempFilenameWithText(text) assert.Nil(t, err) @@ -99,6 +120,16 @@ func TestLastLineWithLastNewline(t *testing.T) { assert.Equal(t, longLine, val) } +func TestLastLineWithoutLastNewline(t *testing.T) { + filename, err := fs.TempFilenameWithText(longLine) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := LastLine(filename) + assert.Nil(t, err) + assert.Equal(t, longLine, val) +} + func TestLastLineShort(t *testing.T) { filename, err := fs.TempFilenameWithText(shortText) assert.Nil(t, err) @@ -123,3 +154,13 @@ func TestLastLineError(t *testing.T) { _, err := LastLine("/tmp/does-not-exist") assert.Error(t, err) } + +func TestLastLineEmptyFile(t *testing.T) { + filename, err := fs.TempFilenameWithText(emptyContent) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := LastLine(filename) + assert.Nil(t, err) + assert.Equal(t, "", val) +}