diff --git a/commands/grep.go b/commands/grep.go index 15e292b..9a8eaee 100644 --- a/commands/grep.go +++ b/commands/grep.go @@ -12,39 +12,62 @@ import ( // Grep implements the POSIX grep command. // -// https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/ +// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html func Grep(virtOS vos.VOS) int { cmd := &SimpleCommand{ - Use: "grep [-iv] PATTERN [FILE]...", + Use: "grep [-E|-F] [-iv] PATTERN [FILE]...", Short: "Search files for text matching a pattern.", } invert := cmd.Flags().Bool('v', "Select lines not matching any of the specified patterns.") ignoreCase := cmd.Flags().Bool('i', "Perform pattern matching in searches without regard to case.") showLineNumbers := cmd.Flags().Bool('n', "Show line numbers.") + fixedStrings := cmd.Flags().Bool('F', "Treat patterns as strings rather than regular expressions.") + extendedRegex := cmd.Flags().Bool('E', "Use extended regular expressions.") + + var patternList string + patternListFlag := cmd.Flags().Flag(&patternList, 'e', "Pattern to be used during the search for input.") return cmd.Run(virtOS, func() int { args := cmd.Flags().Args() - if len(args) == 0 { + // NOTE: Officially, the PATTERN argument supports multiple patterns delimited by newlines. + // It's a very rare case so we'll ignore it here. + var pattern string + switch { + case patternListFlag.Seen(): + pattern = patternList + + case len(args) == 0: cmd.LogProgramError(virtOS, errors.New("missing argument PATTERN")) return 1 + + default: + pattern = args[0] + args = args[1:] + } + + switch { + case *extendedRegex || virtOS.Args()[0] == "egrep": + // Treat the pattern as regex. + case *fixedStrings: + pattern = regexp.QuoteMeta(pattern) + default: + // TODO: grep should treat the normal mode as "basic" regular expressions + // rather than extended: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03 } - // NOTE: Officially, the PATTERN argument supports multiple patterns delimited by newlines. - // It's a very rare case so we'll ignore it here. - pattern := args[0] if *ignoreCase { pattern = "(?i)" + pattern } + regex, err := regexp.Compile(pattern) if err != nil { cmd.LogProgramError(virtOS, err) return 2 } - files := args[1:] - showFileName := len(files) > 1 - return cmd.RunEachFileOrStdin(virtOS, files, func(name string, fd io.Reader) error { + showFileName := len(args) > 1 + return cmd.RunEachFileOrStdin(virtOS, args, func(name string, fd io.Reader) error { w := virtOS.Stdout() scanner := bufio.NewScanner(fd) @@ -77,4 +100,5 @@ var _ vos.ProcessFunc = Grep func init() { addBinCmd("grep", Grep) + addBinCmd("egrep", Grep) }