Merge branch 'vendor/GCC47'
[dragonfly.git] / contrib / grep / src / pcresearch.c
1 /* pcresearch.c - searching subroutines using PCRE for grep.
2    Copyright 2000, 2007, 2009-2012 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 size_t
105 Pexecute (char const *buf, size_t size, size_t *match_size,
106           char const *start_ptr)
107 {
108 #if !HAVE_LIBPCRE
109   /* We can't get here, because Pcompile would have been called earlier.  */
110   error (EXIT_TROUBLE, 0, _("internal error"));
111   return -1;
112 #else
113   /* This array must have at least two elements; everything after that
114      is just for performance improvement in pcre_exec.  */
115   int sub[300];
116
117   const char *line_buf, *line_end, *line_next;
118   int e = PCRE_ERROR_NOMATCH;
119   ptrdiff_t start_ofs = start_ptr ? start_ptr - buf : 0;
120
121   /* PCRE can't limit the matching to single lines, therefore we have to
122      match each line in the buffer separately.  */
123   for (line_next = buf;
124        e == PCRE_ERROR_NOMATCH && line_next < buf + size;
125        start_ofs -= line_next - line_buf)
126     {
127       line_buf = line_next;
128       line_end = memchr (line_buf, eolbyte, (buf + size) - line_buf);
129       if (line_end == NULL)
130         line_next = line_end = buf + size;
131       else
132         line_next = line_end + 1;
133
134       if (start_ptr && start_ptr >= line_end)
135         continue;
136
137       if (INT_MAX < line_end - line_buf)
138         error (EXIT_TROUBLE, 0, _("exceeded PCRE's line length limit"));
139
140       e = pcre_exec (cre, extra, line_buf, line_end - line_buf,
141                      start_ofs < 0 ? 0 : start_ofs, 0,
142                      sub, sizeof sub / sizeof *sub);
143     }
144
145   if (e <= 0)
146     {
147       switch (e)
148         {
149         case PCRE_ERROR_NOMATCH:
150           return -1;
151
152         case PCRE_ERROR_NOMEMORY:
153           error (EXIT_TROUBLE, 0, _("memory exhausted"));
154
155         case PCRE_ERROR_MATCHLIMIT:
156           error (EXIT_TROUBLE, 0,
157                  _("exceeded PCRE's backtracking limit"));
158
159         default:
160           abort ();
161         }
162     }
163   else
164     {
165       /* Narrow down to the line we've found.  */
166       char const *beg = line_buf + sub[0];
167       char const *end = line_buf + sub[1];
168       char const *buflim = buf + size;
169       char eol = eolbyte;
170       if (!start_ptr)
171         {
172           /* FIXME: The case when '\n' is not found indicates a bug:
173              Since grep is line oriented, the match should never contain
174              a newline, so there _must_ be a newline following.
175            */
176           if (!(end = memchr (end, eol, buflim - end)))
177             end = buflim;
178           else
179             end++;
180           while (buf < beg && beg[-1] != eol)
181             --beg;
182         }
183
184       *match_size = end - beg;
185       return beg - buf;
186     }
187 #endif
188 }