github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/core/grep/grep.go (about) 1 // Copyright 2012-2017 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 // grep searches file contents using regular expressions. 6 // 7 // Synopsis: 8 // grep [-vrlq] [FILE]... 9 // 10 // Options: 11 // -v: print only non-matching lines 12 // -r: recursive 13 // -l: list only files 14 // -q: don't print matches; exit on first match 15 package main 16 17 import ( 18 "bufio" 19 "flag" 20 "fmt" 21 "log" 22 "os" 23 "path/filepath" 24 "regexp" 25 "strings" 26 ) 27 28 type grepResult struct { 29 match bool 30 c *grepCommand 31 line *string 32 } 33 34 type grepCommand struct { 35 name string 36 *os.File 37 } 38 39 type oneGrep struct { 40 c chan *grepResult 41 } 42 43 var ( 44 match = flag.Bool("v", true, "Print only non-matching lines") 45 recursive = flag.Bool("r", false, "recursive") 46 noshowmatch = flag.Bool("l", false, "list only files") 47 quiet = flag.Bool("q", false, "Don't print matches; exit on first match") 48 count = flag.Bool("c", false, "Just show counts") 49 caseinsensitive = flag.Bool("i", false, "case-insensitive matching") 50 showname bool 51 allGrep = make(chan *oneGrep) 52 nGrep int 53 matchCount int 54 ) 55 56 // grep reads data from the os.File embedded in grepCommand. 57 // It creates a chan of grepResults and pushes a pointer to it into allGrep. 58 // It matches each line against the re and pushes the matching result 59 // into the chan. 60 // Bug: this chan should be created by the caller and passed in 61 // to preserve file name order. Oops. 62 // If we are only looking for a match, we exit as soon as the condition is met. 63 // "match" means result of re.Match == match flag. 64 func grep(f *grepCommand, re *regexp.Regexp) { 65 nGrep++ 66 r := bufio.NewReader(f) 67 res := make(chan *grepResult, 1) 68 allGrep <- &oneGrep{res} 69 for { 70 if i, err := r.ReadString('\n'); err == nil { 71 m := re.Match([]byte(i)) 72 if m == *match { 73 res <- &grepResult{re.Match([]byte(i)), f, &i} 74 if *noshowmatch { 75 break 76 } 77 } 78 } else { 79 break 80 } 81 } 82 close(res) 83 f.Close() 84 } 85 86 func printmatch(r *grepResult) { 87 var prefix string 88 if r.match == *match { 89 matchCount++ 90 } 91 if *count { 92 return 93 } 94 if showname { 95 fmt.Printf("%v", r.c.name) 96 prefix = ":" 97 } 98 if *noshowmatch { 99 return 100 } 101 if r.match == *match { 102 fmt.Printf("%v%v", prefix, *r.line) 103 } 104 } 105 106 func main() { 107 r := ".*" 108 flag.Parse() 109 a := flag.Args() 110 if len(a) > 0 { 111 r = a[0] 112 } 113 if *caseinsensitive && !strings.HasPrefix(r, "(?i)") { 114 r = "(?i)" + r 115 } 116 re := regexp.MustCompile(r) 117 // very special case, just stdin ... 118 if len(a) < 2 { 119 go grep(&grepCommand{"<stdin>", os.Stdin}, re) 120 } else { 121 showname = len(a[1:]) > 1 122 // generate a chan of file names, bounded by the size of the chan. This in turn 123 // throttles the opens. 124 treenames := make(chan string, 128) 125 go func() { 126 for _, v := range a[1:] { 127 // we could parallelize the open part but people might want 128 // things to be in order. I don't care but who knows. 129 // just ignore the errors. If there is not a single one that works, 130 // then all the sizes will be 0 and we'll just fall through. 131 filepath.Walk(v, func(name string, fi os.FileInfo, err error) error { 132 if err != nil { 133 // This is non-fatal because grep searches through 134 // all the files it has access to. 135 log.Print(err) 136 return nil 137 } 138 if fi.IsDir() && !*recursive { 139 fmt.Fprintf(os.Stderr, "grep: %v: Is a directory\n", name) 140 return filepath.SkipDir 141 } 142 if err != nil { 143 fmt.Fprintf(os.Stderr, "%v: %v\n", name, err) 144 return err 145 } 146 treenames <- name 147 return nil 148 }) 149 } 150 close(treenames) 151 }() 152 153 files := make(chan *grepCommand) 154 // convert the file names to a stream of os.File 155 go func() { 156 for i := range treenames { 157 fp, err := os.Open(i) 158 if err != nil { 159 fmt.Fprintf(os.Stderr, "can't open %s: %v\n", i, err) 160 continue 161 } 162 files <- &grepCommand{i, fp} 163 } 164 close(files) 165 }() 166 // now kick off the greps 167 // bug: file name order is not preserved here. Darn. 168 169 for f := range files { 170 go grep(f, re) 171 } 172 } 173 174 for c := range allGrep { 175 for r := range c.c { 176 // exit on first match. 177 if *quiet { 178 os.Exit(0) 179 } 180 printmatch(r) 181 } 182 nGrep-- 183 if nGrep == 0 { 184 break 185 } 186 } 187 if *quiet { 188 os.Exit(1) 189 } 190 if *count { 191 fmt.Printf("%d\n", matchCount) 192 } 193 }