Commit message: grep: fix integer-overflow issues in main program

* NEWS: Document this.
* bootstrap.conf (gnulib_modules): Add inttypes, xstrtoimax.
Remove xstrtoumax.
* src/main.c: Include <inttypes.h>, for INTMAX_MAX, PRIdMAX.
(context_length_arg, prtext, grepbuf, grep, grepfile)
(get_nondigit_option, main):
Use intmax_t, not int, for line counts.
(context_length_arg, main): Silently ceiling line counts
to maximum value, since there's no practical difference between
doing that and using infinite-precision arithmetic.
(out_before, out_after, pending): Now intmax_t, not int.
(max_count, outleft): Now intmax_t, not off_t.
(prepend_args, prepend_default_options, main):
Use size_t, not int, for sizes.
(prepend_default_options): Check for int and size_t overflow.
CVE: CVE-2012-5667
CWE: CWE-189
Vulnerability description: Multiple integer overflows in GNU Grep before 2.11 might allow context-dependent attackers to execute arbitrary code via vectors involving a long input line that triggers a heap-based buffer overflow.

Project: grep

prtext (char const *beg, char const *lim, intmax_t *nlinesp)
{
  static int used;	/* avoid printing SEP_STR_GROUP before any output */
  char const *bp, *p;
  char eol = eolbyte;
  intmax_t i, n;

  if (!out_quiet && pending > 0)
    prpending (beg);

  p = beg;

  if (!out_quiet)
    {
      /* Deal with leading context crap. */

      bp = lastout ? lastout : bufbeg;
      for (i = 0; i < out_before; ++i)
        if (p > bp)
          do
            --p;
          while (p[-1] != eol);

      /* We print the SEP_STR_GROUP separator only if our output is
         discontiguous from the last output in the file. */
      if ((out_before || out_after) && used && p != lastout && group_separator)
        {
          pr_sgr_start_if (sep_color);
          fputs (group_separator, stdout);
          pr_sgr_end_if (sep_color);
          fputc ('\n', stdout);
        }

      while (p < beg)
        {
          char const *nl = memchr (p, eol, beg - p);
          nl++;
          prline (p, nl, SEP_CHAR_REJECTED);
          p = nl;
        }
    }

  if (nlinesp)
    {
      /* Caller wants a line count. */
      for (n = 0; p < lim && n < outleft; n++)
        {
          char const *nl = memchr (p, eol, lim - p);
          nl++;
          if (!out_quiet)
            prline (p, nl, SEP_CHAR_SELECTED);
          p = nl;
        }
      *nlinesp = n;

      /* relying on it that this function is never called when outleft = 0.  */
      after_last_match = bufoffset - (buflim - p);
    }
  else if (!out_quiet)
    prline (beg, lim, SEP_CHAR_SELECTED);

  pending = out_quiet ? 0 : out_after;
  used = 1;
}

Project: grep

grep (int fd, char const *file, struct stats *stats)
{
  intmax_t nlines, i;
  int not_text;
  size_t residue, save;
  char oldc;
  char *beg;
  char *lim;
  char eol = eolbyte;

  if (!reset (fd, file, stats))
    return 0;

  if (file && directories == RECURSE_DIRECTORIES
      && S_ISDIR (stats->stat.st_mode))
    {
      /* Close fd now, so that we don't open a lot of file descriptors
         when we recurse deeply.  */
      if (close (fd) != 0)
        suppressible_error (file, errno);
      return grepdir (file, stats) - 2;
    }

  totalcc = 0;
  lastout = 0;
  totalnl = 0;
  outleft = max_count;
  after_last_match = 0;
  pending = 0;

  nlines = 0;
  residue = 0;
  save = 0;

  if (! fillbuf (save, stats))
    {
      suppressible_error (filename, errno);
      return 0;
    }

  not_text = (((binary_files == BINARY_BINARY_FILES && !out_quiet)
               || binary_files == WITHOUT_MATCH_BINARY_FILES)
              && memchr (bufbeg, eol ? '\0' : '\200', buflim - bufbeg));
  if (not_text && binary_files == WITHOUT_MATCH_BINARY_FILES)
    return 0;
  done_on_match += not_text;
  out_quiet += not_text;

  for (;;)
    {
      lastnl = bufbeg;
      if (lastout)
        lastout = bufbeg;

      beg = bufbeg + save;

      /* no more data to scan (eof) except for maybe a residue -> break */
      if (beg == buflim)
        break;

      /* Determine new residue (the length of an incomplete line at the end of
         the buffer, 0 means there is no incomplete last line).  */
      oldc = beg[-1];
      beg[-1] = eol;
      for (lim = buflim; lim[-1] != eol; lim--)
        continue;
      beg[-1] = oldc;
      if (lim == beg)
        lim = beg - residue;
      beg -= residue;
      residue = buflim - lim;

      if (beg < lim)
        {
          if (outleft)
            nlines += grepbuf (beg, lim);
          if (pending)
            prpending (lim);
          if ((!outleft && !pending) || (nlines && done_on_match && !out_invert))
            goto finish_grep;
        }

      /* The last OUT_BEFORE lines at the end of the buffer will be needed as
         leading context if there is a matching line at the begin of the
         next data. Make beg point to their begin.  */
      i = 0;
      beg = lim;
      while (i < out_before && beg > bufbeg && beg != lastout)
        {
          ++i;
          do
            --beg;
          while (beg[-1] != eol);
        }

      /* detect if leading context is discontinuous from last printed line.  */
      if (beg != lastout)
        lastout = 0;

      /* Handle some details and read more data to scan.  */
      save = residue + lim - beg;
      if (out_byte)
        totalcc = add_count (totalcc, buflim - bufbeg - save);
      if (out_line)
        nlscan (beg);
      if (! fillbuf (save, stats))
        {
          suppressible_error (filename, errno);
          goto finish_grep;
        }
    }
  if (residue)
    {
      *buflim++ = eol;
      if (outleft)
        nlines += grepbuf (bufbeg + save - residue, buflim);
      if (pending)
        prpending (buflim);
    }

 finish_grep:
  done_on_match -= not_text;
  out_quiet -= not_text;
  if ((not_text & ~out_quiet) && nlines != 0)
    printf (_("Binary file %s matches\n"), filename);
  return nlines;
}

