diff --git a/src/Makefile.am b/src/Makefile.am
index 5cc8d59..f974747 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,10 +21,11 @@ AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
 # Tell the linker to omit references to unused shared libraries.
 AM_LDFLAGS = $(IGNORE_UNUSED_LIBRARIES_CFLAGS)
 
-bin_PROGRAMS = grep egrep fgrep
+bin_PROGRAMS = grep egrep fgrep jgrep
 grep_SOURCES  = grep.c
 egrep_SOURCES = egrep.c
 fgrep_SOURCES = fgrep.c
+jgrep_SOURCES = jgrep.c
 noinst_HEADERS = grep.h dfa.h kwset.h search.h system.h mbsupport.h
 
 noinst_LIBRARIES = libgrep.a
diff --git a/src/grep.c b/src/grep.c
index 1b2198f..8948405 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -24,6 +24,7 @@ struct matcher const matchers[] = {
   { "egrep",   Ecompile, EGexecute },
   { "awk",     Acompile, EGexecute },
   { "fgrep",   Fcompile, Fexecute },
+  { "jgrep",   Jcompile, Fexecute },
   { "perl",    Pcompile, Pexecute },
   { NULL, NULL, NULL },
 };
@@ -31,5 +32,5 @@ struct matcher const matchers[] = {
 const char before_options[] =
 N_("PATTERN is, by default, a basic regular expression (BRE).\n");
 const char after_options[] =
-N_("'egrep' means 'grep -E'.  'fgrep' means 'grep -F'.\n\
-Direct invocation as either 'egrep' or 'fgrep' is deprecated.\n");
+N_("'egrep' means 'grep -E'.  'fgrep' means 'grep -F'.  'jgrep' means 'grep -J'.\n\
+Direct invocation as either 'egrep', 'fgrep', or 'jgrep' is deprecated.\n");
diff --git a/src/kwsearch.c b/src/kwsearch.c
index f1a802e..3c0ba46 100644
--- a/src/kwsearch.c
+++ b/src/kwsearch.c
@@ -168,3 +168,48 @@ Fexecute (char const *buf, size_t size, size_t *match_size,
  out:
   return ret_val;
 }
+
+void
+Jcompile (char const *pattern, size_t size)
+{
+  char const *err;
+  size_t psize = size;
+  char const *pat = (match_icase && MB_CUR_MAX > 1
+                     ? mbtolower (pattern, &psize)
+                     : pattern);
+
+  kwsinit (&kwset);
+
+  char const *beg = pat;
+  do
+    {
+      char const *lim;
+      char const *end;
+      for (lim = beg;; ++lim)
+        {
+          end = lim;
+          if (lim >= pat + psize)
+            break;
+         if (*lim == '\0')
+           {
+             lim++;
+             break;
+           }
+#if HAVE_DOS_FILE_CONTENTS
+         if (*lim == '\r' && lim + 1 < pat + psize && lim[1] == '\n')
+           {
+             lim += 2;
+             break;
+           }
+#endif
+        }
+
+      if ((err = kwsincr (kwset, beg, end - beg)) != NULL)
+        error (EXIT_TROUBLE, 0, "%s", err);
+      beg = lim;
+    }
+  while (beg < pat + psize);
+
+  if ((err = kwsprep (kwset)) != NULL)
+    error (EXIT_TROUBLE, 0, "%s", err);
+}
diff --git a/src/main.c b/src/main.c
index 10fbfac..8b937d3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -276,7 +276,7 @@ static struct exclude *excluded_patterns;
 static struct exclude *excluded_directory_patterns;
 /* Short options.  */
 static char const short_options[] =
-"0123456789A:B:C:D:EFGHIPTUVX:abcd:e:f:hiLlm:noqRrsuvwxyZz";
+"0123456789A:B:C:D:EFGHIJPTUVX:abcd:e:f:hiLlm:noqRrsuvwxyZz";
 
 /* Non-boolean long options that have no corresponding short equivalents.  */
 enum
@@ -300,6 +300,7 @@ static struct option const long_options[] =
   {"extended-regexp", no_argument, NULL, 'E'},
   {"fixed-regexp",    no_argument, NULL, 'F'},
   {"fixed-strings",   no_argument, NULL, 'F'},
+  {"fixed-multiline", no_argument, NULL, 'J'},
   {"perl-regexp",     no_argument, NULL, 'P'},
   {"after-context", required_argument, NULL, 'A'},
   {"before-context", required_argument, NULL, 'B'},
@@ -816,15 +817,13 @@ print_line_head (char const *beg, char const *lim, int sep)
   if (out_line)
     {
       if (lastnl < lim)
-        {
-          nlscan (beg);
-          totalnl = add_count (totalnl, 1);
-          lastnl = lim;
-        }
+        nlscan (beg);
       if (pending_sep)
         print_sep (sep);
-      print_offset (totalnl, 4, line_num_color);
+      print_offset (totalnl + 1, 4, line_num_color);
       pending_sep = 1;
+      if (lastnl < lim)
+        nlscan (lim);
     }
 
   if (out_byte)
@@ -1573,6 +1572,7 @@ Regexp selection and interpretation:\n"), program_name);
         printf (_("\
   -E, --extended-regexp     PATTERN is an extended regular expression (ERE)\n\
   -F, --fixed-strings       PATTERN is a set of newline-separated fixed strings\n\
+  -J, --fixed-multiline     PATTERN is a set of NUL-separated fixed multiline strings\n\
   -G, --basic-regexp        PATTERN is a basic regular expression (BRE)\n\
   -P, --perl-regexp         PATTERN is a Perl regular expression\n"));
   /* -X is undocumented on purpose. */
@@ -1961,6 +1961,10 @@ main (int argc, char **argv)
         setmatcher ("fgrep");
         break;
 
+      case 'J':
+        setmatcher ("jgrep");
+        break;
+
       case 'P':
         setmatcher ("perl");
         break;
diff --git a/src/search.h b/src/search.h
index 3074407..7d4f458 100644
--- a/src/search.h
+++ b/src/search.h
@@ -48,6 +48,7 @@ extern size_t EGexecute (char const *, size_t, size_t *, char const *);
 /* kwsearch.c */
 extern void Fcompile (char const *, size_t);
 extern size_t Fexecute (char const *, size_t, size_t *, char const *);
+extern void Jcompile (char const *, size_t);
 
 /* pcresearch.c */
 extern void Pcompile (char const *, size_t);
