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