Project: grep

fillbuf (size_t save, struct stats const *stats)
{
  size_t fillsize = 0;
  int cc = 1;
  char *readbuf;
  size_t readsize;

  /* Offset from start of buffer to start of old stuff
     that we want to save.  */
  size_t saved_offset = buflim - save - buffer;

  if (pagesize <= buffer + bufalloc - buflim)
    {
      readbuf = buflim;
      bufbeg = buflim - save;
    }
  else
    {
      size_t minsize = save + pagesize;
      size_t newsize;
      size_t newalloc;
      char *newbuf;

      /* Grow newsize until it is at least as great as minsize.  */
      for (newsize = bufalloc - pagesize - 1; newsize < minsize; newsize *= 2)
        if (newsize * 2 < newsize || newsize * 2 + pagesize + 1 < newsize * 2)
          xalloc_die ();

      /* Try not to allocate more memory than the file size indicates,
         as that might cause unnecessary memory exhaustion if the file
         is large.  However, do not use the original file size as a
         heuristic if we've already read past the file end, as most
         likely the file is growing.  */
      if (S_ISREG (stats->stat.st_mode))
        {
          off_t to_be_read = stats->stat.st_size - bufoffset;
          off_t maxsize_off = save + to_be_read;
          if (0 <= to_be_read && to_be_read <= maxsize_off
              && maxsize_off == (size_t) maxsize_off
              && minsize <= (size_t) maxsize_off
              && (size_t) maxsize_off < newsize)
            newsize = maxsize_off;
        }

      /* Add enough room so that the buffer is aligned and has room
         for byte sentinels fore and aft.  */
      newalloc = newsize + pagesize + 1;

      newbuf = bufalloc < newalloc ? xmalloc (bufalloc = newalloc) : buffer;
      readbuf = ALIGN_TO (newbuf + 1 + save, pagesize);
      bufbeg = readbuf - save;
      memmove (bufbeg, buffer + saved_offset, save);
      bufbeg[-1] = eolbyte;
      if (newbuf != buffer)
        {
          free (buffer);
          buffer = newbuf;
        }
    }

  readsize = buffer + bufalloc - readbuf;
  readsize -= readsize % pagesize;

  if (! fillsize)
    {
      ssize_t bytesread;
      while ((bytesread = read (bufdesc, readbuf, readsize)) < 0
             && errno == EINTR)
        continue;
      if (bytesread < 0)
        cc = 0;
      else
        fillsize = bytesread;
    }

  bufoffset += fillsize;
#if defined HAVE_DOS_FILE_CONTENTS
  if (fillsize)
    fillsize = undossify_input (readbuf, fillsize);
#endif
  buflim = readbuf + fillsize;
  return cc;
}

Project: grep

print_line_tail (const char *beg, const char *lim, const char *line_color)
{
  size_t eol_size;
  size_t tail_size;

  eol_size   = (lim > beg && lim[-1] == eolbyte);
  eol_size  += (lim - eol_size > beg && lim[-(1 + eol_size)] == '\r');
  tail_size  =  lim - eol_size - beg;

  if (tail_size > 0)
    {
      pr_sgr_start (line_color);
      fwrite (beg, 1, tail_size, stdout);
      beg += tail_size;
      pr_sgr_end (line_color);
    }

  return beg;
}

Project: grep

prline (char const *beg, char const *lim, int sep)
{
  int matching;
  const char *line_color;
  const char *match_color;

  if (!only_matching)
    print_line_head (beg, lim, sep);

  matching = (sep == SEP_CHAR_SELECTED) ^ !!out_invert;

  if (color_option)
    {
      line_color = (((sep == SEP_CHAR_SELECTED)
                     ^ (out_invert && (color_option < 0)))
                    ? selected_line_color  : context_line_color);
      match_color = (sep == SEP_CHAR_SELECTED
                     ? selected_match_color : context_match_color);
    }
  else
    line_color = match_color = NULL; /* Shouldn't be used.  */

  if ((only_matching && matching)
      || (color_option  && (*line_color || *match_color)))
    {
      /* We already know that non-matching lines have no match (to colorize).  */
      if (matching && (only_matching || *match_color))
        beg = print_line_middle (beg, lim, line_color, match_color);

      /* FIXME: this test may be removable.  */
      if (!only_matching && *line_color)
        beg = print_line_tail (beg, lim, line_color);
    }

  if (!only_matching && lim > beg)
    fwrite (beg, 1, lim - beg, stdout);

  if (ferror (stdout))
    {
      write_error_seen = 1;
      error (EXIT_TROUBLE, 0, _("write error"));
    }

  lastout = lim;

  if (line_buffered)
    fflush (stdout);
}

Project: grep

reset (int fd, char const *file, struct stats *stats)
{
  if (! pagesize)
    {
      pagesize = getpagesize ();
      if (pagesize == 0 || 2 * pagesize + 1 <= pagesize)
        abort ();
      bufalloc = ALIGN_TO (INITIAL_BUFSIZE, pagesize) + pagesize + 1;
      buffer = xmalloc (bufalloc);
    }

  bufbeg = buflim = ALIGN_TO (buffer + 1, pagesize);
  bufbeg[-1] = eolbyte;
  bufdesc = fd;

  if (S_ISREG (stats->stat.st_mode))
    {
      if (file)
        bufoffset = 0;
      else
        {
          bufoffset = lseek (fd, 0, SEEK_CUR);
          if (bufoffset < 0)
            {
              suppressible_error (_("lseek failed"), errno);
              return 0;
            }
        }
    }
  return 1;
}

Project: grep

