gitlab.com/apertussolutions/u-root@v7.0.0+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 }