proper handling of regex lists.
[apps/madmutt.git] / lib-lib / rx.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or (at
5  *  your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful, but
8  *  WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
15  *  MA 02110-1301, USA.
16  *
17  *  Copyright © 2006 Pierre Habouzit
18  */
19 /*
20  * This file is part of mutt-ng, see http://www.muttng.org/.
21  * It's licensed under the GNU General Public License,
22  * please see the file GPL in the top level source directory.
23  */
24
25 #include "lib-lib.h"
26
27 rx_t *rx_compile(const char *s, int flags)
28 {
29     rx_t *pp = p_new(rx_t, 1);
30
31     pp->pattern = m_strdup(s);
32     pp->rx = p_new(regex_t, 1);
33
34     if (REGCOMP(pp->rx, NONULL(s), flags) != 0) {
35         rx_delete(&pp);
36     }
37
38     return pp;
39 }
40
41 int rx_validate(const char *s, char *errbuf, ssize_t errlen)
42 {
43     regex_t re;
44     int res;
45
46     p_clear(&re, 1);
47     res = REGCOMP(&re, NONULL(s), 0);
48     if (res) {
49         regerror(res, &re, errbuf, errlen);
50     }
51     regfree(&re);
52
53     return res;
54 }
55
56 void rx_set_template(rx_t *rx, const char *tpl)
57 {
58     const char *p = tpl;
59
60     m_strreplace(&rx->tpl, tpl);
61     rx->nmatch = 0;
62
63     while ((p = strchr(p, '%'))) {
64         if (isdigit(*++p)) {
65             int n = strtol(p, (char **)&p, 10);
66             rx->nmatch = MAX(n, rx->nmatch);
67         } else {
68             if (*p == '%')
69                 p++;
70         }
71     }
72
73     rx->nmatch++;     /* match 0 is always the whole expr */
74 }
75
76 void rx_wipe(rx_t *rx)
77 {
78     p_delete(&rx->pattern);
79     regfree(rx->rx);
80     p_delete(&rx->rx);
81     p_delete(&rx->tpl);
82 }
83
84 int rx_list_match(rx_t *l, const char *s)
85 {
86     if (m_strisempty(s))
87         return 0;
88
89     while (l) {
90         if (!REGEXEC(l->rx, s))
91             return 1;
92         l = l->next;
93     }
94
95     return 0;
96 }
97
98 int rx_list_match2(rx_t *l, const char *s, char *dst, int dlen)
99 {
100     static regmatch_t *pmatch = NULL;
101     static int nmatch = 0;
102     int pos = 0;
103
104     if (m_strisempty(s))
105         return 0;
106
107     for (; l; l = l->next) {
108         if (l->nmatch > nmatch) {
109             p_realloc(&pmatch, l->nmatch);
110             nmatch = l->nmatch;
111         }
112
113         if (regexec(l->rx, s, l->nmatch, pmatch, 0) == 0) {
114             /* Copy template into dst, with substitutions. */
115             const char *p = l->tpl, *q;
116
117             for (q = strchr(p, '%'); q; q = strchr(p + 1, '%')) {
118                 int n;
119
120                 pos += m_strncpy(dst + pos, dlen - pos, p, q - p);
121
122                 if (!isdigit((unsigned char)q[1])) {
123                     p = q + (q[1] == '%');
124                     continue;
125                 }
126
127                 n = strtol(q + 1, (char **)&p, 10); /* find pmatch index */
128                 pos += m_strncpy(dst + pos, dlen - pos, s + pmatch[n].rm_so,
129                                  pmatch[n].rm_eo - pmatch[n].rm_so);
130             }
131
132             pos += m_strcpy(dst + pos, dlen - pos, p);
133             return 1;
134         }
135     }
136
137     return 0;
138 }
139
140 rx_t **rx_lookup(rx_t **l, const char *pat)
141 {
142     if (m_strisempty(pat))
143         return NULL;
144
145     while (*l) {
146         if (!m_strcmp((*l)->pattern, pat))
147             return l;
148         l = &(*l)->next;
149     }
150
151     return l;
152 }
153
154 void rx_list_add(rx_t **l, rx_t *rxp)
155 {
156     l = rx_lookup(l, rxp->pattern);
157     if (*l) {
158         rx_t *r = rx_list_pop(l);
159         rx_delete(&r);
160     }
161     rx_list_push(l, rxp);
162 }
163
164 void rx_list_add2(rx_t **l, rx_t **rxp)
165 {
166     l = rx_lookup(l, (*rxp)->pattern);
167     if (*l) {
168         rx_t *r = rx_list_pop(l);
169         rx_delete(&r);
170     }
171     if (m_strisempty((*rxp)->tpl)) {
172         rx_delete(rxp);
173     } else {
174         rx_list_push(l, *rxp);
175     }
176 }
177
178 void rx_list_remove(rx_t **l, const rx_t *r)
179 {
180     l = rx_lookup(l, r->pattern);
181     if (*l) {
182         rx_t *tmp = rx_list_pop(l);
183         rx_delete(&tmp);
184     }
185 }
186
187 int rx_sanitize_string(char *dst, ssize_t n, const char *src)
188 {
189     while (*src) {
190         if (n <= 1)
191             break;
192
193         /* these characters must be escaped in regular expressions */
194         if (strchr("^.[$()|*+?{\\", *src)) {
195             if (n <= 2)
196                 break;
197
198             *dst++ = '\\';
199             n--;
200         }
201
202         *dst++ = *src++;
203         n--;
204     }
205
206     *dst = '\0';
207
208     return *src ? -1 : 0;
209 }