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