seems fairly simple to me, but a few comments

- Could mflag be removed and replaced by overloading mcount = -1
  as the no -m condition

- What do other systems do with "-m 0"

> +                     errno = 0;
> +                     mlimit = mcount = strtonum(optarg, 1, LLONG_MAX,
- you should not touch errno like that, this isn't the insane strtol
  interface

> +                             errx(2, "mcount %s", errstr);
-  that is a poor error message


> On Thu, Dec 07, 2017 at 05:36:59PM +0200, Paul Irofti wrote:
> > Hi,
> > 
> > The -m, or --max-count, option allows the user to stop grep(1)
> > processing after a given number of matches are found.
> > If you use constructs such as
> > 
> >   $ grep foo foo.txt | head -n1
> > 
> > you will want to use the much faster version
> > 
> >   $ grep -m1 foo foo.txt
> > 
> > This option already exists in at least Linux, FreeBSD and NetBSD.
> > 
> > For example, I need grep -m for vimtex (see Issue #1018 on GitHub[0]).
> > But, if I had known that this exists, I would have used it in the
> > past with other scripts.
> > 
> > The following diff is adapted from FreeBSD and I tested it with vimtex
> > and a few hand-crafted examples. We could probably do some tricks with
> > mcount and mlimit, but I do not think it is worth it. OK?
> > 
> > [0] -- https://github.com/lervag/vimtex/issues/1018
> 
> Here is a new diff that corrects usage to show that -m expects an
> argument (as suggested by deraadt@) and also adds the manpage bits.
> 
> Index: grep.1
> ===================================================================
> RCS file: /cvs/src/usr.bin/grep/grep.1,v
> retrieving revision 1.43
> diff -u -p -u -p -r1.43 grep.1
> --- grep.1    13 Jan 2015 04:45:34 -0000      1.43
> +++ grep.1    7 Dec 2017 18:00:37 -0000
> @@ -44,6 +44,7 @@
>  .Op Fl C Ns Op Ar num
>  .Op Fl e Ar pattern
>  .Op Fl f Ar file
> +.Op Fl m Ar num
>  .Op Fl -binary-files Ns = Ns Ar value
>  .Op Fl -context Ns Op = Ns Ar num
>  .Op Fl -line-buffered
> @@ -216,6 +217,10 @@ Pathnames are listed once per file searc
>  If the standard input is searched, the string
>  .Dq (standard input)
>  is written.
> +.It Fl m Ar num
> +Stop after
> +.Ar num
> +matches.
>  .It Fl n
>  Each output line is preceded by its relative line number in the file,
>  starting at line 1.
> Index: grep.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/grep/grep.c,v
> retrieving revision 1.55
> diff -u -p -u -p -r1.55 grep.c
> --- grep.c    28 Nov 2015 01:17:12 -0000      1.55
> +++ grep.c    7 Dec 2017 18:00:38 -0000
> @@ -71,6 +71,9 @@ int  cflag;         /* -c: only show a count of
>  int   hflag;         /* -h: don't print filename headers */
>  int   iflag;         /* -i: ignore case */
>  int   lflag;         /* -l: only show names of files with matches */
> +int   mflag;         /* -m x: stop reading the files after x matches */
> +long long mcount;    /* count for -m */
> +long long mlimit;    /* requested value for -m */
>  int   nflag;         /* -n: show line numbers in front of matching lines */
>  int   oflag;         /* -o: print each match */
>  int   qflag;         /* -q: quiet mode (don't output anything) */
> @@ -111,15 +114,16 @@ usage(void)
>  #else
>           "usage: %s [-abcEFGHhIiLlnoqRsUVvwxZ] [-A num] [-B num] [-C[num]]\n"
>  #endif
> -         "\t[-e pattern] [-f file] [--binary-files=value] 
> [--context[=num]]\n"
> -         "\t[--line-buffered] [pattern] [file ...]\n", __progname);
> +         "\t[-e pattern] [-f file] [-m num] [--binary-files=value]\n"
> +         "\t[--context[=num]] [--line-buffered] [pattern] [file ...]\n",
> +         __progname);
>       exit(2);
>  }
>  
>  #ifdef NOZ
> -static const char optstr[] = "0123456789A:B:CEFGHILRUVabce:f:hilnoqrsuvwxy";
> +static const char optstr[] = 
> "0123456789A:B:CEFGHILRUVabce:f:hilm:noqrsuvwxy";
>  #else
> -static const char optstr[] = "0123456789A:B:CEFGHILRUVZabce:f:hilnoqrsuvwxy";
> +static const char optstr[] = 
> "0123456789A:B:CEFGHILRUVZabce:f:hilm:noqrsuvwxy";
>  #endif
>  
>  static const struct option long_options[] =
> @@ -147,6 +151,7 @@ static const struct option long_options[
>       {"ignore-case",         no_argument,            NULL, 'i'},
>       {"files-without-match", no_argument,            NULL, 'L'},
>       {"files-with-matches",  no_argument,            NULL, 'l'},
> +     {"max-count",           required_argument,      NULL, 'm'},
>       {"line-number",         no_argument,            NULL, 'n'},
>       {"quiet",               no_argument,            NULL, 'q'},
>       {"silent",              no_argument,            NULL, 'q'},
> @@ -375,6 +380,14 @@ main(int argc, char *argv[])
>               case 'l':
>                       Lflag = 0;
>                       lflag = qflag = 1;
> +                     break;
> +             case 'm':
> +                     mflag = 1;
> +                     errno = 0;
> +                     mlimit = mcount = strtonum(optarg, 1, LLONG_MAX,
> +                        &errstr);
> +                     if (errstr != NULL)
> +                             errx(2, "mcount %s", errstr);
>                       break;
>               case 'n':
>                       nflag = 1;
> Index: grep.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/grep/grep.h,v
> retrieving revision 1.24
> diff -u -p -u -p -r1.24 grep.h
> --- grep.h    14 Dec 2015 20:02:07 -0000      1.24
> +++ grep.h    7 Dec 2017 18:00:38 -0000
> @@ -66,14 +66,17 @@ extern int         cflags, eflags;
>  /* Command line flags */
>  extern int    Aflag, Bflag, Eflag, Fflag, Hflag, Lflag,
>                Rflag, Zflag,
> -              bflag, cflag, hflag, iflag, lflag, nflag, oflag, qflag, sflag,
> -              vflag, wflag, xflag;
> +              bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, qflag,
> +              sflag, vflag, wflag, xflag;
>  extern int    binbehave;
>  
>  extern int    first, matchall, patterns, tail, file_err;
>  extern char    **pattern;
>  extern fastgrep_t *fg_pattern;
>  extern regex_t       *r_pattern;
> +
> +/* For -m max-count */
> +extern long long mcount, mlimit;
>  
>  /* For regex errors  */
>  #define RE_ERROR_BUF 512
> Index: util.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/grep/util.c,v
> retrieving revision 1.57
> diff -u -p -u -p -r1.57 util.c
> --- util.c    3 Apr 2017 16:18:35 -0000       1.57
> +++ util.c    7 Dec 2017 18:00:38 -0000
> @@ -97,6 +97,8 @@ procfile(char *fn)
>       file_t *f;
>       int c, t, z, nottext;
>  
> +     mcount = mlimit;
> +
>       if (fn == NULL) {
>               fn = "(standard input)";
>               f = grep_fdopen(STDIN_FILENO, "r");
> @@ -140,6 +142,8 @@ procfile(char *fn)
>                       linesqueued++;
>               }
>               c += t;
> +             if (mflag && mcount <= 0)
> +                     break;
>       }
>       if (Bflag > 0)
>               clearqueue();
> @@ -223,6 +227,10 @@ redo:
>  print:
>       if (vflag)
>               c = !c;
> +
> +     /* Count the matches if we have a match limit */
> +     if (mflag)
> +             mcount -= c;
>  
>       if (c && binbehave == BIN_FILE_BIN && nottext)
>               return c; /* Binary file */

Reply via email to