Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / grep / src / pcresearch.c
1 /* pcresearch.c - searching subroutines using PCRE for grep.
2    Copyright 2000, 2007, 2009-2011 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
17    02110-1301, USA.  */
18
19 /* Written August 1992 by Mike Haertel. */
20
21 #include <config.h>
22 #include "search.h"
23 #if HAVE_PCRE_H
24 # include <pcre.h>
25 #elif HAVE_PCRE_PCRE_H
26 # include <pcre/pcre.h>
27 #endif
28
29 #if HAVE_LIBPCRE
30 /* Compiled internal form of a Perl regular expression.  */
31 static pcre *cre;
32
33 /* Additional information about the pattern.  */
34 static pcre_extra *extra;
35 #endif
36
37 void
38 Pcompile (char const *pattern, size_t size)
39 {
40 #if !HAVE_LIBPCRE
41   error (EXIT_TROUBLE, 0, "%s",
42          _("support for the -P option is not compiled into "
43            "this --disable-perl-regexp binary"));
44 #else
45   int e;
46   char const *ep;
47   char *re = xnmalloc (4, size + 7);
48   int flags = PCRE_MULTILINE | (match_icase ? PCRE_CASELESS : 0);
49   char const *patlim = pattern + size;
50   char *n = re;
51   char const *p;
52   char const *pnul;
53
54   /* FIXME: Remove these restrictions.  */
55   if (memchr(pattern, '\n', size))
56     error (EXIT_TROUBLE, 0, _("the -P option only supports a single pattern"));
57
58   *n = '\0';
59   if (match_lines)
60     strcpy (n, "^(");
61   if (match_words)
62     strcpy (n, "\\b(");
63   n += strlen (n);
64
65   /* The PCRE interface doesn't allow NUL bytes in the pattern, so
66      replace each NUL byte in the pattern with the four characters
67      "\000", removing a preceding backslash if there are an odd
68      number of backslashes before the NUL.
69
70      FIXME: This method does not work with some multibyte character
71      encodings, notably Shift-JIS, where a multibyte character can end
72      in a backslash byte.  */
73   for (p = pattern; (pnul = memchr (p, '\0', patlim - p)); p = pnul + 1)
74     {
75       memcpy (n, p, pnul - p);
76       n += pnul - p;
77       for (p = pnul; pattern < p && p[-1] == '\\'; p--)
78         continue;
79       n -= (pnul - p) & 1;
80       strcpy (n, "\\000");
81       n += 4;
82     }
83
84   memcpy (n, p, patlim - p);
85   n += patlim - p;
86   *n = '\0';
87   if (match_words)
88     strcpy (n, ")\\b");
89   if (match_lines)
90     strcpy (n, ")$");
91
92   cre = pcre_compile (re, flags, &ep, &e, pcre_maketables ());
93   if (!cre)
94     error (EXIT_TROUBLE, 0, "%s", ep);
95
96   extra = pcre_study (cre, 0, &ep);
97   if (ep)
98     error (EXIT_TROUBLE, 0, "%s", ep);
99
100   free (re);
101 #endif
102 }
103
104 /* Pexecute is a no-return function when building --without-pcre.  */
105 #if !HAVE_LIBPCRE
106 # define WITHOUT_PCRE_NORETURN _GL_ATTRIBUTE_NORETURN
107 #else
108 # define WITHOUT_PCRE_NORETURN /* empty */
109 #endif
110
111 size_t WITHOUT_PCRE_NORETURN
112 Pexecute (char const *buf, size_t size, size_t *match_size,
113           char const *start_ptr)
114 {
115 #if !HAVE_LIBPCRE
116   abort ();
117 #else
118   /* This array must have at least two elements; everything after that
119      is just for performance improvement in pcre_exec.  */
120   int sub[300];
121
122   const char *line_buf, *line_end, *line_next;
123   int e = PCRE_ERROR_NOMATCH;
124   ptrdiff_t start_ofs = start_ptr ? start_ptr - buf : 0;
125
126   /* PCRE can't limit the matching to single lines, therefore we have to
127      match each line in the buffer separately.  */
128   for (line_next = buf;
129        e == PCRE_ERROR_NOMATCH && line_next < buf + size;
130        start_ofs -= line_next - line_buf)
131     {
132       line_buf = line_next;
133       line_end = memchr (line_buf, eolbyte, (buf + size) - line_buf);
134       if (line_end == NULL)
135         line_next = line_end = buf + size;
136       else
137         line_next = line_end + 1;
138
139       if (start_ptr && start_ptr >= line_end)
140         continue;
141
142       e = pcre_exec (cre, extra, line_buf, line_end - line_buf,
143                      start_ofs < 0 ? 0 : start_ofs, 0,
144                      sub, sizeof sub / sizeof *sub);
145     }
146
147   if (e <= 0)
148     {
149       switch (e)
150         {
151         case PCRE_ERROR_NOMATCH:
152           return -1;
153
154         case PCRE_ERROR_NOMEMORY:
155           error (EXIT_TROUBLE, 0, _("memory exhausted"));
156
157         case PCRE_ERROR_MATCHLIMIT:
158           error (EXIT_TROUBLE, 0,
159                  _("exceeded PCRE's backtracking limit"));
160
161         default:
162           abort ();
163         }
164     }
165   else
166     {
167       /* Narrow down to the line we've found.  */
168       char const *beg = line_buf + sub[0];
169       char const *end = line_buf + sub[1];
170       char const *buflim = buf + size;
171       char eol = eolbyte;
172       if (!start_ptr)
173         {
174           /* FIXME: The case when '\n' is not found indicates a bug:
175              Since grep is line oriented, the match should never contain
176              a newline, so there _must_ be a newline following.
177            */
178           if (!(end = memchr (end, eol, buflim - end)))
179             end = buflim;
180           else
181             end++;
182           while (buf < beg && beg[-1] != eol)
183             --beg;
184         }
185
186       *match_size = end - beg;
187       return beg - buf;
188     }
189 #endif
190 }