Add support for the non-standard grep -m extension.
authorpirofti <pirofti@openbsd.org>
Sat, 9 Dec 2017 18:38:37 +0000 (18:38 +0000)
committerpirofti <pirofti@openbsd.org>
Sat, 9 Dec 2017 18:38:37 +0000 (18:38 +0000)
grep -m num stops after a maximum of num matches are found.
We support -m0 to match GNU behaviour, but we do not allow negative
numbers.

Manpage help from jmc@, OK deraadt@.

usr.bin/grep/grep.1
usr.bin/grep/grep.c
usr.bin/grep/grep.h
usr.bin/grep/util.c

index 6e8d767..44fcf2c 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: grep.1,v 1.43 2015/01/13 04:45:34 daniel Exp $
+.\"    $OpenBSD: grep.1,v 1.44 2017/12/09 18:38:37 pirofti Exp $
 .\" Copyright (c) 1980, 1990, 1993
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
@@ -28,7 +28,7 @@
 .\"
 .\"    @(#)grep.1      8.3 (Berkeley) 4/18/94
 .\"
-.Dd $Mdocdate: January 13 2015 $
+.Dd $Mdocdate: December 9 2017 $
 .Dt GREP 1
 .Os
 .Sh NAME
@@ -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 searched.
 If the standard input is searched, the string
 .Dq (standard input)
 is written.
+.It Fl m Ar num, Fl Fl max-count Ns = Ns 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.
@@ -354,7 +359,7 @@ utility is compliant with the
 specification.
 .Pp
 The flags
-.Op Fl AaBbCGHhILoRUVwZ
+.Op Fl AaBbCGHhILmoRUVwZ
 are extensions to that specification, and the behaviour of the
 .Fl f
 flag when used with an empty pattern file is left undefined.
index 907246c..401ed50 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: grep.c,v 1.55 2015/11/28 01:17:12 gsoares Exp $       */
+/*     $OpenBSD: grep.c,v 1.56 2017/12/09 18:38:37 pirofti Exp $       */
 
 /*-
  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
@@ -71,6 +71,9 @@ int    cflag;         /* -c: only show a count of matching lines */
 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'},
@@ -376,6 +381,14 @@ main(int argc, char *argv[])
                        Lflag = 0;
                        lflag = qflag = 1;
                        break;
+               case 'm':
+                       mflag = 1;
+                       mlimit = mcount = strtonum(optarg, 0, LLONG_MAX,
+                          &errstr);
+                       if (errstr != NULL)
+                               errx(2, "invalid max-count %s: %s",
+                                   optarg, errstr);
+                       break;
                case 'n':
                        nflag = 1;
                        break;
index 21b1cab..bbf7f8c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: grep.h,v 1.24 2015/12/14 20:02:07 mmcc Exp $  */
+/*     $OpenBSD: grep.h,v 1.25 2017/12/09 18:38:37 pirofti Exp $       */
 
 /*-
  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
@@ -66,8 +66,8 @@ 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;
@@ -75,6 +75,9 @@ 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
 extern char     re_error[RE_ERROR_BUF + 1];    /* Seems big enough */
index 9cfed4c..f7c5407 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: util.c,v 1.57 2017/04/03 16:18:35 tedu Exp $  */
+/*     $OpenBSD: util.c,v 1.58 2017/12/09 18:38:37 pirofti Exp $       */
 
 /*-
  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
@@ -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");
@@ -126,6 +128,8 @@ procfile(char *fn)
        if (Bflag > 0)
                initqueue();
        for (c = 0;  c == 0 || !(lflag || qflag); ) {
+               if (mflag && mlimit == 0)
+                       break;
                ln.off += ln.len + 1;
                if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL)
                        break;
@@ -140,6 +144,8 @@ procfile(char *fn)
                        linesqueued++;
                }
                c += t;
+               if (mflag && mcount <= 0)
+                       break;
        }
        if (Bflag > 0)
                clearqueue();
@@ -224,6 +230,10 @@ 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 */