Hello everyone,

one year ago there was a thread for grep --null [0] and there seemed to
be a general agreement (in the diff, not only in how wrong the name
--null feels.)  In the end it wasn't committed, I got distracted and
continued to happily using my patched ~/bin/grep

[0]: https://marc.info/?l=openbsd-tech&m=160975008513761&w=2

The attached diff was tweaked by benno@ (also after some comments from
tedu@) and now i've just fixed an issue in printline which reminded me
that this is still pending.

(we need to print the NUL byte after the filename and skip the next
separator if we want to follow the GNU grep behavior: freebsd' grep does
this, netbsd by glancing at the code doesn't.)

One thing that wasn't clear in the other thread is the choice of the
flag: GNU grep has "-Z, --null" and "-z, --null-data" while our grep has
-Z to force it to behave as zgrep and no -z.  We also can't use -0
because it stands for "print 0 lines of context" (both in GNU grep and
ours grep); while it's unlikely that someone is relying on it, it
currently "works" and dumping NULs into some pipeline not prepared for
them is not sensible.  (e.g. a script that does `| grep -$n'...)

ok?

diff 8f97b6e548e6b59d6e9019eeb9ee638e555c44c2 
4cccbdfb2bec5951dcf534df8036beb8a0f85271
blob - 5cc228df222c54a0553f289b5da8bbbe6afd171e
blob + e1edae7e432a155ddf71433717f58c8017744386
--- usr.bin/grep/grep.1
+++ usr.bin/grep/grep.1
@@ -49,6 +49,7 @@
 .Op Fl -context Ns Op = Ns Ar num
 .Op Fl -label Ns = Ns Ar name
 .Op Fl -line-buffered
+.Op Fl -null
 .Op Ar pattern
 .Op Ar
 .Ek
@@ -297,6 +298,16 @@ instead of the filename before lines.
 Force output to be line buffered.
 By default, output is line buffered when standard output is a terminal
 and block buffered otherwise.
+.It Fl -null
+Output a zero byte instead of the character that normally follows a
+file name.
+This option makes the output unambiguous, even in the presence of file
+names containing unusual characters like newlines, making the output
+suitable for use with the
+.Fl 0
+option to
+.Xr xargs 1 .
+This option is a non-POSIX extension and may not be portable.
 .El
 .Sh EXIT STATUS
 The
blob - f41b5e20ca68c9e9a36d2f7dd3c44329c621f29b
blob + 279d949fae764262a350e66ce1c21a6864284eb6
--- usr.bin/grep/grep.c
+++ usr.bin/grep/grep.c
@@ -80,6 +80,7 @@ int    vflag;         /* -v: only show non-matching lines */
 int     wflag;         /* -w: pattern must start and end on word boundaries */
 int     xflag;         /* -x: pattern must match entire line */
 int     lbflag;        /* --line-buffered */
+int     nullflag;      /* --null */
 const char *labelname; /* --label=name */
 
 int binbehave = BIN_FILE_BIN;
@@ -89,6 +90,7 @@ enum {
        HELP_OPT,
        MMAP_OPT,
        LINEBUF_OPT,
+       NULL_OPT,
        LABEL_OPT,
 };
 
@@ -134,6 +136,7 @@ static const struct option long_options[] =
        {"mmap",                no_argument,            NULL, MMAP_OPT},
        {"label",               required_argument,      NULL, LABEL_OPT},
        {"line-buffered",       no_argument,            NULL, LINEBUF_OPT},
+       {"null",                no_argument,            NULL, NULL_OPT},
        {"after-context",       required_argument,      NULL, 'A'},
        {"before-context",      required_argument,      NULL, 'B'},
        {"context",             optional_argument,      NULL, 'C'},
@@ -436,6 +439,9 @@ main(int argc, char *argv[])
                case LINEBUF_OPT:
                        lbflag = 1;
                        break;
+               case NULL_OPT:
+                       nullflag = 1;
+                       break;
                case HELP_OPT:
                default:
                        usage();
blob - 731bbcc35af4cbf870aaf70ce9913e375142d4d8
blob + 16a1e94a0bcf58498dd4df8d6c7692f1ffdb8045
--- usr.bin/grep/grep.h
+++ usr.bin/grep/grep.h
@@ -68,7 +68,7 @@ extern int     cflags, eflags;
 extern int      Aflag, Bflag, Eflag, Fflag, Hflag, Lflag,
                 Rflag, Zflag,
                 bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, qflag,
-                sflag, vflag, wflag, xflag;
+                sflag, vflag, wflag, xflag, nullflag;
 extern int      binbehave;
 extern const char *labelname;
 
blob - bcef0897881eb7890ab3e568a92a77ae5fe1f4a7
blob + 39bf78b02d561c7cbb8c518300cc725aa373fb71
--- usr.bin/grep/util.c
+++ usr.bin/grep/util.c
@@ -172,13 +172,13 @@ procfile(char *fn)
 
        if (cflag) {
                if (!hflag)
-                       printf("%s:", ln.file);
+                       printf("%s%c", ln.file, nullflag ? '\0' : ':');
                printf("%llu%s\n", c, overflow ? "+" : "");
        }
        if (lflag && c != 0)
-               printf("%s\n", fn);
+               printf("%s%c", fn, nullflag ? '\0' : '\n');
        if (Lflag && c == 0)
-               printf("%s\n", fn);
+               printf("%s%c", fn, nullflag ? '\0' : '\n');
        if (c && !cflag && !lflag && !Lflag &&
            binbehave == BIN_FILE_BIN && nottext && !qflag)
                printf("Binary file %s matches\n", fn);
@@ -651,27 +651,32 @@ grep_revstr(unsigned char *str, int len)
 void
 printline(str_t *line, int sep, regmatch_t *pmatch)
 {
-       int n;
+       int n, printsep;
 
        n = 0;
+       printsep = !nullflag;
        if (!hflag) {
                fputs(line->file, stdout);
+               if (nullflag)
+                       putchar(0);
                ++n;
        }
        if (nflag) {
-               if (n)
+               if (n && printsep)
                        putchar(sep);
                printf("%lld", line->line_no);
+               printsep = 1;
                ++n;
        }
        if (bflag) {
-               if (n)
+               if (n && printsep)
                        putchar(sep);
                printf("%lld", (long long)line->off +
                    (pmatch ? pmatch->rm_so : 0));
+               printsep = 1;
                ++n;
        }
-       if (n)
+       if (n && printsep)
                putchar(sep);
        if (pmatch)
                fwrite(line->dat + pmatch->rm_so,


Reply via email to