23a9b08c893ed2c27994b3f02a41024f2f416cf8
[vte.git] / src / vteregex.c
1 /*
2  * Copyright (C) 2003 Red Hat, Inc.
3  *
4  * This is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU Library General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #ident "$Id$"
20 #include "../config.h"
21 #include <sys/types.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <glib.h>
27
28 #include "vteregex.h"
29
30 #if defined(USE_GNU_REGEX)
31 #include <regex.h>
32 #elif defined(USE_PCRE)
33 #include <pcre.h>
34 #else
35 #include <regex.h>
36 #endif
37
38 static gint
39 compare_matches(gconstpointer a, gconstpointer b)
40 {
41         const struct _vte_regex_match *A, *B;
42         A = a;
43         B = b;
44         if (B->rm_so != A->rm_so) {
45                 return B->rm_so - A->rm_so;
46         }
47         return B->rm_eo - A->rm_eo;
48 }
49
50 static void
51 _vte_regex_sort_matches(struct _vte_regex_match *matches, gsize n_matches)
52 {
53         GArray *array;
54         if (n_matches <= 1) {
55                 return;
56         }
57         array = g_array_new(0, 0, sizeof(struct _vte_regex_match));
58         g_array_append_vals(array, matches, n_matches);
59         g_array_sort(array, compare_matches);
60         memmove(matches, array->data,
61                 n_matches * sizeof(struct _vte_regex_match));
62         g_array_free(array, TRUE);
63 }
64
65 #if defined(USE_GNU_REGEX)
66
67 struct _vte_regex {
68         struct re_pattern_buffer buffer;
69 };
70
71 struct _vte_regex *
72 _vte_regex_compile(const char *pattern)
73 {
74         struct _vte_regex *ret;
75         const char *res;
76
77         ret = g_malloc0(sizeof(struct _vte_regex));
78         res = re_compile_pattern(pattern, strlen(pattern), &ret->buffer);
79         if (res != NULL) {
80                 g_free(ret);
81                 return NULL;
82         }
83         return ret;
84 }
85
86 void
87 _vte_regex_free(struct _vte_regex *regex)
88 {
89         regfree(&regex->buffer);
90         g_free(regex);
91 }
92
93 int
94 _vte_regex_exec(struct _vte_regex *regex, const char *string,
95                 gsize nmatch, struct _vte_regex_match *matches)
96 {
97         struct re_registers registers;
98         int i, length, ret;
99
100         length = strlen(string);
101         registers.num_regs = 0;
102         registers.start = NULL;
103         registers.end = NULL;
104         ret = re_search(&regex->buffer,
105                         string, length,
106                         0, length - 1,
107                         &registers);
108         if (ret >= 0) {
109                 for (i = 0; i < nmatch; i++) {
110                         matches[i].rm_so = -1;
111                         matches[i].rm_eo = -1;
112                 }
113                 for (i = 0; (i < nmatch) && (i < registers.num_regs); i++) {
114                         matches[i].rm_so = registers.start[i];
115                         matches[i].rm_eo = registers.end[i];
116                 }
117                 if ((i == nmatch) || (matches[i].rm_so == -1)) {
118                         _vte_regex_sort_matches(matches, i);
119                 }
120         }
121         if (ret >= 0) {
122                 return 0;
123         }
124         return -1;
125 }
126
127 #elif defined(USE_PCRE)
128
129 struct _vte_regex {
130         pcre *pcre;
131         pcre_extra *extra;
132 };
133
134 struct _vte_regex *
135 _vte_regex_compile(const char *pattern)
136 {
137         struct _vte_regex *ret;
138         const char *err;
139         int err_offset;
140
141         ret = g_malloc(sizeof(struct _vte_regex));
142
143         ret->pcre = pcre_compile(pattern, PCRE_UTF8, &err, &err_offset, NULL);
144         if (ret->pcre == NULL) {
145                 g_free(ret);
146                 return NULL;
147         }
148
149         ret->extra = pcre_study(ret->pcre, 0, &err);
150         if (ret->extra == NULL) {
151                 pcre_free(ret->pcre);
152                 g_free(ret);
153                 return NULL;
154         }
155
156         return ret;
157 }
158
159 void
160 _vte_regex_free(struct _vte_regex *regex)
161 {
162         pcre_free(regex->pcre);
163         pcre_free(regex->extra);
164         g_free(regex);
165 }
166
167 int
168 _vte_regex_exec(struct _vte_regex *regex, const char *string,
169                 gsize nmatch, struct _vte_regex_match *matches)
170 {
171         int i, n_matches, *ovector, ovector_length, length;
172
173         for (i = 0; i < nmatch; i++) {
174                 matches[i].rm_so = -1;
175                 matches[i].rm_eo = -1;
176         }
177
178         length = strlen(string);
179         ovector_length = 3 * (length + 1);
180         ovector = g_malloc(sizeof(int) * ovector_length);
181
182         i = pcre_exec(regex->pcre, regex->extra, string, length,
183                       0, 0, ovector, ovector_length);
184
185         if (i < 0) {
186                 g_free(ovector);
187                 return -1;
188         }
189
190         n_matches = i;
191         while (i > 0) {
192                 i--;
193                 if (i < nmatch) {
194                         matches[i].rm_so = ovector[i * 2];
195                         matches[i].rm_eo = ovector[i * 2 + 1];
196                 }
197         }
198         _vte_regex_sort_matches(matches, n_matches);
199
200         return 0;
201 }
202
203 #else
204
205 struct _vte_regex {
206         regex_t posix_regex;
207 };
208
209 struct _vte_regex *
210 _vte_regex_compile(const char *pattern)
211 {
212         struct _vte_regex *ret;
213         int i;
214
215         ret = g_malloc(sizeof(struct _vte_regex));
216         i = regcomp(&ret->posix_regex, pattern, REG_EXTENDED);
217         if (i != 0) {
218                 g_free(ret);
219                 return NULL;
220         }
221         return ret;
222 }
223
224 void
225 _vte_regex_free(struct _vte_regex *regex)
226 {
227         regfree(&regex->posix_regex);
228         g_free(regex);
229 }
230
231 int
232 _vte_regex_exec(struct _vte_regex *regex, const char *string,
233                 gsize nmatch, struct _vte_regex_match *matches)
234 {
235         regmatch_t *posix_matches;
236         int i, ret;
237
238         posix_matches = g_malloc(nmatch * sizeof(regmatch_t));
239         ret = regexec(&regex->posix_regex, string, nmatch, posix_matches, 0);
240         if (ret == 0) {
241                 for (i = 0; i < nmatch; i++) {
242                         matches[i].rm_so = -1;
243                         matches[i].rm_eo = -1;
244                 }
245                 for (i = 0; i < nmatch; i++) {
246                         matches[i].rm_so = posix_matches[i].rm_so;
247                         matches[i].rm_eo = posix_matches[i].rm_eo;
248                         if (matches[i].rm_so == -1) {
249                                 _vte_regex_sort_matches(matches, i);
250                                 break;
251                         }
252                 }
253         }
254         g_free(posix_matches);
255         if (ret == 0) {
256                 return 0;
257         }
258         return -1;
259 }
260
261 #endif