]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/breg.c
Backport from BEE
[bacula/bacula] / bacula / src / lib / breg.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2006-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  * Manipulation routines for BREGEXP list
18  *
19  *  Eric Bollengier, March 2007
20  *
21  */
22
23
24 #include "bacula.h"
25
26 #include "breg.h"
27 #include "mem_pool.h"
28
29 BREGEXP *new_bregexp(const char *motif)
30 {
31    Dmsg0(500, "bregexp: creating new bregexp object\n");
32    BREGEXP *self = (BREGEXP *)bmalloc(sizeof(BREGEXP));
33    memset(self, 0, sizeof(BREGEXP));
34
35    if (!self->extract_regexp(motif)) {
36       Dmsg0(100, "bregexp: extract_regexp error\n");
37       free_bregexp(self);
38       return NULL;
39    }
40
41    self->result = get_pool_memory(PM_FNAME);
42    self->result[0] = '\0';
43
44    return self;
45 }
46
47 void free_bregexp(BREGEXP *self)
48 {
49    Dmsg0(500, "bregexp: freeing BREGEXP object\n");
50
51    if (!self) {
52       return;
53    }
54
55    if (self->expr) {
56       bfree(self->expr);
57    }
58    if (self->result) {
59       free_pool_memory(self->result);
60    }
61    regfree(&self->preg);
62    bfree(self);
63 }
64
65 /* Free a bregexps alist
66  */
67 void free_bregexps(alist *bregexps)
68 {
69    Dmsg0(500, "bregexp: freeing all BREGEXP object\n");
70
71    BREGEXP *elt;
72    foreach_alist(elt, bregexps) {
73       free_bregexp(elt);
74    }
75 }
76
77 /* Apply all regexps to fname
78  */
79 bool apply_bregexps(const char *fname, alist *bregexps, char **result)
80 {
81    BREGEXP *elt;
82    bool ok=false;
83
84    char *ret = (char *) fname;
85    foreach_alist(elt, bregexps) {
86       ret = elt->replace(ret);
87       ok = ok || elt->success;
88    }
89    Dmsg2(500, "bregexp: fname=%s ret=%s\n", fname, ret);
90
91    *result = ret;
92    return ok;
93 }
94
95 /* return an alist of BREGEXP or return NULL if it's not a
96  * where=!tmp!opt!ig,!temp!opt!i
97  */
98 alist *get_bregexps(const char *where)
99 {
100    char *p = (char *)where;
101    alist *list = New(alist(10, not_owned_by_alist));
102    BREGEXP *reg;
103
104    reg = new_bregexp(p);
105
106    while(reg) {
107       p = reg->eor;
108       list->append(reg);
109       reg = new_bregexp(p);
110    }
111
112    if (list->size()) {
113       return list;
114    } else {
115       delete list;
116       return NULL;
117    }
118 }
119
120 bool BREGEXP::extract_regexp(const char *motif)
121 {
122    if ( !motif ) {
123       return false;
124    }
125
126    char sep = motif[0];
127
128    if (!(sep == '!' ||
129          sep == ':' ||
130          sep == ';' ||
131          sep == '|' ||
132          sep == ',' ||
133          sep == '&' ||
134          sep == '%' ||
135          sep == '=' ||
136          sep == '~' ||
137          sep == '/' ||
138          sep == '<' ||
139          sep == '#'   ))
140    {
141       return false;
142    }
143
144    char *search = (char *) motif + 1;
145    int options = REG_EXTENDED | REG_NEWLINE;
146    bool ok = false;
147
148    /* extract 1st part */
149    char *dest = expr = bstrdup(motif);
150
151    while (*search && !ok) {
152       if (search[0] == '\\' && search[1] == sep) {
153          *dest++ = *++search;       /* we skip separator */
154
155       } else if (search[0] == '\\' && search[1] == '\\') {
156          *dest++ = *++search;       /* we skip the second \ */
157
158       } else if (*search == sep) {  /* we found end of expression */
159          *dest++ = '\0';
160
161          if (subst) {           /* already have found motif */
162             ok = true;
163
164          } else {
165             *dest++ = *++search; /* we skip separator */
166             subst = dest;        /* get replaced string */
167          }
168
169       } else {
170          *dest++ = *search++;
171       }
172    }
173    *dest = '\0';                /* in case of */
174
175    if (!ok || !subst) {
176       /* bad regexp */
177       return false;
178    }
179
180    ok = false;
181    /* find options */
182    while (*search && !ok) {
183       if (*search == 'i') {
184          options |= REG_ICASE;
185
186       } else if (*search == 'g') {
187               /* recherche multiple*/
188
189       } else if (*search == sep) {
190          /* skip separator */
191
192       } else {                  /* end of options */
193          ok = true;
194       }
195       search++;
196    }
197
198    int rc = regcomp(&preg, expr, options);
199    if (rc != 0) {
200       char prbuf[500];
201       regerror(rc, &preg, prbuf, sizeof(prbuf));
202       Dmsg1(100, "bregexp: compile error: %s\n", prbuf);
203       return false;
204    }
205
206    eor = search;                /* useful to find the next regexp in where */
207
208    return true;
209 }
210
211 /* return regexp->result */
212 char *BREGEXP::replace(const char *fname)
213 {
214    success = false;             /* use this.success to known if it's ok */
215    int flen = strlen(fname);
216    int rc = regexec(&preg, fname, BREG_NREGS, regs, 0);
217
218    if (rc == REG_NOMATCH) {
219       Dmsg0(500, "bregexp: regex mismatch\n");
220       return return_fname(fname, flen);
221    }
222
223    int len = compute_dest_len(fname, regs);
224
225    if (len) {
226       result = check_pool_memory_size(result, len);
227       edit_subst(fname, regs);
228       success = true;
229       Dmsg2(500, "bregexp: len = %i, result_len = %i\n", len, strlen(result));
230
231    } else {                     /* error in substitution */
232       Dmsg0(100, "bregexp: error in substitution\n");
233       return return_fname(fname, flen);
234    }
235
236    return result;
237 }
238
239 char *BREGEXP::return_fname(const char *fname, int len)
240 {
241    result = check_pool_memory_size(result, len+1);
242    strcpy(result,fname);
243    return result;
244 }
245
246 int BREGEXP::compute_dest_len(const char *fname, regmatch_t pmatch[])
247 {
248    int len=0;
249    char *p;
250    char *psubst = subst;
251    int no;
252
253    if (!fname || !pmatch) {
254       return 0;
255    }
256
257    /* match failed ? */
258    if (pmatch[0].rm_so < 0) {
259       return 0;
260    }
261
262    for (p = psubst++; *p ; p = psubst++) {
263       /* match $1 \1 back references */
264       if ((*p == '$' || *p == '\\') && ('0' <= *psubst && *psubst <= '9')) {
265          no = *psubst++ - '0';
266
267          /* we check if the back reference exists */
268          /* references can not match if we are using (..)? */
269
270          if (pmatch[no].rm_so >= 0 && pmatch[no].rm_eo >= 0) {
271             len += pmatch[no].rm_eo - pmatch[no].rm_so;
272          }
273
274       } else {
275          len++;
276       }
277    }
278
279    /* $0 is replaced by subst */
280    len -= pmatch[0].rm_eo - pmatch[0].rm_so;
281    len += strlen(fname) + 1;
282
283    return len;
284 }
285
286 char *BREGEXP::edit_subst(const char *fname, regmatch_t pmatch[])
287 {
288    int i;
289    char *p;
290    char *psubst = subst;
291    int no;
292    int len;
293
294    /* il faut recopier fname dans dest
295     *  on recopie le debut fname -> pmatch->start[0]
296     */
297
298    for (i = 0; i < pmatch[0].rm_so ; i++) {
299       result[i] = fname[i];
300    }
301
302    /* on recopie le motif de remplacement (avec tous les $x) */
303
304    for (p = psubst++; *p ; p = psubst++) {
305       /* match $1 \1 back references */
306       if ((*p == '$' || *p == '\\') && ('0' <= *psubst && *psubst <= '9')) {
307          no = *psubst++ - '0';
308
309          /* have a back reference ? */
310          if (pmatch[no].rm_so >= 0 && pmatch[no].rm_eo >= 0) {
311             len = pmatch[no].rm_eo - pmatch[no].rm_so;
312             bstrncpy(result + i, fname + pmatch[no].rm_so, len + 1);
313             i += len ;
314          }
315
316       } else {
317          result[i++] = *p;
318       }
319    }
320
321    /* we copy what is out of the match */
322    strcpy(result + i, fname + pmatch[0].rm_eo);
323
324    return result;
325 }
326
327 /* escape sep char and \
328  * dest must be long enough (src*2+1)
329  * return end of the string */
330 char *bregexp_escape_string(char *dest, const char *src, const char sep)
331 {
332    char *ret = dest;
333    while (*src)
334    {
335       if (*src == sep) {
336          *dest++ = '\\';
337       } else if (*src == '\\') {
338          *dest++ = '\\';
339       }
340       *dest++ = *src++;
341    }
342    *dest = '\0';
343
344    return ret;
345 }
346
347 static const char regexp_sep = '!';
348 static const char *str_strip_prefix = "!%s!!i";
349 static const char *str_add_prefix   = "!^!%s!";
350 static const char *str_add_suffix   = "!([^/])$!$1%s!";
351
352 int bregexp_get_build_where_size(char *strip_prefix,
353                                  char *add_prefix,
354                                  char *add_suffix)
355 {
356    int str_size = ((strip_prefix?strlen(strip_prefix)+strlen(str_strip_prefix):0) +
357                    (add_prefix?strlen(add_prefix)+strlen(str_add_prefix)      :0) +
358                    (add_suffix?strlen(add_suffix)+strlen(str_add_suffix)      :0) )
359          /* escape + 3*, + \0 */
360             * 2    + 3   + 1;
361
362    Dmsg1(200, "bregexp_get_build_where_size = %i\n", str_size);
363    return str_size;
364 }
365
366 /* build a regexp string with user arguments
367  * Usage :
368  *
369  * int len = bregexp_get_build_where_size(a,b,c) ;
370  * char *dest = (char *) bmalloc (len * sizeof(char));
371  * bregexp_build_where(dest, len, a, b, c);
372  * bfree(dest);
373  *
374  */
375 char *bregexp_build_where(char *dest, int str_size,
376                           char *strip_prefix,
377                           char *add_prefix,
378                           char *add_suffix)
379 {
380    int len=0;
381
382    POOLMEM *str_tmp = get_memory(str_size);
383
384    *str_tmp = *dest = '\0';
385
386    if (strip_prefix) {
387       len += bsnprintf(dest, str_size - len, str_strip_prefix,
388                        bregexp_escape_string(str_tmp, strip_prefix, regexp_sep));
389    }
390
391    if (add_suffix) {
392       if (len) dest[len++] = ',';
393
394       len += bsnprintf(dest + len,  str_size - len, str_add_suffix,
395                        bregexp_escape_string(str_tmp, add_suffix, regexp_sep));
396    }
397
398    if (add_prefix) {
399       if (len) dest[len++] = ',';
400
401       len += bsnprintf(dest + len, str_size - len, str_add_prefix,
402                        bregexp_escape_string(str_tmp, add_prefix, regexp_sep));
403    }
404
405    free_pool_memory(str_tmp);
406
407    return dest;
408 }
409
410
411 void BREGEXP::debug()
412 {
413    printf("expr=[%s]\n", expr);
414    printf("subst=[%s]\n", subst);
415    printf("result=%s\n", NPRT(result));
416 }