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