github.com/andrewsun2898/u-root@v6.0.1-0.20200616011413-4b2895c1b815+incompatible/cmds/exp/tac/tac.go (about)

     1  // Copyright 2012-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // tac concatenates files and prints to stdout in reverse order,
     6  // file by file
     7  //
     8  // Synopsis:
     9  //     tac <file...>
    10  //
    11  // Description:
    12  //
    13  // Options:
    14  package main
    15  
    16  import (
    17  	"flag"
    18  	"io"
    19  	"log"
    20  	"os"
    21  	"sync"
    22  )
    23  
    24  const ReadSize int64 = 4096
    25  
    26  type ReadAtSeeker interface {
    27  	io.ReaderAt
    28  	io.Seeker
    29  }
    30  
    31  func tacOne(w io.Writer, r ReadAtSeeker) error {
    32  	var b [ReadSize]byte
    33  	// Get current EOF. While the file may be growing, there's
    34  	// only so much we can do.
    35  	loc, err := r.Seek(0, io.SeekEnd)
    36  	if err != nil {
    37  		return err
    38  	}
    39  	var wg sync.WaitGroup
    40  	wg.Add(1)
    41  	c := make(chan byte)
    42  	go func(r <-chan byte, w io.Writer) {
    43  		defer wg.Done()
    44  		line := string(<-r)
    45  		for c := range r {
    46  			if c == '\n' {
    47  				if _, err := w.Write([]byte(line)); err != nil {
    48  					log.Fatal(err)
    49  				}
    50  				line = ""
    51  			}
    52  			line = string(c) + line
    53  		}
    54  		if _, err := w.Write([]byte(line)); err != nil {
    55  			log.Fatal(err)
    56  		}
    57  	}(c, w)
    58  
    59  	for loc > 0 {
    60  		n := ReadSize
    61  		if loc < ReadSize {
    62  			n = loc
    63  		}
    64  
    65  		amt, err := r.ReadAt(b[:n], loc-int64(n))
    66  		if err != nil && err != io.EOF {
    67  			return err
    68  		}
    69  		loc -= int64(amt)
    70  		for i := range b[:amt] {
    71  			o := amt - i - 1
    72  			c <- b[o]
    73  		}
    74  	}
    75  	close(c)
    76  	wg.Wait()
    77  	return nil
    78  }
    79  
    80  func tac(w io.Writer, files []string) error {
    81  	for _, name := range files {
    82  		f, err := os.Open(name)
    83  		if err != nil {
    84  			return err
    85  		}
    86  		err = tacOne(w, f)
    87  		f.Close() // Don't defer, you might get EMFILE for no good reason.
    88  		if err != nil {
    89  			return err
    90  		}
    91  
    92  	}
    93  	return nil
    94  }
    95  
    96  func main() {
    97  	flag.Parse()
    98  
    99  	if flag.NArg() == 0 {
   100  		log.Fatalf("Sorry, can't reverse lines from stdin; can't seek")
   101  	}
   102  
   103  	if err := tac(os.Stdout, flag.Args()); err != nil {
   104  		log.Fatalf("tac: %v", err)
   105  	}
   106  }