parse_grep_colors (void)
{
  const char *p;
  char *q;
  char *name;
  char *val;

  p = getenv ("GREP_COLORS"); /* Plural! */
  if (p == NULL || *p == '\0')
    return;

  /* Work off a writable copy.  */
  q = xstrdup (p);

  name = q;
  val = NULL;
  /* From now on, be well-formed or you're gone.  */
  for (;;)
    if (*q == ':' || *q == '\0')
      {
        char c = *q;
        struct color_cap const *cap;

        *q++ = '\0'; /* Terminate name or val.  */
        /* Empty name without val (empty cap)
         * won't match and will be ignored.  */
        for (cap = color_dict; cap->name; cap++)
          if (STREQ (cap->name, name))
            break;
        /* If name unknown, go on for forward compatibility.  */
        if (cap->var && val)
          *(cap->var) = val;
        if (cap->fct)
          cap->fct ();
        if (c == '\0')
          return;
        name = q;
        val = NULL;
      }
    else if (*q == '=')
      {
        if (q == name || val)
          return;
        *q++ = '\0'; /* Terminate name.  */
        val = q; /* Can be the empty string.  */
      }
    else if (val == NULL)
      q++; /* Accumulate name.  */
    else if (*q == ';' || (*q >= '0' && *q <= '9'))
      q++; /* Accumulate val.  Protect the terminal from being sent crap.  */
    else
      return;
}

Project: grep

color_cap_ne_fct (void)
{
  sgr_start = "\33[%sm";
  sgr_end   = "\33[m";
}

Project: grep

