github.com/karrick/gorill@v1.10.3/filesReader.go (about)

     1  package gorill
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  )
     7  
     8  // FilesReader is an io.ReadCloser that can be used to read over the contents of
     9  // all of the files specified by pathnames. It only opens a single file handle
    10  // at a time. When reading from the currently open file handle returns io.EOF,
    11  // it closes that file handle, and the next Read will cause the following file
    12  // in the series to be opened and read from.
    13  type FilesReader struct {
    14  	// Pathnames is a list of remaining files to read. It is kept up to date
    15  	// where when a file is opened, its name is removed from the head of the
    16  	// list.
    17  	Pathnames []string
    18  
    19  	// fh is the currently open file handle from which Read operations will take
    20  	// place. It can be nil, in which case Read will attempt to open the next
    21  	// file in the series and read from it.
    22  	fh io.ReadCloser
    23  }
    24  
    25  // Close forgets the list of remaining files in the series, then closes the
    26  // currently open file handle, returning any error from the operating system.
    27  func (fr *FilesReader) Close() error {
    28  	fr.Pathnames = nil
    29  	if fr.fh == nil {
    30  		return nil
    31  	}
    32  	err := fr.fh.Close()
    33  	fr.fh = nil
    34  	return err
    35  }
    36  
    37  // Read reads up to len(p) bytes into p. It returns the number of bytes read (0
    38  // <= n <= len(p) and any error encountered.
    39  func (fr *FilesReader) Read(p []byte) (int, error) {
    40  	if fr.fh == nil {
    41  		if err := fr.next(); err != nil {
    42  			return 0, err
    43  		}
    44  	}
    45  	for {
    46  		nr, err := fr.fh.Read(p)
    47  		if err == io.EOF {
    48  			if err = fr.fh.Close(); err != nil {
    49  				return nr, err
    50  			}
    51  			if err = fr.next(); err != nil {
    52  				return nr, err
    53  			}
    54  			if nr == 0 {
    55  				continue
    56  			}
    57  		}
    58  		return nr, err
    59  	}
    60  }
    61  
    62  // Next closes the currently open file handle and opens the next file in the
    63  // series. If there are no files left it returns io.EOF. It can be used to skip
    64  // the remaining contents of the currently open file. Additional Read operations
    65  // will be invoked against the following file in the series, if non empty.
    66  func (fr *FilesReader) Next() error {
    67  	if fr.fh != nil {
    68  		err := fr.fh.Close()
    69  		fr.fh = nil
    70  		if err != nil {
    71  			return err
    72  		}
    73  	}
    74  	return fr.next()
    75  }
    76  
    77  // next opens the next file in the series. If there are no files left it returns
    78  // io.EOF.
    79  func (fr *FilesReader) next() error {
    80  	if len(fr.Pathnames) == 0 {
    81  		return io.EOF
    82  	}
    83  
    84  	// The special string "-" reads from standard input, until EOF, but will not
    85  	// close standard input on EOF.
    86  	switch fr.Pathnames[0] {
    87  	case "-":
    88  		fr.fh = NopCloseReader(os.Stdin)
    89  	default:
    90  		fh, err := os.Open(fr.Pathnames[0])
    91  		if err != nil {
    92  			return err
    93  		}
    94  		fr.fh = fh
    95  	}
    96  
    97  	fr.Pathnames = fr.Pathnames[1:]
    98  	return nil
    99  }