Skip to content

Commit ed3223c

Browse files
authored
Merge pull request #386 from walles/johan/open-dev-fds
Handle paging non-seekable files in `/dev`
2 parents 261be14 + e142632 commit ed3223c

File tree

3 files changed

+87
-12
lines changed

3 files changed

+87
-12
lines changed

internal/reader/reader.go

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ func (reader *ReaderImpl) consumeLinesFromStream(stream io.Reader) {
259259
// Preallocating the line pool and the lines slice improves large file
260260
// reading performance by 10%.
261261
linePool := linePool{}
262-
if reader.FileName != nil && reader.GetLineCount() == 0 {
262+
if reader.FileName != nil && reader.GetLineCount() == 0 && isSeekableFile(reader.FileName) {
263263
lineCount, err := countLines(*reader.FileName)
264264
if err != nil {
265265
log.Warn("Failed to count lines in file: ", err)
@@ -378,6 +378,11 @@ func (reader *ReaderImpl) tailFile() error {
378378
return nil
379379
}
380380

381+
if !isSeekableFile(fileName) {
382+
log.Debugf("Giving up on tailing, %s is not seekable", *fileName)
383+
return nil
384+
}
385+
381386
log.Debugf("Tailing file %s", *fileName)
382387

383388
for {
@@ -446,6 +451,28 @@ func (reader *ReaderImpl) tailFile() error {
446451
}
447452
}
448453

454+
func isSeekableFile(fileName *string) bool {
455+
if fileName == nil {
456+
return false
457+
}
458+
459+
file, err := os.Open(*fileName)
460+
if err != nil {
461+
return false
462+
}
463+
464+
defer func() {
465+
err := file.Close()
466+
if err != nil {
467+
log.Debugf("Failed to close %s while checking seekability: %s", *fileName, err)
468+
}
469+
}()
470+
471+
_, err = file.Seek(0, io.SeekCurrent)
472+
473+
return err == nil
474+
}
475+
449476
// NewFromStream creates a new stream reader
450477
//
451478
// The display name can be an empty string ("").
@@ -568,30 +595,39 @@ func NewFromTextForTesting(name string, text string) *ReaderImpl {
568595
return returnMe
569596
}
570597

571-
// Duplicate of moor/moor.go:TryOpen
572598
func TryOpen(filename string) error {
573599
// Try opening the file
574600
tryMe, err := os.Open(filename)
575601
if err != nil {
576602
return err
577603
}
578604

579-
// Try reading a byte
580-
buffer := make([]byte, 1)
581-
_, err = tryMe.Read(buffer)
605+
fileInfo, err := tryMe.Stat()
606+
if err != nil {
607+
closeErr := tryMe.Close()
608+
if closeErr != nil {
609+
return fmt.Errorf("failed to close %s after stat error: %w", filename, closeErr)
610+
}
611+
612+
return err
613+
}
614+
615+
if fileInfo.IsDir() {
616+
closeErr := tryMe.Close()
617+
if closeErr != nil {
618+
return fmt.Errorf("failed to close directory %s: %w", filename, closeErr)
619+
}
582620

583-
if err != nil && err.Error() == "EOF" {
584-
// Empty file, this is fine
585-
err = nil
621+
return fmt.Errorf("%s is a directory", filename)
586622
}
587623

588624
closeErr := tryMe.Close()
589-
if err == nil && closeErr != nil {
625+
if closeErr != nil {
590626
// Everything worked up until Close(), report the Close() error
591627
return closeErr
592628
}
593629

594-
return err
630+
return nil
595631
}
596632

597633
// From: https://stackoverflow.com/a/52153000/473672

internal/reader/reader_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,38 @@ func TestReadStreamDoneYesHighlighting(t *testing.T) {
311311
assert.NilError(t, testMe.Wait())
312312
}
313313

314+
func TestReadFromDevFd(t *testing.T) {
315+
if runtime.GOOS == "windows" {
316+
t.Skip("/dev/fd is not available on Windows")
317+
318+
return
319+
}
320+
321+
readPipe, writePipe, err := os.Pipe()
322+
assert.NilError(t, err)
323+
defer readPipe.Close() //nolint:errcheck
324+
325+
_, err = writePipe.WriteString("test\n")
326+
assert.NilError(t, err)
327+
assert.NilError(t, writePipe.Close())
328+
329+
fileName := "/dev/fd/" + strconv.Itoa(int(readPipe.Fd()))
330+
testMe, err := NewFromFilename(fileName, formatters.TTY, ReaderOptions{Style: styles.Get("native")})
331+
assert.NilError(t, err)
332+
assert.NilError(t, testMe.Wait())
333+
334+
lines := testMe.GetLines(linemetadata.Index{}, 10)
335+
assert.Equal(t, len(lines.Lines), 1)
336+
assert.Equal(t, lines.Lines[0].Plain(), "test")
337+
}
338+
339+
func TestTryOpenDirectory(t *testing.T) {
340+
tempDir := t.TempDir()
341+
342+
err := TryOpen(tempDir)
343+
assert.Assert(t, err != nil, "TryOpen should fail on directories")
344+
}
345+
314346
func TestReadTextDone(t *testing.T) {
315347
testMe := NewFromTextForTesting("", "Johan")
316348

internal/reader/zopen.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ func ZOpen(filename string) (io.ReadCloser, string, error) {
2626
return nil, "", err
2727
}
2828

29+
_, err = file.Seek(0, 0)
30+
if err != nil {
31+
// File is not seekable, so we can't probe its contents.
32+
// https://github.com/walles/moor/issues/385
33+
return file, filename, nil
34+
}
35+
2936
// Read the first 6 bytes to determine the compression type
3037
firstBytes := make([]byte, 6)
3138
_, err = file.Read(firstBytes)
@@ -54,8 +61,8 @@ func ZOpen(filename string) (io.ReadCloser, string, error) {
5461
newName := strings.TrimSuffix(filename, ".gz")
5562

5663
// Ref: https://github.com/walles/moor/issues/194
57-
if strings.HasSuffix(newName, ".tgz") {
58-
newName = strings.TrimSuffix(newName, ".tgz") + ".tar"
64+
if before, ok := strings.CutSuffix(newName, ".tgz"); ok {
65+
newName = before + ".tar"
5966
}
6067

6168
return reader, newName, err

0 commit comments

Comments
 (0)