usage (int status)
{
  if (status != 0)
    {
      fprintf (stderr, _("Usage: %s [OPTION]... PATTERN [FILE]...\n"),
               program_name);
      fprintf (stderr, _("Try '%s --help' for more information.\n"),
               program_name);
    }
  else
    {
      printf (_("Usage: %s [OPTION]... PATTERN [FILE]...\n"), program_name);
      printf (_("\
Search for PATTERN in each FILE or standard input.\n"));
      fputs (_(before_options), stdout);
      printf (_("\
Example: %s -i 'hello world' menu.h main.c\n\
\n\
Regexp selection and interpretation:\n"), program_name);
      if (matchers[1].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\
  -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. */
      printf (_("\
  -e, --regexp=PATTERN      use PATTERN for matching\n\
  -f, --file=FILE           obtain PATTERN from FILE\n\
  -i, --ignore-case         ignore case distinctions\n\
  -w, --word-regexp         force PATTERN to match only whole words\n\
  -x, --line-regexp         force PATTERN to match only whole lines\n\
  -z, --null-data           a data line ends in 0 byte, not newline\n"));
      printf (_("\
\n\
Miscellaneous:\n\
  -s, --no-messages         suppress error messages\n\
  -v, --invert-match        select non-matching lines\n\
  -V, --version             print version information and exit\n\
      --help                display this help and exit\n\
      --mmap                deprecated no-op; evokes a warning\n"));
      printf (_("\
\n\
Output control:\n\
  -m, --max-count=NUM       stop after NUM matches\n\
  -b, --byte-offset         print the byte offset with output lines\n\
  -n, --line-number         print line number with output lines\n\
      --line-buffered       flush output on every line\n\
  -H, --with-filename       print the file name for each match\n\
  -h, --no-filename         suppress the file name prefix on output\n\
      --label=LABEL         use LABEL as the standard input file name prefix\n\
"));
      printf (_("\
  -o, --only-matching       show only the part of a line matching PATTERN\n\
  -q, --quiet, --silent     suppress all normal output\n\
      --binary-files=TYPE   assume that binary files are TYPE;\n\
                            TYPE is `binary', `text', or `without-match'\n\
  -a, --text                equivalent to --binary-files=text\n\
"));
      printf (_("\
  -I                        equivalent to --binary-files=without-match\n\
  -d, --directories=ACTION  how to handle directories;\n\
                            ACTION is `read', `recurse', or `skip'\n\
  -D, --devices=ACTION      how to handle devices, FIFOs and sockets;\n\
                            ACTION is `read' or `skip'\n\
  -R, -r, --recursive       equivalent to --directories=recurse\n\
"));
      printf (_("\
      --include=FILE_PATTERN  search only files that match FILE_PATTERN\n\
      --exclude=FILE_PATTERN  skip files and directories matching FILE_PATTERN\n\
      --exclude-from=FILE   skip files matching any file pattern from FILE\n\
      --exclude-dir=PATTERN  directories that match PATTERN will be skipped.\n\
"));
      printf (_("\
  -L, --files-without-match  print only names of FILEs containing no match\n\
  -l, --files-with-matches  print only names of FILEs containing matches\n\
  -c, --count               print only a count of matching lines per FILE\n\
  -T, --initial-tab         make tabs line up (if needed)\n\
  -Z, --null                print 0 byte after FILE name\n"));
      printf (_("\
\n\
Context control:\n\
  -B, --before-context=NUM  print NUM lines of leading context\n\
  -A, --after-context=NUM   print NUM lines of trailing context\n\
  -C, --context=NUM         print NUM lines of output context\n\
"));
      printf (_("\
  -NUM                      same as --context=NUM\n\
      --color[=WHEN],\n\
      --colour[=WHEN]       use markers to highlight the matching strings;\n\
                            WHEN is `always', `never', or `auto'\n\
  -U, --binary              do not strip CR characters at EOL (MSDOS/Windows)\n\
  -u, --unix-byte-offsets   report offsets as if CRs were not there\n\
                            (MSDOS/Windows)\n\
\n"));
      fputs (_(after_options), stdout);
      printf (_("\
When FILE is -, read standard input.  With no FILE, read . if a command-line\n\
-r is given, - otherwise.  If fewer than two FILEs are given, assume -h.\n\
Exit status is 0 if any line is selected, 1 otherwise;\n\
if any error occurs and -q is not given, the exit status is 2.\n"));
      printf (_("\nReport bugs to: %s\n"), PACKAGE_BUGREPORT);
      printf (_("GNU Grep home page: <%s>\n"),
              "http://www.gnu.org/software/grep/");
      fputs (_("General help using GNU software: <http://www.gnu.org/gethelp/>\n"),
             stdout);

    }
  exit (status);
}

Project: grep

prepend_default_options (char const *options, int *pargc, char ***pargv)
{
  if (options && *options)
    {
      char *buf = xmalloc (strlen (options) + 1);
      size_t prepended = prepend_args (options, buf, NULL);
      int argc = *pargc;
      char *const *argv = *pargv;
      char **pp;
      enum { MAX_ARGS = MIN (INT_MAX, SIZE_MAX / sizeof *pp - 1) };
      if (MAX_ARGS - argc < prepended)
        xalloc_die ();
      pp = xmalloc ((prepended + argc + 1) * sizeof *pp);
      *pargc = prepended + argc;
      *pargv = pp;
      *pp++ = *argv++;
      pp += prepend_args (options, buf, pp);
      while ((*pp++ = *argv++))
        continue;
      return prepended;
    }

  return 0;
}

Project: grep

grepbuf (char const *beg, char const *lim)
{
  intmax_t nlines, n;
  char const *p;
  size_t match_offset;
  size_t match_size;

  nlines = 0;
  p = beg;
  while ((match_offset = do_execute (p, lim - p, &match_size,
                                     NULL)) != (size_t) -1)
    {
      char const *b = p + match_offset;
      char const *endp = b + match_size;
      /* Avoid matching the empty line at the end of the buffer. */
      if (b == lim)
        break;
      if (!out_invert)
        {
          prtext (b, endp, NULL);
          nlines++;
          outleft--;
          if (!outleft || done_on_match)
            {
              if (exit_on_match)
                exit (EXIT_SUCCESS);
              after_last_match = bufoffset - (buflim - endp);
              return nlines;
            }
        }
      else if (p < b)
        {
          prtext (p, b, &n);
          nlines += n;
          outleft -= n;
          if (!outleft)
            return nlines;
        }
      p = endp;
    }
  if (out_invert && p < lim)
    {
      prtext (p, lim, &n);
      nlines += n;
      outleft -= n;
    }
  return nlines;
}

Project: grep

print_line_middle (const char *beg, const char *lim,
                   const char *line_color, const char *match_color)
{
  size_t match_size;
  size_t match_offset;
  const char *cur = beg;
  const char *mid = NULL;

  while (cur < lim
         && ((match_offset = execute (beg, lim - beg, &match_size,
                                      beg + (cur - beg))) != (size_t) -1))
    {
      char const *b = beg + match_offset;

      /* Avoid matching the empty line at the end of the buffer. */
      if (b == lim)
        break;

      /* Avoid hanging on grep --color "" foo */
      if (match_size == 0)
        {
          /* Make minimal progress; there may be further non-empty matches.  */
          /* XXX - Could really advance by one whole multi-octet character.  */
          match_size = 1;
          if (!mid)
            mid = cur;
        }
      else
        {
          /* This function is called on a matching line only,
             but is it selected or rejected/context?  */
          if (only_matching)
            print_line_head (b, lim, (out_invert ? SEP_CHAR_REJECTED
                                      : SEP_CHAR_SELECTED));
          else
            {
              pr_sgr_start (line_color);
              if (mid)
                {
                  cur = mid;
                  mid = NULL;
                }
              fwrite (cur, sizeof (char), b - cur, stdout);
            }

          pr_sgr_start_if (match_color);
          fwrite (b, sizeof (char), match_size, stdout);
          pr_sgr_end_if (match_color);
          if (only_matching)
            fputs ("\n", stdout);
        }
      cur = b + match_size;
    }

  if (only_matching)
    cur = lim;
  else if (mid)
    cur = mid;

  return cur;
}

Project: grep

prepend_args (char const *options, char *buf, char **argv)
{
  char const *o = options;
  char *b = buf;
  size_t n = 0;

  for (;;)
    {
      while (c_isspace ((unsigned char) *o))
        o++;
      if (!*o)
        return n;
      if (argv)
        argv[n] = b;
      n++;

      do
        if ((*b++ = *o++) == '\\' && *o)
          b[-1] = *o++;
      while (*o && ! c_isspace ((unsigned char) *o));

      *b++ = '\0';
    }
}

Project: grep

context_length_arg (char const *str, intmax_t *out)
{
  switch (xstrtoimax (str, 0, 10, out, ""))
    {
    case LONGINT_OK:
    case LONGINT_OVERFLOW:
      if (0 <= *out)
        break;
      /* Fall through.  */
    default:
      error (EXIT_TROUBLE, 0, "%s: %s", str,
             _("invalid context length argument"));
    }
}

Project: grep

nlscan (char const *lim)
{
  size_t newlines = 0;
  char const *beg;
  for (beg = lastnl; beg < lim; beg++)
    {
      beg = memchr (beg, eolbyte, lim - beg);
      if (!beg)
        break;
      newlines++;
    }
  totalnl = add_count (totalnl, newlines);
  lastnl = lim;
}

Project: grep

clean_up_stdout (void)
{
  if (! write_error_seen)
    close_stdout ();
}

Project: grep

do_execute (char const *buf, size_t size, size_t *match_size, char const *start_ptr)
{
  size_t result;
  const char *line_next;

  /* With the current implementation, using --ignore-case with a multi-byte
     character set is very inefficient when applied to a large buffer
     containing many matches.  We can avoid much of the wasted effort
     by matching line-by-line.

     FIXME: this is just an ugly workaround, and it doesn't really
     belong here.  Also, PCRE is always using this same per-line
     matching algorithm.  Either we fix -i, or we should refactor
     this code---for example, we could add another function pointer
     to struct matcher to split the buffer passed to execute.  It would
     perform the memchr if line-by-line matching is necessary, or just
     return buf + size otherwise.  */
  if (MB_CUR_MAX == 1 || !match_icase)
    return execute (buf, size, match_size, start_ptr);

  for (line_next = buf; line_next < buf + size; )
    {
      const char *line_buf = line_next;
      const char *line_end = memchr (line_buf, eolbyte, (buf + size) - line_buf);
      if (line_end == NULL)
        line_next = line_end = buf + size;
      else
        line_next = line_end + 1;

      if (start_ptr && start_ptr >= line_end)
        continue;

      result = execute (line_buf, line_next - line_buf, match_size, start_ptr);
      if (result != (size_t) -1)
        return (line_buf - buf) + result;
    }

  return (size_t) -1;
}

Project: grep

print_offset (uintmax_t pos, int min_width, const char *color)
{
  /* Do not rely on printf to print pos, since uintmax_t may be longer
     than long, and long long is not portable.  */

  char buf[sizeof pos * CHAR_BIT];
  char *p = buf + sizeof buf;

  do
    {
      *--p = '0' + pos % 10;
      --min_width;
    }
  while ((pos /= 10) != 0);

  /* Do this to maximize the probability of alignment across lines.  */
  if (align_tabs)
    while (--min_width >= 0)
      *--p = ' ';

  pr_sgr_start_if (color);
  fwrite (p, 1, buf + sizeof buf - p, stdout);
  pr_sgr_end_if (color);
}

Project: grep

setmatcher (char const *m)
{
  static char const *matcher;
  unsigned int i;

  if (!m)
    {
      compile = matchers[0].compile;
      execute = matchers[0].execute;
      if (!matchers[1].name)
        matcher = matchers[0].name;
    }

  else if (matcher)
    {
      if (matcher && STREQ (matcher, m))
        ;

      else if (!matchers[1].name)
        error (EXIT_TROUBLE, 0, _("%s can only use the %s pattern syntax"),
               program_name, matcher);
      else
        error (EXIT_TROUBLE, 0, _("conflicting matchers specified"));
    }

  else
    {
      for (i = 0; matchers[i].name; i++)
        if (STREQ (m, matchers[i].name))
          {
            compile = matchers[i].compile;
            execute = matchers[i].execute;
            matcher = m;
            return;
          }

      error (EXIT_TROUBLE, 0, _("invalid matcher %s"), m);
    }
}

Project: grep

main (int argc, char **argv)
{
  char *keys;
  size_t keycc, oldcc, keyalloc;
  int with_filenames;
  size_t cc;
  int opt, status, prepended;
  int prev_optind, last_recursive;
  intmax_t default_context;
  FILE *fp;
  exit_failure = EXIT_TROUBLE;
  initialize_main (&argc, &argv);
  set_program_name (argv[0]);
  program_name = argv[0];

  keys = NULL;
  keycc = 0;
  with_filenames = 0;
  eolbyte = '\n';
  filename_mask = ~0;

  max_count = INTMAX_MAX;

  /* The value -1 means to use DEFAULT_CONTEXT. */
  out_after = out_before = -1;
  /* Default before/after context: chaged by -C/-NUM options */
  default_context = 0;
  /* Changed by -o option */
  only_matching = 0;

  /* Internationalization. */
#if defined HAVE_SETLOCALE
  setlocale (LC_ALL, "");
#endif
#if defined ENABLE_NLS
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
#endif

  exit_failure = EXIT_TROUBLE;
  atexit (clean_up_stdout);

  last_recursive = 0;
  prepended = prepend_default_options (getenv ("GREP_OPTIONS"), &argc, &argv);
  setmatcher (NULL);

  while (prev_optind = optind,
         (opt = get_nondigit_option (argc, argv, &default_context)) != -1)
    switch (opt)
      {
      case 'A':
        context_length_arg (optarg, &out_after);
        break;

      case 'B':
        context_length_arg (optarg, &out_before);
        break;

      case 'C':
        /* Set output match context, but let any explicit leading or
           trailing amount specified with -A or -B stand. */
        context_length_arg (optarg, &default_context);
        break;

      case 'D':
        if (STREQ (optarg, "read"))
          devices = READ_DEVICES;
        else if (STREQ (optarg, "skip"))
          devices = SKIP_DEVICES;
        else
          error (EXIT_TROUBLE, 0, _("unknown devices method"));
        break;

      case 'E':
        setmatcher ("egrep");
        break;

      case 'F':
        setmatcher ("fgrep");
        break;

      case 'P':
        setmatcher ("perl");
        break;

      case 'G':
        setmatcher ("grep");
        break;

      case 'X': /* undocumented on purpose */
        setmatcher (optarg);
        break;

      case 'H':
        with_filenames = 1;
        no_filenames = 0;
        break;

      case 'I':
        binary_files = WITHOUT_MATCH_BINARY_FILES;
        break;

      case 'T':
        align_tabs = 1;
        break;

      case 'U':
#if defined HAVE_DOS_FILE_CONTENTS
        dos_use_file_type = DOS_BINARY;
#endif
        break;

      case 'u':
#if defined HAVE_DOS_FILE_CONTENTS
        dos_report_unix_offset = 1;
#endif
        break;

      case 'V':
        show_version = 1;
        break;

      case 'a':
        binary_files = TEXT_BINARY_FILES;
        break;

      case 'b':
        out_byte = 1;
        break;

      case 'c':
        count_matches = 1;
        break;

      case 'd':
        directories = XARGMATCH ("--directories", optarg,
                                 directories_args, directories_types);
        if (directories == RECURSE_DIRECTORIES)
          last_recursive = prev_optind;
        break;

      case 'e':
        cc = strlen (optarg);
        keys = xrealloc (keys, keycc + cc + 1);
        strcpy (&keys[keycc], optarg);
        keycc += cc;
        keys[keycc++] = '\n';
        break;

      case 'f':
        fp = STREQ (optarg, "-") ? stdin : fopen (optarg, "r");
        if (!fp)
          error (EXIT_TROUBLE, errno, "%s", optarg);
        for (keyalloc = 1; keyalloc <= keycc + 1; keyalloc *= 2)
          ;
        keys = xrealloc (keys, keyalloc);
        oldcc = keycc;
        while (!feof (fp)
               && (cc = fread (keys + keycc, 1, keyalloc - 1 - keycc, fp)) > 0)
          {
            keycc += cc;
            if (keycc == keyalloc - 1)
              keys = x2nrealloc (keys, &keyalloc, sizeof *keys);
          }
        if (fp != stdin)
          fclose (fp);
        /* Append final newline if file ended in non-newline. */
        if (oldcc != keycc && keys[keycc - 1] != '\n')
          keys[keycc++] = '\n';
        break;

      case 'h':
        with_filenames = 0;
        no_filenames = 1;
        break;

      case 'i':
      case 'y':			/* For old-timers . . . */
        match_icase = 1;
        break;

      case 'L':
        /* Like -l, except list files that don't contain matches.
           Inspired by the same option in Hume's gre. */
        list_files = -1;
        break;

      case 'l':
        list_files = 1;
        break;

      case 'm':
        switch (xstrtoimax (optarg, 0, 10, &max_count, ""))
          {
          case LONGINT_OK:
          case LONGINT_OVERFLOW:
            break;

          default:
            error (EXIT_TROUBLE, 0, _("invalid max count"));
          }
        break;

      case 'n':
        out_line = 1;
        break;

      case 'o':
        only_matching = 1;
        break;

      case 'q':
        exit_on_match = 1;
        exit_failure = 0;
        break;

      case 'R':
      case 'r':
        directories = RECURSE_DIRECTORIES;
        last_recursive = prev_optind;
        break;

      case 's':
        suppress_errors = 1;
        break;

      case 'v':
        out_invert = 1;
        break;

      case 'w':
        match_words = 1;
        break;

      case 'x':
        match_lines = 1;
        break;

      case 'Z':
        filename_mask = 0;
        break;

      case 'z':
        eolbyte = '\0';
        break;

      case BINARY_FILES_OPTION:
        if (STREQ (optarg, "binary"))
          binary_files = BINARY_BINARY_FILES;
        else if (STREQ (optarg, "text"))
          binary_files = TEXT_BINARY_FILES;
        else if (STREQ (optarg, "without-match"))
          binary_files = WITHOUT_MATCH_BINARY_FILES;
        else
          error (EXIT_TROUBLE, 0, _("unknown binary-files type"));
        break;

      case COLOR_OPTION:
        if (optarg)
          {
            if (!strcasecmp (optarg, "always") || !strcasecmp (optarg, "yes")
                || !strcasecmp (optarg, "force"))
              color_option = 1;
            else if (!strcasecmp (optarg, "never") || !strcasecmp (optarg, "no")
                     || !strcasecmp (optarg, "none"))
              color_option = 0;
            else if (!strcasecmp (optarg, "auto") || !strcasecmp (optarg, "tty")
                     || !strcasecmp (optarg, "if-tty"))
              color_option = 2;
            else
              show_help = 1;
          }
        else
          color_option = 2;
        break;

      case EXCLUDE_OPTION:
        if (!excluded_patterns)
          excluded_patterns = new_exclude ();
        add_exclude (excluded_patterns, optarg, EXCLUDE_WILDCARDS);
        break;
      case EXCLUDE_FROM_OPTION:
        if (!excluded_patterns)
          excluded_patterns = new_exclude ();
        if (add_exclude_file (add_exclude, excluded_patterns, optarg,
                              EXCLUDE_WILDCARDS, '\n') != 0)
          {
            error (EXIT_TROUBLE, errno, "%s", optarg);
          }
        break;

      case EXCLUDE_DIRECTORY_OPTION:
        if (!excluded_directory_patterns)
          excluded_directory_patterns = new_exclude ();
        add_exclude (excluded_directory_patterns, optarg, EXCLUDE_WILDCARDS);
        break;

      case INCLUDE_OPTION:
        if (!included_patterns)
          included_patterns = new_exclude ();
        add_exclude (included_patterns, optarg,
                     EXCLUDE_WILDCARDS | EXCLUDE_INCLUDE);
        break;

      case GROUP_SEPARATOR_OPTION:
        group_separator = optarg;
        break;

      case LINE_BUFFERED_OPTION:
        line_buffered = 1;
        break;

      case LABEL_OPTION:
        label = optarg;
        break;

      case MMAP_OPTION:
        error (0, 0, _("the --mmap option has been a no-op since 2010"));
        break;

      case 0:
        /* long options */
        break;

      default:
        usage (EXIT_TROUBLE);
        break;

      }

  if (color_option == 2)
    color_option = isatty (STDOUT_FILENO) && should_colorize ();
  init_colorize ();

  /* POSIX.2 says that -q overrides -l, which in turn overrides the
     other output options.  */
  if (exit_on_match)
    list_files = 0;
  if (exit_on_match | list_files)
    {
      count_matches = 0;
      done_on_match = 1;
    }
  out_quiet = count_matches | done_on_match;

  if (out_after < 0)
    out_after = default_context;
  if (out_before < 0)
    out_before = default_context;

  if (color_option)
    {
      /* Legacy.  */
      char *userval = getenv ("GREP_COLOR");
      if (userval != NULL && *userval != '\0')
        selected_match_color = context_match_color = userval;

      /* New GREP_COLORS has priority.  */
      parse_grep_colors ();
    }

  if (show_version)
    {
      version_etc (stdout, program_name, PACKAGE_NAME, VERSION, AUTHORS,
                   (char *) NULL);
      exit (EXIT_SUCCESS);
    }

  if (show_help)
    usage (EXIT_SUCCESS);

  struct stat tmp_stat;
  if (fstat (STDOUT_FILENO, &tmp_stat) == 0 && S_ISREG (tmp_stat.st_mode))
    out_stat = tmp_stat;

  if (keys)
    {
      if (keycc == 0)
        {
          /* No keys were specified (e.g. -f /dev/null).  Match nothing.  */
          out_invert ^= 1;
          match_lines = match_words = 0;
        }
      else
        /* Strip trailing newline. */
        --keycc;
    }
  else if (optind < argc)
    {
      /* A copy must be made in case of an xrealloc() or free() later.  */
      keycc = strlen (argv[optind]);
      keys = xmalloc (keycc + 1);
      strcpy (keys, argv[optind++]);
    }
  else
    usage (EXIT_TROUBLE);

  compile (keys, keycc);
  free (keys);

  if ((argc - optind > 1 && !no_filenames) || with_filenames)
    out_file = 1;

#ifdef SET_BINARY
  /* Output is set to binary mode because we shouldn't convert
     NL to CR-LF pairs, especially when grepping binary files.  */
  if (!isatty (1))
    SET_BINARY (1);
#endif

  if (max_count == 0)
    exit (EXIT_FAILURE);

  if (optind < argc)
    {
      status = 1;
      do
        {
          char *file = argv[optind];
          if (!STREQ (file, "-")
              && (included_patterns || excluded_patterns
                  || excluded_directory_patterns))
            {
              if (isdir (file))
                {
                  if (excluded_directory_patterns
                      && excluded_file_name (excluded_directory_patterns,
                                             file))
                    continue;
                }
              else
                {
                  if (included_patterns
                      && excluded_file_name (included_patterns, file))
                    continue;
                  if (excluded_patterns
                      && excluded_file_name (excluded_patterns, file))
                    continue;
                }
            }
          status &= grepfile (STREQ (file, "-") ? (char *) NULL : file,
                              &stats_base);
        }
      while (++optind < argc);
    }
  else if (directories == RECURSE_DIRECTORIES && prepended < last_recursive)
    {
      status = 1;
      if (stat (".", &stats_base.stat) == 0)
        status = grepdir (NULL, &stats_base);
      else
        suppressible_error (".", errno);
    }
  else
    status = grepfile ((char *) NULL, &stats_base);

  /* We register via atexit() to test stdout.  */
  exit (errseen ? EXIT_TROUBLE : status);
}

Project: grep

print_sep (char sep)
{
  pr_sgr_start_if (sep_color);
  fputc (sep, stdout);
  pr_sgr_end_if (sep_color);
}

Project: grep

print_line_head (char const *beg, char const *lim, int sep)
{
  int pending_sep = 0;

  if (out_file)
    {
      print_filename ();
      if (filename_mask)
        pending_sep = 1;
      else
        fputc (0, stdout);
    }

  if (out_line)
    {
      if (lastnl < lim)
        {
          nlscan (beg);
          totalnl = add_count (totalnl, 1);
          lastnl = lim;
        }
      if (pending_sep)
        print_sep (sep);
      print_offset (totalnl, 4, line_num_color);
      pending_sep = 1;
    }

  if (out_byte)
    {
      uintmax_t pos = add_count (totalcc, beg - bufbeg);
#if defined HAVE_DOS_FILE_CONTENTS
      pos = dossified_pos (pos);
#endif
      if (pending_sep)
        print_sep (sep);
      print_offset (pos, 6, byte_num_color);
      pending_sep = 1;
    }

  if (pending_sep)
    {
      /* This assumes sep is one column wide.
         Try doing this any other way with Unicode
         (and its combining and wide characters)
         filenames and you're wasting your efforts.  */
      if (align_tabs)
        fputs ("\t\b", stdout);

      print_sep (sep);
    }
}

Project: grep

grepdir (char const *dir, struct stats const *stats)
{
  char const *dir_or_dot = (dir ? dir : ".");
  struct stats const *ancestor;
  char *name_space;
  int status = 1;
  if (excluded_directory_patterns
      && excluded_file_name (excluded_directory_patterns, dir))
    return 1;

  /* Mingw32 does not support st_ino.  No known working hosts use zero
     for st_ino, so assume that the Mingw32 bug applies if it's zero.  */
  if (stats->stat.st_ino)
    {
      for (ancestor = stats; (ancestor = ancestor->parent) != 0;  )
        {
          if (ancestor->stat.st_ino == stats->stat.st_ino
              && ancestor->stat.st_dev == stats->stat.st_dev)
            {
              if (!suppress_errors)
                error (0, 0, _("warning: %s: %s"), dir,
                       _("recursive directory loop"));
              errseen = 1;
              return 1;
            }
        }
    }

  name_space = savedir (dir_or_dot, stats->stat.st_size, included_patterns,
                        excluded_patterns, excluded_directory_patterns);

  if (! name_space)
    {
      if (errno)
        suppressible_error (dir_or_dot, errno);
      else
        xalloc_die ();
    }
  else
    {
      size_t dirlen = 0;
      int needs_slash = 0;
      char *file_space = NULL;
      char const *namep = name_space;
      struct stats child;
      if (dir)
        {
          dirlen = strlen (dir);
          needs_slash = ! (dirlen == FILE_SYSTEM_PREFIX_LEN (dir)
                           || ISSLASH (dir[dirlen - 1]));
        }
      child.parent = stats;
      out_file += !no_filenames;
      while (*namep)
        {
          size_t namelen = strlen (namep);
          char const *file;
          if (! dir)
            file = namep;
          else
            {
              file_space = xrealloc (file_space, dirlen + 1 + namelen + 1);
              strcpy (file_space, dir);
              file_space[dirlen] = '/';
              strcpy (file_space + dirlen + needs_slash, namep);
              file = file_space;
            }
          namep += namelen + 1;
          status &= grepfile (file, &child);
        }
      out_file -= !no_filenames;
      free (file_space);
      free (name_space);
    }

  return status;
}

Project: grep

get_nondigit_option (int argc, char *const *argv, intmax_t *default_context)
{
  static int prev_digit_optind = -1;
  int opt, this_digit_optind, was_digit;
  char buf[INT_BUFSIZE_BOUND (intmax_t) + 4];
  char *p = buf;

  was_digit = 0;
  this_digit_optind = optind;
  while (opt = getopt_long (argc, (char **) argv, short_options, long_options,
                            NULL),
         '0' <= opt && opt <= '9')
    {
      if (prev_digit_optind != this_digit_optind || !was_digit)
        {
          /* Reset to start another context length argument.  */
          p = buf;
        }
      else
        {
          /* Suppress trivial leading zeros, to avoid incorrect
             diagnostic on strings like 00000000000.  */
          p -= buf[0] == '0';
        }

      if (p == buf + sizeof buf - 4)
        {
          /* Too many digits.  Append "..." to make context_length_arg
             complain about "X...", where X contains the digits seen
             so far.  */
          strcpy (p, "...");
          p += 3;
          break;
        }
      *p++ = opt;

      was_digit = 1;
      prev_digit_optind = this_digit_optind;
      this_digit_optind = optind;
    }
  if (p != buf)
    {
      *p = '\0';
      context_length_arg (buf, default_context);
    }

  return opt;
}

Project: grep

prpending (char const *lim)
{
  if (!lastout)
    lastout = bufbeg;
  while (pending > 0 && lastout < lim)
    {
      char const *nl = memchr (lastout, eolbyte, lim - lastout);
      size_t match_size;
      --pending;
      if (outleft
          || ((execute (lastout, nl + 1 - lastout,
                        &match_size, NULL) == (size_t) -1)
              == !out_invert))
        prline (lastout, nl + 1, SEP_CHAR_REJECTED);
      else
        pending = 0;
    }
}

Project: grep

print_filename (void)
{
  pr_sgr_start_if (filename_color);
  fputs (filename, stdout);
  pr_sgr_end_if (filename_color);
}

Project: grep

grepfile (char const *file, struct stats *stats)
{
  int desc;
  intmax_t count;
  int status;

  filename = (file ? file : label ? label : _("(standard input)"));

  if (! file)
    desc = STDIN_FILENO;
  else if (devices == SKIP_DEVICES)
    {
      /* Don't open yet, since that might have side effects on a device.  */
      desc = -1;
    }
  else
    {
      /* When skipping directories, don't worry about directories
         that can't be opened.  */
      desc = open (file, O_RDONLY);
      if (desc < 0 && directories != SKIP_DIRECTORIES)
        {
          suppressible_error (file, errno);
          return 1;
        }
    }

  if (desc < 0
      ? stat (file, &stats->stat) != 0
      : fstat (desc, &stats->stat) != 0)
    {
      suppressible_error (filename, errno);
      if (file)
        close (desc);
      return 1;
    }

  if ((directories == SKIP_DIRECTORIES && S_ISDIR (stats->stat.st_mode))
      || (devices == SKIP_DEVICES && (S_ISCHR (stats->stat.st_mode)
                                      || S_ISBLK (stats->stat.st_mode)
                                      || S_ISSOCK (stats->stat.st_mode)
                                      || S_ISFIFO (stats->stat.st_mode))))
    {
      if (file)
        close (desc);
      return 1;
    }

  /* If there is a regular file on stdout and the current file refers
     to the same i-node, we have to report the problem and skip it.
     Otherwise when matching lines from some other input reach the
     disk before we open this file, we can end up reading and matching
     those lines and appending them to the file from which we're reading.
     Then we'd have what appears to be an infinite loop that'd terminate
     only upon filling the output file system or reaching a quota.
     However, there is no risk of an infinite loop if grep is generating
     no output, i.e., with --silent, --quiet, -q.
     Similarly, with any of these:
       --max-count=N (-m) (for N >= 2)
       --files-with-matches (-l)
       --files-without-match (-L)
     there is no risk of trouble.
     For --max-count=1, grep stops after printing the first match,
     so there is no risk of malfunction.  But even --max-count=2, with
     input==output, while there is no risk of infloop, there is a race
     condition that could result in "alternate" output.  */
  if (!out_quiet && list_files == 0 && 1 < max_count
      && S_ISREG (out_stat.st_mode) && out_stat.st_ino
      && SAME_INODE (stats->stat, out_stat))
    {
      if (! suppress_errors)
        error (0, 0, _("input file %s is also the output"), quote (filename));
      errseen = 1;
      if (file)
        close (desc);
      return 1;
    }

  if (desc < 0)
    {
      desc = open (file, O_RDONLY);
      if (desc < 0)
        {
          suppressible_error (file, errno);
          return 1;
        }
    }

#if defined SET_BINARY
  /* Set input to binary mode.  Pipes are simulated with files
     on DOS, so this includes the case of "foo | grep bar".  */
  if (!isatty (desc))
    SET_BINARY (desc);
#endif

  count = grep (desc, file, stats);
  if (count < 0)
    status = count + 2;
  else
    {
      if (count_matches)
        {
          if (out_file)
            {
              print_filename ();
              if (filename_mask)
                print_sep (SEP_CHAR_SELECTED);
              else
                fputc (0, stdout);
            }
          printf ("%" PRIdMAX "\n", count);
        }

      status = !count;
      if (list_files == 1 - 2 * status)
        {
          print_filename ();
          fputc ('\n' & filename_mask, stdout);
        }

      if (! file)
        {
          off_t required_offset = outleft ? bufoffset : after_last_match;
          if (required_offset != bufoffset
              && lseek (desc, required_offset, SEEK_SET) < 0
              && S_ISREG (stats->stat.st_mode))
            suppressible_error (filename, errno);
        }
      else
        while (close (desc) != 0)
          if (errno != EINTR)
            {
              suppressible_error (file, errno);
              break;
            }
    }

  return status;
}

Project: grep

add_count (uintmax_t a, uintmax_t b)
{
  uintmax_t sum = a + b;
  if (sum < a)
    error (EXIT_TROUBLE, 0, _("input is too large to count"));
  return sum;
}